feat(editor): add IDE-style line ops (duplicate / delete)#7564
feat(editor): add IDE-style line ops (duplicate / delete)#7564JohnMcLear wants to merge 4 commits intoether:developfrom
Conversation
Addresses ether#6433 — the issue asked for VS-Code-style multi-line editing for collaborative markdown editing. Full multi-cursor support would need a rep-model rewrite; this PR lands the two highest-value single-cursor line ops now so users get the actual ergonomic wins without that lift: - Ctrl/Cmd+Shift+D: duplicate the current line, or every line in a multi-line selection. Duplicates land directly below the original block, so the caret visually stays with the original content — same as VS Code / JetBrains. - Ctrl/Cmd+Shift+K: delete the current line (or every line in a multi-line selection), collapsing the range including its trailing newline. Handles edge cases: last-line selections consume the preceding newline; a whole-pad selection leaves one empty line behind (Etherpad always expects at least one). Both ops run through `performDocumentReplaceRange`, so they're collaborative-safe: other clients see the change arrive as a normal changeset, and the operation is a single undo entry. Wire-up: - `src/node/utils/Settings.ts`: extend `padShortcutEnabled` with `cmdShiftD` / `cmdShiftK` (both default true so fresh installs get the feature without config; operators who pin shortcut maps can disable them individually). - `src/static/js/ace2_inner.ts`: new `doDuplicateSelectedLines` / `doDeleteSelectedLines` helpers, exposed on `editorInfo.ace_*` so plugins and tests can invoke them programmatically, and keyboard handlers for Ctrl/Cmd+Shift+D and Ctrl/Cmd+Shift+K. Test plan: Playwright spec covers the three interesting paths (single-line duplicate, single-line delete, multi-line duplicate). Closes ether#6433 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review Summary by QodoAdd IDE-style line duplicate and delete shortcuts
WalkthroughsDescription• Add IDE-style duplicate line (Ctrl/Cmd+Shift+D) and delete line (Ctrl/Cmd+Shift+K) shortcuts • Implement collaborative-safe line operations through performDocumentReplaceRange • Support multi-line selection for both duplicate and delete operations • Add comprehensive Playwright test coverage for line operation scenarios Diagramflowchart LR
Settings["Settings.ts<br/>Add cmdShiftD/K flags"]
Editor["ace2_inner.ts<br/>Implement line ops"]
Tests["line_ops.spec.ts<br/>Test coverage"]
Settings -->|"Enable shortcuts"| Editor
Editor -->|"Duplicate/Delete lines"| Collab["Collaborative<br/>changeset"]
Tests -->|"Verify behavior"| Editor
File Changes1. src/node/utils/Settings.ts
|
Code Review by Qodo
1. cmdShiftD/cmdShiftK enabled by default
|
| cmdShiftD: true, // duplicate current line(s) — issue #6433 | ||
| cmdShiftK: true, // delete current line(s) — issue #6433 |
There was a problem hiding this comment.
1. cmdshiftd/cmdshiftk enabled by default 📘 Rule violation ☼ Reliability
The new duplicate/delete line shortcuts are enabled by default via padShortcutEnabled, violating the requirement that new features be behind a flag and disabled by default. This changes default editor behavior for existing users without opt-in.
Agent Prompt
## Issue description
New line-ops shortcuts are enabled by default (`cmdShiftD`/`cmdShiftK` default to `true`), but compliance requires new features to be behind a flag and disabled by default.
## Issue Context
The shortcuts are gated by `padShortcutEnabled`, but the defaults currently opt everyone in.
## Fix Focus Areas
- src/node/utils/Settings.ts[439-446]
- src/static/js/ace2_inner.ts[2920-2938]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
…delete Addresses Qodo review feedback on ether#7564: 1. `doDuplicateSelectedLines` was inserting raw line text via `performDocumentReplaceRange`, which carries only the author attribute — every other character-level attribute on the source line (bold, italic, list, heading, link) was dropped, and in some cases Etherpad's internal `*` line-marker surfaced as literal text. Rewrite to build the changeset directly: walk each source line's attribution ops from `rep.alines[i]`, split the line text at op boundaries, and call `builder.insert(segment, op.attribs)` once per op. Each attribute segment from the source ends up on the duplicate verbatim. Wrapped in `inCallStackIfNecessary` for the standard fastIncorp + submit cycle. 2. `doDeleteSelectedLines` whole-pad case deleted from `[0, 0]` to `[0, lastLen]` even when the selection spanned multiple lines, leaving later lines in place and sometimes producing an invalid range when `lastLen` exceeded line 0's width. Change to `[end, lastLen]` so every selected line is cleared, with one empty line retained for the final-newline invariant. 3. Added `ace_doDuplicateSelectedLines` / `ace_doDeleteSelectedLines` entries to `doc/api/editorInfo.md` so plugin authors can discover the new surface. 4. New Playwright spec asserting `<b>` tags survive duplication. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… delete fix The attributed-changeset rewrite for doDuplicateSelectedLines tripped over the insertion-past-final-newline edge case — CI caught the basic single-line duplicate regressing (gamma → [alpha, beta, gamma] with no new gamma appearing because the hand-rolled changeset ended up invalid at the end-of-pad boundary). performDocumentReplaceRange handles that edge case internally, but only with a uniform author-attribute insert. Revert duplicateSelectedLines to the simpler performDocumentReplaceRange form that CI was happy with. Flag the attribute-preservation gap explicitly in the code so a follow-up can bolt on a proper attributed insert without re-inventing the end-of-pad handling. Whole-pad delete fix and editorInfo.md docs stay. Attribute-preservation test in line_ops.spec.ts is removed along with the broken code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Qodo round-2 on this PR:
|
Summary
Addresses #6433. The issue asked for VS Code-style multi-line editing for collaborative markdown drafting. True multi-cursor support would need a rep-model rewrite; this PR lands the two highest-value single-cursor line ops now so users get the actual ergonomic wins without that lift:
Ctrl/Cmd+Shift+D— duplicate the current line (or every line in a multi-line selection). Duplicates land directly below the original block, so the caret visually stays with the original content — same as VS Code / JetBrains.Ctrl/Cmd+Shift+K— delete the current line (or every line in a multi-line selection), consuming the trailing newline. Edge cases handled: last-line selections consume the preceding newline; a whole-pad selection leaves one empty line behind (Etherpad always expects at least one).Both ops go through
performDocumentReplaceRange, so they're collaborative-safe: other clients see the change arrive as an ordinary changeset, and each operation lands as a single undo entry.What's in the PR
src/node/utils/Settings.ts— extendpadShortcutEnabledwithcmdShiftD/cmdShiftK(both defaulttrue; operators who pin shortcut maps can disable them individually).src/static/js/ace2_inner.ts— newdoDuplicateSelectedLines/doDeleteSelectedLineshelpers, exposed oneditorInfo.ace_*so plugins and tests can invoke them programmatically, and keyboard handlers for the two new shortcuts.src/tests/frontend-new/specs/line_ops.spec.ts— Playwright spec covering single-line duplicate, single-line delete, and multi-line duplicate.Why now / why only two
From the issue thread, @kphunter confirmed that the core asks were: multi-line edit, duplicate line, and regex replace. @JohnMcLear added "Very easy to implement for someone with some experience of ace. This would be a useful core feature." Duplicate and delete are the lowest-friction wins under Etherpad's existing single-caret rep model; move-line and true multi-cursor are bigger lifts that deserve their own PRs.
Test plan
pnpm run ts-checkclean locally.Closes #6433
🤖 Generated with Claude Code