Skip to content

Commit

Permalink
Add rewrap action (#365)
Browse files Browse the repository at this point in the history
* Initial attempt

* Initial working version

* Add tests

* Add another test

Co-authored-by: Andreas Arvidsson <andreas.arvidsson87@gmail.com>
  • Loading branch information
pokey and AndreasArvidsson authored Dec 7, 2021
1 parent d855232 commit b6b22f8
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 39 deletions.
Binary file added images/squareRepackHarp.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions src/actions/Rewrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { TextEditor } from "vscode";
import {
Action,
ActionPreferences,
ActionReturnValue,
Graph,
SelectionWithContext,
TypedSelection,
} from "../typings/Types";
import { repeat } from "../util/array";

export default class Rewrap implements Action {
getTargetPreferences: () => ActionPreferences[] = () => [
{
insideOutsideType: "inside",
modifier: {
type: "surroundingPair",
delimiter: "any",
delimiterInclusion: undefined,
},
},
];

constructor(private graph: Graph) {
this.run = this.run.bind(this);
}

run(
[targets]: [TypedSelection[]],
left: string,
right: string
): Promise<ActionReturnValue> {
const boundaries: TypedSelection[] = targets.flatMap((target) => {
const boundary = target.selectionContext.boundary;

if (boundary == null || boundary.length !== 2) {
throw Error("Target must have an opening and closing delimiter");
}

return boundary.map((edge) =>
constructSimpleTypedSelection(target.selection.editor, edge)
);
});

const replacementTexts = repeat([left, right], targets.length);

return this.graph.actions.replace.run([boundaries], replacementTexts);
}
}

function constructSimpleTypedSelection(
editor: TextEditor,
selection: SelectionWithContext
): TypedSelection {
return {
selection: {
selection: selection.selection,
editor,
},
selectionType: "token",
selectionContext: selection.context,
insideOutsideType: null,
position: "contents",
};
}
2 changes: 2 additions & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { Sort, Reverse } from "./Sort";
import Call from "./Call";
import WrapWithSnippet from "./WrapWithSnippet";
import Deselect from "./Deselect";
import Rewrap from "./Rewrap";

class Actions implements ActionRecord {
constructor(private graph: Graph) {}
Expand Down Expand Up @@ -57,6 +58,7 @@ class Actions implements ActionRecord {
replace = new Replace(this.graph);
replaceWithTarget = new Bring(this.graph);
reverseTargets = new Reverse(this.graph);
rewrapWithPairedDelimiter = new Rewrap(this.graph);
scrollToBottom = new ScrollToBottom(this.graph);
scrollToCenter = new ScrollToCenter(this.graph);
scrollToTop = new ScrollToTop(this.graph);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,45 @@ export function extractSelectionFromSurroundingPairOffsets(
surroundingPairOffsets: SurroundingPairOffsets,
delimiterInclusion: DelimiterInclusion
): SelectionWithContext[] {
const interior = [
{
selection: new Selection(
document.positionAt(
baseOffset + surroundingPairOffsets.leftDelimiter.end
),
document.positionAt(
baseOffset + surroundingPairOffsets.rightDelimiter.start
)
),
context: {},
},
];

const boundary = [
{
selection: new Selection(
document.positionAt(
baseOffset + surroundingPairOffsets.leftDelimiter.start
),
document.positionAt(
baseOffset + surroundingPairOffsets.leftDelimiter.end
)
),
context: {},
},
{
selection: new Selection(
document.positionAt(
baseOffset + surroundingPairOffsets.rightDelimiter.start
),
document.positionAt(
baseOffset + surroundingPairOffsets.rightDelimiter.end
)
),
context: {},
},
];

// If delimiter inclusion is null, do default behavior and include the
// delimiters
if (delimiterInclusion == null) {
Expand All @@ -33,50 +72,18 @@ export function extractSelectionFromSurroundingPairOffsets(
baseOffset + surroundingPairOffsets.rightDelimiter.end
)
),
context: {},
context: {
boundary,
interior,
},
},
];
}

switch (delimiterInclusion) {
case "interiorOnly":
return [
{
selection: new Selection(
document.positionAt(
baseOffset + surroundingPairOffsets.leftDelimiter.end
),
document.positionAt(
baseOffset + surroundingPairOffsets.rightDelimiter.start
)
),
context: {},
},
];
return interior;
case "excludeInterior":
return [
{
selection: new Selection(
document.positionAt(
baseOffset + surroundingPairOffsets.leftDelimiter.start
),
document.positionAt(
baseOffset + surroundingPairOffsets.leftDelimiter.end
)
),
context: {},
},
{
selection: new Selection(
document.positionAt(
baseOffset + surroundingPairOffsets.rightDelimiter.start
),
document.positionAt(
baseOffset + surroundingPairOffsets.rightDelimiter.end
)
),
context: {},
},
];
return boundary;
}
}
2 changes: 1 addition & 1 deletion src/processTargets/processSelectionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,11 @@ function getTokenSelectionContext(
}

