diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2022-08-02 11:57:39 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2022-08-08 11:22:32 +0200 |
| commit | f687e27e0385c7f9bab8d967293061158fc3f504 (patch) | |
| tree | 13ea17d05751f61e8f3b6c6ad057a076ec739488 | |
| parent | 3eb3901d7f1d4aae134cb64aa703af67c3c27cdf (diff) | |
read-print: Read and render vertical space.
* guix/read-print.scm (<vertical-space>, vertical-space?)
(vertical-space, vertical-space-height): New variables.
(combine-vertical-space, canonicalize-vertical-space)
(read-vertical-space): New procedures.
(read-with-comments): Use it in the #\newline case.
(pretty-print-with-comments): Add #:format-vertical-space and honor it.
Add case for 'vertical-space?'.
* guix/scripts/style.scm (format-package-definition): Pass
#:format-vertical-space to 'object->string*'.
* tests/read-print.scm ("read-with-comments: list with blank line")
("read-with-comments: list with multiple blank lines")
("read-with-comments: top-level blank lines")
("pretty-print-with-comments, canonicalize-vertical-space"): New tests.
Add a couple of additional round-trip tests.
| -rw-r--r-- | guix/read-print.scm | 54 | ||||
| -rw-r--r-- | guix/scripts/style.scm | 3 | ||||
| -rw-r--r-- | tests/read-print.scm | 76 |
3 files changed, 129 insertions, 4 deletions
diff --git a/guix/read-print.scm b/guix/read-print.scm index 732d0dc1f87..2b626ba2811 100644 --- a/guix/read-print.scm +++ b/guix/read-print.scm | |||
| @@ -30,6 +30,11 @@ | |||
| 30 | 30 | ||
| 31 | blank? | 31 | blank? |
| 32 | 32 | ||
| 33 | vertical-space | ||
| 34 | vertical-space? | ||
| 35 | vertical-space-height | ||
| 36 | canonicalize-vertical-space | ||
| 37 | |||
| 33 | comment | 38 | comment |
| 34 | comment? | 39 | comment? |
| 35 | comment->string | 40 | comment->string |
| @@ -58,6 +63,26 @@ | |||
| 58 | 63 | ||
| 59 | (define blank? (record-predicate <blank>)) | 64 | (define blank? (record-predicate <blank>)) |
| 60 | 65 | ||
| 66 | (define <vertical-space> | ||
| 67 | (make-record-type '<vertical-space> '(height) | ||
| 68 | #:parent <blank> | ||
| 69 | #:extensible? #f)) | ||
| 70 | |||
| 71 | (define vertical-space? (record-predicate <vertical-space>)) | ||
| 72 | (define vertical-space (record-type-constructor <vertical-space>)) | ||
| 73 | (define vertical-space-height (record-accessor <vertical-space> 'height)) | ||
| 74 | |||
| 75 | (define (combine-vertical-space x y) | ||
| 76 | "Return vertical space as high as the combination of X and Y." | ||
| 77 | (vertical-space (+ (vertical-space-height x) | ||
| 78 | (vertical-space-height y)))) | ||
| 79 | |||
| 80 | (define canonicalize-vertical-space | ||
| 81 | (let ((unit (vertical-space 1))) | ||
| 82 | (lambda (space) | ||
| 83 | "Return a vertical space corresponding to a single blank line." | ||
| 84 | unit))) | ||
| 85 | |||
| 61 | (define <comment> | 86 | (define <comment> |
| 62 | ;; Comments. | 87 | ;; Comments. |
| 63 | (make-record-type '<comment> '(str margin?) | 88 | (make-record-type '<comment> '(str margin?) |
| @@ -80,6 +105,19 @@ end with newline, otherwise an error is raised." | |||
| 80 | (&message (message "invalid comment string"))))) | 105 | (&message (message "invalid comment string"))))) |
| 81 | (string->comment str margin?)) | 106 | (string->comment str margin?)) |
| 82 | 107 | ||
| 108 | (define (read-vertical-space port) | ||
| 109 | "Read from PORT until a non-vertical-space character is met, and return a | ||
| 110 | single <vertical-space> record." | ||
| 111 | (define (space? chr) | ||
| 112 | (char-set-contains? char-set:whitespace chr)) | ||
| 113 | |||
| 114 | (let loop ((height 1)) | ||
| 115 | (match (read-char port) | ||
| 116 | (#\newline (loop (+ 1 height))) | ||
| 117 | ((? eof-object?) (vertical-space height)) | ||
| 118 | ((? space?) (loop height)) | ||
| 119 | (chr (unread-char chr port) (vertical-space height))))) | ||
| 120 | |||
| 83 | (define (read-with-comments port) | 121 | (define (read-with-comments port) |
| 84 | "Like 'read', but include <blank> objects when they're encountered." | 122 | "Like 'read', but include <blank> objects when they're encountered." |
| 85 | ;; Note: Instead of implementing this functionality in 'read' proper, which | 123 | ;; Note: Instead of implementing this functionality in 'read' proper, which |
| @@ -107,7 +145,9 @@ end with newline, otherwise an error is raised." | |||
| 107 | eof) ;oops! | 145 | eof) ;oops! |
| 108 | (chr | 146 | (chr |
| 109 | (cond ((eqv? chr #\newline) | 147 | (cond ((eqv? chr #\newline) |
| 110 | (loop #t return)) | 148 | (if blank-line? |
| 149 | (read-vertical-space port) | ||
| 150 | (loop #t return))) | ||
| 111 | ((char-set-contains? char-set:whitespace chr) | 151 | ((char-set-contains? char-set:whitespace chr) |
| 112 | (loop blank-line? return)) | 152 | (loop blank-line? return)) |
| 113 | ((memv chr '(#\( #\[)) | 153 | ((memv chr '(#\( #\[)) |
| @@ -297,6 +337,7 @@ semicolons." | |||
| 297 | (define* (pretty-print-with-comments port obj | 337 | (define* (pretty-print-with-comments port obj |
| 298 | #:key | 338 | #:key |
| 299 | (format-comment identity) | 339 | (format-comment identity) |
| 340 | (format-vertical-space identity) | ||
| 300 | (indent 0) | 341 | (indent 0) |
| 301 | (max-width 78) | 342 | (max-width 78) |
| 302 | (long-list 5)) | 343 | (long-list 5)) |
| @@ -306,7 +347,8 @@ included in the output. | |||
| 306 | 347 | ||
| 307 | Lists longer than LONG-LIST are written as one element per line. Comments are | 348 | Lists longer than LONG-LIST are written as one element per line. Comments are |
| 308 | passed through FORMAT-COMMENT before being emitted; a useful value for | 349 | passed through FORMAT-COMMENT before being emitted; a useful value for |
| 309 | FORMAT-COMMENT is 'canonicalize-comment'." | 350 | FORMAT-COMMENT is 'canonicalize-comment'. Vertical space is passed through |
| 351 | FORMAT-VERTICAL-SPACE; a useful value of 'canonicalize-vertical-space'." | ||
| 310 | (define (list-of-lists? head tail) | 352 | (define (list-of-lists? head tail) |
| 311 | ;; Return true if HEAD and TAIL denote a list of lists--e.g., a list of | 353 | ;; Return true if HEAD and TAIL denote a list of lists--e.g., a list of |
| 312 | ;; 'let' bindings. | 354 | ;; 'let' bindings. |
| @@ -394,6 +436,14 @@ FORMAT-COMMENT is 'canonicalize-comment'." | |||
| 394 | port))) | 436 | port))) |
| 395 | (display (make-string indent #\space) port) | 437 | (display (make-string indent #\space) port) |
| 396 | indent) | 438 | indent) |
| 439 | ((? vertical-space? space) | ||
| 440 | (unless delimited? (newline port)) | ||
| 441 | (let loop ((i (vertical-space-height (format-vertical-space space)))) | ||
| 442 | (unless (zero? i) | ||
| 443 | (newline port) | ||
| 444 | (loop (- i 1)))) | ||
| 445 | (display (make-string indent #\space) port) | ||
| 446 | indent) | ||
| 397 | (('quote lst) | 447 | (('quote lst) |
| 398 | (unless delimited? (display " " port)) | 448 | (unless delimited? (display " " port)) |
| 399 | (display "'" port) | 449 | (display "'" port) |
diff --git a/guix/scripts/style.scm b/guix/scripts/style.scm index 5c0ecc0896e..2e14bc68fd0 100644 --- a/guix/scripts/style.scm +++ b/guix/scripts/style.scm | |||
| @@ -316,7 +316,8 @@ PACKAGE." | |||
| 316 | (object->string* exp | 316 | (object->string* exp |
| 317 | (location-column | 317 | (location-column |
| 318 | (package-definition-location package)) | 318 | (package-definition-location package)) |
| 319 | #:format-comment canonicalize-comment))))) | 319 | #:format-comment canonicalize-comment |
| 320 | #:format-vertical-space canonicalize-vertical-space))))) | ||
| 320 | 321 | ||
| 321 | (define (package-location<? p1 p2) | 322 | (define (package-location<? p1 p2) |
| 322 | "Return true if P1's location is \"before\" P2's." | 323 | "Return true if P1's location is \"before\" P2's." |
diff --git a/tests/read-print.scm b/tests/read-print.scm index e9ba1127d4f..f915b7e2d21 100644 --- a/tests/read-print.scm +++ b/tests/read-print.scm | |||
| @@ -19,7 +19,8 @@ | |||
| 19 | (define-module (tests-style) | 19 | (define-module (tests-style) |
| 20 | #:use-module (guix read-print) | 20 | #:use-module (guix read-print) |
| 21 | #:use-module (guix gexp) ;for the reader extensions | 21 | #:use-module (guix gexp) ;for the reader extensions |
| 22 | #:use-module (srfi srfi-64)) | 22 | #:use-module (srfi srfi-64) |
| 23 | #:use-module (ice-9 match)) | ||
| 23 | 24 | ||
| 24 | (define-syntax-rule (test-pretty-print str args ...) | 25 | (define-syntax-rule (test-pretty-print str args ...) |
| 25 | "Test equality after a round-trip where STR is passed to | 26 | "Test equality after a round-trip where STR is passed to |
| @@ -40,6 +41,35 @@ | |||
| 40 | (call-with-input-string "(a . b)" | 41 | (call-with-input-string "(a . b)" |
| 41 | read-with-comments)) | 42 | read-with-comments)) |
| 42 | 43 | ||
| 44 | (test-equal "read-with-comments: list with blank line" | ||
| 45 | `(list with ,(vertical-space 1) blank line) | ||
| 46 | (call-with-input-string "\ | ||
| 47 | (list with | ||
| 48 | |||
| 49 | blank line)\n" | ||
| 50 | read-with-comments)) | ||
| 51 | |||
| 52 | (test-equal "read-with-comments: list with multiple blank lines" | ||
| 53 | `(list with ,(comment ";multiple\n" #t) | ||
| 54 | ,(vertical-space 3) blank lines) | ||
| 55 | (call-with-input-string "\ | ||
| 56 | (list with ;multiple | ||
| 57 | |||
| 58 | |||
| 59 | |||
| 60 | blank lines)\n" | ||
| 61 | read-with-comments)) | ||
| 62 | |||
| 63 | (test-equal "read-with-comments: top-level blank lines" | ||
| 64 | (list (vertical-space 2) '(a b c) (vertical-space 2)) | ||
| 65 | (call-with-input-string " | ||
| 66 | |||
| 67 | (a b c)\n\n" | ||
| 68 | (lambda (port) | ||
| 69 | (list (read-with-comments port) | ||
| 70 | (read-with-comments port) | ||
| 71 | (read-with-comments port))))) | ||
| 72 | |||
| 43 | (test-pretty-print "(list 1 2 3 4)") | 73 | (test-pretty-print "(list 1 2 3 4)") |
| 44 | (test-pretty-print "((a . 1) (b . 2))") | 74 | (test-pretty-print "((a . 1) (b . 2))") |
| 45 | (test-pretty-print "(a b c . boom)") | 75 | (test-pretty-print "(a b c . boom)") |
| @@ -181,6 +211,24 @@ mnopqrstuvwxyz.\")" | |||
| 181 | `(cons \"--without-any-problem\" | 211 | `(cons \"--without-any-problem\" |
| 182 | ,flags)))") | 212 | ,flags)))") |
| 183 | 213 | ||
| 214 | (test-pretty-print "\ | ||
| 215 | (vertical-space one: | ||
| 216 | |||
| 217 | two: | ||
| 218 | |||
| 219 | |||
| 220 | three: | ||
| 221 | |||
| 222 | |||
| 223 | |||
| 224 | end)") | ||
| 225 | |||
| 226 | (test-pretty-print "\ | ||
| 227 | (vertical-space one | ||
| 228 | |||
| 229 | ;; Comment after blank line. | ||
| 230 | two)") | ||
| 231 | |||
| 184 | (test-equal "pretty-print-with-comments, canonicalize-comment" | 232 | (test-equal "pretty-print-with-comments, canonicalize-comment" |
| 185 | "\ | 233 | "\ |
| 186 | (list abc | 234 | (list abc |
| @@ -206,4 +254,30 @@ mnopqrstuvwxyz.\")" | |||
| 206 | #:format-comment | 254 | #:format-comment |
| 207 | canonicalize-comment))))) | 255 | canonicalize-comment))))) |
| 208 | 256 | ||
| 257 | (test-equal "pretty-print-with-comments, canonicalize-vertical-space" | ||
| 258 | "\ | ||
| 259 | (list abc | ||
| 260 | |||
| 261 | def | ||
| 262 | |||
| 263 | ;; last one | ||
| 264 | ghi)" | ||
| 265 | (let ((sexp (call-with-input-string | ||
| 266 | "\ | ||
| 267 | (list abc | ||
| 268 | |||
| 269 | |||
| 270 | |||
| 271 | def | ||
| 272 | |||
| 273 | |||
| 274 | ;; last one | ||
| 275 | ghi)" | ||
| 276 | read-with-comments))) | ||
| 277 | (call-with-output-string | ||
| 278 | (lambda (port) | ||
| 279 | (pretty-print-with-comments port sexp | ||
| 280 | #:format-vertical-space | ||
| 281 | canonicalize-vertical-space))))) | ||
| 282 | |||
| 209 | (test-end) | 283 | (test-end) |
