etherpad-lite/doc
John McLear 884ac93b4e
feat(editor): add IDE-style line ops (duplicate / delete) (#7564)
* 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>
2026-04-29 09:21:05 +01:00
..
.vitepress chore: Rename some occurences of etherpad-lite to etherpad (#7552) 2026-04-19 16:53:57 +02:00
api feat(editor): add IDE-style line ops (duplicate / delete) (#7564) 2026-04-29 09:21:05 +01:00
assets normalized 3rd level headings and adapted the max-width of the body (#5994) 2023-10-19 22:12:32 +02:00
public Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
.gitignore Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
cli.md chore: adapted documentation for migrateDB.ts (#7088) 2025-08-24 20:50:16 +02:00
cookies.adoc normalized 3rd level headings and adapted the max-width of the body (#5994) 2023-10-19 22:12:32 +02:00
cookies.md Add creator-owned pad settings defaults (#7545) 2026-04-19 11:13:44 +01:00
database.adoc Added docs as asciidoctor with cross platform support. (#5733) 2023-06-21 13:13:31 +01:00
demo.md Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
docker.adoc feat!: replace Abiword with LibreOffice and add DOCX export (#7539) 2026-04-19 09:08:22 +01:00
docker.md ci: publish Docker images to GHCR alongside Docker Hub (#7569) 2026-04-20 10:19:11 +01:00
documentation.adoc Added docs as asciidoctor with cross platform support. (#5733) 2023-06-21 13:13:31 +01:00
documentation.md Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
index.adoc Added docs as asciidoctor with cross platform support. (#5733) 2023-06-21 13:13:31 +01:00
index.md Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
localization.adoc fixed format of json (#5992) 2023-10-19 21:33:12 +02:00
localization.md Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
npm-trusted-publishing.md chore: updated node to supported 22,24,25 (#7628) 2026-04-28 22:45:28 +02:00
package.json build(deps-dev): bump the dev-dependencies group with 2 updates (#7384) 2026-03-20 16:16:16 +00:00
PLUGIN_FRONTEND_TESTS.md ci(playwright): discover plugin frontend specs (closes #7622) (#7623) 2026-04-28 06:33:43 +01:00
plugins.adoc normalized 3rd level headings and adapted the max-width of the body (#5994) 2023-10-19 22:12:32 +02:00
plugins.md chore: remove dead root files (.travis.yml, .lgtm.yml, *.bat) (#7531) 2026-04-16 17:29:24 +01:00
skins.adoc Added docs as asciidoctor with cross platform support. (#5733) 2023-06-21 13:13:31 +01:00
skins.md feat: first version 2025-08-01 21:42:05 +02:00
stats.adoc chore: added documentation for prometheus 2025-08-25 21:13:38 +02:00
stats.md Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00