A binding whose value is a block ends its let's bindings, a tab between let and its name is refused, and only a binding-shaped line under a let gets the alignment advice
This commit is contained in:
parent
b5380b4750
commit
63dc853121
@ -486,12 +486,15 @@ on its line, which is not the statement's."
|
||||
|
||||
(defun flan-fln--binding-column (l)
|
||||
"The column of the first name on the `let' line L: where the let's other
|
||||
bindings line up."
|
||||
bindings line up. Nil when a tab sits before the name: a tab has no one
|
||||
width, and the reader refuses it there."
|
||||
(save-excursion
|
||||
(goto-char (flan-fln--first-char l))
|
||||
(skip-chars-forward "let")
|
||||
(skip-chars-forward " \t")
|
||||
(current-column)))
|
||||
(let ((from (point)))
|
||||
(skip-chars-forward " \t")
|
||||
(unless (save-excursion (search-backward "\t" from t))
|
||||
(current-column)))))
|
||||
|
||||
(defun flan-fln--binding-shape-p (l &optional global)
|
||||
"Non-nil if the joined line L reads as a binding: a name, `~x', a
|
||||
@ -1364,7 +1367,7 @@ whose brackets have closed is no block POS can join."
|
||||
;; 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))))
|
||||
(if (memq b stack) stack
|
||||
(if (or (null b) (memq b stack)) stack
|
||||
(cons (car stack) (cons b (cdr stack))))))
|
||||
(t stack))))
|
||||
|
||||
|
||||
@ -922,6 +922,10 @@ defconst(k, 3)
|
||||
(test-flan-fln--tabs "fn f()\n let a = 1\n| b = 2" 1) 6)
|
||||
(test-flan-fln--is "at the top level too"
|
||||
(test-flan-fln--tabs "let a = 1\n|" 2) 4)
|
||||
(test-flan-fln--is "but not after a tab between let and its name"
|
||||
(list (test-flan-fln--tabs "fn f()\n let\ta = 1\n|" 1)
|
||||
(test-flan-fln--tabs "fn f()\n let\ta = 1\n|" 2))
|
||||
'(2 0))
|
||||
(dolist (c '(("let r = match n" "a let's match")
|
||||
("let r = if c" "a let's if")
|
||||
("x = if c" "an assignment's if")
|
||||
|
||||
@ -289,11 +289,40 @@ let let_misaligned loc ~let_line ~name ~name_col =
|
||||
let let_after_block loc ~let_line ~name =
|
||||
let bt, _ = source_line loc.Loc.line in
|
||||
failk "let-after-block" loc
|
||||
"this line lines up with %s, the first name of the let on line %d, as one \
|
||||
more binding of it would. That let's value is the block under its line, \
|
||||
and a let whose value is a block takes no more bindings. Give this one a \
|
||||
let of its own, at that let's column:\n\n let %s"
|
||||
name let_line bt
|
||||
"this line lines up as one more binding of the let on line %d, after %s, \
|
||||
whose value is the block above it. A binding whose value is a block ends \
|
||||
its let's bindings, so this one needs a let of its own, at that let's \
|
||||
column:\n\n let %s"
|
||||
let_line name bt
|
||||
|
||||
(* Whether the tokens from [i] start a line shaped as a binding: [name =],
|
||||
[name:], a [[...]], [{...}] or [(op)] pattern, or [~x]. The alignment
|
||||
advice is for such lines only; any other line under a let is some other
|
||||
mistake. *)
|
||||
let binding_shaped (arr : token array) i =
|
||||
let n = Array.length arr in
|
||||
let at k = if i + k < n then arr.(i + k).tok else EOF in
|
||||
match at 0, at 1 with
|
||||
| NAME s, (NAME "=" | COLON) -> s <> "" && s.[0] <> '.'
|
||||
| (LB | LC | LP | UNQ), _ -> true
|
||||
| _ -> false
|
||||
|
||||
(* A tab between [let] and its first name: the bindings under the let line
|
||||
up with that name, and a tab has no one width to line up with. *)
|
||||
let let_gap_tab (t : token) (nm : token) =
|
||||
if t.loc.Loc.eline = nm.loc.Loc.line then begin
|
||||
let _, lines = !source in
|
||||
if nm.loc.Loc.line <= Array.length lines then
|
||||
let line = lines.(nm.loc.Loc.line - 1) in
|
||||
let a = t.loc.Loc.ecol - 1 and b = nm.loc.Loc.col - 1 in
|
||||
if a >= 0 && b <= String.length line && b > a
|
||||
&& String.contains (String.sub line a (b - a)) '\t' then
|
||||
failk "tab" t.loc
|
||||
"there is a tab between let and %s. The bindings under a let line up \
|
||||
with its first name, and a tab has no one width, so put a space \
|
||||
there: let %s"
|
||||
(show nm.tok) (show nm.tok)
|
||||
end
|
||||
|
||||
let point (l : Loc.t) = { l with Loc.line = l.Loc.eline; col = l.Loc.ecol }
|
||||
|
||||
@ -472,7 +501,8 @@ let layout ?(snippet = false) ?(base = 1) ?indent (toks : token list) : token ar
|
||||
in
|
||||
match owner (i - 1) with
|
||||
| Some j when arr.(j).tok = NAME "let" && j + 1 < n
|
||||
&& arr.(j).loc.Loc.col = List.hd !stack ->
|
||||
&& arr.(j).loc.Loc.col = List.hd !stack
|
||||
&& binding_shaped arr i ->
|
||||
let nm = arr.(j + 1) in
|
||||
let let_line = arr.(j).loc.Loc.line and name = show nm.tok in
|
||||
if col = nm.loc.Loc.col then let_after_block t.loc ~let_line ~name
|
||||
@ -1655,22 +1685,34 @@ and binding_lines : 'a. ?global:bool -> st -> first:Form.t -> name_col:int -> on
|
||||
let_misaligned t.loc ~let_line:first.loc.Loc.line ~name:(text_of first)
|
||||
~name_col
|
||||
in
|
||||
let rec go acc =
|
||||
(* [block] is the binding before this line when its value took the
|
||||
lines under it, which ends the let's bindings. *)
|
||||
let rec go ?block acc =
|
||||
match (peek p).tok with
|
||||
| DEDENT -> ignore (advance p); List.rev acc
|
||||
| EOF -> List.rev acc
|
||||
| INDENT -> misaligned (peek_at p 1)
|
||||
| INDENT when binding_shaped p.toks (p.i + 1) -> misaligned (peek_at p 1)
|
||||
| INDENT -> ignore (advance p); not_binding ()
|
||||
| _ when binding_line ~global p ->
|
||||
if (peek p).loc.Loc.col <> name_col then misaligned (peek p);
|
||||
go (one () :: acc)
|
||||
| _ ->
|
||||
failk "let-block" (where_ p)
|
||||
"this line is indented under let %s, and the only lines that go \
|
||||
there are more bindings of the let, lined up with its first name:\n\n\
|
||||
\ let a = 1\n b = a + 1\n\n\
|
||||
A let's names last to the end of the block the let is in, so any \
|
||||
other line after it goes at the let's column"
|
||||
(text_of first)
|
||||
(match block with
|
||||
| Some (name, line) -> let_after_block (peek p).loc ~let_line:line ~name
|
||||
| None -> ());
|
||||
let i0 = p.i and t0 = peek p in
|
||||
let x = one () in
|
||||
let took = ref false in
|
||||
for k = i0 to p.i - 1 do if p.toks.(k).tok = INDENT then took := true done;
|
||||
go ?block:(if !took then Some (text_of_tok t0, t0.loc.Loc.line) else None)
|
||||
(x :: acc)
|
||||
| _ -> not_binding ()
|
||||
and not_binding () =
|
||||
failk "let-block" (where_ p)
|
||||
"this line is indented under let %s, and the only lines that go \
|
||||
there are more bindings of the let, lined up with its first name:\n\n\
|
||||
\ let a = 1\n b = a + 1\n\n\
|
||||
A let's names last to the end of the block the let is in, so any \
|
||||
other line after it goes at the let's column"
|
||||
(text_of first)
|
||||
in
|
||||
go []
|
||||
end
|
||||
@ -1712,6 +1754,7 @@ and binding_line ?(global = false) p =
|
||||
and let_stmt (s : st) : Form.t list =
|
||||
let p = s.p in
|
||||
let t = advance p in
|
||||
let_gap_tab t (peek p);
|
||||
let name_col = (peek p).loc.Loc.col in
|
||||
let (target, v) =
|
||||
let_values := (name_col, text_of_tok (peek p), t.loc.Loc.line) :: !let_values;
|
||||
@ -2542,6 +2585,7 @@ let read_all ?(line = 1) ?col ?indent ?(global_let = true) ~file src =
|
||||
| DEDENT -> ignore (advance s.p); []
|
||||
| NAME "let" when header_follow s.p "let" ->
|
||||
let t = peek s.p in
|
||||
let_gap_tab t (peek_at s.p 1);
|
||||
let name_col = (peek_at s.p 1).loc.Loc.col in
|
||||
let f =
|
||||
let_values := (name_col, text_of_tok (peek_at s.p 1), t.loc.Loc.line) :: !let_values;
|
||||
|
||||
@ -203,9 +203,10 @@ Each item: the proposal, then the reason in one line.
|
||||
is `(let [row (+ r 1) col (the i32 (- c 1)) {x .x} p] rest…)`. A name
|
||||
that is an operator word is written in parentheses, `(not) = 3`. A
|
||||
binding at another column than the first name, or any other line indented
|
||||
there, is refused. A let whose first value is a block (`= match x`, a
|
||||
lambda header, `=` and the lines under it) takes no more bindings under
|
||||
it. At the top level each such line is one more global, `(def col i32 …)`,
|
||||
there, is refused, and so is a tab between `let` and its first name. A
|
||||
binding whose value is a block (`= match x`, a lambda header, `=` and the
|
||||
lines under it) is its let's last; the printer starts a new `let` after
|
||||
one. At the top level each such line is one more global, `(def col i32 …)`,
|
||||
and may be `name: T` alone as a global's own line may; a pattern there is
|
||||
refused, since a global binds one name. A `let` is otherwise flat: its scope is the rest of its
|
||||
block. To end it early, put it in a `do:` block.
|
||||
|
||||
@ -718,8 +718,10 @@ let () =
|
||||
"(let [a 1 b (+ a 1) c (the i32 b)] (g c))";
|
||||
reads ~global:false "patterns among them" "let p = q()\n {x .x} = p\n [h & t] = xs\ng(x, h)"
|
||||
"(let [p (q) {x .x} p [h & t] xs] (g x h))";
|
||||
reads ~global:false "a binding with a block value" "let a = 1\n b =\n f()\n a\n c = match b\n 1 -> 2\n _ -> 3\ng(c)"
|
||||
"(let [a 1 b (do (f) a) c (match b 1 2 _ 3)] (g c))";
|
||||
reads ~global:false "a block value last in a group" "let a = 1\n c = match a\n 1 -> 2\n _ -> 3\ng(c)"
|
||||
"(let [a 1 c (match a 1 2 _ 3)] (g c))";
|
||||
reads ~global:false "a let after a block value" "let a = 1\n b =\n f()\n a\nlet c = 2\ng(c)"
|
||||
"(let [a 1 b (do (f) a) c 2] (g c))";
|
||||
reads ~global:false "a let after a group merges into it" "let a = 1\n b = 2\nlet c = 3\ng(c)"
|
||||
"(let [a 1 b 2 c 3] (g c))";
|
||||
reads ~global:false "a group in a body" "fn f()\n let a = 1\n b = 2\n a + b"
|
||||
@ -753,11 +755,25 @@ let () =
|
||||
"indent/let-align" "Move it to column 5";
|
||||
(* A let whose value is a block takes no more bindings under it. *)
|
||||
refuses "a binding after a lambda block" "fn f()\n let g = fn(x) =>\n x + 1\n y = 2\n g(y)"
|
||||
"indent/let-after-block" "lines up with g, the first name of the let on line 2";
|
||||
"indent/let-after-block" "one more binding of the let on line 2, after g, whose value is the block above it";
|
||||
refuses "a binding after a match's arms" "fn f(a)\n let g = match a\n 1 -> 2\n _ -> 3\n y = 2\n g"
|
||||
"indent/let-after-block" "let of its own, at that let's column:\n\n let y = 2";
|
||||
refuses "a binding after a deeper block" "fn f()\n let g =\n h()\n y = 2\n g"
|
||||
"indent/let-after-block" "lines up with g";
|
||||
"indent/let-after-block" "after g";
|
||||
refuses "a binding after a later binding's block" "fn f()\n let a = 0\n g = fn(x) =>\n x\n h = 2\n h"
|
||||
"indent/let-after-block" "one more binding of the let on line 3, after g, whose value is the block above it. A binding whose value is a block ends its let's bindings, so this one needs a let of its own, at that let's column:\n\n let h = 2";
|
||||
refuses "a global after a global's block" "let a = 0\n g =\n h()\n b = 2"
|
||||
"indent/let-after-block" "after g";
|
||||
(* Only a line shaped as a binding gets the alignment advice. *)
|
||||
refuses "a statement between a let and its lambda's block" "fn f()\n let g = fn(x) =>\n x + 1\n print(g)"
|
||||
"indent/dedent" "belongs to neither";
|
||||
refuses "an else between a let and its if's block" "fn f(c)\n let g = if c\n 1\n else\n 2\n g"
|
||||
"indent/dedent" "belongs to neither";
|
||||
refuses ~global:false "a statement deeper than a binding" "let x = 1\n y = 2\n g()\nh()"
|
||||
"indent/let-block" "more bindings of the let";
|
||||
(* A tab between let and its first name. *)
|
||||
refuses "a tab after a local let" "fn f()\n let\ta = 1\n a" "indent/tab" "put a space there: let a";
|
||||
refuses "a tab after a global let" "let \ta = 1" "indent/tab" "a tab between let and a";
|
||||
(* Indices separate as a vector's elements do. *)
|
||||
reads "indices separated by spaces" "x = grid[row col].color-idx"
|
||||
"(set x (.color-idx (at grid row col)))";
|
||||
@ -973,6 +989,9 @@ let () =
|
||||
prints "a block value ends the group"
|
||||
"(defn f [] i32 (let [a 1 b 2 c (do (g) a) d 4 e 5] (+ a b c d e)))"
|
||||
" let a = 1\n b = 2\n let c =\n g()\n a\n let d = 4\n e = 5\n";
|
||||
prints "a block value starts and ends a let of its own"
|
||||
"(defn f [] i32 (let [a 0 b 1 g (fn [x] (p x) x) h 2 k 3] (+ a h k)))"
|
||||
" let a = 0\n b = 1\n let g = fn(x) =>\n p(x)\n x\n let h = 2\n k = 3\n";
|
||||
prints "typed and pattern bindings in a group"
|
||||
"(defn f [p dyn] i32 (let [a (the i32 1) {x .x} p [h & t] xs] (+ a x h)))"
|
||||
" let a: i32 = 1\n {x .x} = p\n [h & t] = xs\n";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user