Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rewrap action #365

Merged
merged 5 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way processToken won't clobber things it doesn't know about

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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing that processToken still causes whitespace cleanup for surrounding pair scopes

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