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--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)
"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
@ -1365,9 +1383,9 @@ the reader refuses any line there."
(let* ((prev (flan-fln--prev-code pos))
(stack (mapcar #'car
(seq-remove (lambda (e)
(and (cdr e) (flan-fln--binding-let (cdr e))
(flan-fln--open-ended-p (cdr e))))
(flan-fln--stack pos)))))
(and (cdr e) (flan-fln--bindings-ended-p (cdr e))))
(flan-fln--stack pos))))
(owner (and prev (flan-fln--outside-block (flan-fln--logical-start prev) pos))))
(cond
;; A lambda's block goes under the line its `=>' ends, which may be a
;; 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
;; its first name -- second, so RET after a let stays at its column.
;; After a binding line, that column is on the stack, first.
((and prev (flan-fln--let-line-p (flan-fln--logical-start prev)))
(let ((b (flan-fln--binding-column (flan-fln--logical-start prev))))
;; A block lambda whose brackets closed on the line above counts as
;; 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
(cons (car stack) (cons b (cdr 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")
("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)
"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--in (car c) (flan-fln--block-levels (point)))
(cadr c)))