Summary
Del deletes whatever is selected immediately. On a node with children that is a lot of
document for one unmodified keypress, and the keypress is easy to make by accident — Del sits
next to the navigation keys, needs no modifier, and the tree is where the cursor already is.
Ask first.
Measured
The tree gives a row to every element and every attribute, so a "node" is much bigger than
it looks. What one Del removes, at worst, in the two documents in play:
| Document |
Biggest single deletion |
|
sandbox/testdata/robin-hood.xml |
one <division> |
74 elements + 447 attributes = 521 tree rows |
| the full Robin Hood book |
one <division> |
267 elements + 1632 attributes = 1899 tree rows |
A chapter, gone on one keypress, with no prompt and nothing on screen that says how much went.
The document root is refused, so a division is the realistic worst case — and a division is
exactly what you have selected while moving around a book.
It is worse than it sounds today, because of #18: after a delete the view jumps to the parent,
so the rows that vanished are usually not on screen afterwards either. You cannot easily see
what you just did.
What this changes
A deliberate decision, recorded in the code:
// Del deletes the selected node (undoable, so no confirmation). src/Fux/Program.cs:1036
That reasoning is not wrong — the delete is undoable, and ^Z restores it in its exact
position. The case for changing it is that undo only helps someone who notices. A stray Del
on a collapsed <division> removes a chapter, moves the view somewhere else, and leaves no
mark; the next ^Z might be several edits away, by which point the user is reconstructing
rather than undoing.
Proposed change
Route Del and Edit ▸ Delete (src/Fux/Program.cs:488 and :328, both already funnelled
through DeleteSelected) through a prompt, using the existing ModalQuery
(src/Fux/Program.cs:800) — the same machinery the unsaved-changes prompt uses
(ConfirmDiscard, :1404).
The prompt should say what is going, not just ask. "Delete this node?" trains people to hit
Enter; "Delete <division> and the 1899 rows under it?" is a question worth reading:
┌──────────────────┤Delete├───────────────────┐
│ Delete <division> and 1899 rows under it? │
│ │
│ ⟦► Cancel ◄⟧ ⟦ Delete ⟧ │
└─────────────────────────────────────────────┘
- Cancel is the default button, so a reflexive Enter does not delete.
- Undo is unaffected — this is a guard in front of it, not a replacement for it.
Open decision: does it ask every time?
Worth settling before implementing, because it changes how the editor feels:
- Always. Simplest and most predictable, but it puts a modal in front of deleting a single
attribute — the most common delete there is, and the most trivially undone.
- Only when something goes with it (the node has children or attributes). A leaf costs
nothing to restore and needs no ceremony; a subtree does. Proportionate, and the rule is easy
to state.
- Only above a threshold of rows. Most proportionate, but "why did it ask that time?" is a
question the user has to carry around.
Recommendation: confirm when the node is not a leaf, and phrase the prompt with the row count.
Implementation note
The drill's existing Del check will hang unless it is updated in the same change.
src/Fux/Drill.cs:374 presses Key.DeleteChar and asserts the node is gone (:375). With a
prompt in the way, that nested Run blocks the key injector and nothing answers it — the run
hangs rather than fails, which in CI means a job that sits until it times out. It needs the
§14a recipe: arm an AddTimeout before the keypress to answer the prompt, and remove the
timeout afterwards so it cannot fire inside the next modal.
Acceptance criteria
Out of scope
- A "do not ask again" setting. There is now a config file (
~/.config/fux/snippets.xml) that
could host preferences, but adding a settings mechanism is its own piece of work and this
should not be the thing that drags one in.
- Confirmations for any other destructive edit —
^X on a node value is already undoable and
visible, and nothing else removes structure without asking.
Summary
Deldeletes whatever is selected immediately. On a node with children that is a lot ofdocument for one unmodified keypress, and the keypress is easy to make by accident —
Delsitsnext to the navigation keys, needs no modifier, and the tree is where the cursor already is.
Ask first.
Measured
The tree gives a row to every element and every attribute, so a "node" is much bigger than
it looks. What one
Delremoves, at worst, in the two documents in play:sandbox/testdata/robin-hood.xml<division><division>A chapter, gone on one keypress, with no prompt and nothing on screen that says how much went.
The document root is refused, so a division is the realistic worst case — and a division is
exactly what you have selected while moving around a book.
It is worse than it sounds today, because of #18: after a delete the view jumps to the parent,
so the rows that vanished are usually not on screen afterwards either. You cannot easily see
what you just did.
What this changes
A deliberate decision, recorded in the code:
// Del deletes the selected node (undoable, so no confirmation). src/Fux/Program.cs:1036That reasoning is not wrong — the delete is undoable, and
^Zrestores it in its exactposition. The case for changing it is that undo only helps someone who notices. A stray
Delon a collapsed
<division>removes a chapter, moves the view somewhere else, and leaves nomark; the next
^Zmight be several edits away, by which point the user is reconstructingrather than undoing.
Proposed change
Route
Deland Edit ▸ Delete (src/Fux/Program.cs:488and:328, both already funnelledthrough
DeleteSelected) through a prompt, using the existingModalQuery(
src/Fux/Program.cs:800) — the same machinery the unsaved-changes prompt uses(
ConfirmDiscard,:1404).The prompt should say what is going, not just ask. "Delete this node?" trains people to hit
Enter; "Delete
<division>and the 1899 rows under it?" is a question worth reading:Open decision: does it ask every time?
Worth settling before implementing, because it changes how the editor feels:
attribute — the most common delete there is, and the most trivially undone.
nothing to restore and needs no ceremony; a subtree does. Proportionate, and the rule is easy
to state.
question the user has to carry around.
Recommendation: confirm when the node is not a leaf, and phrase the prompt with the row count.
Implementation note
The drill's existing
Delcheck will hang unless it is updated in the same change.src/Fux/Drill.cs:374pressesKey.DeleteCharand asserts the node is gone (:375). With aprompt in the way, that nested
Runblocks the key injector and nothing answers it — the runhangs rather than fails, which in CI means a job that sits until it times out. It needs the
§14a recipe: arm an
AddTimeoutbefore the keypress to answer the prompt, and remove thetimeout afterwards so it cannot fire inside the next modal.
Acceptance criteria
Delon a node that would take other nodes with it prompts before deleting.untouched and pushes nothing onto the undo stack.
^Zstill restores it in place.Delcheck answers the prompt rather than hanging, and a new checkasserts that cancelling leaves the DOM and the undo stack alone.
readme.md's key table saysDelasks first.Out of scope
~/.config/fux/snippets.xml) thatcould host preferences, but adding a settings mechanism is its own piece of work and this
should not be the thing that drags one in.
^Xon a node value is already undoable andvisible, and nothing else removes structure without asking.