Skip to content
Closed
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
169 changes: 95 additions & 74 deletions src/actions/Wrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,93 +31,114 @@ export default class Wrap implements Action {
left: string,
right: string
): Promise<ActionReturnValue> {
const thatMark = flatten(
await runOnTargetsForEachEditor<SelectionWithEditor[]>(
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 excludeInteriorTargets = targets.filter(
(target) => target.selectionContext.excludeInterior
);
targets = targets.filter(
(target) => !target.selectionContext.excludeInterior
);

const edits: Edit[] = boundaries.flatMap(({ start, end }) => [
{
text: left,
range: start,
},
{
text: right,
range: end,
isReplace: true,
},
]);
const thatMark = [];

const delimiterSelectionInfos: FullSelectionInfo[] =
boundaries.flatMap(({ start, end }) => {
return [
getSelectionInfo(
document,
start,
DecorationRangeBehavior.OpenClosed
),
getSelectionInfo(
document,
end,
DecorationRangeBehavior.ClosedOpen
),
];
});
// Only bounds are selected. Rewrapping existing bounds makes no sense. Replace them instead.
if (excludeInteriorTargets.length) {
const { thatMark: replaceThatMark } =
await this.graph.actions.replace.run(
[excludeInteriorTargets],
excludeInteriorTargets.map((t, i) => (i % 2 === 0 ? left : right))
);
thatMark.push(...replaceThatMark!);
}

const cursorSelectionInfos = editor.selections.map((selection) =>
getSelectionInfo(
document,
selection,
DecorationRangeBehavior.ClosedClosed
)
);
thatMark.push(
...flatten(
await runOnTargetsForEachEditor<SelectionWithEditor[]>(
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,
},
]);

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,
}));
}
)
)
);

Expand Down
5 changes: 5 additions & 0 deletions src/processTargets/modifiers/processModifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
18 changes: 13 additions & 5 deletions src/processTargets/processSelectionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
spokenForm: round wrap air and bounds bat
languageId: typescript
command:
actionName: wrapWithPairedDelimiter
partialTargets:
- type: list
elements:
- type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: a}
- type: primitive
modifier: {type: surroundingPair, delimiter: any, delimiterInclusion: excludeInterior}
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: 0}
end: {line: 0, character: 1}
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:
- 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}}]}]
1 change: 1 addition & 0 deletions src/typings/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ export interface SelectionContext {
trailingDelimiterRange?: vscode.Range | null;

isNotebookCell?: boolean;
excludeInterior?: boolean;
}

export interface TypedSelection {
Expand Down