GNU bug report logs - #52974
[PATCH 0/5] Formatting package definitions with 'guix style'

Previous Next

Package: guix-patches;

Reported by: Ludovic Courtès <ludo <at> gnu.org>

Date: Mon, 3 Jan 2022 10:54:02 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 52974 in the body.
You can then email your comments to 52974 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#52974; Package guix-patches. (Mon, 03 Jan 2022 10:54:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ludovic Courtès <ludo <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Mon, 03 Jan 2022 10:54:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 0/5] Formatting package definitions with 'guix style'
Date: Mon,  3 Jan 2022 11:53:35 +0100
Hello Guix!

This patch set extends ‘guix style’ so that it can format
package definitions following our conventions.  It adds a
new ‘-S’ option to ‘guix style’, which allows you to select
a “styling rule”.

To obtain the same behavior as before, you now need to run
‘guix style -S inputs’ (this is an incompatibility); ‘guix
style’ alone is equivalent to ‘guix style -S format’, which
formats the given package definition(s).

The code of the pretty printer is not pretty, but it does a
rather good job, notably because it recognizes our special
forms and rules, unlike ‘pretty-print’.  I’m sure we could
spend months tweaking it, but I think it’s reached the point
where we can recommend it.

This nicely replaces ‘etc/indent-code.el’ and will hopefully
make the lives of contributors easier.

Thoughts?

Eventually we should change importers to use this instead of
(ice-9 pretty-print).  I also envision a rule to rewrite Rust
packages to look like regular packages, as discussed earlier
with Efraim, which will benefit from this work.

Ludo’.

Ludovic Courtès (5):
  style: Improve pretty printer and add tests.
  style: Allow special forms to be scoped.
  style: Add support for "newline forms".
  style: Add '--styling' option.
  style: '-S format' canonicalizes comments.

 doc/contributing.texi  |  18 +-
 doc/guix.texi          |  60 +++++-
 etc/indent-code.el     | 120 ------------
 guix/scripts/style.scm | 415 +++++++++++++++++++++++++++++++++++------
 tests/style.scm        | 162 +++++++++++++++-
 5 files changed, 571 insertions(+), 204 deletions(-)
 delete mode 100755 etc/indent-code.el


base-commit: 637dec9d45db4df2a3e6aa565fa2c5cf6bb77768
-- 
2.33.0





Information forwarded to guix-patches <at> gnu.org:
bug#52974; Package guix-patches. (Mon, 03 Jan 2022 11:25:02 GMT) Full text and rfc822 format available.

Message #8 received at 52974 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 52974 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 1/5] style: Improve pretty printer and add tests.
Date: Mon,  3 Jan 2022 12:24:35 +0100
* guix/scripts/style.scm (vhashq): New macro.
(%special-forms): New variable.
(special-form?): New procedure.
(pretty-print-with-comments): Add many clauses and tweak existing
rules.
* tests/style.scm (test-pretty-print): New macro.
<top level>: Add 'test-pretty-print' tests.
---
 guix/scripts/style.scm | 270 +++++++++++++++++++++++++++++++++--------
 tests/style.scm        |  95 +++++++++++++++
 2 files changed, 316 insertions(+), 49 deletions(-)

diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm
index 3b246e9c66..a5204d02ef 100644
--- a/guix/scripts/style.scm
+++ b/guix/scripts/style.scm
@@ -40,11 +40,15 @@ (define-module (guix scripts style)
   #:use-module (ice-9 control)
   #:use-module (ice-9 match)
   #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 vlist)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-37)
-  #:export (guix-style))
+  #:export (pretty-print-with-comments
+            read-with-comments
+
+            guix-style))
 
 
 ;;;
