* feat(editor): add IDE-style line ops (duplicate / delete)
Addresses #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#6433
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(6433): type the bodyLines helper parameter
* fix(6433): preserve char attributes on duplicate + correct whole-pad delete
Addresses Qodo review feedback on #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>
* revert(6433): drop the attributed-duplicate changeset, keep whole-pad 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>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OK, first up, SOMEBODY *cough*analphabet*cough* screwed up the docs
by making them all use the wrong heading level. Not cool, guy. I
had to change them so they would compile right.
But anyway, now the docs will build into sexy-looking HTML and will
shortly be hosted on marktraceur.info.
Fixed the makefile to work properly.
Run:
* `make clean` for removing old doc-build(s)
* `make docs` for running new doc-build(s)