diff --git a/src/json-crdt-peritext-ui/events/PeritextEventTarget.ts b/src/json-crdt-peritext-ui/events/PeritextEventTarget.ts index 8492037cd1..4b7438e66b 100644 --- a/src/json-crdt-peritext-ui/events/PeritextEventTarget.ts +++ b/src/json-crdt-peritext-ui/events/PeritextEventTarget.ts @@ -29,8 +29,8 @@ export class PeritextEventTarget extends TypedEventTarget { this.dispatch('insert', {text}); } - public delete(len: DeleteDetail['len'], unit?: DeleteDetail['unit']): void { - this.dispatch('delete', {len, unit}); + public delete(len: DeleteDetail['len'], unit?: DeleteDetail['unit'], at?: DeleteDetail['at']): void { + this.dispatch('delete', {len, unit, at}); } public cursor(detail: CursorDetail): void { diff --git a/src/json-crdt-peritext-ui/events/__tests__/delete.spec.ts b/src/json-crdt-peritext-ui/events/__tests__/delete.spec.ts index eb231367d6..a295b17b78 100644 --- a/src/json-crdt-peritext-ui/events/__tests__/delete.spec.ts +++ b/src/json-crdt-peritext-ui/events/__tests__/delete.spec.ts @@ -159,6 +159,28 @@ const testSuite = (getKit: () => Kit) => { kit.et.delete(DIRECTION, 'word'); expect(kit.editor.text()).toBe('abcde klmnopqrstuvwxyz'); }); + + test('can delete a word at specific position', async () => { + const kit = setup(); + kit.editor.cursor.setAt(10); + kit.et.insert(' '); + kit.editor.cursor.setAt(5); + kit.et.insert(' '); + kit.editor.delCursors(); + expect(kit.editor.text()).toBe('abcde fghij klmnopqrstuvwxyz'); + kit.et.delete(0, 'word', 8); + expect(kit.editor.text()).toBe('abcde klmnopqrstuvwxyz'); + expect(kit.editor.cursor.start.viewPos()).toBe(6); + expect(kit.editor.cursor.isCollapsed()).toBe(true); + kit.et.delete(0, 'word', 3); + expect(kit.editor.text()).toBe(' klmnopqrstuvwxyz'); + expect(kit.editor.cursor.start.viewPos()).toBe(0); + expect(kit.editor.cursor.isCollapsed()).toBe(true); + kit.et.delete(0, 'word', 10); + expect(kit.editor.text()).toBe(' '); + expect(kit.editor.cursor.start.viewPos()).toBe(2); + expect(kit.editor.cursor.isCollapsed()).toBe(true); + }); }); describe('lines', () => { diff --git a/src/json-crdt-peritext-ui/events/types.ts b/src/json-crdt-peritext-ui/events/types.ts index 1afe3e38c6..1d23da4781 100644 --- a/src/json-crdt-peritext-ui/events/types.ts +++ b/src/json-crdt-peritext-ui/events/types.ts @@ -1,4 +1,5 @@ import type {Point} from '../../json-crdt-extensions/peritext/rga/Point'; +import type {Position as EditorPosition} from '../../json-crdt-extensions/peritext/editor/types'; import type {SliceType} from '../../json-crdt-extensions/peritext/slice/types'; /** @@ -57,6 +58,17 @@ export interface DeleteDetail { * @default 'char' */ unit?: 'char' | 'word' | 'line'; + + /** + * Position in the document to start the deletion from. If not specified, the + * deletion is executed for all cursors in the document at their current + * positions. If specified, only one cursor will be placed at the specified + * position and the deletion will be executed from that position (while all + * other cursors will be removed). + * + * @default undefined + */ + at?: Position; } /** @@ -117,7 +129,7 @@ export interface CursorDetail { * second `0 | 1` member specifies the anchor point of the character: `0` * for the beginning of the character and `1` for the end of the character. */ - at?: number | [at: number, anchor: 0 | 1] | Point; + at?: Position; /** * Specify the length of the movement or selection in units specified by the @@ -164,6 +176,21 @@ export interface InlineDetail { // biome-ignore lint: empty interface is expected export type MarkerDetail = {}; +/** + * Position represents a caret position in the document. The position can either + * be an instance of {@link Point} or a numeric position in the document, which + * will be immediately converted to a {@link Point} instance. + * + * If a number is provided, the number represents the character index in the + * document, where `0` is the beginning of the document and `1` is the position + * right after the first character, etc. + * + * If 2-tuple is provided, the first element is the character index and the + * second `0 | 1` member specifies the anchor point of the character: `0` + * for the beginning of the character and `1` for the end of the character. + */ +export type Position = EditorPosition; + export type PeritextEventMap = { change: ChangeDetail; insert: InsertDetail;