Skip to content

Commit b966e0d

Browse files
authored
[0005] fmt: 优化 #() 结构的格式化 (#775)
1 parent 0f0843e commit b966e0d

4 files changed

Lines changed: 111 additions & 25 deletions

File tree

devel/0005.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# [0005] gf fmt reader vector 多行格式优化
2+
3+
## 任务相关的代码文件
4+
- tools/fmt/liii/goldfmt-format.scm
5+
- tools/fmt/tests/liii/goldfmt-format/format-string-test.scm
6+
- tests/liii/json/json-to-string-test.scm
7+
8+
## 如何测试
9+
```bash
10+
xmake b goldfish
11+
bin/gf fmt tools/fmt/
12+
bin/gf tests/tools/fmt/tests/liii/goldfmt-format/
13+
```
14+
15+
## 2026/05/07 gf fmt reader vector 多行格式末尾添加 `) ;#` 标记
16+
17+
### What
18+
优化 `gf fmt` 对 reader vector(`#(...)`)的多行格式输出,使其在跨行时末尾添加 `) ;#` 注释标记,提高可读性和括号匹配清晰度。
19+
20+
1. 新增 `last-line-column` 函数:计算字符串最后一行的列号,用于精确控制内容在行内的起始位置
21+
2. 新增 `reader-append-close` 函数:判断在关闭括号前是否需要先换行(当末尾为 `;#` 时追加换行和缩进)
22+
3. `reader-append-rest` 函数新增 `close-indent` 参数:传递缩进上下文信息,使闭括号位置正确对齐
23+
4. `format-reader-vector-multiline` 中将末尾的 `)` 替换为 `\n) ;#`,并在 `reader-append-rest` 中传递正确缩进
24+
5. 优化 `reader-append-rest` 中 item 的缩进计算:使用 `last-line-column` 而非固定 `rest-indent`,使后续元素在已有内容后内联时位置更精确
25+
26+
### Why
27+
之前多行 reader vector 的格式如:
28+
```scheme
29+
#((("text" . "1") ("type" . "text"))
30+
(("text" . "2") ("type" . "text")))
31+
```
32+
多层嵌套时闭括号 `)` 混在一起难以区分。改为在末尾添加 `;#` 注释标记,配合换行和缩进,使 vector 的结尾一目了然:
33+
```scheme
34+
#((("text" . "1") ("type" . "text"))
35+
(("text" . "2") ("type" . "text"))
36+
) ;#
37+
```
38+
39+
### How
40+
- `reader-append-close` 通过检查结果字符串是否以 `;#` 结尾来判断是否需要先换行:如果是,则追加 `\n<缩进>)`;否则直接追加 `)`。这一设计确保了单行格式不受影响。
41+
- `last-line-column` 遍历字符串,遇到换行符则重置列号,否则累加,最终返回最后一行的列位置。
42+
- 测试用例同步更新:`json-to-string-test.scm``format-string-test.scm` 中的多行 reader vector 期望值全部更新为带 `) ;#` 的新格式。

tests/liii/json/json-to-string-test.scm

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@
6363

6464
(check (json->string '(("messages"
6565
. #((("role" . "user") ("content" . #(1 2 3)))
66-
(("role" . "user") ("content" . "中文")))))
66+
(("role" . "user") ("content" . "中文"))
67+
) ;#
68+
))
6769
) ;json->string
6870
=>
6971
"{\"messages\":[{\"role\":\"user\",\"content\":[1,2,3]},{\"role\":\"user\",\"content\":\"中文\"}]}"
@@ -74,8 +76,12 @@
7476
. #((("role" . "user")
7577
("content"
7678
. #((("text" . "1") ("type" . "text"))
77-
(("text" . "2") ("type" . "text")))))
78-
(("role" . "user") ("content" . "中文")))))
79+
(("text" . "2") ("type" . "text"))
80+
) ;#
81+
))
82+
(("role" . "user") ("content" . "中文"))
83+
) ;#
84+
))
7985
) ;json->string
8086
=>
8187
"{\"messages\":[{\"role\":\"user\",\"content\":[{\"text\":\"1\",\"type\":\"text\"},{\"text\":\"2\",\"type\":\"text\"}]},{\"role\":\"user\",\"content\":\"中文\"}]}"

