From ba3ad6b1bca388d8bf9689ff493cc69b8420105c Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 26 Sep 2026 01:05:00 +0700 Subject: [PATCH] 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 --- emacs/flan-fln-mode.el | 58 ++++++++++++++++++++++++++++++++----- emacs/test-flan-fln-live.el | 10 ++++++- emacs/test-flan-fln.el | 25 ++++++++++++++++ 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/emacs/flan-fln-mode.el b/emacs/flan-fln-mode.el index 50db36f2..34b3cdad 100644 --- a/emacs/flan-fln-mode.el +++ b/emacs/flan-fln-mode.el @@ -285,6 +285,23 @@ depth, outside strings and comments, or nil." (flan-fln--joined-end l)))) (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) "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." @@ -675,7 +692,21 @@ forms, where the clause line itself is not." (arm (plist-get arm :value)) ((and l (flan-fln--clause-line-p l)) (flan-fln--clause-target l)) (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) "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 ((looking-at "elif[ \t]+") (cons (match-end 0) (or (flan-fln--then l) end))) - ((and (looking-at "else[ \t]+") (< (match-end 0) end)) - (cons (match-end 0) end)) + ((flan-fln--clause-value l)) (t (flan-fln--body-bounds 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 (save-match-data (goto-char (line-beginning-position)) - (re-search-forward "\\(?:^[ \t]*fn-?[ \t]\\|\\_ i64 restart retry() \"Try again\" 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 match d Dir.north -> 7 @@ -276,7 +282,9 @@ comment(): ("a: i64, b" "a typed lambda, from its fn") ("a * 2" "a typed lambda's 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)) (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" diff --git a/emacs/test-flan-fln.el b/emacs/test-flan-fln.el index 544fb8a0..10721aaf 100644 --- a/emacs/test-flan-fln.el +++ b/emacs/test-flan-fln.el @@ -639,6 +639,18 @@ of its line with AT-END." (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))))) '(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" (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)") @@ -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 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--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 @@ -954,6 +973,12 @@ of its line with AT-END." "5") ("yak" ,(test-flan-fln--at test-flan-fln--wrapped "Some(_)") " 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") ,(substring test-flan-fln--settle (string-search "fn step" test-flan-fln--settle)))))