# Tab after a dot in insert mode: "what could go here". # # Each wait for zls's answer allows a minute: it returns as soon as the answer # is drawn (2 s alone), and a zls slowed by a loaded machine (the parallel # snap run) took over 15 s, which failed the old wait. The answer is a # `+Search` buffer of zls's candidates, one row each at the cursor (a # completion item names no declaration) — nothing is inserted and nothing pops # up. # # a.zig is std-free for the reason lsp.snap's is: these goldens must not encode # this machine's stdlib version or its install path. Its enum members are # nonsense words, and the dot is TYPED into an empty switch body, so a row # naming line 2 or 3 can only have come from a member's declaration — the line # the cursor is on is line 8 and holds nothing but the dot. # # Typing it is the point rather than pointing at one already there: the moment # the dot lands the file STOPS PARSING (`switch (s) { . }` loses the whole # switch to the parser's error recovery), and that is the normal input to a # completion, not a corner case. # # The helix corpora cannot reach any of this — hxOpenFileContent names its pane # `/hxcase.txt`, so lsp.speaks is false for all 1042 cases — which makes this # script the only end-to-end coverage the feature has. file a.zig const Shade = enum {\n verdigris,\n cinnabar,\n};\n\nfn pick(s: Shade) u8 {\n return switch (s) {\n \n };\n}\n\n// note. see below\n file notes.txt abc.def ghi\n start 41 120 a.zig wait 8000 verdigris stable 700 20000 snap opened # Tab with no dot before it is still Tab: line 10 is the closing `}`, and this # is the plain insert_tab path putting spaces in front of it. press left 8 13 release left 8 13 key i key tab stable 400 5000 snap indent key esc settle 100 # A dot the backend cannot answer must STILL indent. Line 12 is a comment, so # the position context is `.comment` and no candidate can exist — and because # Tab had already diverted to the backend, the indent has to happen # retroactively when the empty answer arrives (lspResponse), on the condition # that the cursor has not moved meanwhile. Four spaces go in after `// note.` # and the keystroke is not eaten, which is the whole contract. press left 16 15 release left 16 15 stable 400 5000 key i key tab stable 700 15000 snap commentindent key esc settle 100 # Line 8 (grid row 9, 1-based click row 10) is the switch's empty body. End # puts the cursor past its indent, and then the dot is typed. press left 8 11 release left 8 11 stable 400 5000 key i key end text . stable 400 5000 snap athedot key tab # The rows that prove it: `verdigris` and `cinnabar` can only have come from # resolving the switch's condition to `Shade` and asking what it declares. # Line 8 still reads `.` and nothing else — a completion INSERTS NOTHING. wait 60000 +Search stable 700 30000 snap completion # Two more Tabs. Every one of them answers, and every answer REFILLS the one # `+Search` rather than opening its twin. The SECOND press is already the whole # assertion: without the refill it leaves a second byte-identical `+Search` and # this golden diverges. What TWENTY do is only worse in degree — fifteen # stacked panes, the file crushed to one visible line, and from the sixteenth # on freeSlot returns null and the keystroke is eaten for the rest of the # session — and that story is told here rather than replayed in three seconds of # every suite run. One file pane, one results pane, same as after the first. key tab settle 150 key tab settle 150 key tab stable 700 30000 snap refill # The pane is still in insert mode, which is right — the query answered beside # the cursor and typing goes on — so walking the answer means leaving it first, # the same as for any other results buffer. key esc settle 100 # Several rows, so the buffer IS the picker and n walks it; completion does not # jump the way a goto with one row does. The first step selects row 1, and the # Enter after it looks that row and lands SELECTING `verdigris` on line 2 of # a.zig. key n key enter stable 700 15000 snap step # A pane the backend does not speak: same keystrokes, same dot before the # cursor, and Tab must still indent — synchronously this time, since the core # never asks. The gate is lsp.speaks, which the core consults and the backend # owns. start 41 120 notes.txt wait 8000 abc.def stable 700 20000 press left 12 4 release left 12 4 key i key tab stable 400 5000 snap txtindent # ---- the two genuinely INCOMPLETE states ---- # # Everything above types its dot into syntax that is otherwise finished, which # is the easy half and is why a repair chosen by parse-error count looked like # it worked. Mid-typing, the line is short of its terminator too. # # `const z: Shade = .` is ONE parse error with or without a placeholder # identifier spliced in, so the repair has to be chosen by whether it makes the # dot reachable in the tree, never by counting errors. file c.zig const Shade = enum {\n verdigris,\n cinnabar,\n};\n\npub fn nosemi() void {\n const z: Shade = \n}\n start 19 100 c.zig wait 8000 nosemi stable 700 20000 press left 8 10 release left 8 10 stable 400 5000 key i key end text . stable 400 5000 key tab wait 60000 +Search stable 700 30000 snap nosemi # ...and a construct still hanging OPEN. The switch has no closing brace at # all — the one `}` in the file closes it and leaves the function unclosed — # which makes Zig's recovery throw the whole declaration away. No placeholder # sitting at the dot can survive that, so the repair has to close the construct # as well, on the same line (a spelling containing a newline would move every # line number the rows are built from). file d.zig const Shade = enum {\n verdigris,\n cinnabar,\n};\n\npub fn nobrace(s: Shade) void {\n switch (s) {\n \n}\n start 19 100 d.zig wait 8000 nobrace stable 700 20000 press left 8 11 release left 8 11 stable 400 5000 key i key end text . stable 400 5000 key tab wait 60000 +Search stable 700 30000 snap nobrace