Commit73e3e4faRecorded3 Mar 2026Repositorysigil-format

Optimize formatter analysis phase: move indent checks into lparen branch

Message

Move check-indent-decrease and check-indent-mismatch inside the lparen case branch instead of calling them on every token. Compute form-type once per lparen and pass to both helpers. Inline content-token? check with direct eq? comparisons. Use direct vector-ref for token fields.

Analysis phase: ~2.6s -> ~1.3s on build.sgl. Total: 4.5s -> 2.5s.

Changed
 src/sigil/format.sgl | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------
 1 file changed, 63 insertions(+), 74 deletions(-)
Diff
src/sigil/format.sglmodified
@@ -510,27 +510,19 @@
510
511
;; Helper: check if a form appears at unexpectedly deep indent
512
;; This suggests a previous form was closed prematurely
513
(define (check-indent-mismatch state tok next-tokens chars)
514
(let ((tok-type (token-type tok))
515
(tok-indent (token-indent tok))
516
(tok-line (token-line tok))
+513
;; Called only for lparen/lbracket at line start with pre-computed form-type
+514
(define (check-indent-mismatch state tok form-type)
+515
(let ((tok-indent (vector-ref tok 5))
516
(current-depth (analysis-state-depth state)))
518
;; Only check opening parens at start of line (column should be indent+1)
519
(if (and (memq tok-type '(lparen lbracket))
520
(= (token-column tok) (+ tok-indent 1)))
521
(let ((expected-indent (expected-indent-for-depth current-depth))
522
(form-type (get-form-type next-tokens chars)))
523
;; If indent is deeper than expected for current depth
524
;; AND it looks like a major form (define, let, etc.), warn
525
;; This catches cases like begin closing too early
526
(if (and form-type
527
(memq form-type '(define define-syntax define-library define-struct
528
let let* letrec begin))
529
(> tok-indent expected-indent)) ; Deeper than expected
+517
(if (and form-type
+518
(memq form-type '(define define-syntax define-library define-struct
+519
let let* letrec begin)))
+520
(let ((expected-indent (* current-depth 2)))
+521
(if (> tok-indent expected-indent)
522
(set-analysis-state-warnings! state
523
(cons (format-warning
532
line: tok-line
533
column: (token-column tok)
+524
line: (vector-ref tok 3)
+525
column: (vector-ref tok 4)
526
message: (format "~a at column ~a is deeper than expected ~a for nesting depth ~a - check if previous form closed too early"
527
form-type (+ tok-indent 1) (+ expected-indent 1) current-depth)
528
code: "W001")
@@ -550,41 +542,34 @@
542
;; Helper: check if we should infer closing parens
543
;; Trigger when we see a form (like define) at the same indentation
544
;; as an unclosed form of the same type in the stack
553
;; prev-tok is the last non-whitespace/newline token seen
554
(define (check-indent-decrease state tok next-tokens prev-content-line chars)
555
(let ((tok-type (token-type tok))
556
(tok-indent (token-indent tok))
557
(tok-line (token-line tok)))
558
;; Only check when we see an opening paren at the start of a line
559
(if (and (memq tok-type '(lparen lbracket))
560
(= (token-column tok) (+ tok-indent 1))) ; first token on line
561
(let ((form-type (get-form-type next-tokens chars)))
562
;; Check if there's an unclosed form of the same type at same indent
563
(if (and form-type
564
(memq form-type '(define define-syntax define-struct let let* letrec)))
565
(let ((match-count (find-matching-form-in-stack state form-type tok-indent)))
566
(if match-count
567
;; Close all parens up to and including the matching one
568
;; Use prev-content-line so ) goes at end of previous content
569
(let ((close-at-line (or prev-content-line tok-line)))
570
(let loop ((to-close (+ match-count 1)))
571
(if (and (> to-close 0) (not (null? (analysis-state-paren-stack state))))
572
(let ((top (car (analysis-state-paren-stack state))))
573
(set-analysis-state-inferences! state
574
(cons (paren-inference
575
line: close-at-line
576
column: 9999 ; End of line marker
577
type: 'close
578
confidence: 'high
579
reason: (format "New ~a at same level - ~a from line ~a should close"
580
form-type
581
(if (eq? (paren-info-type top) 'lparen) "(" "[")
582
(paren-info-line top)))
583
(analysis-state-inferences state)))
584
(set-analysis-state-paren-stack! state (cdr (analysis-state-paren-stack state)))
585
(set-analysis-state-depth! state (- (analysis-state-depth state) 1))
586
(loop (- to-close 1))))))))))))
587
#f)
+545
;; Called only for lparen/lbracket at line start with pre-computed form-type
+546
(define (check-indent-decrease state tok form-type prev-content-line)
+547
(if (and form-type
+548
(memq form-type '(define define-syntax define-struct let let* letrec)))
+549
(let ((tok-indent (vector-ref tok 5))
+550
(tok-line (vector-ref tok 3)))
+551
(let ((match-count (find-matching-form-in-stack state form-type tok-indent)))
+552
(if match-count
+553
;; Close all parens up to and including the matching one
+554
;; Use prev-content-line so ) goes at end of previous content
+555
(let ((close-at-line (or prev-content-line tok-line)))
+556
(let loop ((to-close (+ match-count 1)))
+557
(if (and (> to-close 0) (not (null? (analysis-state-paren-stack state))))
+558
(let ((top (car (analysis-state-paren-stack state))))
+559
(set-analysis-state-inferences! state
+560
(cons (paren-inference
+561
line: close-at-line
+562
column: 9999 ; End of line marker
+563
type: 'close
+564
confidence: 'high
+565
reason: (format "New ~a at same level - ~a from line ~a should close"
+566
form-type
+567
(if (eq? (paren-info-type top) 'lparen) "(" "[")
+568
(paren-info-line top)))
+569
(analysis-state-inferences state)))
+570
(set-analysis-state-paren-stack! state (cdr (analysis-state-paren-stack state)))
+571
(set-analysis-state-depth! state (- (analysis-state-depth state) 1))
+572
(loop (- to-close 1)))))))))))
573
574
;; Helper: check if a token is content (not whitespace/newline/comment)
575
(define (content-token? tok)
@@ -626,33 +611,36 @@
611
(reverse (analysis-state-errors state))
612
(reverse (analysis-state-warnings state))))
613
629
(let ((tok (car ts))
630
(rest (cdr ts)))
631
;; Check for indent-based inference
632
(check-indent-decrease state tok rest prev-content-line chars)
633
;; Check for indent mismatch (suggests premature closing)
634
(check-indent-mismatch state tok rest chars)
+614
(let* ((tok (car ts))
+615
(rest (cdr ts))
+616
(ttype (vector-ref tok 0)))
617
618
;; Process token
637
(case (token-type tok)
+619
(case ttype
620
((lparen)
639
(set-analysis-state-paren-stack! state
640
(cons (paren-info
641
type: 'lparen
642
line: (token-line tok)
643
column: (token-column tok)
644
indent: (token-indent tok)
645
form-type: (get-form-type rest chars))
646
(analysis-state-paren-stack state)))
647
(set-analysis-state-depth! state (+ (analysis-state-depth state) 1)))
+621
;; Compute form-type once; check indent only at line start
+622
(let* ((at-line-start (= (vector-ref tok 4) (+ (vector-ref tok 5) 1)))
+623
(form-type (get-form-type rest chars)))
+624
(when at-line-start
+625
(check-indent-decrease state tok form-type prev-content-line)
+626
(check-indent-mismatch state tok form-type))
+627
(set-analysis-state-paren-stack! state
+628
(cons (paren-info
+629
type: 'lparen
+630
line: (vector-ref tok 3)
+631
column: (vector-ref tok 4)
+632
indent: (vector-ref tok 5)
+633
form-type: form-type)
+634
(analysis-state-paren-stack state)))
+635
(set-analysis-state-depth! state (+ (analysis-state-depth state) 1))))
636
637
((lbracket)
638
(set-analysis-state-paren-stack! state
639
(cons (paren-info
640
type: 'lbracket
653
line: (token-line tok)
654
column: (token-column tok)
655
indent: (token-indent tok)
+641
line: (vector-ref tok 3)
+642
column: (vector-ref tok 4)
+643
indent: (vector-ref tok 5)
644
form-type: #f)
645
(analysis-state-paren-stack state)))
646
(set-analysis-state-depth! state (+ (analysis-state-depth state) 1)))
@@ -760,8 +748,9 @@
748
749
;; Update prev-content-line if this is a content token
750
(loop rest tok
763
(if (content-token? tok)
764
(token-line tok)
+751
(if (not (or (eq? ttype 'whitespace) (eq? ttype 'newline)
+752
(eq? ttype 'comment) (eq? ttype 'eof)))
+753
(vector-ref tok 3)
754
prev-content-line)))))))
755
756
;; ============================================================