diff --git a/emacs/flan-fln-mode.el b/emacs/flan-fln-mode.el index d87ad0b7..f9d35cee 100644 --- a/emacs/flan-fln-mode.el +++ b/emacs/flan-fln-mode.el @@ -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)))) diff --git a/emacs/test-flan-fln.el b/emacs/test-flan-fln.el index c01ad997..07d8bc01 100644 --- a/emacs/test-flan-fln.el +++ b/emacs/test-flan-fln.el @@ -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)))