Skip to content

Confirm before deleting a node that takes others with it #21

Description

@MarcelInTO

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

  • Del on a node that would take other nodes with it prompts before deleting.
  • The prompt names the node and says how much goes with it.
  • Cancel is the default; Esc cancels; cancelling leaves the document and the selection
    untouched and pushes nothing onto the undo stack.
  • Confirming deletes exactly as today, and ^Z still restores it in place.
  • Edit ▸ Delete behaves identically to the key.
  • The existing drill Del check answers the prompt rather than hanging, and a new check
    asserts that cancelling leaves the DOM and the undo stack alone.
  • readme.md's key table says Del asks first.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions