Opportunity
After a jump, the editor is quietly holding a selection you didn't make, and nothing gets you out of it. The next thing you type deletes a block of code.
You jump to a line to read or change it: Go to line (Ctrl+G L / gl), Go to symbol (gs), Back/Forward (Ctrl+G P/N), or a result in the Find sidebar. If anything was selected when you jumped, the jump doesn't replace the selection. It stretches it from the old anchor to wherever you landed, sometimes hundreds of lines. You then type, and all of that text is gone. Undo gets it back, but only if you notice.
It's worse after a Find result. The match Find selects doesn't behave like a selection you made with Shift. The arrow keys don't collapse it, they grow it, and Esc leaves it where it is. So the reviewer's report is exactly right: "there seems no way to take the cursor out of selection mode".
I checked this with throwaway host tests on origin/main f1ac508:
| Starting from |
Then |
What's selected afterwards |
| Nothing selected |
Go to line 30 |
nothing ✓ |
| Shift+↓ ×2 (14 chars) |
Go to line 30 |
223 chars, old anchor → line 30 |
| A Find result's match |
Go to line 30 |
195 chars |
| …the same |
↓ |
203 chars, still growing |
| …the same |
Esc, then ↓ |
211 chars, Esc did nothing |
| Shift+↓ ×2 |
Esc |
still selected |
Three separate defects add up to this:
EditorTab.MoveCursor, which every jump goes through, moves the insertion point but leaves the selection anchor in place (EditorTab.cs:262 on 5cd1883, still the same). Go to line, Go to symbol, Back/Forward and opening a Find result all call it (WorkbenchHost.cs:973, 1023, 1085, 1523, Workbench.cs:151).
EditorTab.Select, used for Find and Find-sidebar matches (Workbench.cs:124, FindController.cs:188), creates a selection that Terminal.Gui treats as a sticky emacs-style mark rather than a Shift selection. Every move extends a sticky mark.
- In the editor,
Esc removes extra cursors if there are any and otherwise focuses the editor (WorkbenchHost.cs:611, 644). It never clears a selection. VS Code users expect it to.
This is the vision's first theme: "the small things that send you back to another editor". And it's the worst kind, because it destroys work silently. The leader key isn't the cause: Ctrl+Space is Terminal.Gui's own set-mark key, but the workbench consumes it before the editor sees it, which I checked.
Evidence
- #305, the reviewer's report.
- The probe table above, run against
f1ac508.
- VS Code: Go to Line lands with the cursor and no selection.
Escape runs cancelSelection when there's a selection, and removeSecondaryCursors first when there are several (default keybindings). A selection that Find makes is collapsed by the next arrow key like any other.
Options considered
- Do nothing, and rely on undo. Good: free. Bad: the failure is silent and destroys text, and it hits the vision's user in the middle of reading unfamiliar code, which is when they're least likely to notice a block vanish.
- Only make jumps drop the selection. Good: one line in
MoveCursor, and it fixes the headline case. Bad: a Find result's sticky selection still grows with every arrow key and can't be escaped, which is the second half of the report.
- Only make
Esc clear the selection. Good: gives you a way out, and matches VS Code. Bad: you can only use the way out if you notice you need it. A jump would still select 200 lines behind your back.
- Turn Terminal.Gui's sticky-mark mode off altogether, by unbinding
ToggleExtend and never setting the sticky flag. Good: removes the whole class of problem. Bad: the sticky state comes from how TuiCode creates selections (Select), not from a key, so unbinding a key wouldn't fix it on its own. It also reaches into TG's private state (we already do that for carets) for something the next option does at our own boundary.
- Chosen: fix all three defects where they start. Jumps land with nothing selected, a selection TuiCode makes behaves like one you made with Shift, and
Esc clears a selection. Good: every selection then behaves the way a VS Code user expects, whoever made it. Each fix is small and can be tested on its own, and each one is noticeable. Bad: three small changes instead of one. Esc gains one more meaning in the editor, but it's the meaning VS Code gives it, and the order stays predictable (below).
Proposal
A jump puts the cursor where you asked and nothing more. Any selection responds to the arrow keys and to Esc the way you'd expect.
- Jumps land with nothing selected. Go to line, Go to symbol, Back/Forward, and opening a result from the Find sidebar or the Review tab all put a plain cursor at the target, whatever was selected before. A jump that is meant to select (Find's next/previous match) still selects its match and nothing else.
- A selection made for you behaves like one you made. After Find selects a match, a plain arrow key collapses it and moves on. Shift+arrow grows it from the match, as in VS Code.
Esc clears the selection in the editor. In order, Esc in the editor now:
- closes the find bar, if it's open (unchanged),
- removes extra cursors, if there are any (unchanged),
- clears the selection, if there is one (new),
- otherwise does what it does today.
It's a command like any other, Clear selection: Editor-scoped, enabled only while something is selected (so Esc falls through otherwise), listed in the palette and rebindable in Settings → Keyboard Shortcuts.
AGENTS.md gets one line on the rule that a jump never carries a selection, and one next to the existing Esc precedence note.
Mockup
A 14-character selection on line 3, then Go to line 40.
Today, the jump stretches the selection from line 3 down to 40 (the selection is shown as ░):
┌ Program.cs ───────────────────────────────────────────┐
│ 3 │ var ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ 4 │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ … │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ 40 │ ░░░░░░░░▏return result; │
└───────────────────────────────────────────────────────┘
Ln 40, Col 9 (1,212 selected)
After, the cursor is on line 40 and nothing is selected:
┌ Program.cs ───────────────────────────────────────────┐
│ 38 │ } │
│ 39 │ │
│ 40 │ ▏ return result; │
│ 41 │ } │
└───────────────────────────────────────────────────────┘
Ln 40, Col 1
Esc with a selection and no dialog open clears the selection and leaves the cursor where it was:
before: │ 12 │ var ░░░░░░▏= Parse(text); Ln 12, Col 15 (6 selected)
Esc
after: │ 12 │ var parsed▏= Parse(text); Ln 12, Col 15
In the palette (Ctrl+E) and in Settings → Keyboard Shortcuts:
┌ Commands ──────────────────────────────────────┐
│ > clear sel │
│ Clear selection Esc │
└────────────────────────────────────────────────┘
Scope
In
- Every jump that goes through
MoveCursor lands with nothing selected.
- Selections made by
Select (Find, Find-sidebar results, replace) collapse on a plain arrow key and extend on Shift+arrow, like a Shift selection.
- A Clear selection command,
Editor-scoped, enabled only while something is selected, bound to Esc by default and slotted into the order above.
- Host tests for each row of the probe table, now passing, and for the
Esc order.
Out
- Shift+Go to line ("select to line"). VS Code doesn't have it either. It can be a pitch of its own if someone wants it.
- Changing what Find's next/previous match selects.
- Mouse selection, which already behaves.
- Other
Esc meanings outside the editor (Explorer cut, dialogs).
- Terminal.Gui's own
Ctrl+Space set-mark binding, which the leader already shadows.
Rough breakdown
- A jump doesn't take a selection with it. Go to line, Go to symbol, Back/Forward and opening a result all land with a plain cursor. This fixes the headline case and the data loss.
- A match Find selected lets go when you move. A plain arrow collapses a Find-made selection, and Shift+arrow grows it. This fixes "there seems no way out".
Esc clears the selection. The Clear selection command, and its place in the Esc order.
All three touch the editor's selection handling, so I'd order them 1 → 2 → 3, each depending on the one before. Each is small.
Open questions
- Multiple cursors and
Esc. With several cursors that each have a selection, the first Esc should remove the extra cursors (as today) and a second should clear the selection that's left. That's VS Code's order. Recommendation: yes, two presses.
- A mnemonic for Clear selection?
Esc is quicker than the leader plus two letters, and the vision's "every action is a command" is met by the palette and Settings. Recommendation: no mnemonic.
- Should Go to line keep a selection if you started one on purpose? VS Code doesn't, and it's what caused this bug. Recommendation: no, always drop it.
Original idea
The Goto line command not only goes to the selected line, it also selects everything from the old cursor position to the new cursor position. This should not happen.
Further, there seems no way to take the cursor out of selection mode - moving the arrow keys around simply changes what's selected so that any typing implicitly deletes a big block of text (unintentionally). Pressing Esc when text is selected (and when no dialogs like the command palette etc are open, which would capture Esc instead) should remove the current selection.
Opportunity
After a jump, the editor is quietly holding a selection you didn't make, and nothing gets you out of it. The next thing you type deletes a block of code.
You jump to a line to read or change it: Go to line (
Ctrl+G L/gl), Go to symbol (gs), Back/Forward (Ctrl+G P/N), or a result in the Find sidebar. If anything was selected when you jumped, the jump doesn't replace the selection. It stretches it from the old anchor to wherever you landed, sometimes hundreds of lines. You then type, and all of that text is gone. Undo gets it back, but only if you notice.It's worse after a Find result. The match Find selects doesn't behave like a selection you made with Shift. The arrow keys don't collapse it, they grow it, and
Escleaves it where it is. So the reviewer's report is exactly right: "there seems no way to take the cursor out of selection mode".I checked this with throwaway host tests on
origin/mainf1ac508:Esc, then ↓Escdid nothingEscThree separate defects add up to this:
EditorTab.MoveCursor, which every jump goes through, moves the insertion point but leaves the selection anchor in place (EditorTab.cs:262on5cd1883, still the same). Go to line, Go to symbol, Back/Forward and opening a Find result all call it (WorkbenchHost.cs:973, 1023, 1085, 1523,Workbench.cs:151).EditorTab.Select, used for Find and Find-sidebar matches (Workbench.cs:124,FindController.cs:188), creates a selection that Terminal.Gui treats as a sticky emacs-style mark rather than a Shift selection. Every move extends a sticky mark.Escremoves extra cursors if there are any and otherwise focuses the editor (WorkbenchHost.cs:611, 644). It never clears a selection. VS Code users expect it to.This is the vision's first theme: "the small things that send you back to another editor". And it's the worst kind, because it destroys work silently. The leader key isn't the cause:
Ctrl+Spaceis Terminal.Gui's own set-mark key, but the workbench consumes it before the editor sees it, which I checked.Evidence
f1ac508.EscaperunscancelSelectionwhen there's a selection, andremoveSecondaryCursorsfirst when there are several (default keybindings). A selection that Find makes is collapsed by the next arrow key like any other.Options considered
MoveCursor, and it fixes the headline case. Bad: a Find result's sticky selection still grows with every arrow key and can't be escaped, which is the second half of the report.Escclear the selection. Good: gives you a way out, and matches VS Code. Bad: you can only use the way out if you notice you need it. A jump would still select 200 lines behind your back.ToggleExtendand never setting the sticky flag. Good: removes the whole class of problem. Bad: the sticky state comes from how TuiCode creates selections (Select), not from a key, so unbinding a key wouldn't fix it on its own. It also reaches into TG's private state (we already do that for carets) for something the next option does at our own boundary.Escclears a selection. Good: every selection then behaves the way a VS Code user expects, whoever made it. Each fix is small and can be tested on its own, and each one is noticeable. Bad: three small changes instead of one.Escgains one more meaning in the editor, but it's the meaning VS Code gives it, and the order stays predictable (below).Proposal
A jump puts the cursor where you asked and nothing more. Any selection responds to the arrow keys and to
Escthe way you'd expect.Escclears the selection in the editor. In order,Escin the editor now:It's a command like any other, Clear selection:
Editor-scoped, enabled only while something is selected (soEscfalls through otherwise), listed in the palette and rebindable in Settings → Keyboard Shortcuts.AGENTS.mdgets one line on the rule that a jump never carries a selection, and one next to the existingEscprecedence note.Mockup
A 14-character selection on line 3, then Go to line
40.Today, the jump stretches the selection from line 3 down to 40 (the selection is shown as
░):After, the cursor is on line 40 and nothing is selected:
Escwith a selection and no dialog open clears the selection and leaves the cursor where it was:In the palette (
Ctrl+E) and in Settings → Keyboard Shortcuts:Scope
In
MoveCursorlands with nothing selected.Select(Find, Find-sidebar results, replace) collapse on a plain arrow key and extend on Shift+arrow, like a Shift selection.Editor-scoped, enabled only while something is selected, bound toEscby default and slotted into the order above.Escorder.Out
Escmeanings outside the editor (Explorer cut, dialogs).Ctrl+Spaceset-mark binding, which the leader already shadows.Rough breakdown
Escclears the selection. The Clear selection command, and its place in theEscorder.All three touch the editor's selection handling, so I'd order them 1 → 2 → 3, each depending on the one before. Each is small.
Open questions
Esc. With several cursors that each have a selection, the firstEscshould remove the extra cursors (as today) and a second should clear the selection that's left. That's VS Code's order. Recommendation: yes, two presses.Escis quicker than the leader plus two letters, and the vision's "every action is a command" is met by the palette and Settings. Recommendation: no mnemonic.Original idea