return {
...selectionContext,
isInDelimitedList,
containingListDelimiter: " ",
leadingDelimiterRange: isInDelimitedList ? leadingDelimiterRange : null,
trailingDelimiterRange: isInDelimitedList ? trailingDelimiterRange : null,
outerSelection: selectionContext.outerSelection,
};
}

Expand Down
38 changes: 38 additions & 0 deletions src/test/suite/fixtures/recorded/actions/curlyRepackRound.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
languageId: plaintext
command:
version: 1
spokenForm: curly repack round
action: rewrapWithPairedDelimiter
targets:
- type: primitive
modifier: {type: surroundingPair, delimiter: parentheses}
extraArgs: ['{', '}']
initialState:
documentContents: |-
([hello])
(there)
selections:
- anchor: {line: 0, character: 5}
active: {line: 0, character: 5}
- anchor: {line: 1, character: 5}
active: {line: 1, character: 5}
marks: {}
finalState:
documentContents: |-
{[hello]}
{there}
selections:
- anchor: {line: 0, character: 5}
active: {line: 0, character: 5}
- anchor: {line: 1, character: 5}
active: {line: 1, character: 5}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 1}
- anchor: {line: 0, character: 8}
active: {line: 0, character: 9}
- anchor: {line: 1, character: 0}
active: {line: 1, character: 1}
- anchor: {line: 1, character: 6}
active: {line: 1, character: 7}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: parentheses}}]
31 changes: 31 additions & 0 deletions src/test/suite/fixtures/recorded/actions/squareRepackHarp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
languageId: plaintext
command:
version: 1
spokenForm: square repack harp
action: rewrapWithPairedDelimiter
targets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: h}
extraArgs: ['[', ']']
initialState:
documentContents: |
(hello)
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
marks:
default.h:
start: {line: 0, character: 1}
end: {line: 0, character: 6}
finalState:
documentContents: |
[hello]
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 1}
- anchor: {line: 0, character: 6}
active: {line: 0, character: 7}
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any}}]
31 changes: 31 additions & 0 deletions src/test/suite/fixtures/recorded/actions/squareRepackLeper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
languageId: plaintext
command:
version: 1
spokenForm: square repack leper
action: rewrapWithPairedDelimiter
targets:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: (}
extraArgs: ['[', ']']
initialState:
documentContents: |
(hello)
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
marks:
default.(:
start: {line: 0, character: 0}
end: {line: 0, character: 1}
finalState:
documentContents: |
[hello]
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 1}
- anchor: {line: 0, character: 6}
active: {line: 0, character: 7}
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: (}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any}}]
28 changes: 28 additions & 0 deletions src/test/suite/fixtures/recorded/actions/squareRepackPair.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
languageId: plaintext
command:
version: 1
spokenForm: square repack pair
action: rewrapWithPairedDelimiter
targets:
- type: primitive
modifier: {type: surroundingPair, delimiter: any}
extraArgs: ['[', ']']
initialState:
documentContents: |
(hello)
selections:
- anchor: {line: 0, character: 2}
active: {line: 0, character: 2}
marks: {}
finalState:
documentContents: |
[hello]
selections:
- anchor: {line: 0, character: 2}
active: {line: 0, character: 2}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 1}
- anchor: {line: 0, character: 6}
active: {line: 0, character: 7}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any}}]
26 changes: 26 additions & 0 deletions src/test/suite/fixtures/recorded/actions/squareRepackThis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
languageId: plaintext
command:
version: 1
spokenForm: square repack this
action: rewrapWithPairedDelimiter
targets:
- type: primitive
mark: {type: cursor}
extraArgs: ['[', ']']
initialState:
documentContents: (hello)
selections:
- anchor: {line: 0, character: 4}
active: {line: 0, character: 4}
marks: {}
finalState:
documentContents: "[hello]"
selections:
- anchor: {line: 0, character: 4}
active: {line: 0, character: 4}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 1}
- anchor: {line: 0, character: 6}
active: {line: 0, character: 7}
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: surroundingPair, delimiter: any}}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
languageId: plaintext
command:
version: 1
spokenForm: chuck pair harp
action: remove
targets:
- type: primitive
modifier: {type: surroundingPair, delimiter: any}
mark: {type: decoratedSymbol, symbolColor: default, character: h}
initialState:
documentContents: |
(hello) (there)
selections:
- anchor: {line: 0, character: 13}
active: {line: 0, character: 13}
marks:
default.h:
start: {line: 0, character: 1}
end: {line: 0, character: 6}
finalState:
documentContents: |
(there)
selections:
- anchor: {line: 0, character: 5}
active: {line: 0, character: 5}
thatMark:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: surroundingPair, delimiter: any}}]
Loading

0 comments on commit b6b22f8

Please sign in to comment.