summaryrefslogtreecommitdiff
path: root/src/normal.zig
Commit message (Collapse)AuthorAge
* Shift-Esc always backs out: in every mode and pane it goes to the previous ↵Gabriel Schneider17 hours
| | | | | | | | | | | | | | | pane, as Esc does from normal, and a raw program never sees it; Ctrl-b alone toggles raw Shift-Esc switched a terminal in normal or insert mode into raw, so the one key meant to get out of anything got a script or a person into a program instead. It is now handled before any mode: an open prompt is closed, insert mode left, a pending count dropped, and Last runs, the pane keeping its mode (a raw terminal stays raw). Ctrl-b is the only switch between raw and normal. The keymap's two chords become one, back_out; the cheatsheet row, the guide's mode table and the tutor say so, and the tty and builtins goldens are re-recorded. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Zig has textobjects: helix's query, ported to the zig grammar pardes buildsGabriel Schneider27 hours
| | | | | | | | | | | | | | | pardes builds maxxnino/tree-sitter-zig, whose node names are not those of the grammar helix's query targets, so the query is ported by hand (vendor/queries/zig, its MPL header saying it is modified and how): a function is a Decl holding an FnProto and a Block with pub beside it, struct/enum/union one ContainerDecl, parameters and arguments ParamDeclList and FnCallArguments, and comments come in three kinds. That grammar's comments take their line's newline, and a node ending in a newline is now taken without it, as helix's grammars have it. The replay test gains fifteen Zig cases from the installed hx; none walks call arguments, which the reference helix counts as parameters and 25.07.1's query does not. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ]f, mif and the other textobject keys use helix's own queriesGabriel Schneider27 hours
| | | | | | | | | | | | | | | helix's textobjects.scm for pardes's grammars are vendored in vendor/queries under MPL-2.0, each with a header naming its source and what it inherits, inlined; a README says what is there and what is not. syntax.objectAt and objectNext run them over the file's kept parse, with their #eq? and #match? predicates, for ]f [f ]t [t ]a [a ]c [c ]T [T ]e [e ]x [x and mi/ma with f t a c T e x. erlang's and zig's queries do not compile against the grammar versions pardes builds and are left out; fortran, markdown and powershell have none in helix. A test replays sixteen Python and JSON cases whose results were taken from the installed hx, and another holds that every vendored query compiles. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A selection's other ranges live out of line, made at the second range and ↵Gabriel Schneider27 hours
| | | | | | | | | | | | | | freed at one The ranges beside the primary were an inline array in every Text, which at 1024 grew each pane by about 60 KiB. They are now allocated when a second range appears (Text.reserveSels) and given back when the selection is one range again (Text.dropSels); a column's or the workspace's tag allocates from Text.gpa, any other text from its pane's. A refused tag edit restores them by copy. The Pane size budget is back to 128 KiB, and a test holds that a single cursor allocates nothing for them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* gw labels the words in view and jumps to the one whose label is typedGabriel Schneider27 hours
| | | | | | | | | | | | helix goto_word: the words of two or more word characters between the view's first and last lines get two-letter labels, nearest the cursor first, one forward and one back in turn (modal.jumpWords). The next two keys choose one and select it, stretching the selection to it in select mode; any other key takes the labels away. The words live on Pane.jump, and renderBody paints their labels over the body's cells in the selection colours, the one change on the render side. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Z arms the view keys until Esc, and zm puts the cursor's column mid-viewGabriel Schneider27 hours
| | | | | | | | | Z is helix's sticky view mode: the z keys stay armed after each until Esc, which leaves the mode and does nothing else (no pane hop). zm is align_view_middle: an unwrapped view scrolls sideways so the cursor's column is in its middle. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Tab after text leaves the syntax node in insert mode; Shift-Tab indentsGabriel Schneider27 hours
| | | | | | | | | | | helix smart_tab: in insert mode, Tab where text precedes the cursor on its line moves past the end of the node the cursor is in, the parent-node end of Alt-e; in leading blanks it indents. Only where the file has a grammar: the reference pins smart tab off, which a file without a grammar agrees with, so there Tab still indents. Shift-Tab is insert_tab, and the differential harness now reads the S- modifier. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* g. goes to where the last edit endedGabriel Schneider27 hours
| | | | | | | | | | helix goto_last_modification: every range moves to the end of the last change (the start of a deletion, the end of an insertion), extending in select mode. The position is kept on the Text in surface rows, set where setEditText hands a body its new text, so a terminal's typed text has one too and moves with its scrollback. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Alt-o, Alt-i and the other tree-sitter keys walk the file's syntax treeGabriel Schneider27 hours
| | | | | | | | | | | | | | helix's expand/shrink, previous/next sibling, all siblings, all children and parent-node end/start (Alt-o/up, Alt-i/down, Alt-p/left, Alt-right, Alt-a, Alt-I, Alt-e, Alt-b; Alt-n stays the new terminal) walk a whole-file parse the file keeps (File.node_tree), made on the first such key after an edit and reused until the next, never per key. syntax.walkNodes follows helix's TreeCursor over every node, anonymous ones included, and Alt-i goes back to what Alt-o grew from while the selection still holds it. The harness runs without grammars, so the cases prove the plain-text no-op; a test replays eleven JSON walks whose results were taken from the installed hx. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* K and Alt-K keep or drop the ranges a regex matches in; Alt-: turns every ↵Gabriel Schneider27 hours
| | | | | | | | | | | range forward K and Alt-K share the s/S prompt, which now carries a mode rather than a split flag: a range stays when a match starts inside it (K) or when none does (Alt-K), the primary becomes the first, and keeping none leaves the selection as it was (helix keep_or_remove_matches). Alt-: is ensure_selections_forward. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A selection holds up to 1024 ranges on the desktop, 64 on the boardGabriel Schneider27 hours
| | | | | | | | | | | The limit was 64 everywhere, which helix-golf's enumerate_and_align outgrows (%s |\d+ makes 121 ranges). It is now memory.limits.selections, per platform, and the counts that hold it are u16. The ranges stay inline in each Text, so a pane grows by about 80 KiB on the desktop (three texts and the regex prompt's snapshot); the Pane size budget in Terminal.zig moves from 128 to 160 KiB and says why. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Q records keys into a register and q types them again; . repeats the last insertGabriel Schneider27 hours
| | | | | | | | | | | | | Macro.zig keeps keys to be typed again. Q records into @ (or "<reg>) in helix's key notation until the next Q; q replays a register's keys count times through the modal handling, where Esc does not hop panes and Enter, Tab and Space neither look, execute nor open the leader. . replays the normal command that entered the last insert session once and what was typed in it count times (helix repeat_last_insert). a at the end of the text now adds the newline helix's append_mode does, which a terminal's text does not. helix-golf's invert_dictionary passes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Every range lives through insert mode, carried through each editGabriel Schneider27 hours
| | | | | | | | | | | | | | Insert mode dropped the selection on the first key and restored only the primary's appended-over span on Esc. Now each insert-mode edit is made at the bare cursor as before, and the range is then carried through it with helix's Range::map rules: typing slides an i range and stretches an a range, Enter (which now also drops the blanks before the cursor, as helix does) slides or stretches by what the cursor moved, and an arrow collapses. The replay gives every other range the same. Esc after a pulls each range's end back by one (helix restore_cursor), which replaces Text.append_at. The wiX-edit-drops-sel and msel-append waivers are gone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Registers hold a value per range, and "<reg> names oneGabriel Schneider27 hours
| | | | | | | | | | | | | Registers.zig replaces the one yank buffer: every register keeps a value per range, y fills them in document order, and p, P, R and insert Ctrl-r put value i at range i, repeating the last (helix paste_impl). "<reg> names the register for the next command; _ swallows, # numbers the ranges, . is each range's text, % the file's name, / the last s/S pattern, and + and * are the system clipboard through the ClipYank and ClipPaste paths. SPC y now writes + alone, as helix's does. The acme chords and a paste into a terminal take the default register joined by newlines. The msel-yank-paste waiver is gone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* & aligns the selections into columnsGabriel Schneider27 hours
| | | | | | | | | | | helix-golf's enumerate_and_align ends on %s |\d+<ret>&. align_selections treats the k-th range of each line as column k and puts spaces before each range until its head reaches the widest head of its column, measured in display cells, which File.rawDisplayCol already gives. A range over several lines refuses, as in helix. The example itself still needs more than 64 ranges, which the selection model does not hold. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Alt-( and Alt-) rotate the text of the selectionsGabriel Schneider27 hours
| | | | | | | | | | | helix-golf swaps keys and values with rotate_selection_contents (webS_to_ <ret><A-(>, then per line). Each range's text moves to the next range (the previous one for Alt-(), in one edit; a count rotates by that many ranges, each range comes back over the text it now holds in its own direction, and the primary follows its text. That is the harness's helix; 25.07.1 read the count as a group size and left the primary alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Alt-J joins lines and selects the spaces it put inGabriel Schneider27 hours
| | | | | | | | | | helix-golf's text_into_array turns lines into an array with %<A-s>ms"<A-J>i,: join_selections_space joins like J and leaves one cursor on each space it inserted. J and Alt-J now make one edit over every range, as helix does, rather than J being replayed per range; that is what lets Alt-J hand back all the spaces as the new selection. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A regular expression search is bounded by a step budget patched into mvzr, ↵Gabriel Schneider27 hours
| | | | | | | | | | | | | | | | | | not windows and a repeat cap The windows returned wrong matches: a candidate reaching a window's edge was left to the next window, half a window on, which could answer a match starting mid-token rather than the leftmost, and addr then pointed data's next write at the wrong bytes. The repeat cap missed mvzr's own worst case, a chain of a?, and alternation under a repeat, each exponential inside one mvzr call the deadline could not interrupt; and it refused ordinary s/S patterns. build.zig now patches the fetched mvzr at build time with a step counter on its backtracking recursion (matchPattern), so a fresh fetch keeps it and a moved anchor stops the build; regexp.zig gives each compiled pattern a budget, about 300 ms here, and a search that spends it fails as taking too long. Windows, the cap and their special cases are gone, and matches are exact again. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A regular expression search comes back: costly patterns refused, long lines ↵Gabriel Schneider27 hours
| | | | | | | | | | | | | | | windowed, 300 ms deadline mvzr backtracks with no bound on its work (a*a*a*a*x over a hundred a's takes a second, each repeat multiplying by the haystack length), and a search runs holding the editor's turn, so one pasted pattern froze the editor. pardes does not write or vendor a regex engine, so regexp.zig bounds what it hands mvzr: more than four repeats is refused, a line longer than a window sized from measured worst cases is searched in half-overlapping windows, and a deadline stops the search. addr names each failure; normal s/S keeps what it found. The prescan also stops reading an escaped backslash before n as a newline, and ends a class where mvzr does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Normal mode's s and S search as addr does, through one shared regexp.zigGabriel Schneider27 hours
| | | | | | | | | | | | The sam-style calling convention for mvzr (a line per haystack, . made [^\\n] only when a pattern names \\n) lived in addr.zig; normal mode's s and S called mvzr over the raw selection, where ^ meant the selection's start and . crossed lines. src/regexp.zig now holds the one Regex (compile, find) both call, and fs.zig and pardes.zig drop an unused mvzr import. hxdiff and hxparity stay at their known 17 and 6 mismatches, none of them regex cases. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Let s, S and | pressed in a tag answer for the tag's own textGabriel Schneider27 hours
| | | | | | | | | | A tag is a text like the body, so selecting on a regex and piping act on the text they are pressed in: a prompt records the text it answers for, the tag keeps the keyboard while it is typed, and a header's prompt goes on the active pane's band. `/` still searches the body from anywhere, as acme's Look from a tag searches the body. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Have Text.pane return nothing for a header instead of trappingGabriel Schneider27 hours
| | | | | | | | | A column's or the workspace's tag has no pane, and asking one for it hit unreachable; every caller guarded against that by checking `what` first. An optional makes each caller say what it does without a pane, so a forgotten guard no longer compiles into a crash. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* File the tag code into tagline.zig and draw tags beside bodiesGabriel Schneider27 hours
| | | | | | | | | | | With tags reduced to Texts, what is left of them is the computed prefix, the default and saved tails, entering and leaving a tag and the headers: that goes to tagline.zig, as acme keeps the tag half of a window in wind.c. Tag and header drawing moves next to body drawing in body_layer.zig, and the tag hit helpers go to tag_layer.zig with the Hit they read, where sameCell now also tells the lines of a taller tag apart. The docs describe the tag as a Text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Make the column and workspace tags Texts with the pane tag's keysGabriel Schneider27 hours
| | | | | | | | | | | | | The headers had their own one-line editor with its own keys: Enter always executed, h/l and J/K moved between headers and panes, Ctrl-c/x/v cut and pasted, and TagLine refused a newline that 9P accepted. They are now Texts like a pane's tag, with no prefix and a default until edited, so normal and insert mode, undo and the look and execute keys are the body's. Moving between them is the window keys' job: Up from a pane with nothing above it reaches its column's tag and then the workspace's, Down comes back, and Left and Right walk the column tags. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Make a pane's tag a Text edited with the body's own keysGabriel Schneider27 hours
| | | | | | | | | | | | | | The tag was a fixed buffer with its own one-line editor, a cursor measured across the path, and a hijack of the body's mode while it was typed into. It is now the pane's second Text: its own characters after the computed prefix (path, dirty marker, PDF page), which is never stored in it, so a rename or a dirty marker never moves its cursor. Normal and insert mode are the body's, undo included; `:` moves the keyboard between body and tag, the tag remembers its cursor and starts on Save, and executing a word from it hands the keyboard back. The mouse still sees the prefix: a sweep selects across it and a click on the path drafts a new name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Give prompts their own line to type into instead of the tag's tailGabriel Schneider27 hours
| | | | | | | | | | | A search, pipe or Save prompt wrote its marker and answer onto the end of the tag, so the tag's cursor, its selection, its 9P writes and a prompt abandoned by a click all shared one buffer: the abandoned text stayed in the tag and a 9P write landed inside the prompt. The answer is now its own Text on the pane, typed in insert mode and shown on the notice band where it was already drawn, and the tag never holds any of it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Give a pane's body its own Text holding the cursor, selections, mode and undoGabriel Schneider27 hours
| | | | | | | | | | | acme keeps what edits a text in its Text (dat.h:171-190) and the window holds a body and a tag of that type. The cursor, the selections, the modal state and the edit-buffer undo move off Pane into Text.zig, Pane holds them as its body, and the edit and normal-mode operations take the Text they edit. Nothing changes in behaviour; this is the step that lets the tag become a second Text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Move exec out of pardes.zig into exec.zigGabriel Schneider27 hours
| | | | | | | | | | | | | | | | | | | | Pure move, no behaviour change (acme keeps this in exec.c): execute, commandText, max_exec_depth, executeBuiltinLine, applyStartupConfig, runBuiltin and applySettingBuiltin; the getarg-style operand code (withArg, PointerOperand, pointerOperand, heldSelection, chordEachSel); takesCommandLine and the terminals commands run in (spawnTty, spawnV9fsTty, spawnTtyWithMount, evictLonePristineTty, replaceStillborn, ttyForDir); placeDoc; and the save path (submitSave, saveFile, saveTo, askWrite), with the acme-chords test, go verbatim to exec.zig. The methods become free functions taking `p: *Pardes`. executeBuiltinLine is called from ~170 places as `p.executeBuiltinLine(..)`, so Pardes keeps one declaration alias for it and those call sites stay; the other 104 calls change from `p.execute(..)` to `exec.execute(p, ..)`. ninep/ctl.zig has a local named exec, so it writes `pardes.exec.execute` instead of importing the file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Move looking out of pardes.zig into look.zigGabriel Schneider27 hours
| | | | | | | | | | | | | | | | | | | | | | Pure move, no behaviour change (acme keeps this in look.c): expanding the word under a click (ExpandedWord, expandedWord, expandedSel, cursorWordSel), the n/N walk (LookFrom, lookEdge, lookStand, lookWalkPanes, lookPast, wholeRowSpan, lookSpanIn, lookWalk, landLookSpot, noteLookSource, armLookWalk), search results (Search, SearchStart, submitSearch, lookFirstHit, runSearch, searchStep, jumpResult), the look-hover preview (LookHoverWait, LookHoverPreview, FileWordSpan, PdfWordPreview, invalidateLookHover, cancelLookHover, lookHoverPane, noteLookHover, refreshLookHoverFromRaw, advanceLookHover) and lookAt with its targets (focusPaneLine, selectSpan, openPaneTarget, focusPaneByPath, clearNavigationSelection, resolveLookTarget, locationText, canonicalLookLocation, pdfLinkLocation, followPdfLink), with five tests, go verbatim to the end of look.zig after its word and target resolution. The methods become free functions taking `p: *Pardes`; their 143 call sites change from `p.lookAt(..)` to `look.lookAt(p, ..)` (tests reach them as `pardes.look.x`). Inside look.zig the moved code's `look.` prefix drops. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Move helix normal mode out of pardes.zig into normal.zigGabriel Schneider27 hours
Pure move, no behaviour change: the key-to-role matching (isPrefix, roleBindingName, normalInput), multiOnce, the selection regex prompt (startSelRegex, selRegexArmed, applySelRegex), KeyState, Replay and replaySels, handleNormal, executeNormalAction and handlePdfNormal, with two tests, go verbatim to normal.zig. The methods become free functions taking `p: *Pardes`; their 22 call sites (handleKey, the mouse, look walk, lsp, runBuiltin, body drawing and edit.zig) change from `p.handleNormal(..)` to `normal.handleNormal(p, ..)`. test/lspbench.zig's second anchor follows its needle into src/normal.zig. Co-Authored-By: Claude Opus 5.5 <[email protected]>