From 9ad4a11fa088a3d15f6fdb9aec155d684ac14569 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 7 Aug 2024 16:15:49 +0200 Subject: [PATCH 1/7] Support arbitrary scope and insertion delimiter for drop, float and puff --- .../actions/insertEmptyLines/floatThis.yml | 8 +- .../actions/insertEmptyLines/floatThis10.yml | 8 +- .../actions/insertEmptyLines/floatThis2.yml | 8 +- .../actions/insertEmptyLines/floatThis3.yml | 8 +- .../actions/insertEmptyLines/floatThis4.yml | 8 +- .../actions/insertEmptyLines/floatThis5.yml | 8 +- .../actions/insertEmptyLines/floatThis6.yml | 8 +- .../actions/insertEmptyLines/floatThis7.yml | 8 +- .../actions/insertEmptyLines/floatThis8.yml | 8 +- .../actions/insertEmptyLines/floatThis9.yml | 8 +- .../actions/insertEmptyLines/puffThis.yml | 10 +- .../actions/insertEmptyLines/puffThis10.yml | 10 +- .../actions/insertEmptyLines/puffThis11.yml | 10 +- .../actions/insertEmptyLines/puffThis19.yml | 10 +- .../actions/insertEmptyLines/puffThis2.yml | 10 +- .../actions/insertEmptyLines/puffThis20.yml | 10 +- .../actions/insertEmptyLines/puffThis3.yml | 10 +- .../actions/insertEmptyLines/puffThis4.yml | 10 +- .../actions/insertEmptyLines/puffThis5.yml | 10 +- .../actions/insertEmptyLines/puffThis6.yml | 10 +- .../actions/insertEmptyLines/puffThis7.yml | 2 +- .../actions/insertEmptyLines/puffThis8.yml | 10 +- .../actions/insertEmptyLines/puffThis9.yml | 10 +- packages/common/src/types/GeneralizedRange.ts | 4 +- .../cursorless-engine/src/actions/Actions.ts | 15 +- .../src/actions/InsertEmptyLines.ts | 141 +++++++++++------- .../processTargets/targets/DestinationImpl.ts | 25 ++-- .../src/typings/target.types.ts | 5 +- 28 files changed, 220 insertions(+), 172 deletions(-) diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis.yml index c721912a29..9600fb624f 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis.yml @@ -18,13 +18,13 @@ finalState: documentContents: |+ selections: - - anchor: {line: 1, character: 0} - active: {line: 1, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 1, character: 0} - end: {line: 1, character: 0} + start: {line: 0, character: 0} + end: {line: 0, character: 0} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis10.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis10.yml index ae64ea3c58..a54fe374a2 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis10.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis10.yml @@ -21,13 +21,13 @@ finalState: hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 9} + end: {line: 1, character: 9} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis2.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis2.yml index cf2fe24010..db7b4dd629 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis2.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis2.yml @@ -18,13 +18,13 @@ finalState: documentContents: | hello selections: - - anchor: {line: 1, character: 0} - active: {line: 1, character: 0} + - anchor: {line: 0, character: 5} + active: {line: 0, character: 5} thatMark: - type: UntypedTarget contentRange: - start: {line: 1, character: 0} - end: {line: 1, character: 0} + start: {line: 0, character: 5} + end: {line: 0, character: 5} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis3.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis3.yml index d45eeab965..6d80e2f801 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis3.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis3.yml @@ -18,13 +18,13 @@ finalState: documentContents: |2 hello selections: - - anchor: {line: 1, character: 0} - active: {line: 1, character: 0} + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} thatMark: - type: UntypedTarget contentRange: - start: {line: 1, character: 0} - end: {line: 1, character: 0} + start: {line: 0, character: 9} + end: {line: 0, character: 9} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis4.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis4.yml index 19e96cb1dd..260980210c 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis4.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis4.yml @@ -18,13 +18,13 @@ finalState: documentContents: |2 selections: - - anchor: {line: 1, character: 0} - active: {line: 1, character: 0} + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} thatMark: - type: UntypedTarget contentRange: - start: {line: 1, character: 0} - end: {line: 1, character: 0} + start: {line: 0, character: 4} + end: {line: 0, character: 4} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis5.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis5.yml index c28303dbb5..a4fb8ec563 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis5.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis5.yml @@ -21,13 +21,13 @@ finalState: whatever hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 9} + end: {line: 1, character: 9} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis6.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis6.yml index 614dd28818..a566efb556 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis6.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis6.yml @@ -21,13 +21,13 @@ finalState: whatever hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 5} + active: {line: 1, character: 5} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 5} + end: {line: 1, character: 5} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis7.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis7.yml index d6142c7b18..8516c8ebe1 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis7.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis7.yml @@ -20,13 +20,13 @@ finalState: hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 0} + end: {line: 1, character: 0} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis8.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis8.yml index 1ab0e331f3..f0b2208dce 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis8.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis8.yml @@ -20,13 +20,13 @@ finalState: hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 0} + end: {line: 1, character: 0} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatThis9.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatThis9.yml index 2a4d2270ed..c8c7ebca34 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/floatThis9.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatThis9.yml @@ -21,13 +21,13 @@ finalState: hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 4} + end: {line: 1, character: 4} isReversed: false hasExplicitRange: true ide: diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis.yml index cca933c0e2..f4628a4987 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis.yml @@ -19,18 +19,18 @@ finalState: selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 0, character: 0} + end: {line: 0, character: 0} isReversed: false hasExplicitRange: true ide: flashes: - style: justAdded - range: {type: line, start: 1, end: 1} + range: {type: line, start: 0, end: 0} - style: justAdded range: {type: line, start: 1, end: 1} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis10.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis10.yml index c1e2203751..7e3b6ec6f3 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis10.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis10.yml @@ -22,13 +22,13 @@ finalState: selections: - - anchor: {line: 3, character: 0} - active: {line: 3, character: 0} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} thatMark: - type: UntypedTarget contentRange: - start: {line: 3, character: 0} - end: {line: 3, character: 0} + start: {line: 2, character: 4} + end: {line: 2, character: 4} isReversed: false hasExplicitRange: true ide: @@ -36,4 +36,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 3, end: 3} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis11.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis11.yml index 112dd8bf5f..f1cf982414 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis11.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis11.yml @@ -22,13 +22,13 @@ finalState: hello selections: - - anchor: {line: 3, character: 0} - active: {line: 3, character: 0} + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} thatMark: - type: UntypedTarget contentRange: - start: {line: 3, character: 0} - end: {line: 3, character: 0} + start: {line: 2, character: 9} + end: {line: 2, character: 9} isReversed: false hasExplicitRange: true ide: @@ -36,4 +36,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 3, end: 3} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis19.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis19.yml index 3bc5e6cbaa..6d828e7229 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis19.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis19.yml @@ -23,13 +23,13 @@ finalState: selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 0} + end: {line: 1, character: 0} isReversed: false hasExplicitRange: true ide: @@ -37,4 +37,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 3, end: 3} + range: {type: line, start: 2, end: 2} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis2.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis2.yml index 60d96f16a3..1b042ffb67 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis2.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis2.yml @@ -19,13 +19,13 @@ finalState: hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 5} + active: {line: 1, character: 5} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 5} + end: {line: 1, character: 5} isReversed: false hasExplicitRange: true ide: @@ -33,4 +33,4 @@ ide: - style: justAdded range: {type: line, start: 0, end: 0} - style: justAdded - range: {type: line, start: 1, end: 1} + range: {type: line, start: 2, end: 2} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis20.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis20.yml index 67003a258c..a553033c80 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis20.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis20.yml @@ -25,13 +25,13 @@ finalState: there selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 0} + end: {line: 1, character: 0} isReversed: false hasExplicitRange: true ide: @@ -39,4 +39,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 3, end: 3} + range: {type: line, start: 2, end: 2} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis3.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis3.yml index 4ad694a160..2233f9c928 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis3.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis3.yml @@ -19,13 +19,13 @@ finalState: hello selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 9} + end: {line: 1, character: 9} isReversed: false hasExplicitRange: true ide: @@ -33,4 +33,4 @@ ide: - style: justAdded range: {type: line, start: 0, end: 0} - style: justAdded - range: {type: line, start: 1, end: 1} + range: {type: line, start: 2, end: 2} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis4.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis4.yml index f2ab6c3a1b..d857b8a6d0 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis4.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis4.yml @@ -19,13 +19,13 @@ finalState: selections: - - anchor: {line: 2, character: 0} - active: {line: 2, character: 0} + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} thatMark: - type: UntypedTarget contentRange: - start: {line: 2, character: 0} - end: {line: 2, character: 0} + start: {line: 1, character: 4} + end: {line: 1, character: 4} isReversed: false hasExplicitRange: true ide: @@ -33,4 +33,4 @@ ide: - style: justAdded range: {type: line, start: 0, end: 0} - style: justAdded - range: {type: line, start: 1, end: 1} + range: {type: line, start: 2, end: 2} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis5.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis5.yml index 726442ef5b..8011895354 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis5.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis5.yml @@ -22,13 +22,13 @@ finalState: hello selections: - - anchor: {line: 3, character: 0} - active: {line: 3, character: 0} + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} thatMark: - type: UntypedTarget contentRange: - start: {line: 3, character: 0} - end: {line: 3, character: 0} + start: {line: 2, character: 9} + end: {line: 2, character: 9} isReversed: false hasExplicitRange: true ide: @@ -36,4 +36,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 3, end: 3} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis6.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis6.yml index 68269a09d2..2d12891b45 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis6.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis6.yml @@ -22,13 +22,13 @@ finalState: hello selections: - - anchor: {line: 3, character: 0} - active: {line: 3, character: 0} + - anchor: {line: 2, character: 5} + active: {line: 2, character: 5} thatMark: - type: UntypedTarget contentRange: - start: {line: 3, character: 0} - end: {line: 3, character: 0} + start: {line: 2, character: 5} + end: {line: 2, character: 5} isReversed: false hasExplicitRange: true ide: @@ -36,4 +36,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 3, end: 3} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis7.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis7.yml index 5e7a60856f..1c7c33da26 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis7.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis7.yml @@ -36,4 +36,4 @@ ide: - style: justAdded range: {type: line, start: 1, end: 1} - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 3, end: 3} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis8.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis8.yml index 4c7a21332c..25e9a3dfa5 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis8.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis8.yml @@ -21,18 +21,18 @@ finalState: selections: - - anchor: {line: 3, character: 0} - active: {line: 3, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 3, character: 0} - end: {line: 3, character: 0} + start: {line: 1, character: 0} + end: {line: 1, character: 0} isReversed: false hasExplicitRange: true ide: flashes: - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 1, end: 1} - style: justAdded range: {type: line, start: 2, end: 2} diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffThis9.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffThis9.yml index d3ac84354e..812d003649 100644 --- a/data/fixtures/recorded/actions/insertEmptyLines/puffThis9.yml +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffThis9.yml @@ -21,18 +21,18 @@ finalState: selections: - - anchor: {line: 3, character: 0} - active: {line: 3, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} thatMark: - type: UntypedTarget contentRange: - start: {line: 3, character: 0} - end: {line: 3, character: 0} + start: {line: 1, character: 0} + end: {line: 1, character: 0} isReversed: false hasExplicitRange: true ide: flashes: - style: justAdded - range: {type: line, start: 2, end: 2} + range: {type: line, start: 1, end: 1} - style: justAdded range: {type: line, start: 2, end: 2} diff --git a/packages/common/src/types/GeneralizedRange.ts b/packages/common/src/types/GeneralizedRange.ts index e89bd924de..bdb7368eab 100644 --- a/packages/common/src/types/GeneralizedRange.ts +++ b/packages/common/src/types/GeneralizedRange.ts @@ -53,8 +53,8 @@ export function isLineRange(range: GeneralizedRange): range is LineRange { return range.type === "line"; } -export function toLineRange(range: Range): LineRange { - return { type: "line", start: range.start.line, end: range.end.line }; +export function toLineRange({ start, end }: Range): LineRange { + return { type: "line", start: start.line, end: end.line }; } export function toCharacterRange({ start, end }: Range): CharacterRange { diff --git a/packages/cursorless-engine/src/actions/Actions.ts b/packages/cursorless-engine/src/actions/Actions.ts index 1e975a5d9e..b8710ab46f 100644 --- a/packages/cursorless-engine/src/actions/Actions.ts +++ b/packages/cursorless-engine/src/actions/Actions.ts @@ -108,9 +108,18 @@ export class Actions implements ActionRecord { this.rangeUpdater, this.modifierStageFactory, ); - insertEmptyLineAfter = new InsertEmptyLineAfter(this.rangeUpdater); - insertEmptyLineBefore = new InsertEmptyLineBefore(this.rangeUpdater); - insertEmptyLinesAround = new InsertEmptyLinesAround(this.rangeUpdater); + insertEmptyLineAfter = new InsertEmptyLineAfter( + this.rangeUpdater, + this.modifierStageFactory, + ); + insertEmptyLineBefore = new InsertEmptyLineBefore( + this.rangeUpdater, + this.modifierStageFactory, + ); + insertEmptyLinesAround = new InsertEmptyLinesAround( + this.rangeUpdater, + this.modifierStageFactory, + ); insertSnippet = new InsertSnippet( this.rangeUpdater, this.snippets, diff --git a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts index 8cc4551095..ed7c7bfd87 100644 --- a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts +++ b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts @@ -1,65 +1,60 @@ -import { FlashStyle, Range, toLineRange } from "@cursorless/common"; -import { flatten } from "lodash-es"; +import { + FlashStyle, + RangeExpansionBehavior, + toCharacterRange, + toLineRange, + zipStrict, + type InsertionMode, +} from "@cursorless/common"; import type { RangeUpdater } from "../core/updateSelections/RangeUpdater"; import { performEditsAndUpdateSelections } from "../core/updateSelections/updateSelections"; +import { containingLineIfUntypedModifier } from "../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; +import type { ModifierStageFactory } from "../processTargets/ModifierStageFactory"; +import type { ModifierStage } from "../processTargets/PipelineStages.types"; import { ide } from "../singletons/ide.singleton"; import type { Target } from "../typings/target.types"; +import type { EditWithRangeUpdater as EditWithRangeType } from "../typings/Types"; import { runOnTargetsForEachEditor } from "../util/targetUtils"; import type { ActionReturnValue, SimpleAction } from "./actions.types"; -class InsertEmptyLines implements SimpleAction { +interface EditWithFlashType extends EditWithRangeType { + isLine: boolean; +} + +abstract class InsertEmptyLines implements SimpleAction { + getFinalStages(): ModifierStage[] { + return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; + } + constructor( private rangeUpdater: RangeUpdater, - private insertAbove: boolean, - private insertBelow: boolean, + private modifierStageFactory: ModifierStageFactory, ) { this.run = this.run.bind(this); } - private getRanges(targets: Target[]) { - let lines = targets.flatMap((target) => { - const lines: number[] = []; - if (this.insertAbove) { - lines.push(target.contentRange.start.line); - } - if (this.insertBelow) { - lines.push(target.contentRange.end.line + 1); - } - return lines; - }); - // Remove duplicates - lines = [...new Set(lines)]; - - return lines.map((line) => new Range(line, 0, line, 0)); - } - - private getEdits(ranges: Range[]) { - return ranges.map((range) => ({ - range, - text: "\n", - })); - } - async run(targets: Target[]): Promise { - const results = flatten( - await runOnTargetsForEachEditor(targets, async (editor, targets) => { - const ranges = this.getRanges(targets); - const edits = this.getEdits(ranges); + const results = await runOnTargetsForEachEditor( + targets, + async (editor, targets) => { + const edits = this.getEdits(targets); const contentSelections = targets.map( (target) => target.thatTarget.contentSelection, ); - const editableEditor = ide().getEditableTextEditor(editor); const { contentSelections: updatedThatSelections, - ranges: lineSelections, + editRanges: updatedEditRanges, } = await performEditsAndUpdateSelections({ rangeUpdater: this.rangeUpdater, - editor: editableEditor, + editor: ide().getEditableTextEditor(editor), edits, selections: { contentSelections, - ranges, + editRanges: { + selections: edits.map((edit) => edit.range), + behavior: RangeExpansionBehavior.openOpen, + }, }, }); @@ -68,25 +63,23 @@ class InsertEmptyLines implements SimpleAction { editor, selection, })), - lineSelections: lineSelections.map((selection, index) => ({ - editor, - range: - ranges[index].start.line < editor.document.lineCount - 1 - ? new Range( - selection.start.translate(-1, undefined), - selection.end.translate(-1, undefined), - ) - : selection, - })), + flashRanges: zipStrict(edits, updatedEditRanges).map( + ([edit, editRange]) => ({ + editor, + // Exclude the new line delimiter from the range for line edits + range: edit.isLine ? edit.updateRange(editRange) : editRange, + isLine: edit.isLine, + }), + ), }; - }), + }, ); await ide().flashRanges( results.flatMap((result) => - result.lineSelections.map(({ editor, range }) => ({ + result.flashRanges.map(({ editor, range, isLine }) => ({ editor, - range: toLineRange(range), + range: isLine ? toLineRange(range) : toCharacterRange(range), style: FlashStyle.justAdded, })), ), @@ -96,22 +89,58 @@ class InsertEmptyLines implements SimpleAction { return { thatSelections: thatMark }; } + + protected abstract getEdits(targets: Target[]): EditWithFlashType[]; } export class InsertEmptyLinesAround extends InsertEmptyLines { - constructor(rangeUpdater: RangeUpdater) { - super(rangeUpdater, true, true); + constructor( + rangeUpdater: RangeUpdater, + modifierStageFactory: ModifierStageFactory, + ) { + super(rangeUpdater, modifierStageFactory); + } + + protected getEdits(targets: Target[]) { + return targets.flatMap((target) => [ + constructChangeEdit(target, "before"), + constructChangeEdit(target, "after"), + ]); } } export class InsertEmptyLineAbove extends InsertEmptyLines { - constructor(rangeUpdater: RangeUpdater) { - super(rangeUpdater, true, false); + constructor( + rangeUpdater: RangeUpdater, + modifierStageFactory: ModifierStageFactory, + ) { + super(rangeUpdater, modifierStageFactory); + } + + protected getEdits(targets: Target[]) { + return targets.map((target) => constructChangeEdit(target, "before")); } } export class InsertEmptyLineBelow extends InsertEmptyLines { - constructor(rangeUpdater: RangeUpdater) { - super(rangeUpdater, false, true); + constructor( + rangeUpdater: RangeUpdater, + modifierStageFactory: ModifierStageFactory, + ) { + super(rangeUpdater, modifierStageFactory); + } + + protected getEdits(targets: Target[]) { + return targets.map((target) => constructChangeEdit(target, "after")); } } + +function constructChangeEdit( + target: Target, + insertionMode: InsertionMode, +): EditWithFlashType { + return { + ...target.toDestination(insertionMode).constructChangeEdit("", true), + isLine: target.isLine, + }; +} diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index a76fe7836a..2c4a551b22 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -80,18 +80,24 @@ export class DestinationImpl implements Destination { return "edit"; } - constructChangeEdit(text: string): EditWithRangeUpdater { + constructChangeEdit( + text: string, + skipIndentation = false, + ): EditWithRangeUpdater { return this.insertionMode === "before" || this.insertionMode === "after" - ? this.constructEditWithDelimiters(text) + ? this.constructEditWithDelimiters(text, skipIndentation) : this.constructEditWithoutDelimiters(text); } - private constructEditWithDelimiters(text: string): EditWithRangeUpdater { + private constructEditWithDelimiters( + text: string, + skipIndentation: boolean, + ): EditWithRangeUpdater { const range = this.getEditRange(); - const editText = this.getEditText(text); + const editText = this.getEditText(text, skipIndentation); const updateRange = (range: Range) => { - return this.updateRange(range, text); + return this.updateRange(range, text, skipIndentation); }; return { @@ -134,19 +140,20 @@ export class DestinationImpl implements Destination { return new Range(position, position); } - private getEditText(text: string) { + private getEditText(text: string, skipIndentation: boolean) { + const indentationString = skipIndentation ? "" : this.indentationString; const insertionText = - this.indentationString + (this.insertionPrefix ?? "") + text; + indentationString + (this.insertionPrefix ?? "") + text; return this.isBefore ? insertionText + this.insertionDelimiter : this.insertionDelimiter + insertionText; } - private updateRange(range: Range, text: string) { + private updateRange(range: Range, text: string, skipIndentation: boolean) { const baseStartOffset = this.editor.document.offsetAt(range.start) + - this.indentationString.length + + (skipIndentation ? 0 : this.indentationString.length) + (this.insertionPrefix?.length ?? 0); const startIndex = this.isBefore diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 1e54b9ce42..18178c57ae 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -216,5 +216,8 @@ export interface Destination { withTarget(target: Target): Destination; getEditNewActionType(): EditNewActionType; /** Constructs change/insertion edit. Adds delimiter before/after if needed */ - constructChangeEdit(text: string): EditWithRangeUpdater; + constructChangeEdit( + text: string, + skipIndentation?: boolean, + ): EditWithRangeUpdater; } From 65cd6991d5f416453145b05a1c107475aa86527d Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 7 Aug 2024 16:17:21 +0200 Subject: [PATCH 2/7] Added token tests --- .../actions/insertEmptyLines/dropToken.yml | 30 +++++++++++++++++++ .../actions/insertEmptyLines/floatToken.yml | 30 +++++++++++++++++++ .../actions/insertEmptyLines/puffToken.yml | 30 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 data/fixtures/recorded/actions/insertEmptyLines/dropToken.yml create mode 100644 data/fixtures/recorded/actions/insertEmptyLines/floatToken.yml create mode 100644 data/fixtures/recorded/actions/insertEmptyLines/puffToken.yml diff --git a/data/fixtures/recorded/actions/insertEmptyLines/dropToken.yml b/data/fixtures/recorded/actions/insertEmptyLines/dropToken.yml new file mode 100644 index 0000000000..20853767bb --- /dev/null +++ b/data/fixtures/recorded/actions/insertEmptyLines/dropToken.yml @@ -0,0 +1,30 @@ +languageId: plaintext +command: + version: 7 + spokenForm: drop token + action: + name: insertEmptyLineBefore + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: foo + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: " foo" + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 1} + end: {line: 0, character: 4} + isReversed: false + hasExplicitRange: true diff --git a/data/fixtures/recorded/actions/insertEmptyLines/floatToken.yml b/data/fixtures/recorded/actions/insertEmptyLines/floatToken.yml new file mode 100644 index 0000000000..ed96c48d22 --- /dev/null +++ b/data/fixtures/recorded/actions/insertEmptyLines/floatToken.yml @@ -0,0 +1,30 @@ +languageId: plaintext +command: + version: 7 + spokenForm: float token + action: + name: insertEmptyLineAfter + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: foo + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "foo " + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 0} + end: {line: 0, character: 3} + isReversed: false + hasExplicitRange: true diff --git a/data/fixtures/recorded/actions/insertEmptyLines/puffToken.yml b/data/fixtures/recorded/actions/insertEmptyLines/puffToken.yml new file mode 100644 index 0000000000..6987b43948 --- /dev/null +++ b/data/fixtures/recorded/actions/insertEmptyLines/puffToken.yml @@ -0,0 +1,30 @@ +languageId: plaintext +command: + version: 7 + spokenForm: puff token + action: + name: insertEmptyLinesAround + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: token} + usePrePhraseSnapshot: true +initialState: + documentContents: foo + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: " foo " + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 1} + end: {line: 0, character: 4} + isReversed: false + hasExplicitRange: true From 2a28176e7ed37cd4444b37c59e06c4dcf28a4de1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 12 Aug 2024 00:33:45 +0200 Subject: [PATCH 3/7] Added changelog --- ...24-08-supportArbitraryInsertionDelimiterIndrinkFloat.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md diff --git a/changelog/2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md b/changelog/2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md new file mode 100644 index 0000000000..2887f7092c --- /dev/null +++ b/changelog/2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md @@ -0,0 +1,7 @@ +--- +tags: [enhancement] +pullRequest: 2646 +--- + +Support arbitrary scope and insertion delimiter for drop, float and puff. +This new implementation allows us to do things like `"puff token"` to insert spaces. `"float item"` to insert `, ` etc. From b1605e83ac5e03e87f19d7669983447eae36aa1b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 12 Aug 2024 00:34:17 +0200 Subject: [PATCH 4/7] rename file --- ...md => 2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md => 2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md} (100%) diff --git a/changelog/2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md b/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md similarity index 100% rename from changelog/2024-08-supportArbitraryInsertionDelimiterIndrinkFloat.md rename to changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md From 794dd260c708adf8524a3b28a775334def49b18f Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 12 Aug 2024 00:35:18 +0200 Subject: [PATCH 5/7] Update packages/cursorless-engine/src/actions/InsertEmptyLines.ts Co-authored-by: Phil Cohen --- packages/cursorless-engine/src/actions/InsertEmptyLines.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts index ed7c7bfd87..9f2607a292 100644 --- a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts +++ b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts @@ -21,6 +21,7 @@ interface EditWithFlashType extends EditWithRangeType { isLine: boolean; } +/** NB: this now also inserts insertion delimiters where appropriate, not just empty lines */ abstract class InsertEmptyLines implements SimpleAction { getFinalStages(): ModifierStage[] { return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; From ee1453b774233d503637f268b5be11429fe2d338 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 12 Aug 2024 00:37:17 +0200 Subject: [PATCH 6/7] Update changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md Co-authored-by: Phil Cohen --- .../2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md b/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md index 2887f7092c..68beabb8b1 100644 --- a/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md +++ b/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md @@ -3,5 +3,4 @@ tags: [enhancement] pullRequest: 2646 --- -Support arbitrary scope and insertion delimiter for drop, float and puff. -This new implementation allows us to do things like `"puff token"` to insert spaces. `"float item"` to insert `, ` etc. +`drop`, `float` and `puff` now behave like `drink` and `pour` for non-line targets: for example, `"puff token"` will insert spaces around the token; `"float item"` will insert `, `s as needed, and so on. From 240e653cb2e7f518b2570fa0d1c32ab1445b9f6f Mon Sep 17 00:00:00 2001 From: Phil Cohen Date: Sun, 11 Aug 2024 15:38:02 -0700 Subject: [PATCH 7/7] Update 2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md --- .../2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md b/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md index 68beabb8b1..9ab1953266 100644 --- a/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md +++ b/changelog/2024-08-supportArbitraryInsertionDelimiterDrinkFloat.md @@ -3,4 +3,4 @@ tags: [enhancement] pullRequest: 2646 --- -`drop`, `float` and `puff` now behave like `drink` and `pour` for non-line targets: for example, `"puff token"` will insert spaces around the token; `"float item"` will insert `, `s as needed, and so on. +`drop`, `float` and `puff` now behave like `drink` and `pour` for non-line targets: for example, `"puff token"` will insert spaces around the token; `"float item"` will insert `,`s as needed, and so on.