etherpad-lite/src/static/js
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
..
pluginfw feat(packaging): add Debian (.deb) build via nfpm with systemd unit (v2) (#7583) 2026-04-27 10:33:30 +01:00
types Add creator-owned pad settings defaults (#7545) 2026-04-19 11:13:44 +01:00
vendors fix(a11y): dialog semantics, focus management, icon labels, html lang (#7584) 2026-04-24 03:04:18 +01:00
ace.ts fix: ignore errors from browser extensions in error handler and editor init (#7456) 2026-04-05 01:58:38 +01:00
ace2_common.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
ace2_inner.ts feat(editor): add IDE-style line ops (duplicate / delete) (#7564) 2026-04-29 09:21:05 +01:00
AttributeManager.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
AttributeMap.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
AttributePool.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
attributes.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
basic_error_handler.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
broadcast.ts feat: add timeslider line numbers (#7542) 2026-04-19 11:18:54 +01:00
broadcast_revisions.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
broadcast_slider.ts feat: add timeslider playback speed setting (#7541) 2026-04-19 09:08:37 +01:00
Builder.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
caretPosition.ts Feat/bundle js (#6511) 2024-07-18 08:51:30 +02:00
Changeset.ts build(deps): bump jose from 5.10.0 to 6.0.12 (#7049) 2025-08-04 19:21:59 +02:00
changesettracker.ts Fixed document not accessible in iframe (#6642) 2024-09-09 11:26:12 +02:00
ChangesetUtils.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
chat.ts fix(chat): icon click, disabled toggles, username layout (#7590, #7592, #7593) (#7597) 2026-04-26 09:56:38 +01:00
ChatMessage.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
collab_client.ts fix: warn when a pending edit is not accepted (#7540) 2026-04-19 11:11:07 +01:00
colorutils.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
contentcollector.ts fix(editor): preserve U+00A0 non-breaking space (#3037) (#7585) 2026-04-23 08:57:30 +01:00
cssmanager.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
domline.ts fix(editor): preserve U+00A0 non-breaking space (#3037) (#7585) 2026-04-23 08:57:30 +01:00
index.ts Fixed index html page 2024-07-23 18:14:49 +02:00
l10n.ts feat: make cookie names configurable with prefix setting (#7450) 2026-04-05 01:58:29 +01:00
linestylefilter.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
MergingOpAssembler.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
Op.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
OpAssembler.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
OpIter.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
pad.ts Add creator-owned pad settings defaults (#7545) 2026-04-19 11:13:44 +01:00
pad_automatic_reconnect.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
pad_connectionstatus.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
pad_cookie.ts feat: make cookie names configurable with prefix setting (#7450) 2026-04-05 01:58:29 +01:00
pad_editbar.ts fix(editbar): restore caret to pad after toolbar-select change (#7589) (#7598) 2026-04-26 08:20:40 +01:00
pad_editor.ts Add creator-owned pad settings defaults (#7545) 2026-04-19 11:13:44 +01:00
pad_impexp.ts feat!: replace Abiword with LibreOffice and add DOCX export (#7539) 2026-04-19 09:08:22 +01:00
pad_modals.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
pad_savedrevs.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
pad_userlist.ts chore: fixed recentPads being null 2025-08-12 21:44:28 +02:00
pad_utils.ts fix: RTL URL parameter rtl=false now correctly disables RTL mode (#7464) 2026-04-06 11:22:57 +01:00
rjquery.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
scroll.ts feat: continued with pad link functionality 2025-08-01 21:42:05 +02:00
security.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
skin_variants.ts chore: add button to settings popup for dark mode (#7136) 2025-09-23 17:41:22 +02:00
skiplist.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
SmartOpAssembler.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
socketio.ts fix(socket.io): readd long polling 2025-03-13 14:32:00 +01:00
StringAssembler.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
StringIterator.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
TextLinesMutator.ts Feat/changeset ts (#6594) 2024-08-18 12:14:24 +02:00
timeslider.ts feat: add timeslider line numbers (#7542) 2026-04-19 11:18:54 +01:00
underscore.ts Moved to ts (#6593) 2024-08-17 20:14:36 +02:00
undomodule.ts fix: allow undo of clear authorship colors without disconnect (#7430) 2026-04-19 09:09:03 +01:00
welcome.ts feat: make cookie names configurable with prefix setting (#7450) 2026-04-05 01:58:29 +01:00