tools/fmt/liii/goldfmt-format.scm

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -391,26 +391,29 @@
391391
(define (format-reader-vector-multiline datum indent)
392392
(let* ((prefix (reader-vector-prefix datum))
393393
(item-indent (+ indent (string-length prefix)))
394+
(close-marker (string-append "\n" (spaces indent) ") ;#"))
394395
) ;
395396
(let loop
396397
((items (reader-vector->list datum)) (pieces (list prefix)) (prefix-ready? #t))
397398
(if (null? items)
398-
(apply string-append (reverse (cons ")" pieces)))
399+
(apply string-append (reverse (cons close-marker pieces)))
399400
(let ((item (car items)))
400401
(if (newline-marker-datum? item)
401402
(loop (cdr items)
402403
(cons (spaces item-indent) (cons (reader-newlines (cadr item)) pieces))
403404
#t
404405
) ;loop
405-
(loop (cdr items)
406-
(cons (format-reader-datum-at item item-indent)
407-
(if prefix-ready?
408-
pieces
409-
(cons (string-append "\n" (spaces item-indent)) pieces)
410-
) ;if
411-
) ;cons
412-
#f
413-
) ;loop
406+
(let ((item-text (string-trim (format-reader-datum-at item item-indent))))
407+
(loop (cdr items)
408+
(cons item-text
409+
(if prefix-ready?
410+
pieces
411+
(cons (string-append "\n" (spaces item-indent)) pieces)
412+
) ;if
413+
) ;cons
414+
#f
415+
) ;loop
416+
) ;let
414417
) ;if
415418
) ;let
416419
) ;if
@@ -462,33 +465,61 @@
462465
) ;let
463466
) ;define
464467

465-
(define (reader-append-rest current result rest-indent prefix-ready?)
468+
(define (last-line-column text)
469+
(let loop
470+
((i 0) (column 0))
471+
(if (>= i (string-length text))
472+
column
473+
(loop (+ i 1)
474+
(if (char=? (string-ref text i) #\newline) 0 (+ column 1))
475+
) ;loop
476+
) ;if
477+
) ;let
478+
) ;define
479+
480+
(define (reader-append-close result close-indent)
481+
(if (string-suffix? ";#" result)
482+
(string-append result "\n" (spaces close-indent) ")")
483+
(string-append result ")")
484+
) ;if
485+
) ;define
486+
487+
(define (reader-append-rest current result rest-indent prefix-ready? close-indent)
466488
(cond ((pair? current)
467489
(let ((item (car current)))
468490
(if (newline-marker-datum? item)
469491
(reader-append-rest (cdr current)
470492
(string-append result (reader-newlines (cadr item)) (spaces rest-indent))
471493
rest-indent
472494
#t
495+
close-indent
473496
) ;reader-append-rest
474497
(reader-append-rest (cdr current)
475498
(string-append result
476499
(if prefix-ready? "" (string-append "\n" (spaces rest-indent)))
477-
(format-reader-datum-at item rest-indent)
500+
(format-reader-datum-at item
501+
(if prefix-ready? (last-line-column result) rest-indent)
502+
) ;format-reader-datum-at
478503
) ;string-append
479504
rest-indent
480505
#f
506+
close-indent
481507
) ;reader-append-rest
482508
) ;if
483509
) ;let
484510
) ;
485-
((null? current) (string-append result ")"))
486-
(else (string-append result
487-
(if prefix-ready? "" (string-append "\n" (spaces rest-indent)))
488-
". "
489-
(format-reader-datum-at current (+ rest-indent 2))
490-
")"
491-
) ;string-append
511+
((null? current) (reader-append-close result close-indent))
512+
(else (reader-append-close
513+
(let* ((prefix (if prefix-ready? "" (string-append "\n" (spaces rest-indent)))
514+
) ;prefix
515+
(before-tail (string-append result prefix ". "))
516+
) ;
517+
(string-append before-tail
518+
(format-reader-datum-at current (last-line-column before-tail))
519+
) ;string-append
520+
) ;let*
521+
close-indent
522+
) ;reader-append-close
492523
) ;else
493524
) ;cond
494525
) ;define
@@ -520,7 +551,7 @@
520551
) ;if
521552
) ;body-indent
522553
) ;
523-
(reader-append-rest after-selected with-selected body-indent #f)
554+
(reader-append-rest after-selected with-selected body-indent #f indent)
524555
) ;let*
525556
) ;if
526557
) ;define

tools/fmt/tests/liii/goldfmt-format/format-string-test.scm

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,14 @@
145145

146146
(check (format-datum '(quote #(a (*newline* 1) b)))
147147
=>
148-
"'#(a\n\n b)"
148+
"'#(a\n\n b\n ) ;#"
149+
) ;check
150+
151+
(check (format-datum '#((alpha beta gamma delta epsilon zeta eta theta iota kappa)
152+
(one two three four five six seven eight nine ten)
153+
))
154+
=>
155+
"#((alpha beta gamma delta epsilon zeta eta theta iota kappa)\n (one two three four five six seven eight nine ten)\n) ;#"
149156
) ;check
150157

151158
;; 超过 max-inline-length 的 reader datum 应进入 head-aware 多行排版
@@ -207,7 +214,7 @@
207214
(check (format-string "(check (f '((\"messages\" . #(((\"role\" . \"user\") (\"content\" . #(((\"text\" . \"1\") (\"type\" . \"text\")) ((\"text\" . \"2\") (\"type\" . \"text\"))))) ((\"role\" . \"user\") (\"content\" . \"中文\")))))) => \"ok\")"
208215
) ;format-string
209216
=>
210-
"(check (f '((\"messages\"\n . #(((\"role\" . \"user\")\n (\"content\"\n . #(((\"text\" . \"1\") (\"type\" . \"text\"))\n ((\"text\" . \"2\") (\"type\" . \"text\")))))\n ((\"role\" . \"user\") (\"content\" . \"中文\")))))\n ) ;f\n =>\n \"ok\"\n) ;check\n"
217+
"(check (f '((\"messages\"\n . #(((\"role\" . \"user\")\n (\"content\"\n . #(((\"text\" . \"1\") (\"type\" . \"text\"))\n ((\"text\" . \"2\") (\"type\" . \"text\"))\n ) ;#\n ))\n ((\"role\" . \"user\") (\"content\" . \"中文\"))\n ) ;#\n ))\n ) ;f\n =>\n \"ok\"\n) ;check\n"
211218
) ;check
212219

213220
(check-report)

0 commit comments

Comments
 (0)