In a .fln buffer ik takes the value on a one-line else or elif line, a wrapped fn header's return type is coloured, and C-u C-c C-c on a let the let above merges in stops at its value

This commit is contained in:
Joseph Ferano 2026-09-26 01:05:00 +07:00
parent 02fcb1725e
commit ba3ad6b1bc
3 changed files with 85 additions and 8 deletions

View File

@ -285,6 +285,23 @@ depth, outside strings and comments, or nil."
(flan-fln--joined-end l)))) (flan-fln--joined-end l))))
(and m (car m)))) (and m (car m))))
(defun flan-fln--clause-value (l)
"Bounds of the value on the clause line L itself: `x' of `else x' or of
`elif c then x'; nil when the clause's value is its block."
(let ((end (flan-fln--joined-end l))
(then (flan-fln--then l)))
(save-excursion
(goto-char (flan-fln--first-char l))
(cond
((and (looking-at "elif[ \t]") then)
(goto-char then)
(skip-chars-forward " \t")
(forward-char 4)
(skip-chars-forward " \t")
(and (< (point) end) (cons (point) end)))
((and (looking-at "else[ \t]+") (< (match-end 0) end))
(cons (match-end 0) end))))))
(defun flan-fln--value-start (l) (defun flan-fln--value-start (l)
"Where the value of the joined line L starts, after its first `=' or "Where the value of the joined line L starts, after its first `=' or
`+=' at the line's own depth, or nil when it binds or assigns nothing." `+=' at the line's own depth, or nil when it binds or assigns nothing."
@ -675,7 +692,21 @@ forms, where the clause line itself is not."
(arm (plist-get arm :value)) (arm (plist-get arm :value))
((and l (flan-fln--clause-line-p l)) (flan-fln--clause-target l)) ((and l (flan-fln--clause-line-p l)) (flan-fln--clause-target l))
(t (let ((s (flan-fln--statement-start-at pos))) (t (let ((s (flan-fln--statement-start-at pos)))
(and s (flan-fln--statement-bounds s))))))) (and s (or (flan-fln--merged-let-value s)
(flan-fln--statement-bounds s))))))))
(defun flan-fln--merged-let-value (s)
"Bounds of the value of the `let' at S when the let before it takes it in.
The reader merges consecutive lets into one binding vector, so a second
`let b = 2' starts no form of its own; its value, or its value's block, is
the form that runs where the line stands."
(let ((p (and (flan-fln--let-p s) (flan-fln--sibling s -1))))
(when (and p (flan-fln--let-p p))
(let ((v (flan-fln--value-start s))
(end (flan-fln--joined-end s)))
(if (and v (< v end))
(cons v (cdr (flan-fln--statement-bounds s)))
(flan-fln--body-bounds s))))))
(defun flan-fln--clause-target (l) (defun flan-fln--clause-target (l)
"What a clause line L stops at: an elif's condition, else its block. "What a clause line L stops at: an elif's condition, else its block.
@ -686,8 +717,7 @@ A one-line `else x' stops at x."
(cond (cond
((looking-at "elif[ \t]+") ((looking-at "elif[ \t]+")
(cons (match-end 0) (or (flan-fln--then l) end))) (cons (match-end 0) (or (flan-fln--then l) end)))
((and (looking-at "else[ \t]+") (< (match-end 0) end)) ((flan-fln--clause-value l))
(cons (match-end 0) end))
(t (flan-fln--body-bounds l)))))) (t (flan-fln--body-bounds l))))))
(defun flan-fln--condition (l) (defun flan-fln--condition (l)
@ -1505,8 +1535,20 @@ lambda or a `Fn(...)' type, and not after a match arm's."
(setq found (save-excursion (setq found (save-excursion
(save-match-data (save-match-data
(goto-char (line-beginning-position)) (goto-char (line-beginning-position))
(re-search-forward "\\(?:^[ \t]*fn-?[ \t]\\|\\_<C?[fF]n(\\)" (or (re-search-forward "\\(?:^[ \t]*fn-?[ \t]\\|\\_<C?[fF]n(\\)"
arrow t)))))) arrow t)
;; A header wrapped inside its parentheses: the
;; `)' before the arrow closes a `fn f(' above.
(progn
(goto-char arrow)
(skip-chars-backward " \t")
(and (eq (char-before) ?\))
(let ((open (ignore-errors (scan-lists (point) -1 0))))
(and open
(progn
(goto-char open)
(looking-back "\\(?:^[ \t]*fn-?[ \t]+[^][ \t\n(){},;\":]+\\|\\_<C?[fF]n\\)"
(line-beginning-position)))))))))))))
found)) found))
(defvar flan-fln-font-lock-keywords (defvar flan-fln-font-lock-keywords
@ -1777,10 +1819,12 @@ below a form is not taken: it belongs to what follows."
(bounds-of-thing-at-point 'flan-fln-statement)) (bounds-of-thing-at-point 'flan-fln-statement))
'line)) 'line))
(evil-define-text-object flan-fln-inner-clause (count &optional _beg _end _type) (evil-define-text-object flan-fln-inner-clause (count &optional _beg _end _type)
"A clause's block, its lines; a match arm's value when it is on the line." "A clause's block, its lines; the value on the line of a match arm, a
one-line else or an elif's `then'."
(let* ((c (flan-fln--clause-at (point))) (let* ((c (flan-fln--clause-at (point)))
(arm (and c (flan-fln--arm c))) (arm (and c (flan-fln--arm c)))
(v (and arm (plist-get arm :value)))) (v (if arm (plist-get arm :value)
(and c (flan-fln--clause-value c)))))
(if (and v (= (flan-fln--bol (car v)) c)) (if (and v (= (flan-fln--bol (car v)) c))
(flan-fln--evil v 'exclusive) (flan-fln--evil v 'exclusive)
(flan-fln--evil (flan-fln--whole-lines (and c (flan-fln--body-bounds c))) (flan-fln--evil (flan-fln--whole-lines (and c (flan-fln--body-bounds c)))

View File

@ -74,6 +74,12 @@ fn rs() -> i64
restart retry() \"Try again\" restart retry() \"Try again\"
4 4
fn t2(k: i64) -> i64
let a = 1
let b = 2
let c: i64 = 3
a + b + c + k
fn dir(d: Dir) -> i64 fn dir(d: Dir) -> i64
match d match d
Dir.north -> 7 Dir.north -> 7
@ -276,7 +282,9 @@ comment():
("a: i64, b" "a typed lambda, from its fn") ("a: i64, b" "a typed lambda, from its fn")
("a * 2" "a typed lambda's block") ("a * 2" "a typed lambda's block")
("restart retry" "a restart with a report, at its block") ("restart retry" "a restart with a report, at its block")
("Dir.north" "an enum member's arm, at its value"))) ("Dir.north" "an enum member's arm, at its value")
("let b = 2" "a let the let above takes in, at its value")
("let c: i64" "a typed one, at its value")))
(funcall goto (car c)) (funcall goto (car c))
(let ((reply (flan-fln-eval-defun '(4)))) (let ((reply (flan-fln-eval-defun '(4))))
(test-flan--check (funcall name (format "C-u C-c C-c marks %s where the reader starts it" (test-flan--check (funcall name (format "C-u C-c C-c marks %s where the reader starts it"

View File

@ -639,6 +639,18 @@ of its line with AT-END."
(test-flan-fln--is "an else x after a block if too" (test-flan-fln--is "an else x after a block if too"
(cadr (test-flan-fln--oneline-at "else 2" (lambda () (flan-fln-eval-defun '(4))))) (cadr (test-flan-fln--oneline-at "else 2" (lambda () (flan-fln-eval-defun '(4)))))
'(9 8)) '(9 8))
(test-flan-fln--is "C-u C-c C-c on a let the let above takes in stops at its value"
(test-flan-fln--in "fn t2(n: i64) -> i64\n let a = 1\n |let b = 2\n a + b + n\n"
(plist-get (test-flan-fln--sending (flan-fln-eval-defun '(4))) :pause))
'(3 11))
(test-flan-fln--is "and at its block when the value is one"
(test-flan-fln--in "fn t2(n: i64) -> i64\n let a = 1\n |let b =\n n + 1\n a + b\n"
(plist-get (test-flan-fln--sending (flan-fln-eval-defun '(4))) :pause))
'(4 5))
(test-flan-fln--is "the first let of a run stops at the let"
(test-flan-fln--in "fn t2(n: i64) -> i64\n n + 1\n |let a = 1\n a\n"
(plist-get (test-flan-fln--sending (flan-fln-eval-defun '(4))) :pause))
'(3 3))
(test-flan-fln--is "C-c C-e on a let whose value is a block sends its scope with it" (test-flan-fln--is "C-c C-e on a let whose value is a block sends its scope with it"
(car (test-flan-fln--oneline-at "let r" #'flan-fln-eval-statement)) (car (test-flan-fln--oneline-at "let r" #'flan-fln-eval-statement))
"let r = match n\n 0 -> 1\n _ -> 2\n add(r, 1)") "let r = match n\n 0 -> 1\n _ -> 2\n add(r, 1)")
@ -699,6 +711,13 @@ of its line with AT-END."
(test-flan-fln--is "an enum member as a constant" (funcall face "Dir.north") 'font-lock-constant-face) (test-flan-fln--is "an enum member as a constant" (funcall face "Dir.north") 'font-lock-constant-face)
(test-flan-fln--is "an arm's value is not a type" (funcall face "twice(1)") nil) (test-flan-fln--is "an arm's value is not a type" (funcall face "twice(1)") nil)
(test-flan-fln--is "nor after a pattern with parentheses" (funcall face "r\n") nil))) (test-flan-fln--is "nor after a pattern with parentheses" (funcall face "r\n") nil)))
(test-flan-fln--in "fn far(a: i64,\n b: i64) -> Point\n match a\n Some(x) -> Other\n"
(font-lock-ensure)
(let ((face (lambda (needle)
(save-excursion (goto-char (point-min)) (search-forward needle)
(get-text-property (match-beginning 0) 'face)))))
(test-flan-fln--is "a wrapped fn header's return type is a type" (funcall face "Point") 'font-lock-type-face)
(test-flan-fln--is "an arm's value after a call pattern is not" (funcall face "Other") nil)))
;;; Indentation ;;; Indentation
@ -954,6 +973,12 @@ of its line with AT-END."
"5") "5")
("yak" ,(test-flan-fln--at test-flan-fln--wrapped "Some(_)") ("yak" ,(test-flan-fln--at test-flan-fln--wrapped "Some(_)")
" Some(_) -> 5\n") " Some(_) -> 5\n")
("yik" ,(test-flan-fln--at test-flan-fln--oneline "else 1")
"1")
("yik" ,(test-flan-fln--at test-flan-fln--oneline "elif n")
"0")
("yik" ,(test-flan-fln--at test-flan-fln--oneline "else 2")
"2")
("yid" ,(test-flan-fln--at test-flan-fln--settle "paint-at") ("yid" ,(test-flan-fln--at test-flan-fln--settle "paint-at")
,(substring test-flan-fln--settle ,(substring test-flan-fln--settle
(string-search "fn step" test-flan-fln--settle))))) (string-search "fn step" test-flan-fln--settle)))))