fix for/fold indentation

inspired by Greg's RacketCon talk

also, Rackety
This commit is contained in:
Robby Findler 2014-09-28 15:16:17 -05:00
parent a41cc0c3c0
commit c03c02bccb
3 changed files with 337 additions and 325 deletions

View File

@ -177,7 +177,7 @@
(let () (let ()
(define base-fors (define base-fors
'(for for/list for/hash for/hasheq for/hasheqv for/and for/or '(for for/list for/hash for/hasheq for/hasheqv for/and for/or
for/lists for/first for/last for/fold for/vector for/flvector for/lists for/first for/last for/vector for/flvector
for/sum for/product for/set)) for/sum for/product for/set))
(define untyped-fors (define untyped-fors
(append base-fors (append base-fors
@ -363,6 +363,8 @@
(preferences:set-default 'framework:fixup-open-parens #f boolean?) (preferences:set-default 'framework:fixup-open-parens #f boolean?)
(preferences:set-default 'framework:paren-match #t boolean?) (preferences:set-default 'framework:paren-match #t boolean?)
(let ([defaults-ht (make-hasheq)]) (let ([defaults-ht (make-hasheq)])
(for-each (λ (x) (hash-set! defaults-ht x 'for/fold))
'(for/fold for/fold: for*/fold for*/fold:))
(for-each (λ (x) (hash-set! defaults-ht x 'define)) (for-each (λ (x) (hash-set! defaults-ht x 'define))
'(struct '(struct
local local
@ -441,21 +443,25 @@
type-case)) type-case))
(preferences:set-default (preferences:set-default
'framework:tabify 'framework:tabify
(list defaults-ht #rx"^begin" #rx"^def" #f) (list defaults-ht #rx"^begin" #rx"^def" #f #f)
(list/c hash? (or/c #f regexp?) (or/c #f regexp?) (or/c #f regexp?))) (list/c (hash/c symbol? (or/c 'for/fold 'define 'begin 'lambda) #:flat? #t)
(define old-style-pred? (listof (list/c symbol? symbol?))) (or/c #f regexp?) (or/c #f regexp?) (or/c #f regexp?) (or/c #f regexp?)))
(define pref-pred?
(list/c (or/c
;; old-style prefs
old-style-pred?
;; new-style prefs (define old-style-pred? (listof (list/c symbol? symbol?)))
(define new-style-pred?
(list/c (listof (list/c symbol? symbol?)) ;; additions to defaults (list/c (listof (list/c symbol? symbol?)) ;; additions to defaults
(listof (list/c symbol? symbol?)))) ;; deletions (listof (list/c symbol? symbol?)))) ;; deletions
(define pref-pred?
(or/c (list/c new-style-pred?
(or/c regexp? #f) (or/c regexp? #f)
(or/c regexp? #f) (or/c regexp? #f)
(or/c regexp? #f))) (or/c regexp? #f)
(or/c regexp? #f))
(list/c (or/c old-style-pred? new-style-pred?)
(or/c regexp? #f)
(or/c regexp? #f)
(or/c regexp? #f))))
(define (ht->addition/deletion-lists ht) (define (ht->addition/deletion-lists ht)
(define additions '()) (define additions '())
@ -483,6 +489,11 @@
(hash-set! ht k v)) (hash-set! ht k v))
ht) ht)
(define (pad-to len lst)
(cond
[(null? lst) (build-list len (λ (x) #f))]
[else (cons (car lst) (pad-to (- len 1) (cdr lst)))]))
(preferences:set-un/marshall (preferences:set-un/marshall
'framework:tabify 'framework:tabify
(λ (t) (cons (ht->addition/deletion-lists (list-ref t 0)) (λ (t) (cons (ht->addition/deletion-lists (list-ref t 0))
@ -503,7 +514,7 @@
(cdr l))] (cdr l))]
[else [else
(cons (addition/deletion-lists->ht (list-ref l 0)) (cons (addition/deletion-lists->ht (list-ref l 0))
(cdr l))]))))) (pad-to 4 (cdr l)))])))))
(preferences:set-default 'framework:autosave-delay 30 number?) (preferences:set-default 'framework:autosave-delay 30 number?)

View File

@ -522,38 +522,48 @@
(define/public (tabify-on-return?) #t) (define/public (tabify-on-return?) #t)
(define/public (tabify [pos (get-start-position)]) (define/public (tabify [pos (get-start-position)])
(unless (is-stopped?) (unless (is-stopped?)
(let* ([tabify-prefs (preferences:get 'framework:tabify)] (define tabify-prefs (preferences:get 'framework:tabify))
[last-pos (last-position)] (define last-pos (last-position))
[para (position-paragraph pos)] (define para (position-paragraph pos))
[is-tabbable? (and (> para 0) (define is-tabbable?
(and (> para 0)
(not (memq (classify-position (- (paragraph-start-position para) 1)) (not (memq (classify-position (- (paragraph-start-position para) 1))
'(comment string error))))] '(comment string error)))))
[end (if is-tabbable? (paragraph-start-position para) 0)] (define end (if is-tabbable? (paragraph-start-position para) 0))
[limit (get-limit pos)] (define limit (get-limit pos))
;; "contains" is the start of the initial sub-S-exp ;; "contains" is the start of the initial sub-S-exp
;; in the S-exp that contains "pos". If pos is outside ;; in the S-exp that contains "pos". If pos is outside
;; all S-exps, this will be the start of the initial ;; all S-exps, this will be the start of the initial
;; S-exp ;; S-exp
[contains (define contains
(if is-tabbable? (if is-tabbable?
(backward-containing-sexp end limit) (backward-containing-sexp end limit)
#f)] #f))
[contain-para (and contains (define contain-para (and contains
(position-paragraph contains))] (position-paragraph contains)))
;; "last" is the start of the S-exp just before "pos"
[last ;; last is the start of the S-exp just before "pos"
(define last
(if contains (if contains
(let ([p (get-backward-sexp end)]) (let ([p (get-backward-sexp end)])
(if (and p (p . >= . limit)) (if (and p (p . >= . limit))
p p
(backward-match end limit))) (backward-match end limit)))
#f)] #f))
[last-para (and last (define last-para (and last (position-paragraph last)))
(position-paragraph last))])
;; last2 is the start of the S-exp just before the one before "pos"
(define last2
(if contains
(let ([p (get-backward-sexp last)])
(if (and p (p . >= . limit))
p
(backward-match last limit)))
#f))
(define sizing-dc (or (get-dc) (make-object bitmap-dc% (make-bitmap 1 1)))) (define sizing-dc (or (get-dc) (make-object bitmap-dc% (make-bitmap 1 1))))
(letrec (define (find-offset start-pos)
([find-offset
(λ (start-pos)
(define tab-char? #f) (define tab-char? #f)
(define end-pos (define end-pos
(let loop ([p start-pos]) (let loop ([p start-pos])
@ -579,10 +589,9 @@
get-font))) get-font)))
(values (inexact->exact (floor (/ (- (unbox end-x) (unbox start-x)) w))) (values (inexact->exact (floor (/ (- (unbox end-x) (unbox start-x)) w)))
end-pos end-pos
tab-char?))] tab-char?))
[visual-offset (define (visual-offset pos)
(λ (pos)
(let loop ([p (sub1 pos)]) (let loop ([p (sub1 pos)])
(if (= p -1) (if (= p -1)
0 0
@ -593,45 +602,42 @@
(let ([o (loop (sub1 p))]) (let ([o (loop (sub1 p))])
(+ o (- 8 (modulo o 8))))] (+ o (- 8 (modulo o 8))))]
[(char=? c #\newline) 0] [(char=? c #\newline) 0]
[else (add1 (loop (sub1 p)))])))))] [else (add1 (loop (sub1 p)))])))))
[do-indent
(λ (amt) (define (do-indent amt)
(define pos-start end) (define pos-start end)
(define-values (gwidth curr-offset tab-char?) (find-offset pos-start)) (define-values (gwidth curr-offset tab-char?) (find-offset pos-start))
(unless (and (not tab-char?) (= amt (- curr-offset pos-start))) (unless (and (not tab-char?) (= amt (- curr-offset pos-start)))
(delete pos-start curr-offset) (delete pos-start curr-offset)
(insert (make-string amt #\space) pos-start)))] (insert (make-string amt #\space) pos-start)))
[get-proc (define (get-proc)
(λ () (define id-end (get-forward-sexp contains))
(let ([id-end (get-forward-sexp contains)])
(and (and id-end (> id-end contains)) (and (and id-end (> id-end contains))
(let* ([text (get-text contains id-end)]) (let ([text (get-text contains id-end)])
(or (get-keyword-type text tabify-prefs) (or (get-keyword-type text tabify-prefs)
'other)))))] 'other))))
[procedure-indent (define (procedure-indent)
(λ ()
(case (get-proc) (case (get-proc)
[(begin define) 1] [(begin define) 1]
[(lambda) 3] [(lambda) 3]
[else 0]))] [else 0]))
[special-check (define (define-or-lambda-style?)
(λ () (define proc-name (get-proc))
(let* ([proc-name (get-proc)]) (or (equal? proc-name 'define)
(or (eq? proc-name 'define) (equal? proc-name 'lambda)))
(eq? proc-name 'lambda))))] (define (for/fold-style?)
[curley-brace-sexp? (define proc-name (get-proc))
(λ () (equal? proc-name 'for/fold))
(define up-p (find-up-sexp pos))
(and up-p
(equal? #\{ (get-character up-p))))]
[indent-first-arg (λ (start) (define (indent-first-arg start)
(define-values (gwidth curr-offset tab-char?) (find-offset start)) (define-values (gwidth curr-offset tab-char?) (find-offset start))
gwidth)]) gwidth)
(when (and is-tabbable? (when (and is-tabbable?
(not (char=? (get-character (sub1 end)) (not (char=? (get-character (sub1 end))
#\newline))) #\newline)))
(insert #\newline (paragraph-start-position para))) (insert #\newline (paragraph-start-position para)))
(cond (cond
[(not is-tabbable?) [(not is-tabbable?)
(when (= para 0) (when (= para 0)
@ -645,43 +651,24 @@
[(not contains) [(not contains)
;; Something went wrong matching. Should we get here? ;; Something went wrong matching. Should we get here?
(do-indent 0)] (do-indent 0)]
;; disable this to accommodate PLAI programs;
;; return to this when a #lang capability is set up.
#;
[(curley-brace-sexp?)
;; when we are directly inside an sexp that uses {}s,
;; we indent in a more C-like fashion (to help Scribble)
(define first-curley (find-up-sexp pos))
(define containing-curleys
(let loop ([pos first-curley])
(let ([next (find-up-sexp pos)])
(if (and next
(equal? (get-character next) #\{))
(+ (loop next) 1)
1))))
(define close-first-curley (get-forward-sexp first-curley))
(define para (position-paragraph pos))
(when (and close-first-curley
(<= (paragraph-start-position para)
close-first-curley
(paragraph-end-position para)))
(set! containing-curleys (max 0 (- containing-curleys 1))))
(do-indent (* containing-curleys 2))]
[(not last) [(not last)
;; We can't find a match backward from pos, ;; We can't find a match backward from pos,
;; but we seem to be inside an S-exp, so ;; but we seem to be inside an S-exp, so
;; go "up" an S-exp, and move forward past ;; go "up" an S-exp, and move forward past
;; the associated paren ;; the associated paren
(let ([enclosing (find-up-sexp pos)]) (define enclosing (find-up-sexp pos))
(if enclosing (if enclosing
(do-indent (+ (visual-offset enclosing) 1)) (do-indent (+ (visual-offset enclosing) 1))
(do-indent 0)))] (do-indent 0))]
[(= contains last) [(= contains last)
;; There's only one S-expr in the S-expr ;; this is the first expression in the define
;; containing "pos"
(do-indent (+ (visual-offset contains) (do-indent (+ (visual-offset contains)
(procedure-indent)))] (procedure-indent)))]
[(special-check) [(and (for/fold-style?)
(= contains last2))
(do-indent (- last (paragraph-start-position last-para)))]
[(or (define-or-lambda-style?)
(for/fold-style?))
;; In case of "define", etc., ignore the position of last ;; In case of "define", etc., ignore the position of last
;; and just indent under the "define" ;; and just indent under the "define"
(do-indent (add1 (visual-offset contains)))] (do-indent (add1 (visual-offset contains)))]
@ -691,10 +678,11 @@
;; so indent to follow the first S-exp's end ;; so indent to follow the first S-exp's end
;; unless there are just two sexps and the second is an ellipsis. ;; unless there are just two sexps and the second is an ellipsis.
;; in that case, we just ignore the ellipsis ;; in that case, we just ignore the ellipsis
(let ([name-length (let ([id-end (get-forward-sexp contains)]) (define id-end (get-forward-sexp contains))
(define name-length
(if id-end (if id-end
(- id-end contains) (- id-end contains)
0))]) 0))
(cond (cond
[(second-sexp-is-ellipsis? contains) [(second-sexp-is-ellipsis? contains)
(do-indent (visual-offset contains))] (do-indent (visual-offset contains))]
@ -704,17 +692,17 @@
(do-indent (+ (visual-offset contains) (do-indent (+ (visual-offset contains)
name-length name-length
(indent-first-arg (+ contains (indent-first-arg (+ contains
name-length))))]))] name-length))))])]
[else [else
;; No particular special case, so indent to match first ;; No particular special case, so indent to match first
;; S-expr that start on the previous line ;; S-expr that starts on the previous line
(let loop ([last last][last-para last-para]) (let loop ([last last][last-para last-para])
(let* ([next-to-last (backward-match last limit)] (let* ([next-to-last (backward-match last limit)]
[next-to-last-para (and next-to-last [next-to-last-para (and next-to-last
(position-paragraph next-to-last))]) (position-paragraph next-to-last))])
(if (equal? last-para next-to-last-para) (if (equal? last-para next-to-last-para)
(loop next-to-last next-to-last-para) (loop next-to-last next-to-last-para)
(do-indent (visual-offset last)))))]))))) (do-indent (visual-offset last)))))])))
;; returns #t if `contains' is at a position on a line with an sexp, an ellipsis and nothing else. ;; returns #t if `contains' is at a position on a line with an sexp, an ellipsis and nothing else.
;; otherwise, returns #f ;; otherwise, returns #f
@ -1341,12 +1329,13 @@
(|{| |}|)))))) (|{| |}|))))))
;; get-keyword-type : string (list ht regexp regexp regexp) ;; get-keyword-type : string (list ht regexp regexp regexp)
;; -> (union #f 'lambda 'define 'begin) ;; -> (or/c #f 'lambda 'define 'begin 'for/fold)
(define (get-keyword-type text pref) (define (get-keyword-type text pref)
(let* ([ht (car pref)] (define ht (car pref))
[beg-reg (cadr pref)] (define beg-reg (list-ref pref 1))
[def-reg (caddr pref)] (define def-reg (list-ref pref 2))
[lam-reg (cadddr pref)]) (define lam-reg (list-ref pref 3))
(define for/fold-reg (list-ref pref 4))
(hash-ref (hash-ref
ht ht
(with-handlers ((exn:fail:read? (λ (x) #f))) (with-handlers ((exn:fail:read? (λ (x) #f)))
@ -1356,7 +1345,8 @@
[(and beg-reg (regexp-match beg-reg text)) 'begin] [(and beg-reg (regexp-match beg-reg text)) 'begin]
[(and def-reg (regexp-match def-reg text)) 'define] [(and def-reg (regexp-match def-reg text)) 'define]
[(and lam-reg (regexp-match lam-reg text)) 'lambda] [(and lam-reg (regexp-match lam-reg text)) 'lambda]
[else #f]))))) [(and for/fold-reg (regexp-match for/fold-reg text)) 'for/fold]
[else #f]))))
;; in-position? : text (list symbol) -> boolean ;; in-position? : text (list symbol) -> boolean
@ -2104,28 +2094,27 @@
(define (make-indenting-prefs-panel p) (define (make-indenting-prefs-panel p)
(define get-keywords (define get-keywords
(λ (hash-table) (λ (hash-table)
(letrec ([all-keywords (hash-map hash-table list)] (define all-keywords (hash-map hash-table list))
[pick-out (λ (wanted in out) (define (pick-out wanted in out)
(cond (cond
[(null? in) (sort out string<=?)] [(null? in) (sort out string<?)]
[else (if (eq? wanted (cadr (car in))) [else (if (eq? wanted (cadr (car in)))
(pick-out wanted (cdr in) (pick-out wanted (cdr in)
(cons (format "~s" (car (car in))) out)) (cons (format "~s" (car (car in))) out))
(pick-out wanted (cdr in) out))]))]) (pick-out wanted (cdr in) out))]))
(values (pick-out 'begin all-keywords null) (values (pick-out 'begin all-keywords null)
(pick-out 'define all-keywords null) (pick-out 'define all-keywords null)
(pick-out 'lambda all-keywords null))))) (pick-out 'lambda all-keywords null)
(define-values (begin-keywords define-keywords lambda-keywords) (pick-out 'for/fold all-keywords null))))
(define-values (begin-keywords define-keywords lambda-keywords for/fold-keywords)
(get-keywords (car (preferences:get 'framework:tabify)))) (get-keywords (car (preferences:get 'framework:tabify))))
(define add-button-callback (define ((add-button-callback keyword-type keyword-symbol list-box) button command)
(λ (keyword-type keyword-symbol list-box) (define new-one
(λ (button command)
(let ([new-one
(keymap:call/text-keymap-initializer (keymap:call/text-keymap-initializer
(λ () (λ ()
(get-text-from-user (get-text-from-user
(format (string-constant enter-new-keyword) keyword-type) (format (string-constant enter-new-keyword) keyword-type)
(format (string-constant x-keyword) keyword-type))))]) (format (string-constant x-keyword) keyword-type)))))
(when new-one (when new-one
(let ([parsed (with-handlers ((exn:fail:read? (λ (x) #f))) (let ([parsed (with-handlers ((exn:fail:read? (λ (x) #f)))
(read (open-input-string new-one)))]) (read (open-input-string new-one)))])
@ -2144,10 +2133,8 @@
(update-list-boxes ht))] (update-list-boxes ht))]
[else (message-box [else (message-box
(string-constant error) (string-constant error)
(format (string-constant expected-a-symbol) new-one))]))))))) (format (string-constant expected-a-symbol) new-one))]))))
(define delete-callback (define ((delete-callback list-box) button command)
(λ (list-box)
(λ (button command)
(define selections (send list-box get-selections)) (define selections (send list-box get-selections))
(define symbols (define symbols
(map (λ (x) (read (open-input-string (send list-box get-string x)))) selections)) (map (λ (x) (read (open-input-string (send list-box get-string x)))) selections))
@ -2155,18 +2142,17 @@
(define pref (preferences:get 'framework:tabify)) (define pref (preferences:get 'framework:tabify))
(define ht (car pref)) (define ht (car pref))
(for-each (λ (x) (hash-remove! ht x)) symbols) (for-each (λ (x) (hash-remove! ht x)) symbols)
(preferences:set 'framework:tabify pref)))) (preferences:set 'framework:tabify pref))
(define main-panel (make-object horizontal-panel% p)) (define main-panel (make-object horizontal-panel% p))
(define make-column (define (make-column string symbol keywords bang-regexp)
(λ (string symbol keywords bang-regexp) (define vert (make-object vertical-panel% main-panel))
(let* ([vert (make-object vertical-panel% main-panel)] (make-object message% (format (string-constant x-like-keywords) string) vert)
[_ (make-object message% (format (string-constant x-like-keywords) string) vert)] (define box (make-object list-box% #f keywords vert void '(multiple)))
[box (make-object list-box% #f keywords vert void '(multiple))] (define button-panel (make-object horizontal-panel% vert))
[button-panel (make-object horizontal-panel% vert)] (define text (new text-field%
[text (new text-field%
(label (string-constant indenting-prefs-extra-regexp)) (label (string-constant indenting-prefs-extra-regexp))
(callback (λ (tf evt) (callback (λ (tf evt)
(let ([str (send tf get-value)]) (define str (send tf get-value))
(cond (cond
[(equal? str "") [(equal? str "")
(bang-regexp #f)] (bang-regexp #f)]
@ -2175,17 +2161,17 @@
(λ (x) (λ (x)
(color-yellow (send tf get-editor)))]) (color-yellow (send tf get-editor)))])
(bang-regexp (regexp str)) (bang-regexp (regexp str))
(clear-color (send tf get-editor)))])))) (clear-color (send tf get-editor)))])))
(parent vert))] (parent vert)))
[add-button (make-object button% (string-constant add-keyword) (define add-button (make-object button% (string-constant add-keyword)
button-panel (add-button-callback string symbol box))] button-panel (add-button-callback string symbol box)))
[delete-button (make-object button% (string-constant remove-keyword) (define delete-button (make-object button% (string-constant remove-keyword)
button-panel (delete-callback box))]) button-panel (delete-callback box)))
(send* button-panel (send* button-panel
(set-alignment 'center 'center) (set-alignment 'center 'center)
(stretchable-height #f)) (stretchable-height #f))
(send add-button min-width (send delete-button get-width)) (send add-button min-width (send delete-button get-width))
(values box text)))) (values box text))
(define (color-yellow text) (define (color-yellow text)
(let ([sd (make-object style-delta%)]) (let ([sd (make-object style-delta%)])
(send sd set-delta-background "yellow") (send sd set-delta-background "yellow")
@ -2217,21 +2203,29 @@
'lambda 'lambda
lambda-keywords lambda-keywords
(λ (x) (update-pref 3 x)))) (λ (x) (update-pref 3 x))))
(define-values (for/fold-list-box for/fold-regexp-text)
(make-column "For/fold"
'for/fold
for/fold-keywords
(λ (x) (update-pref 4 x))))
(define (update-list-boxes hash-table) (define (update-list-boxes hash-table)
(let-values ([(begin-keywords define-keywords lambda-keywords) (get-keywords hash-table)] (define-values (begin-keywords define-keywords lambda-keywords for/fold-keywords)
[(reset) (λ (list-box keywords) (get-keywords hash-table))
(define (reset list-box keywords)
(send list-box clear) (send list-box clear)
(for-each (λ (x) (send list-box append x)) keywords))]) (for-each (λ (x) (send list-box append x)) keywords))
(reset begin-list-box begin-keywords) (reset begin-list-box begin-keywords)
(reset define-list-box define-keywords) (reset define-list-box define-keywords)
(reset lambda-list-box lambda-keywords) (reset lambda-list-box lambda-keywords)
#t)) (reset for/fold-list-box for/fold-keywords)
#t)
(define update-gui (define update-gui
(λ (pref) (λ (pref)
(update-list-boxes (car pref)) (update-list-boxes (car pref))
(send begin-regexp-text set-value (or (object-name (cadr pref)) "")) (send begin-regexp-text set-value (or (object-name (list-ref pref 1)) ""))
(send define-regexp-text set-value (or (object-name (caddr pref)) "")) (send define-regexp-text set-value (or (object-name (list-ref pref 2)) ""))
(send lambda-regexp-text set-value (or (object-name (cadddr pref)) "")))) (send lambda-regexp-text set-value (or (object-name (list-ref pref 3)) ""))
(send for/fold-regexp-text set-value (or (object-name (list-ref pref 4)) ""))))
(preferences:add-callback 'framework:tabify (λ (p v) (update-gui v))) (preferences:add-callback 'framework:tabify (λ (p v) (update-gui v)))
(update-gui (preferences:get 'framework:tabify)) (update-gui (preferences:get 'framework:tabify))
main-panel) main-panel)

View File

@ -31,9 +31,15 @@
(test-text-balanced? 8 "{foo} ((bar) [5.9])" 0 #f #t) (test-text-balanced? 8 "{foo} ((bar) [5.9])" 0 #f #t)
(test-text-balanced? 9 "#(1 2 . 3)" 0 #f #t) (test-text-balanced? 9 "#(1 2 . 3)" 0 #f #t)
(define (test-indentation which before after) (define-syntax (test-indentation stx)
(syntax-case stx ()
[(_ . args)
(with-syntax ([line (syntax-line stx)])
#'(test-indentation/proc line . args))]))
(define (test-indentation/proc line before after)
(test (test
(string->symbol (format "racket:test-indentation-~a" which)) (string->symbol (format "racket:test-indentation-line-~a" line))
(λ (x) (equal? x after)) (λ (x) (equal? x after))
(λ () (λ ()
(queue-sexp-to-mred (queue-sexp-to-mred
@ -45,16 +51,17 @@
(send t tabify-all) (send t tabify-all)
(send t get-text)))))) (send t get-text))))))
(test-indentation 1 "a" "a") (test-indentation "a" "a")
(test-indentation 2 "(a\n b)" "(a\n b)") (test-indentation "(a\n b)" "(a\n b)")
(test-indentation 3 "(a\nb)" "(a\n b)") (test-indentation "(a\nb)" "(a\n b)")
(test-indentation 3 "(a b\nc)" "(a b\n c)") (test-indentation "(a b\nc)" "(a b\n c)")
(test-indentation 3 "(a ...\nb)" "(a ...\n b)") (test-indentation "(a ...\nb)" "(a ...\n b)")
(test-indentation 4 "(lambda (x)\nb)" "(lambda (x)\n b)") (test-indentation "(lambda (x)\nb)" "(lambda (x)\n b)")
(test-indentation 5 "(lambdaa (x)\nb)" "(lambdaa (x)\n b)") (test-indentation "(lambdaa (x)\nb)" "(lambdaa (x)\n b)")
(test-indentation 6 (test-indentation "(define x\n (let/ec return\n (when 1\n (when 2\n\t\t 3))\n 2))"
"(define x\n (let/ec return\n (when 1\n (when 2\n\t\t 3))\n 2))"
"(define x\n (let/ec return\n (when 1\n (when 2\n 3))\n 2))") "(define x\n (let/ec return\n (when 1\n (when 2\n 3))\n 2))")
(test-indentation "(for/fold ([x 1])\n([y 2])\n3\n4)"
"(for/fold ([x 1])\n ([y 2])\n 3\n 4)")
(define (test-magic-square-bracket which before after) (define (test-magic-square-bracket which before after)
(test (test