diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2022-08-02 18:01:35 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2022-08-08 11:53:33 +0200 |
| commit | a15542d26df42dabdb5e2f76d150ae200230c3b0 (patch) | |
| tree | b5c17cf1660c5fc800a3f3773025ad0201848790 | |
| parent | 90ef692e9b48732ae2e3921ff5d101e186506a85 (diff) | |
style: Add '--whole-file' option.
* guix/scripts/style.scm (format-whole-file): New procedure.
(%options, show-help): Add '--whole-file'.
(guix-style): Honor it.
* tests/guix-style.sh: New file.
* Makefile.am (SH_TESTS): Add it.
* doc/guix.texi (Invoking guix style): Document it.
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | doc/guix.texi | 28 | ||||
| -rw-r--r-- | guix/scripts/style.scm | 65 | ||||
| -rw-r--r-- | tests/guix-style.sh | 80 |
4 files changed, 153 insertions, 21 deletions
diff --git a/Makefile.am b/Makefile.am index 2cda20e61c1..f7c42e81533 100644 --- a/Makefile.am +++ b/Makefile.am | |||
| @@ -580,6 +580,7 @@ SH_TESTS = \ | |||
| 580 | tests/guix-package.sh \ | 580 | tests/guix-package.sh \ |
| 581 | tests/guix-package-aliases.sh \ | 581 | tests/guix-package-aliases.sh \ |
| 582 | tests/guix-package-net.sh \ | 582 | tests/guix-package-net.sh \ |
| 583 | tests/guix-style.sh \ | ||
| 583 | tests/guix-system.sh \ | 584 | tests/guix-system.sh \ |
| 584 | tests/guix-home.sh \ | 585 | tests/guix-home.sh \ |
| 585 | tests/guix-archive.sh \ | 586 | tests/guix-archive.sh \ |
diff --git a/doc/guix.texi b/doc/guix.texi index d6460a785ff..9a6a5c307d3 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -14058,9 +14058,12 @@ otherwise. | |||
| 14058 | @node Invoking guix style | 14058 | @node Invoking guix style |
| 14059 | @section Invoking @command{guix style} | 14059 | @section Invoking @command{guix style} |
| 14060 | 14060 | ||
| 14061 | The @command{guix style} command helps packagers style their package | 14061 | The @command{guix style} command helps users and packagers alike style |
| 14062 | definitions according to the latest fashionable trends. The command | 14062 | their package definitions and configuration files according to the |
| 14063 | currently provides the following styling rules: | 14063 | latest fashionable trends. It can either reformat whole files, with the |
| 14064 | @option{--whole-file} option, or apply specific @dfn{styling rules} to | ||
| 14065 | individual package definitions. The command currently provides the | ||
| 14066 | following styling rules: | ||
| 14064 | 14067 | ||
| 14065 | @itemize | 14068 | @itemize |
| 14066 | @item | 14069 | @item |
| @@ -14115,6 +14118,12 @@ the packages. The @option{--styling} or @option{-S} option allows you | |||
| 14115 | to select the style rule, the default rule being @code{format}---see | 14118 | to select the style rule, the default rule being @code{format}---see |
| 14116 | below. | 14119 | below. |
| 14117 | 14120 | ||
| 14121 | To reformat entire source files, the syntax is: | ||
| 14122 | |||
| 14123 | @example | ||
| 14124 | guix style --whole-file @var{file}@dots{} | ||
| 14125 | @end example | ||
| 14126 | |||
| 14118 | The available options are listed below. | 14127 | The available options are listed below. |
| 14119 | 14128 | ||
| 14120 | @table @code | 14129 | @table @code |
| @@ -14122,6 +14131,19 @@ The available options are listed below. | |||
| 14122 | @itemx -n | 14131 | @itemx -n |
| 14123 | Show source file locations that would be edited but do not modify them. | 14132 | Show source file locations that would be edited but do not modify them. |
| 14124 | 14133 | ||
| 14134 | @item --whole-file | ||
| 14135 | @itemx -f | ||
| 14136 | Reformat the given files in their entirety. In that case, subsequent | ||
| 14137 | arguments are interpreted as file names (rather than package names), and | ||
| 14138 | the @option{--styling} option has no effect. | ||
| 14139 | |||
| 14140 | As an example, here is how you might reformat your operating system | ||
| 14141 | configuration (you need write permissions for the file): | ||
| 14142 | |||
| 14143 | @example | ||
| 14144 | guix style -f /etc/config.scm | ||
| 14145 | @end example | ||
| 14146 | |||
| 14125 | @item --styling=@var{rule} | 14147 | @item --styling=@var{rule} |
| 14126 | @itemx -S @var{rule} | 14148 | @itemx -S @var{rule} |
| 14127 | Apply @var{rule}, one of the following styling rules: | 14149 | Apply @var{rule}, one of the following styling rules: |
diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm index 2e14bc68fd0..c0b9ea1a282 100644 --- a/guix/scripts/style.scm +++ b/guix/scripts/style.scm | |||
| @@ -330,6 +330,21 @@ PACKAGE." | |||
| 330 | 330 | ||
| 331 | 331 | ||
| 332 | ;;; | 332 | ;;; |
| 333 | ;;; Whole-file formatting. | ||
| 334 | ;;; | ||
| 335 | |||
| 336 | (define* (format-whole-file file #:rest rest) | ||
| 337 | "Reformat all of FILE." | ||
| 338 | (let ((lst (call-with-input-file file read-with-comments/sequence))) | ||
| 339 | (with-atomic-file-output file | ||
| 340 | (lambda (port) | ||
| 341 | (apply pretty-print-with-comments/splice port lst | ||
| 342 | #:format-comment canonicalize-comment | ||
| 343 | #:format-vertical-space canonicalize-vertical-space | ||
| 344 | rest))))) | ||
| 345 | |||
| 346 | |||
| 347 | ;;; | ||
| 333 | ;;; Options. | 348 | ;;; Options. |
| 334 | ;;; | 349 | ;;; |
| 335 | 350 | ||
| @@ -345,6 +360,9 @@ PACKAGE." | |||
| 345 | (option '(#\e "expression") #t #f | 360 | (option '(#\e "expression") #t #f |
| 346 | (lambda (opt name arg result) | 361 | (lambda (opt name arg result) |
| 347 | (alist-cons 'expression arg result))) | 362 | (alist-cons 'expression arg result))) |
| 363 | (option '(#\f "whole-file") #f #f | ||
| 364 | (lambda (opt name arg result) | ||
| 365 | (alist-cons 'whole-file? #t result))) | ||
| 348 | (option '(#\S "styling") #t #f | 366 | (option '(#\S "styling") #t #f |
| 349 | (lambda (opt name arg result) | 367 | (lambda (opt name arg result) |
| 350 | (alist-cons 'styling-procedure | 368 | (alist-cons 'styling-procedure |
| @@ -400,6 +418,9 @@ Update package definitions to the latest style.\n")) | |||
| 400 | of 'silent', 'safe', or 'always'")) | 418 | of 'silent', 'safe', or 'always'")) |
| 401 | (newline) | 419 | (newline) |
| 402 | (display (G_ " | 420 | (display (G_ " |
| 421 | -f, --whole-file format the entire contents of the given file(s)")) | ||
| 422 | (newline) | ||
| 423 | (display (G_ " | ||
| 403 | -h, --help display this help and exit")) | 424 | -h, --help display this help and exit")) |
| 404 | (display (G_ " | 425 | (display (G_ " |
| 405 | -V, --version display version information and exit")) | 426 | -V, --version display version information and exit")) |
| @@ -426,27 +447,35 @@ Update package definitions to the latest style.\n")) | |||
| 426 | #:build-options? #f)) | 447 | #:build-options? #f)) |
| 427 | 448 | ||
| 428 | (let* ((opts (parse-options)) | 449 | (let* ((opts (parse-options)) |
| 429 | (packages (filter-map (match-lambda | ||
| 430 | (('argument . spec) | ||
| 431 | (specification->package spec)) | ||
| 432 | (('expression . str) | ||
| 433 | (read/eval str)) | ||
| 434 | (_ #f)) | ||
| 435 | opts)) | ||
| 436 | (edit (if (assoc-ref opts 'dry-run?) | 450 | (edit (if (assoc-ref opts 'dry-run?) |
| 437 | edit-expression/dry-run | 451 | edit-expression/dry-run |
| 438 | edit-expression)) | 452 | edit-expression)) |
| 439 | (style (assoc-ref opts 'styling-procedure)) | 453 | (style (assoc-ref opts 'styling-procedure)) |
| 440 | (policy (assoc-ref opts 'input-simplification-policy))) | 454 | (policy (assoc-ref opts 'input-simplification-policy))) |
| 441 | (with-error-handling | 455 | (with-error-handling |
| 442 | (for-each (lambda (package) | 456 | (if (assoc-ref opts 'whole-file?) |
| 443 | (style package #:policy policy | 457 | (let ((files (filter-map (match-lambda |
| 444 | #:edit-expression edit)) | 458 | (('argument . file) file) |
| 445 | ;; Sort package by source code location so that we start editing | 459 | (_ #f)) |
| 446 | ;; files from the bottom and going upward. That way, the | 460 | opts))) |
| 447 | ;; 'location' field of <package> records is not invalidated as | 461 | (unless (eq? format-package-definition style) |
| 448 | ;; we modify files. | 462 | (warning (G_ "'--styling' option has no effect in whole-file mode~%"))) |
| 449 | (sort (if (null? packages) | 463 | (for-each format-whole-file files)) |
| 450 | (fold-packages cons '() #:select? (const #t)) | 464 | (let ((packages (filter-map (match-lambda |
| 451 | packages) | 465 | (('argument . spec) |
| 452 | (negate package-location<?)))))) | 466 | (specification->package spec)) |
| 467 | (('expression . str) | ||
| 468 | (read/eval str)) | ||
| 469 | (_ #f)) | ||
| 470 | opts))) | ||
| 471 | (for-each (lambda (package) | ||
| 472 | (style package #:policy policy | ||
| 473 | #:edit-expression edit)) | ||
| 474 | ;; Sort package by source code location so that we start | ||
| 475 | ;; editing files from the bottom and going upward. That | ||
| 476 | ;; way, the 'location' field of <package> records is not | ||
| 477 | ;; invalidated as we modify files. | ||
| 478 | (sort (if (null? packages) | ||
| 479 | (fold-packages cons '() #:select? (const #t)) | ||
| 480 | packages) | ||
| 481 | (negate package-location<?)))))))) | ||
diff --git a/tests/guix-style.sh b/tests/guix-style.sh new file mode 100644 index 00000000000..58f953a0ec1 --- /dev/null +++ b/tests/guix-style.sh | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | # GNU Guix --- Functional package management for GNU | ||
| 2 | # Copyright © 2022 Ludovic Courtès <ludo@gnu.org> | ||
| 3 | # | ||
| 4 | # This file is part of GNU Guix. | ||
| 5 | # | ||
| 6 | # GNU Guix is free software; you can redistribute it and/or modify it | ||
| 7 | # under the terms of the GNU General Public License as published by | ||
| 8 | # the Free Software Foundation; either version 3 of the License, or (at | ||
| 9 | # your option) any later version. | ||
| 10 | # | ||
| 11 | # GNU Guix is distributed in the hope that it will be useful, but | ||
| 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | # GNU General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU General Public License | ||
| 17 | # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | # | ||
| 20 | # Test 'guix style'. | ||
| 21 | # | ||
| 22 | |||
| 23 | set -e | ||
| 24 | |||
| 25 | guix style --version | ||
| 26 | |||
| 27 | tmpdir="guix-style-$$" | ||
| 28 | trap 'rm -r "$tmpdir"' EXIT | ||
| 29 | |||
| 30 | tmpfile="$tmpdir/os.scm" | ||
| 31 | mkdir "$tmpdir" | ||
| 32 | cat > "$tmpfile" <<EOF | ||
| 33 | ;;; This is a header with three semicolons. | ||
| 34 | ;;; | ||
| 35 | |||
| 36 | (define-module (foo bar) | ||
| 37 | #:use-module (guix) | ||
| 38 | #:use-module (gnu)) | ||
| 39 | |||
| 40 | ;; One blank line and a page break. | ||
| 41 | |||
| 42 | |||
| 43 | ;; And now, the OS. | ||
| 44 | (operating-system | ||
| 45 | (host-name "komputilo") | ||
| 46 | (locale "eo_EO.UTF-8") | ||
| 47 | |||
| 48 | ;; User accounts. | ||
| 49 | (users (cons (user-account | ||
| 50 | (name "alice") | ||
| 51 | (comment "Bob's sister") | ||
| 52 | (group "users") | ||
| 53 | |||
| 54 | ;; Groups fit on one line. | ||
| 55 | (supplementary-groups '("wheel" "audio" "video"))) | ||
| 56 | %base-user-accounts)) | ||
| 57 | |||
| 58 | ;; The services. | ||
| 59 | (services | ||
| 60 | (cons (service mcron-service-type) %base-services))) | ||
| 61 | EOF | ||
| 62 | |||
| 63 | cp "$tmpfile" "$tmpfile.bak" | ||
| 64 | |||
| 65 | initial_hash="$(guix hash "$tmpfile")" | ||
| 66 | |||
| 67 | guix style -f "$tmpfile" | ||
| 68 | if ! test "$initial_hash" = "$(guix hash "$tmpfile")" | ||
| 69 | then | ||
| 70 | cat "$tmpfile" | ||
| 71 | diff -u "$tmpfile.bak" "$tmpfile" | ||
| 72 | false | ||
| 73 | fi | ||
| 74 | |||
| 75 | # Introduce random changes and try again. | ||
| 76 | sed -i "$tmpfile" -e's/ +/ /g' | ||
| 77 | ! test "$initial_hash" = "$(guix hash "$tmpfile")" | ||
| 78 | |||
| 79 | guix style -f "$tmpfile" | ||
| 80 | test "$initial_hash" = "$(guix hash "$tmpfile")" | ||
