From cfb7db27a71ece935e16f64b14b861560501fd16 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 28 Nov 2021 11:43:06 +0100 Subject: [PATCH 1/4] Wrap bounds turns into rewrap --- src/actions/Wrap.ts | 15 ++++++++ .../modifiers/processModifier.ts | 5 +++ src/processTargets/processSelectionType.ts | 18 +++++++--- .../curlyWrapBoundsAirAndBat.yml | 36 +++++++++++++++++++ src/typings/Types.ts | 1 + 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml diff --git a/src/actions/Wrap.ts b/src/actions/Wrap.ts index 0cd11b086b..c6e91c9709 100644 --- a/src/actions/Wrap.ts +++ b/src/actions/Wrap.ts @@ -31,6 +31,21 @@ export default class Wrap implements Action { left: string, right: string ): Promise { + const excludeInteriorTargets = targets.filter( + (target) => target.selectionContext.excludeInterior + ); + targets = targets.filter( + (target) => !target.selectionContext.excludeInterior + ); + + // Only bounds are selected. Rewrapping existing bounds makes no sense. Replaced them instead. + if (excludeInteriorTargets.length) { + this.graph.actions.replace.run( + [excludeInteriorTargets], + excludeInteriorTargets.map((t, i) => (i % 2 === 0 ? left : right)) + ); + } + const thatMark = flatten( await runOnTargetsForEachEditor( targets, diff --git a/src/processTargets/modifiers/processModifier.ts b/src/processTargets/modifiers/processModifier.ts index 8a793b22b3..f26620b2c1 100644 --- a/src/processTargets/modifiers/processModifier.ts +++ b/src/processTargets/modifiers/processModifier.ts @@ -51,6 +51,11 @@ export default function ( case "surroundingPair": result = processSurroundingPair(context, selection, modifier); + if (result && modifier.delimiterInclusion === "excludeInterior") { + result.forEach((selection) => { + selection.context.excludeInterior = true; + }); + } break; } diff --git a/src/processTargets/processSelectionType.ts b/src/processTargets/processSelectionType.ts index 6de76f328f..ca948411dd 100644 --- a/src/processTargets/processSelectionType.ts +++ b/src/processTargets/processSelectionType.ts @@ -22,18 +22,26 @@ export default function ( selection: SelectionWithEditor, selectionContext: SelectionContext ): TypedSelection { + let results: TypedSelection; switch (target.selectionType) { case "token": - return processToken(target, selection, selectionContext); + results = processToken(target, selection, selectionContext); + break; case "notebookCell": - return processNotebookCell(target, selection, selectionContext); + results = processNotebookCell(target, selection, selectionContext); + break; case "document": - return processDocument(target, selection, selectionContext); + results = processDocument(target, selection, selectionContext); + break; case "line": - return processLine(target, selection, selectionContext); + results = processLine(target, selection, selectionContext); + break; case "paragraph": - return processParagraph(target, selection, selectionContext); + results = processParagraph(target, selection, selectionContext); + break; } + results.selectionContext.excludeInterior = selectionContext.excludeInterior; + return results; } function processNotebookCell( diff --git a/src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml b/src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml new file mode 100644 index 0000000000..f6ff2875cf --- /dev/null +++ b/src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml @@ -0,0 +1,36 @@ +spokenForm: curly wrap bounds air and bat +languageId: typescript +command: + actionName: wrapWithPairedDelimiter + partialTargets: + - type: list + elements: + - type: primitive + modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior} + mark: {type: decoratedSymbol, symbolColor: default, character: a} + - type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: b} + extraArgs: ['{', '}'] +initialState: + documentContents: | + (a) + (b) + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} + marks: + default.a: + start: {line: 0, character: 1} + end: {line: 0, character: 2} + default.b: + start: {line: 1, character: 1} + end: {line: 1, character: 2} +finalState: + documentContents: | + (a) + (b) + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} + thatMark: [] +fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: b}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior}}]}] diff --git a/src/typings/Types.ts b/src/typings/Types.ts index a2226a29da..5f9ba746ab 100644 --- a/src/typings/Types.ts +++ b/src/typings/Types.ts @@ -262,6 +262,7 @@ export interface SelectionContext { trailingDelimiterRange?: vscode.Range | null; isNotebookCell?: boolean; + excludeInterior?: boolean; } export interface TypedSelection { From 1a6d47aa394b35fb8812fc1010b056cb47aa3844 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 28 Nov 2021 11:47:10 +0100 Subject: [PATCH 2/4] Fixed spelling --- src/actions/Wrap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/Wrap.ts b/src/actions/Wrap.ts index c6e91c9709..9a52bda07a 100644 --- a/src/actions/Wrap.ts +++ b/src/actions/Wrap.ts @@ -38,7 +38,7 @@ export default class Wrap implements Action { (target) => !target.selectionContext.excludeInterior ); - // Only bounds are selected. Rewrapping existing bounds makes no sense. Replaced them instead. + // Only bounds are selected. Rewrapping existing bounds makes no sense. Replace them instead. if (excludeInteriorTargets.length) { this.graph.actions.replace.run( [excludeInteriorTargets], From 26b2fa549498f8e25a78de7c8dba9f912cac8093 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 28 Nov 2021 11:54:38 +0100 Subject: [PATCH 3/4] Unified that mark --- src/actions/Wrap.ts | 164 +++++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 79 deletions(-) diff --git a/src/actions/Wrap.ts b/src/actions/Wrap.ts index 9a52bda07a..8ae0ab4a49 100644 --- a/src/actions/Wrap.ts +++ b/src/actions/Wrap.ts @@ -38,101 +38,107 @@ export default class Wrap implements Action { (target) => !target.selectionContext.excludeInterior ); + const thatMark = []; + // Only bounds are selected. Rewrapping existing bounds makes no sense. Replace them instead. if (excludeInteriorTargets.length) { - this.graph.actions.replace.run( - [excludeInteriorTargets], - excludeInteriorTargets.map((t, i) => (i % 2 === 0 ? left : right)) - ); + const { thatMark: replaceThatMark } = + await this.graph.actions.replace.run( + [excludeInteriorTargets], + excludeInteriorTargets.map((t, i) => (i % 2 === 0 ? left : right)) + ); + thatMark.push(...replaceThatMark!); } - const thatMark = flatten( - await runOnTargetsForEachEditor( - targets, - async (editor, targets) => { - const { document } = editor; - const boundaries = targets.map((target) => ({ - start: new Selection( - target.selection.selection.start, - target.selection.selection.start - ), - end: new Selection( - target.selection.selection.end, - target.selection.selection.end - ), - })); - - const edits: Edit[] = boundaries.flatMap(({ start, end }) => [ - { - text: left, - range: start, - }, - { - text: right, - range: end, - isReplace: true, - }, - ]); + thatMark.push( + ...flatten( + await runOnTargetsForEachEditor( + targets, + async (editor, targets) => { + const { document } = editor; + const boundaries = targets.map((target) => ({ + start: new Selection( + target.selection.selection.start, + target.selection.selection.start + ), + end: new Selection( + target.selection.selection.end, + target.selection.selection.end + ), + })); - const delimiterSelectionInfos: FullSelectionInfo[] = - boundaries.flatMap(({ start, end }) => { - return [ - getSelectionInfo( - document, - start, - DecorationRangeBehavior.OpenClosed - ), - getSelectionInfo( - document, - end, - DecorationRangeBehavior.ClosedOpen - ), - ]; - }); + const edits: Edit[] = boundaries.flatMap(({ start, end }) => [ + { + text: left, + range: start, + }, + { + text: right, + range: end, + isReplace: true, + }, + ]); - const cursorSelectionInfos = editor.selections.map((selection) => - getSelectionInfo( - document, - selection, - DecorationRangeBehavior.ClosedClosed - ) - ); + const delimiterSelectionInfos: FullSelectionInfo[] = + boundaries.flatMap(({ start, end }) => { + return [ + getSelectionInfo( + document, + start, + DecorationRangeBehavior.OpenClosed + ), + getSelectionInfo( + document, + end, + DecorationRangeBehavior.ClosedOpen + ), + ]; + }); - const thatMarkSelectionInfos = targets.map( - ({ selection: { selection } }) => + const cursorSelectionInfos = editor.selections.map((selection) => getSelectionInfo( document, selection, - DecorationRangeBehavior.OpenOpen + DecorationRangeBehavior.ClosedClosed ) - ); + ); - const [delimiterSelections, cursorSelections, thatMarkSelections] = - await performEditsAndUpdateFullSelectionInfos( - this.graph.rangeUpdater, - editor, - edits, - [ - delimiterSelectionInfos, - cursorSelectionInfos, - thatMarkSelectionInfos, - ] + const thatMarkSelectionInfos = targets.map( + ({ selection: { selection } }) => + getSelectionInfo( + document, + selection, + DecorationRangeBehavior.OpenOpen + ) ); - editor.selections = cursorSelections; + const [delimiterSelections, cursorSelections, thatMarkSelections] = + await performEditsAndUpdateFullSelectionInfos( + this.graph.rangeUpdater, + editor, + edits, + [ + delimiterSelectionInfos, + cursorSelectionInfos, + thatMarkSelectionInfos, + ] + ); - editor.setDecorations( - this.graph.editStyles.justAdded.token, - delimiterSelections - ); - await decorationSleep(); - editor.setDecorations(this.graph.editStyles.justAdded.token, []); + editor.selections = cursorSelections; - return thatMarkSelections.map((selection) => ({ - editor, - selection, - })); - } + editor.setDecorations( + this.graph.editStyles.justAdded.token, + delimiterSelections + ); + await decorationSleep(); + editor.setDecorations(this.graph.editStyles.justAdded.token, []); + + return thatMarkSelections.map((selection) => ({ + editor, + selection, + })); + } + ) ) ); From 9f6e3450c2d77e7822ac79d4d10aeebd8adf0afd Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 28 Nov 2021 11:59:22 +0100 Subject: [PATCH 4/4] Updated test --- .../roundWrapAirAndBoundsBat.yml} | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) rename src/test/suite/fixtures/recorded/{surroundingPair/curlyWrapBoundsAirAndBat.yml => actions/roundWrapAirAndBoundsBat.yml} (59%) diff --git a/src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml b/src/test/suite/fixtures/recorded/actions/roundWrapAirAndBoundsBat.yml similarity index 59% rename from src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml rename to src/test/suite/fixtures/recorded/actions/roundWrapAirAndBoundsBat.yml index f6ff2875cf..3f1a87ff09 100644 --- a/src/test/suite/fixtures/recorded/surroundingPair/curlyWrapBoundsAirAndBat.yml +++ b/src/test/suite/fixtures/recorded/actions/roundWrapAirAndBoundsBat.yml @@ -1,4 +1,4 @@ -spokenForm: curly wrap bounds air and bat +spokenForm: round wrap air and bounds bat languageId: typescript command: actionName: wrapWithPairedDelimiter @@ -6,22 +6,22 @@ command: - type: list elements: - type: primitive - modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior} mark: {type: decoratedSymbol, symbolColor: default, character: a} - type: primitive + modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior} mark: {type: decoratedSymbol, symbolColor: default, character: b} - extraArgs: ['{', '}'] + extraArgs: [(, )] initialState: documentContents: | - (a) - (b) + a + {b} selections: - anchor: {line: 2, character: 0} active: {line: 2, character: 0} marks: default.a: - start: {line: 0, character: 1} - end: {line: 0, character: 2} + start: {line: 0, character: 0} + end: {line: 0, character: 1} default.b: start: {line: 1, character: 1} end: {line: 1, character: 2} @@ -32,5 +32,11 @@ finalState: selections: - anchor: {line: 2, character: 0} active: {line: 2, character: 0} - thatMark: [] -fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: b}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior}}]}] + thatMark: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 1} + - anchor: {line: 1, character: 2} + active: {line: 1, character: 3} + - anchor: {line: 0, character: 0} + active: {line: 0, character: 3} +fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: b}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior}}]}]