@@ -109,15 +113,136 @@ (define (read-with-comments port)
 ;;; Comment-preserving pretty-printer.
 ;;;
 
+(define-syntax vhashq
+  (syntax-rules ()
+    ((_) vlist-null)
+    ((_ (key value) rest ...)
+     (vhash-consq key value (vhashq rest ...)))))
+
+(define %special-forms
+  ;; Forms that are indented specially.  The number is meant to be understood
+  ;; like Emacs' 'scheme-indent-function' symbol property.
+  (vhashq
+   ('begin 1)
+   ('lambda 2)
+   ('lambda* 2)
+   ('match-lambda 1)
+   ('match-lambda* 2)
+   ('define 2)
+   ('define* 2)
+   ('define-public 2)
+   ('define*-public 2)
+   ('define-syntax 2)
+   ('define-syntax-rule 2)
+   ('define-module 2)
+   ('define-gexp-compiler 2)
+   ('let 2)
+   ('let* 2)
+   ('letrec 2)
+   ('letrec* 2)
+   ('match 2)
+   ('when 2)
+   ('unless 2)
+   ('package 1)
+   ('origin 1)
+   ('operating-system 1)
+   ('modify-inputs 2)
+   ('modify-phases 2)
+   ('add-after 3)
+   ('add-before 3)
+   ;; ('replace 2)
+   ('substitute* 2)
+   ('substitute-keyword-arguments 2)
+   ('call-with-input-file 2)
+   ('call-with-output-file 2)
+   ('with-output-to-file 2)
+   ('with-input-from-file 2)))
+
+(define (special-form? symbol)
+  (vhash-assq symbol %special-forms))
+
+(define (escaped-string str)
+  "Return STR with backslashes and double quotes escaped.  Everything else, in
+particular newlines, is left as is."
+  (list->string
+   `(#\"
+     ,@(string-fold-right (lambda (chr lst)
+                            (match chr
+                              (#\" (cons* #\\ #\" lst))
+                              (#\\ (cons* #\\ #\\ lst))
+                              (_   (cons chr lst))))
+                          '()
+                          str)
+     #\")))
+
+(define (string-width str)
+  "Return the \"width\" of STR--i.e., the width of the longest line of STR."
+  (apply max (map string-length (string-split str #\newline))))
+
 (define* (pretty-print-with-comments port obj
                                      #:key
                                      (indent 0)
                                      (max-width 78)
                                      (long-list 5))
+  "Pretty-print OBJ to PORT, attempting to at most MAX-WIDTH character columns
+and assuming the current column is INDENT.  Comments present in OBJ are
+included in the output.
+
+Lists longer than LONG-LIST are written as one element per line."
   (let loop ((indent indent)
              (column indent)
              (delimited? #t)                  ;true if comes after a delimiter
              (obj obj))
+    (define (print-sequence indent column lst delimited?)
+      (define long?
+        (> (length lst) long-list))
+
+      (let print ((lst lst)
+                  (first? #t)
+                  (delimited? delimited?)
+                  (column column))
+        (match lst
+          (()
+           column)
+          ((item . tail)
+           (define newline?
+             ;; Insert a newline if ITEM is itself a list, or if TAIL is long,
+             ;; but only if ITEM is not the first item.  Also insert a newline
+             ;; before a keyword.
+             (and (or (pair? item) long?
+                      (and (keyword? item)
+                           (not (eq? item #:allow-other-keys))))
+                  (not first?) (not delimited?)
+                  (not (comment? item))))
+
+           (when newline?
+             (newline port)
+             (display (make-string indent #\space) port))
+           (let ((column (if newline? indent column)))
+             (print tail #f
+                    (comment? item)
+                    (loop indent column
+                          (or newline? delimited?)
+                          item)))))))
+
+    (define (sequence-would-protrude? indent lst)
+      ;; Return true if elements of LST written at INDENT would protrude
+      ;; beyond MAX-WIDTH.  This is implemented as a cheap test with false
+      ;; negatives to avoid actually rendering all of LST.
+      (find (match-lambda
+              ((? string? str)
+               (>= (+ (string-width str) 2 indent) max-width))
+              ((? symbol? symbol)
+               (>= (+ (string-width (symbol->string symbol)) indent)
+                   max-width))
+              ((? boolean?)
+               (>= (+ 2 indent) max-width))
+              (()
+               (>= (+ 2 indent) max-width))
+              (_                                  ;don't know
+               #f))
+            lst))
+
     (match obj
       ((? comment? comment)
        (if (comment-margin? comment)
@@ -145,57 +270,104 @@ (define* (pretty-print-with-comments port obj
        (unless delimited? (display " " port))
        (display "," port)
        (loop indent (+ column (if delimited? 1 2)) #t lst))
-      (('modify-inputs inputs clauses ...)
-       ;; Special-case 'modify-inputs' to have one clause per line and custom
-       ;; indentation.
-       (let ((head "(modify-inputs "))
+      (('unquote-splicing lst)
+       (unless delimited? (display " " port))
+       (display ",@" port)
+       (loop indent (+ column (if delimited? 2 3)) #t lst))
+      (('gexp lst)
+       (unless delimited? (display " " port))
+       (display "#~" port)
+       (loop indent (+ column (if delimited? 2 3)) #t lst))
+      (('ungexp obj)
+       (unless delimited? (display " " port))
+       (display "#$" port)
+       (loop indent (+ column (if delimited? 2 3)) #t obj))
+      (('ungexp-native obj)
+       (unless delimited? (display " " port))
+       (display "#+" port)
+       (loop indent (+ column (if delimited? 2 3)) #t obj))
+      (('ungexp-splicing lst)
+       (unless delimited? (display " " port))
+       (display "#$@" port)
+       (loop indent (+ column (if delimited? 3 4)) #t lst))
+      (('ungexp-native-splicing lst)
+       (unless delimited? (display " " port))
+       (display "#+@" port)
+       (loop indent (+ column (if delimited? 3 4)) #t lst))
+      (((? special-form? head) arguments ...)
+       ;; Special-case 'let', 'lambda', 'modify-inputs', etc. so the second
+       ;; and following arguments are less indented.
+       (let* ((lead  (- (cdr (vhash-assq head %special-forms)) 1))
+              (head  (symbol->string head))
+              (total (length arguments)))
+         (unless delimited? (display " " port))
+         (display "(" port)
          (display head port)
-         (loop (+ indent 4)
-               (+ column (string-length head))
-               #t
-               inputs)
-         (let* ((indent (+ indent 2))
-                (column (fold (lambda (clause column)
-                                (newline port)
-                                (display (make-string indent #\space)
-                                         port)
-                                (loop indent indent #t clause))
-                              indent
-                              clauses)))
+         (unless (zero? lead)
+           (display " " port))
+
+         ;; Print the first LEAD arguments.
+         (let* ((indent (+ column 2
+                                  (if delimited? 0 1)))
+                (column (+ column 1
+                                  (if (zero? lead) 0 1)
+                                  (if delimited? 0 1)
+                                  (string-length head)))
+                (initial-indent column))
+           (define new-column
+             (let inner ((n lead)
+                         (arguments (take arguments (min lead total)))
+                         (column column))
+               (if (zero? n)
+                   (begin
+                     (newline port)
+                     (display (make-string indent #\space) port)
+                     indent)
+                   (match arguments
+                     (() column)
+                     ((head . tail)
+                      (inner (- n 1) tail
+                             (loop initial-indent
+                                   column
+                                   (= n lead)
+                                   head)))))))
+
+           ;; Print the remaining arguments.
+           (let ((column (print-sequence
+                          indent new-column
+                          (drop arguments (min lead total))
+                          #t)))
+             (display ")" port)
+             (+ column 1)))))
+      ((head tail ...)
+       (let* ((overflow? (>= column max-width))
+              (column    (if overflow?
+                             (+ indent 1)
+                             (+ column (if delimited? 1 2)))))
+         (if overflow?
+             (begin
+               (newline port)
+               (display (make-string indent #\space) port))
+             (unless delimited? (display " " port)))
+         (display "(" port)
+         (let* ((new-column (loop column column #t head))
+                (indent (if (or (>= new-column max-width)
+                                (not (symbol? head))
+                                (sequence-would-protrude?
+                                 (+ new-column 1) tail))
+                            column
+                            (+ new-column 1))))
+           (define column
+             (print-sequence indent new-column tail #f))
            (display ")" port)
            (+ column 1))))
-      ((head tail ...)
-       (unless delimited? (display " " port))
-       (display "(" port)
-       (let* ((new-column (loop indent (+ 1 column) #t head))
-              (indent (+ indent (- new-column column)))
-              (long?  (> (length tail) long-list)))
-         (define column
-           (fold2 (lambda (item column first?)
-                    (define newline?
-                      ;; Insert a newline if ITEM is itself a list, or if TAIL
-                      ;; is long, but only if ITEM is not the first item.
-                      (and (or (pair? item) long?)
-                           (not first?) (not (comment? item))))
-
-                    (when newline?
-                      (newline port)
-                      (display (make-string indent #\space) port))
-                    (let ((column (if newline? indent column)))
-                      (values (loop indent
-                                    column
-                                    (= column indent)
-                                    item)
-                              (comment? item))))
-                  (+ 1 new-column)
-                  #t                              ;first
-                  tail))
-         (display ")" port)
-         (+ column 1)))
       (_
-       (let* ((str (object->string obj))
-              (len (string-length str)))
-         (if (> (+ column 1 len) max-width)
+       (let* ((str (if (string? obj)
+                       (escaped-string obj)
+                       (object->string obj)))
+              (len (string-width str)))
+         (if (and (> (+ column 1 len) max-width)
+                  (not delimited?))
              (begin
                (newline port)
                (display (make-string indent #\space) port)
@@ -204,7 +376,7 @@ (define newline?
              (begin
                (unless delimited? (display " " port))
                (display str port)
-               (+ column (if delimited? 1 2) len))))))))
+               (+ column (if delimited? 0 1) len))))))))
 
 (define (object->string* obj indent)
   (call-with-output-string
diff --git a/tests/style.scm b/tests/style.scm
index ada9197fc1..d9e8d803f4 100644
--- a/tests/style.scm
+++ b/tests/style.scm
@@ -21,6 +21,7 @@ (define-module (tests-style)
   #:use-module (guix scripts style)
   #:use-module ((guix utils) #:select (call-with-temporary-directory))
   #:use-module ((guix build utils) #:select (substitute*))
+  #:use-module (guix gexp)                        ;for the reader extension
   #:use-module (guix diagnostics)
   #:use-module (gnu packages acl)
   #:use-module (gnu packages multiprecision)
@@ -111,6 +112,17 @@ (define* (read-package-field package field #:optional (count 1))
       (lambda (port)
         (read-lines port line count)))))
 
+(define-syntax-rule (test-pretty-print str args ...)
+  "Test equality after a round-trip where STR is passed to
+'read-with-comments' and the resulting sexp is then passed to
+'pretty-print-with-comments'."
+  (test-equal str
+    (call-with-output-string
+      (lambda (port)
+        (let ((exp (call-with-input-string str
+                     read-with-comments)))
+         (pretty-print-with-comments port exp args ...))))))
+
 
 (test-begin "style")
 
@@ -358,6 +370,89 @@ (define file
       (list (package-inputs (@ (my-packages) my-coreutils))
             (read-package-field (@ (my-packages) my-coreutils) 'inputs 4)))))
 
+(test-pretty-print "(list 1 2 3 4)")
+(test-pretty-print "(list 1
+                          2
+                          3
+                          4)"
+                   #:long-list 3
+                   #:indent 20)
+(test-pretty-print "\
+(list abc
+      def)"
+                   #:max-width 11)
+(test-pretty-print "\
+(#:foo
+ #:bar)"
+                   #:max-width 10)
+
+(test-pretty-print "\
+(#:first 1
+ #:second 2
+ #:third 3)")
+
+(test-pretty-print "\
+((x
+  1)
+ (y
+  2)
+ (z
+  3))"
+                   #:max-width 3)
+
+(test-pretty-print "\
+(let ((x 1)
+      (y 2)
+      (z 3)
+      (p 4))
+  (+ x y))"
+                   #:max-width 11)
+
+(test-pretty-print "\
+(lambda (x y)
+  ;; This is a procedure.
+  (let ((z (+ x y)))
+    (* z z)))")
+
+(test-pretty-print "\
+#~(string-append #$coreutils \"/bin/uname\")")
+
+(test-pretty-print "\
+(package
+  (inherit coreutils)
+  (version \"42\"))")
+
+(test-pretty-print "\
+(modify-phases %standard-phases
+  (add-after 'unpack 'post-unpack
+    (lambda _
+      #t))
+  (add-before 'check 'pre-check
+    (lambda* (#:key inputs #:allow-other-keys)
+      do things ...)))")
+
+(test-pretty-print "\
+(#:phases (modify-phases sdfsdf
+            (add-before 'x 'y
+              (lambda _
+                xyz))))")
+
+(test-pretty-print "\
+(description \"abcdefghijkl
+mnopqrstuvwxyz.\")"
+                   #:max-width 30)
+
+(test-pretty-print "\
+(description
+ \"abcdefghijkl
+mnopqrstuvwxyz.\")"
+                   #:max-width 12)
+
+(test-pretty-print "\
+(description
+ \"abcdefghijklmnopqrstuvwxyz\")"
+                   #:max-width 33)
+
 (test-end)
 
 ;; Local Variables:
-- 
2.33.0





Information forwarded to guix-patches <at> gnu.org:
bug#52974; Package guix-patches. (Mon, 03 Jan 2022 11:25:03 GMT) Full text and rfc822 format available.

Message #11 received at 52974 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 52974 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 3/5] style: Add support for "newline forms".
Date: Mon,  3 Jan 2022 12:24:37 +0100
This allows us to express cases where a newline should be inserted
immediately after the head symbol of a list.

* guix/scripts/style.scm (%newline-forms): New variable.
(newline-form?): New procedure.
(pretty-print-with-comments): Handle "newline forms".
* tests/style.scm: Add test.
---
 guix/scripts/style.scm | 40 +++++++++++++++++++++++++++++++++++-----
 tests/style.scm        | 13 +++++++++++++
 2 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm
index 625e942613..00680daa23 100644
--- a/guix/scripts/style.scm
+++ b/guix/scripts/style.scm
@@ -163,6 +163,18 @@ (define %special-forms
    ('with-output-to-file 2)
    ('with-input-from-file 2)))
 
+(define %newline-forms
+  ;; List heads that must be followed by a newline.  The second argument is
+  ;; the context in which they must appear.  This is similar to a special form
+  ;; of 1, except that indent is 1 instead of 2 columns.
+  (vhashq
+   ('arguments '(package))
+   ('sha256 '(origin source package))
+   ('base32 '(sha256 origin))
+   ('search-paths '(package))
+   ('native-search-paths '(package))
+   ('search-path-specification '())))
+
 (define (prefix? candidate lst)
   "Return true if CANDIDATE is a prefix of LST."
   (let loop ((candidate candidate)
@@ -188,6 +200,14 @@ (define (special-form-lead symbol context)
              (and (prefix? prefix context) (- level 1))))
           alist))))
 
+(define (newline-form? symbol context)
+  "Return true if parenthesized expressions starting with SYMBOL must be
+followed by a newline."
+  (match (vhash-assq symbol %newline-forms)
+    (#f #f)
+    ((_ . prefix)
+     (prefix? prefix context))))
+
 (define (escaped-string str)
   "Return STR with backslashes and double quotes escaped.  Everything else, in
 particular newlines, is left as is."
@@ -377,6 +397,7 @@ (define new-column
               (column    (if overflow?
                              (+ indent 1)
                              (+ column (if delimited? 1 2))))
+              (newline?  (newline-form? head context))
               (context   (cons head context)))
          (if overflow?
              (begin
@@ -384,17 +405,26 @@ (define new-column
                (display (make-string indent #\space) port))
              (unless delimited? (display " " port)))
          (display "(" port)
+
          (let* ((new-column (loop column column #t context head))
                 (indent (if (or (>= new-column max-width)
                                 (not (symbol? head))
                                 (sequence-would-protrude?
-                                 (+ new-column 1) tail))
+                                 (+ new-column 1) tail)
+                                newline?)
                             column
                             (+ new-column 1))))
-           (define column
-             (print-sequence context indent new-column tail #f))
-           (display ")" port)
-           (+ column 1))))
+           (when newline?
+             ;; Insert a newline right after HEAD.
+             (newline port)
+             (display (make-string indent #\space) port))
+
+           (let ((column
+                  (print-sequence context indent
+                                  (if newline? indent new-column)
+                                  tail newline?)))
+             (display ")" port)
+             (+ column 1)))))
       (_
        (let* ((str (if (string? obj)
                        (escaped-string obj)
diff --git a/tests/style.scm b/tests/style.scm
index 6c449cb72e..8022688419 100644
--- a/tests/style.scm
+++ b/tests/style.scm
@@ -465,6 +465,19 @@ (define file
   ;; Regular indentation for 'replace' here.
   (replace \"gmp\" gmp))")
 
+(test-pretty-print "\
+(package
+  ;; Here 'sha256', 'base32', and 'arguments' must be
+  ;; immediately followed by a newline.
+  (source (origin
+            (method url-fetch)
+            (sha256
+             (base32
+              \"not a real base32 string\"))))
+  (arguments
+   '(#:phases %standard-phases
+     #:tests? #f)))")
+
 (test-end)
 
 ;; Local Variables:
-- 
2.33.0





Information forwarded to guix-patches <at> gnu.org:
bug#52974; Package guix-patches. (Mon, 03 Jan 2022 11:25:03 GMT) Full text and rfc822 format available.

Message #14 received at 52974 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 52974 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 2/5] style: Allow special forms to be scoped.
Date: Mon,  3 Jan 2022 12:24:36 +0100
* guix/scripts/style.scm (vhashq): Add clause for 'lst, and change
default clause.
(%special-forms): Add context for 'add-after and 'add-before.  Add
'replace.
(prefix?, special-form-lead): New procedures.
(special-form?): Remove.
(pretty-print-with-comments): Add 'context' to the threaded state.
Adjust 'print-sequence' and adjust 'loop' calls accordingly.
* tests/style.scm: Add tests for 'replace.
---
 guix/scripts/style.scm | 88 +++++++++++++++++++++++++++++-------------
 tests/style.scm        | 12 ++++++
 2 files changed, 73 insertions(+), 27 deletions(-)

diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm
index a5204d02ef..625e942613 100644
--- a/guix/scripts/style.scm
+++ b/guix/scripts/style.scm
@@ -114,14 +114,19 @@ (define (read-with-comments port)
 ;;;
 
 (define-syntax vhashq
-  (syntax-rules ()
+  (syntax-rules (quote)
     ((_) vlist-null)
+    ((_ (key (quote (lst ...))) rest ...)
+     (vhash-consq key '(lst ...) (vhashq rest ...)))
     ((_ (key value) rest ...)
-     (vhash-consq key value (vhashq rest ...)))))
+     (vhash-consq key '((() . value)) (vhashq rest ...)))))
 
 (define %special-forms
   ;; Forms that are indented specially.  The number is meant to be understood
-  ;; like Emacs' 'scheme-indent-function' symbol property.
+  ;; like Emacs' 'scheme-indent-function' symbol property.  When given an
+  ;; alist instead of a number, the alist gives "context" in which the symbol
+  ;; is a special form; for instance, context (modify-phases) means that the
+  ;; symbol must appear within a (modify-phases ...) expression.
   (vhashq
    ('begin 1)
    ('lambda 2)
@@ -148,9 +153,9 @@ (define %special-forms
    ('operating-system 1)
    ('modify-inputs 2)
    ('modify-phases 2)
-   ('add-after 3)
-   ('add-before 3)
-   ;; ('replace 2)
+   ('add-after '(((modify-phases) . 3)))
+   ('add-before '(((modify-phases) . 3)))
+   ('replace '(((modify-phases) . 2)))         ;different from 'modify-inputs'
    ('substitute* 2)
    ('substitute-keyword-arguments 2)
    ('call-with-input-file 2)
@@ -158,8 +163,30 @@ (define %special-forms
    ('with-output-to-file 2)
    ('with-input-from-file 2)))
 
-(define (special-form? symbol)
-  (vhash-assq symbol %special-forms))
+(define (prefix? candidate lst)
+  "Return true if CANDIDATE is a prefix of LST."
+  (let loop ((candidate candidate)
+             (lst lst))
+    (match candidate
+      (() #t)
+      ((head1 . rest1)
+       (match lst
+         (() #f)
+         ((head2 . rest2)
+          (and (equal? head1 head2)
+               (loop rest1 rest2))))))))
+
+(define (special-form-lead symbol context)
+  "If SYMBOL is a special form in the given CONTEXT, return its number of
+arguments; otherwise return #f.  CONTEXT is a stack of symbols lexically
+surrounding SYMBOL."
+  (match (vhash-assq symbol %special-forms)
+    (#f #f)
+    ((_ . alist)
+     (any (match-lambda
+            ((prefix . level)
+             (and (prefix? prefix context) (- level 1))))
+          alist))))
 
 (define (escaped-string str)
   "Return STR with backslashes and double quotes escaped.  Everything else, in
@@ -192,8 +219,9 @@ (define* (pretty-print-with-comments port obj
   (let loop ((indent indent)
              (column indent)
              (delimited? #t)                  ;true if comes after a delimiter
+             (context '())                    ;list of "parent" symbols
              (obj obj))
-    (define (print-sequence indent column lst delimited?)
+    (define (print-sequence context indent column lst delimited?)
       (define long?
         (> (length lst) long-list))
 
@@ -223,6 +251,7 @@ (define newline?
                     (comment? item)
                     (loop indent column
                           (or newline? delimited?)
+                          context
                           item)))))))
 
     (define (sequence-would-protrude? indent lst)
@@ -243,6 +272,9 @@ (define (sequence-would-protrude? indent lst)
                #f))
             lst))
 
+    (define (special-form? head)
+      (special-form-lead head context))
+
     (match obj
       ((? comment? comment)
        (if (comment-margin? comment)
@@ -261,45 +293,46 @@ (define (sequence-would-protrude? indent lst)
       (('quote lst)
        (unless delimited? (display " " port))
        (display "'" port)
-       (loop indent (+ column (if delimited? 1 2)) #t lst))
+       (loop indent (+ column (if delimited? 1 2)) #t context lst))
       (('quasiquote lst)
        (unless delimited? (display " " port))
        (display "`" port)
-       (loop indent (+ column (if delimited? 1 2)) #t lst))
+       (loop indent (+ column (if delimited? 1 2)) #t context lst))
       (('unquote lst)
        (unless delimited? (display " " port))
        (display "," port)
-       (loop indent (+ column (if delimited? 1 2)) #t lst))
+       (loop indent (+ column (if delimited? 1 2)) #t context lst))
       (('unquote-splicing lst)
        (unless delimited? (display " " port))
        (display ",@" port)
-       (loop indent (+ column (if delimited? 2 3)) #t lst))
+       (loop indent (+ column (if delimited? 2 3)) #t context lst))
       (('gexp lst)
        (unless delimited? (display " " port))
        (display "#~" port)
-       (loop indent (+ column (if delimited? 2 3)) #t lst))
+       (loop indent (+ column (if delimited? 2 3)) #t context lst))
       (('ungexp obj)
        (unless delimited? (display " " port))
        (display "#$" port)
-       (loop indent (+ column (if delimited? 2 3)) #t obj))
+       (loop indent (+ column (if delimited? 2 3)) #t context obj))
       (('ungexp-native obj)
        (unless delimited? (display " " port))
        (display "#+" port)
-       (loop indent (+ column (if delimited? 2 3)) #t obj))
+       (loop indent (+ column (if delimited? 2 3)) #t context obj))
       (('ungexp-splicing lst)
        (unless delimited? (display " " port))
        (display "#$@" port)
-       (loop indent (+ column (if delimited? 3 4)) #t lst))
+       (loop indent (+ column (if delimited? 3 4)) #t context lst))
       (('ungexp-native-splicing lst)
        (unless delimited? (display " " port))
        (display "#+@" port)
-       (loop indent (+ column (if delimited? 3 4)) #t lst))
+       (loop indent (+ column (if delimited? 3 4)) #t context lst))
       (((? special-form? head) arguments ...)
        ;; Special-case 'let', 'lambda', 'modify-inputs', etc. so the second
        ;; and following arguments are less indented.
-       (let* ((lead  (- (cdr (vhash-assq head %special-forms)) 1))
-              (head  (symbol->string head))
-              (total (length arguments)))
+       (let* ((lead    (special-form-lead head context))
+              (context (cons head context))
+              (head    (symbol->string head))
+              (total   (length arguments)))
          (unless delimited? (display " " port))
          (display "(" port)
          (display head port)
@@ -327,14 +360,14 @@ (define new-column
                      (() column)
                      ((head . tail)
                       (inner (- n 1) tail
-                             (loop initial-indent
-                                   column
+                             (loop initial-indent column
                                    (= n lead)
+                                   context
                                    head)))))))
 
            ;; Print the remaining arguments.
            (let ((column (print-sequence
-                          indent new-column
+                          context indent new-column
                           (drop arguments (min lead total))
                           #t)))
              (display ")" port)
@@ -343,14 +376,15 @@ (define new-column
        (let* ((overflow? (>= column max-width))
               (column    (if overflow?
                              (+ indent 1)
-                             (+ column (if delimited? 1 2)))))
+                             (+ column (if delimited? 1 2))))
+              (context   (cons head context)))
          (if overflow?
              (begin
                (newline port)
                (display (make-string indent #\space) port))
              (unless delimited? (display " " port)))
          (display "(" port)
-         (let* ((new-column (loop column column #t head))
+         (let* ((new-column (loop column column #t context head))
                 (indent (if (or (>= new-column max-width)
                                 (not (symbol? head))
                                 (sequence-would-protrude?
@@ -358,7 +392,7 @@ (define new-column
                             column
                             (+ new-column 1))))
            (define column
-             (print-sequence indent new-column tail #f))
+             (print-sequence context indent new-column tail #f))
            (display ")" port)
            (+ column 1))))
       (_
diff --git a/tests/style.scm b/tests/style.scm
index d9e8d803f4..6c449cb72e 100644
--- a/tests/style.scm
+++ b/tests/style.scm
@@ -453,6 +453,18 @@ (define file
  \"abcdefghijklmnopqrstuvwxyz\")"
                    #:max-width 33)
 
+(test-pretty-print "\
+(modify-phases %standard-phases
+  (replace 'build
+    ;; Nicely indented in 'modify-phases' context.
+    (lambda _
+      #t)))")
+
+(test-pretty-print "\
+(modify-inputs inputs
+  ;; Regular indentation for 'replace' here.
+  (replace \"gmp\" gmp))")
+
 (test-end)
 
 ;; Local Variables:
-- 
2.33.0





Information forwarded to guix-patches <at> gnu.org:
bug#52974; Package guix-patches. (Mon, 03 Jan 2022 11:25:04 GMT) Full text and rfc822 format available.

Message #17 received at 52974 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 52974 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 5/5] style: '-S format' canonicalizes comments.
Date: Mon,  3 Jan 2022 12:24:39 +0100
* guix/scripts/style.scm (canonicalize-comment): New procedure.
(pretty-print-with-comments): Add #:format-comment. and honor it.
(object->string*): Add 'args' and honor them.
(format-package-definition): Pass #:format-comment to
'object->string*'.
* tests/style.scm ("pretty-print-with-comments, canonicalize-comment"):
New test.
---
 guix/scripts/style.scm | 36 +++++++++++++++++++++++++++++-------
 tests/style.scm        | 25 +++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm
index 47549c7e4a..3c83265f8d 100644
--- a/guix/scripts/style.scm
+++ b/guix/scripts/style.scm
@@ -47,6 +47,7 @@ (define-module (guix scripts style)
   #:use-module (srfi srfi-37)
   #:export (pretty-print-with-comments
             read-with-comments
+            canonicalize-comment
 
             guix-style))
 
@@ -226,8 +227,23 @@ (define (string-width str)
   "Return the \"width\" of STR--i.e., the width of the longest line of STR."
   (apply max (map string-length (string-split str #\newline))))
 
+(define (canonicalize-comment c)
+  "Canonicalize comment C, ensuring it has the \"right\" number of leading
+semicolons."
+  (let ((line (string-trim-both
+               (string-trim (comment->string c) (char-set #\;)))))
+    (comment (string-append
+              (if (comment-margin? c)
+                  ";"
+                  (if (string-null? line)
+                      ";;"                        ;no trailing space
+                      ";; "))
+              line "\n")
+             (comment-margin? c))))
+
 (define* (pretty-print-with-comments port obj
                                      #:key
+                                     (format-comment identity)
                                      (indent 0)
                                      (max-width 78)
                                      (long-list 5))
@@ -235,7 +251,9 @@ (define* (pretty-print-with-comments port obj
 and assuming the current column is INDENT.  Comments present in OBJ are
 included in the output.
 
-Lists longer than LONG-LIST are written as one element per line."
+Lists longer than LONG-LIST are written as one element per line.  Comments are
+passed through FORMAT-COMMENT before being emitted; a useful value for
+FORMAT-COMMENT is 'canonicalize-comment'."
   (let loop ((indent indent)
              (column indent)
              (delimited? #t)                  ;true if comes after a delimiter
@@ -300,14 +318,16 @@ (define (special-form? head)
        (if (comment-margin? comment)
            (begin
              (display " " port)
-             (display (comment->string comment) port))
+             (display (comment->string (format-comment comment))
+                      port))
            (begin
              ;; When already at the beginning of a line, for example because
              ;; COMMENT follows a margin comment, no need to emit a newline.
              (unless (= column indent)
                (newline port)
                (display (make-string indent #\space) port))
-             (display (comment->string comment) port)))
+             (display (comment->string (format-comment comment))
+                      port)))
        (display (make-string indent #\space) port)
        indent)
       (('quote lst)
@@ -442,11 +462,12 @@ (define new-column
                (display str port)
                (+ column (if delimited? 0 1) len))))))))
 
-(define (object->string* obj indent)
+(define (object->string* obj indent . args)
   (call-with-output-string
     (lambda (port)
-      (pretty-print-with-comments port obj
-                                  #:indent indent))))
+      (apply pretty-print-with-comments port obj
+             #:indent indent
+             args))))
 
 
 ;;;
@@ -706,7 +727,8 @@ (define* (format-package-definition package
                   read-with-comments)))
        (object->string* exp
                         (location-column
-                         (package-definition-location package)))))))
+                         (package-definition-location package))
+                        #:format-comment canonicalize-comment)))))
 
 (define (package-location<? p1 p2)
   "Return true if P1's location is \"before\" P2's."
diff --git a/tests/style.scm b/tests/style.scm
index 7dae543860..8c6d37a661 100644
--- a/tests/style.scm
+++ b/tests/style.scm
@@ -485,6 +485,31 @@ (define file
    '(#:phases %standard-phases
      #:tests? #f)))")
 
+(test-equal "pretty-print-with-comments, canonicalize-comment"
+  "\
+(list abc
+      ;; Not a margin comment.
+      ;; Ditto.
+      ;;
+      ;; There's a blank line above.
+      def ;margin comment
+      ghi)"
+  (let ((sexp (call-with-input-string
+                  "\
+(list abc
+  ;Not a margin comment.
+  ;;;  Ditto.
+  ;;;;;
+  ; There's a blank line above.
+  def  ;; margin comment
+  ghi)"
+                read-with-comments)))
+    (call-with-output-string
+      (lambda (port)
+        (pretty-print-with-comments port sexp
+                                    #:format-comment
+                                    canonicalize-comment)))))
+
 (test-end)
 
 ;; Local Variables:
-- 
2.33.0





Information forwarded to guix-patches <at> gnu.org:
bug#52974; Package guix-patches. (Mon, 03 Jan 2022 11:25:05 GMT) Full text and rfc822 format available.

Message #20 received at 52974 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 52974 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 4/5] style: Add '--styling' option.
Date: Mon,  3 Jan 2022 12:24:38 +0100
* guix/scripts/style.scm (format-package-definition): New procedure.
(%options, show-help): Add "--styling".
(%default-options): Add 'styling-procedure'.
(guix-style): Honor it.
* tests/style.scm (with-test-package)
("input labels, 'safe' policy")
("input labels, 'safe' policy, nothing changed")
("input labels, margin comment")
("input labels, margin comment on long list")
("input labels, line comment")
("input labels, modify-inputs and margin comment"): Pass "-S inputs".
* etc/indent-code.el: Remove.
* doc/contributing.texi (Formatting Code): Mention "guix style" instead
of "etc/indent-code.el".
(Submitting Patches): Add item for "guix style".
* doc/guix.texi (Invoking guix style): Document "-S" and update.
---
 doc/contributing.texi  |  18 +++----
 doc/guix.texi          |  60 +++++++++++++++++----
 etc/indent-code.el     | 120 -----------------------------------------
 guix/scripts/style.scm |  43 +++++++++++++--
 tests/style.scm        |  17 ++++--
 5 files changed, 111 insertions(+), 147 deletions(-)
 delete mode 100755 etc/indent-code.el

diff --git a/doc/contributing.texi b/doc/contributing.texi
index 72f5ce1e0e..9f97788c0b 100644
--- a/doc/contributing.texi
+++ b/doc/contributing.texi
@@ -959,17 +959,11 @@ If you do not use Emacs, please make sure to let your editor knows these
 rules.  To automatically indent a package definition, you can also run:
 
 @example
-./etc/indent-code.el gnu/packages/@var{file}.scm @var{package}
+./pre-inst-env guix style @var{package}
 @end example
 
 @noindent
-This automatically indents the definition of @var{package} in
-@file{gnu/packages/@var{file}.scm} by running Emacs in batch mode.  To
-indent a whole file, omit the second argument:
-
-@example
-./etc/indent-code.el gnu/services/@var{file}.scm
-@end example
+@xref{Invoking guix style}, for more information.
 
 @cindex Vim, Scheme code editing
 If you are editing code with Vim, we recommend that you run @code{:set
@@ -1038,6 +1032,10 @@ Run @code{guix lint @var{package}}, where @var{package} is the
 name of the new or modified package, and fix any errors it reports
 (@pxref{Invoking guix lint}).
 
+@item
+Run @code{guix style @var{package}} to format the new package definition
+according to the project's conventions (@pxref{Invoking guix style}).
+
 @item
 Make sure the package builds on your platform, using @code{guix build
 @var{package}}.
@@ -1175,8 +1173,8 @@ Examples of unrelated changes include the addition of several packages,
 or a package update along with fixes to that package.
 
 @item
-Please follow our code formatting rules, possibly running the
-@command{etc/indent-code.el} script to do that automatically for you
+Please follow our code formatting rules, possibly running
+@command{guix style} script to do that automatically for you
 (@pxref{Formatting Code}).
 
 @item
diff --git a/doc/guix.texi b/doc/guix.texi
index b72a3d1263..01d4c14e9c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -12771,8 +12771,16 @@ otherwise.
 
 The @command{guix style} command helps packagers style their package
 definitions according to the latest fashionable trends.  The command
-currently focuses on one aspect: the style of package inputs.  It may
-eventually be extended to handle other stylistic matters.
+currently provides the providing styling rules:
+
+@itemize
+@item
+formatting package definitions according to the project's conventions
+(@pxref{Formatting Code});
+
+@item
+rewriting package inputs to the ``new style'', as explained below.
+@end itemize
 
 The way package inputs are written is going through a transition
 (@pxref{package Reference}, for more on package inputs).  Until version
@@ -12803,7 +12811,7 @@ Package Variants}, for more info on @code{modify-inputs}).
 
 In the vast majority of cases, this is a purely mechanical change on the
 surface syntax that does not even incur a package rebuild.  Running
-@command{guix style} can do that for you, whether you're working on
+@command{guix style -S inputs} can do that for you, whether you're working on
 packages in Guix proper or in an external channel.
 
 The general syntax is:
@@ -12813,15 +12821,48 @@ guix style [@var{options}] @var{package}@dots{}
 @end example
 
 This causes @command{guix style} to analyze and rewrite the definition
-of @var{package}@dots{}.  It does so in a conservative way: preserving
-comments and bailing out if it cannot make sense of the code that
-appears in an inputs field.  The available options are listed below.
+of @var{package}@dots{} or, when @var{package} is omitted, of @emph{all}
+the packages.  The @option{--styling} or @option{-S} option allows you
+to select the style rule, the default rule being @code{format}---see
+below.
+
+The available options are listed below.
 
 @table @code
 @item --dry-run
 @itemx -n
 Show source file locations that would be edited but do not modify them.
 
+@item --styling=@var{rule}
+@itemx -S @var{rule}
+Apply @var{rule}, one of the following styling rules:
+
+@table @code
+@item format
+Format the given package definition(s)---this is the default styling
+rule.  For example, a packager running Guix on a checkout
+(@pxref{Running Guix Before It Is Installed}) might want to reformat the
+definition of the Coreutils package like so:
+
+@example
+./pre-inst-env guix style coreutils
+@end example
+
+@item inputs
+Rewrite package inputs to the ``new style'', as described above.  This
+is how you would rewrite inputs of package @code{whatnot} in your own
+channel:
+
+@example
+guix style -L ~/my/channel -S inputs whatnot
+@end example
+
+Rewriting is done in a conservative way: preserving comments and bailing
+out if it cannot make sense of the code that appears in an inputs field.
+The @option{--input-simplification} option described below provides
+fine-grain control over when inputs should be simplified.
+@end table
+
 @item --load-path=@var{directory}
 @itemx -L @var{directory}
 Add @var{directory} to the front of the package module search path
@@ -12840,9 +12881,10 @@ guix style -e '(@@ (gnu packages gcc) gcc-5)'
 styles the @code{gcc-5} package definition.
 
 @item --input-simplification=@var{policy}
-Specify the package input simplification policy for cases where an input
-label does not match the corresponding package name.  @var{policy} may
-be one of the following:
+When using the @code{inputs} styling rule, with @samp{-S inputs}, this
+option specifies the package input simplification policy for cases where
+an input label does not match the corresponding package name.
+@var{policy} may be one of the following:
 
 @table @code
 @item silent
diff --git a/etc/indent-code.el b/etc/indent-code.el
deleted file mode 100755
index bdea8ee8bf..0000000000
--- a/etc/indent-code.el
+++ /dev/null
@@ -1,120 +0,0 @@
-:;exec emacs --batch --quick --load="$0" --funcall=main "$@"
-;;; indent-code.el --- Run Emacs to indent a package definition.
-
-;; Copyright © 2017 Alex Kost <alezost <at> gmail.com>
-;; Copyright © 2017 Ludovic Courtès <ludo <at> gnu.org>
-;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
-;; Copyright © 2020 Tobias Geerinckx-Rice <me <at> tobias.gr>
-
-;; This file is part of GNU Guix.
-
-;; GNU Guix is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; GNU Guix is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This scripts indents the given file or package definition in the specified
-;; file using Emacs.
-
-;;; Code:
-
-;; Load Scheme indentation rules from ".dir-locals.el".
-(with-temp-buffer
-  (scheme-mode)
-  (let ((default-directory (file-name-as-directory load-file-name))
-        (enable-local-variables :all))
-    (hack-dir-local-variables)
-    (hack-local-variables-apply)))
-
-;; Add indentation info for Scheme constructs that are not Guix-specific.
-;; This is normally provided by Geiser but this file is for people who may not
-;; be running Geiser, so we just copy it here (from 'geiser-syntax.el').
-(defmacro guix-syntax--scheme-indent (&rest pairs)
-  `(progn ,@(mapcar (lambda (p)
-                      `(put ',(car p) 'scheme-indent-function ',(cadr p)))
-                    pairs)))
-
-(guix-syntax--scheme-indent
- (and-let* 1)
- (case-lambda 0)
- (catch defun)
- (class defun)
- (dynamic-wind 0)
- (guard 1)
- (let*-values 1)
- (let-values 1)
- (let/ec 1)
- (letrec* 1)
- (match 1)
- (match-lambda 0)
- (match-lambda* 0)
- (match-let scheme-let-indent)
- (match-let* 1)
- (match-letrec 1)
- (opt-lambda 1)
- (parameterize 1)
- (parameterize* 1)
- (receive 2)
- (require-extension 0)
- (syntax-case 2)
- (test-approximate 1)
- (test-assert 1)
- (test-eq 1)
- (test-equal 1)
- (test-eqv 1)
- (test-group-with-cleanup 1)
- (test-runner-on-bad-count! 1)
- (test-runner-on-bad-end-name! 1)
- (test-runner-on-final! 1)
- (test-runner-on-group-begin! 1)
- (test-runner-on-group-end! 1)
- (test-runner-on-test-begin! 1)
- (test-runner-on-test-end! 1)
- (test-with-runner 1)
- (unless 1)
- (when 1)
- (while 1)
- (with-exception-handler 1)
- (with-syntax 1))
-
-
-(defun main ()
-  (pcase command-line-args-left
-    (`(,file-name ,package-name)
-     ;; Indent the definition of PACKAGE-NAME in FILE-NAME.
-     (find-file file-name)
-     (goto-char (point-min))
-     (if (re-search-forward (concat "^(define\\(\\|-public\\) +"
-                                    package-name)
-                            nil t)
-         (let ((indent-tabs-mode nil))
-           (beginning-of-defun)
-           (mark-sexp)
-           (untabify (point) (mark))
-           (indent-sexp)
-           (save-buffer)
-           (message "Done!"))
-       (error "Package '%s' not found in '%s'"
-              package-name file-name)))
-    (`(,file-name)
-     ;; Indent all of FILE-NAME.
-     (find-file file-name)
-     (let ((indent-tabs-mode nil))
-       (untabify (point-min) (point-max))
-       (indent-region (point-min) (point-max))
-       (save-buffer)
-       (message "Done!")))
-    (x
-     (error "Usage: indent-code.el FILE [PACKAGE]"))))
-
-;;; indent-code.el ends here
diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm
index 00680daa23..47549c7e4a 100644
--- a/guix/scripts/style.scm
+++ b/guix/scripts/style.scm
@@ -685,6 +685,29 @@ (define matches?
             (list package-inputs package-native-inputs
                   package-propagated-inputs)))
 
+
+;;;
+;;; Formatting package definitions.
+;;;
+
+(define* (format-package-definition package
+                                    #:key policy
+                                    (edit-expression edit-expression))
+  "Reformat the definition of PACKAGE."
+  (unless (package-definition-location package)
+    (leave (package-location package)
+           (G_ "no definition location for package ~a~%")
+           (package-full-name package)))
+
+  (edit-expression
+   (location->source-properties (package-definition-location package))
+   (lambda (str)
+     (let ((exp (call-with-input-string str
+                  read-with-comments)))
+       (object->string* exp
+                        (location-column
+                         (package-definition-location package)))))))
+
 (define (package-location<? p1 p2)
   "Return true if P1's location is \"before\" P2's."
   (let ((loc1 (package-location p1))
@@ -711,6 +734,15 @@ (define %options
         (option '(#\e "expression") #t #f
                 (lambda (opt name arg result)
                   (alist-cons 'expression arg result)))
+        (option '(#\S "styling") #t #f
+                (lambda (opt name arg result)
+                  (alist-cons 'styling-procedure
+                              (match arg
+                                ("inputs" simplify-package-inputs)
+                                ("format" format-package-definition)
+                                (_ (leave (G_ "~a: unknown styling~%")
+                                          arg)))
+                              result)))
         (option '("input-simplification") #t #f
                 (lambda (opt name arg result)
                   (let ((symbol (string->symbol arg)))
@@ -731,6 +763,9 @@ (define %options
 (define (show-help)
   (display (G_ "Usage: guix style [OPTION]... [PACKAGE]...
 Update package definitions to the latest style.\n"))
+  (display (G_ "
+  -S, --styling=RULE     apply RULE, a styling rule"))
+  (newline)
   (display (G_ "
   -n, --dry-run          display files that would be edited but do nothing"))
   (display (G_ "
@@ -751,7 +786,8 @@ (define (show-help)
 
 (define %default-options
   ;; Alist of default option values.
-  '((input-simplification-policy . silent)))
+  `((input-simplification-policy . silent)
+    (styling-procedure . ,format-package-definition)))
 
 
 ;;;
@@ -778,11 +814,12 @@ (define (parse-options)
          (edit     (if (assoc-ref opts 'dry-run?)
                        edit-expression/dry-run
                        edit-expression))
+         (style    (assoc-ref opts 'styling-procedure))
          (policy   (assoc-ref opts 'input-simplification-policy)))
     (with-error-handling
       (for-each (lambda (package)
-                  (simplify-package-inputs package #:policy policy
-                                           #:edit-expression edit))
+                  (style package #:policy policy
+                         #:edit-expression edit))
                 ;; Sort package by source code location so that we start editing
                 ;; files from the bottom and going upward.  That way, the
                 ;; 'location' field of <package> records is not invalidated as
diff --git a/tests/style.scm b/tests/style.scm
index 8022688419..7dae543860 100644
--- a/tests/style.scm
+++ b/tests/style.scm
@@ -78,7 +78,8 @@ (define file
         (string-append directory "/my-packages.scm"))
 
       ;; Run as a separate process to make sure FILE is reloaded.
-      (system* "guix" "style" "-L" directory "my-coreutils")
+      (system* "guix" "style" "-L" directory "-S" "inputs"
+               "my-coreutils")
       (system* "cat" file)
 
       (load file)
@@ -237,6 +238,7 @@ (define file
         (string-append directory "/my-packages.scm"))
 
       (system* "guix" "style" "-L" directory "my-coreutils"
+               "-S" "inputs"
                "--input-simplification=safe")
 
       (load file)
@@ -258,6 +260,7 @@ (define file
         (string-append directory "/my-packages.scm"))
 
       (system* "guix" "style" "-L" directory "my-coreutils"
+               "-S" "inputs"
                "--input-simplification=safe")
 
       (load file)
@@ -284,7 +287,8 @@ (define file
                         " ;another one\n")))
       (system* "cat" file)
 
-      (system* "guix" "style" "-L" directory "my-coreutils")
+      (system* "guix" "style" "-L" directory "-S" "inputs"
+               "my-coreutils")
 
       (load file)
       (list (package-inputs (@ (my-packages) my-coreutils))
@@ -317,7 +321,8 @@ (define file
                         " ;margin comment\n")))
       (system* "cat" file)
 
-      (system* "guix" "style" "-L" directory "my-coreutils")
+      (system* "guix" "style" "-L" directory "-S" "inputs"
+               "my-coreutils")
 
       (load file)
       (list (package-inputs (@ (my-packages) my-coreutils))
@@ -338,7 +343,8 @@ (define file
         ((",gmp\\)(.*)$" _ rest)
          (string-append ",gmp)\n   ;; line comment!\n" rest)))
 
-      (system* "guix" "style" "-L" directory "my-coreutils")
+      (system* "guix" "style" "-L" directory "-S" "inputs"
+               "my-coreutils")
 
       (load file)
       (list (package-inputs (@ (my-packages) my-coreutils))
@@ -364,7 +370,8 @@ (define file
         ((",acl\\)(.*)$" _ rest)
          (string-append ",acl) ;another one\n" rest)))
 
-      (system* "guix" "style" "-L" directory "my-coreutils")
+      (system* "guix" "style" "-L" directory "-S" "inputs"
+               "my-coreutils")
 
       (load file)
       (list (package-inputs (@ (my-packages) my-coreutils))
-- 
2.33.0





Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Mon, 10 Jan 2022 14:04:02 GMT) Full text and rfc822 format available.

Notification sent to Ludovic Courtès <ludo <at> gnu.org>:
bug acknowledged by developer. (Mon, 10 Jan 2022 14:04:02 GMT) Full text and rfc822 format available.

Message #25 received at 52974-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 52974-done <at> debbugs.gnu.org
Subject: Re: bug#52974: [PATCH 0/5] Formatting package definitions with
 'guix style'
Date: Mon, 10 Jan 2022 15:03:37 +0100
Ludovic Courtès <ludo <at> gnu.org> skribis:

> This patch set extends ‘guix style’ so that it can format
> package definitions following our conventions.  It adds a
> new ‘-S’ option to ‘guix style’, which allows you to select
> a “styling rule”.

Applied!

  0976e92a0f news: Add news entry about 'guix style'.
  5d9a5e2301 style: '-S format' canonicalizes comments.
  c4fe13c294 style: Add '--styling' option.
  6f892630ae style: Add support for "newline forms".
  208a7aa17b style: Allow special forms to be scoped.
  97d0055edb style: Improve pretty printer and add tests.

There wasn’t any formal comment during the review process, which is
unfortunate, but of course we can still tweak ‘guix style’ as we go.

Ludo’.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Tue, 08 Feb 2022 12:24:07 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years and 71 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.