Emacs offers no binding column after a binding's else block, and offers one after a first binding whose bracketed lambda has closed

This commit is contained in:
Joseph Ferano 2026-09-26 12:15:04 +07:00
parent 349e2d7e72
commit f7781dbb78
2 changed files with 34 additions and 6 deletions

View File

@ -1357,6 +1357,24 @@ block lambda inside brackets, whose block closes with them."
(= (car (save-excursion (syntax-ppss (flan-fln--code-end last)))) (= (car (save-excursion (syntax-ppss (flan-fln--code-end last))))
(car (save-excursion (syntax-ppss (flan-fln--first-char l)))))))) (car (save-excursion (syntax-ppss (flan-fln--first-char l))))))))
(defun flan-fln--bindings-ended-p (l)
"Non-nil if the line L, on a block column above, is a let's binding whose
value ended in an open block, so no binding and no clause may follow at its
column. An `if', `handler-case', `handler-bind' or `restart-case' value
still takes a clause there, and so does the line after an `elif', `on' or
`restart'; after an `else', nothing does."
(save-excursion
(goto-char (flan-fln--first-char l))
(cond
((flan-fln--clause-line-p l)
(and (looking-at "else\\_>")
(let ((h (flan-fln--clause-header l)))
(and (not (eq h l)) (flan-fln--binding-let h) (flan-fln--open-ended-p h)))))
(t (and (flan-fln--binding-let l) (flan-fln--open-ended-p l)
(let ((v (flan-fln--value-start l)))
(not (and v (progn (goto-char v)
(looking-at "\\(?:if\\|handler-case\\|handler-bind\\|restart-case\\)\\_>"))))))))))
(defun flan-fln--levels (pos) (defun flan-fln--levels (pos)
"Columns TAB offers POS's line outside brackets, deepest first. "Columns TAB offers POS's line outside brackets, deepest first.
Not the column of a let's binding whose value ended in an open block: that Not the column of a let's binding whose value ended in an open block: that
@ -1365,9 +1383,9 @@ the reader refuses any line there."
(let* ((prev (flan-fln--prev-code pos)) (let* ((prev (flan-fln--prev-code pos))
(stack (mapcar #'car (stack (mapcar #'car
(seq-remove (lambda (e) (seq-remove (lambda (e)
(and (cdr e) (flan-fln--binding-let (cdr e)) (and (cdr e) (flan-fln--bindings-ended-p (cdr e))))
(flan-fln--open-ended-p (cdr e)))) (flan-fln--stack pos))))
(flan-fln--stack pos))))) (owner (and prev (flan-fln--outside-block (flan-fln--logical-start prev) pos))))
(cond (cond
;; A lambda's block goes under the line its `=>' ends, which may be a ;; A lambda's block goes under the line its `=>' ends, which may be a
;; line of a call wrapped inside its brackets. ;; line of a call wrapped inside its brackets.
@ -1380,8 +1398,10 @@ the reader refuses any line there."
;; After a let's line, its next binding's column too, lined up with ;; After a let's line, its next binding's column too, lined up with
;; its first name -- second, so RET after a let stays at its column. ;; its first name -- second, so RET after a let stays at its column.
;; After a binding line, that column is on the stack, first. ;; After a binding line, that column is on the stack, first.
((and prev (flan-fln--let-line-p (flan-fln--logical-start prev))) ;; A block lambda whose brackets closed on the line above counts as
(let ((b (flan-fln--binding-column (flan-fln--logical-start prev)))) ;; that let's line: its block is shut, and a binding may follow.
((and owner (flan-fln--let-line-p owner) (not (flan-fln--open-ended-p owner)))
(let ((b (flan-fln--binding-column owner)))
(if (or (null b) (memq b stack)) stack (if (or (null b) (memq b stack)) stack
(cons (car stack) (cons b (cdr stack)))))) (cons (car stack) (cons b (cdr stack))))))
(t stack)))) (t stack))))

View File

@ -943,7 +943,15 @@ defconst(k, 3)
("fn f()\n let a = 1\n b = match a\n 1 -> 2\n|" (8 2 0) "a match's arms") ("fn f()\n let a = 1\n b = match a\n 1 -> 2\n|" (8 2 0) "a match's arms")
("let a = 0\n g = fn(x) =>\n x + 1\n|" (6 0) "a global's lambda block") ("let a = 0\n g = fn(x) =>\n x + 1\n|" (6 0) "a global's lambda block")
("fn f()\n let a = 1\n g = map(xs, fn(x) =>\n x + 1)\n|" (6 2 0) ("fn f()\n let a = 1\n g = map(xs, fn(x) =>\n x + 1)\n|" (6 2 0)
"but a lambda's brackets closed, where the column stays"))) "but a lambda's brackets closed, where the column stays")
("fn f(c)\n let a = 1\n b = if c\n 1\n else\n 2\n|" (8 2 0)
"an if's else block")
("fn f(c)\n let a = 1\n b = if c\n 1\n|" (8 6 2 0)
"but an if's block, where its else may go")
("fn f(c, d)\n let a = 1\n b = if c\n 1\n elif d\n 2\n|" (8 6 2 0)
"and an elif's block, where another clause may go")
("fn f()\n let g = map(xs, fn(x) =>\n x + 1)\n|" (2 6 0)
"a first binding's bracketed lambda offers the column")))
(test-flan-fln--is (format "no binding column after a binding's open block: %s" (nth 2 c)) (test-flan-fln--is (format "no binding column after a binding's open block: %s" (nth 2 c))
(test-flan-fln--in (car c) (flan-fln--block-levels (point))) (test-flan-fln--in (car c) (flan-fln--block-levels (point)))
(cadr c))) (cadr c)))