mirror of
https://github.com/cursorless-dev/cursorless.git
synced 2024-10-05 05:17:38 +03:00
Added action replace (#135)
This commit is contained in:
parent
563ed19392
commit
9dcf9c0528
@ -63,8 +63,7 @@ export default class CommandAction implements Action {
|
||||
]): Promise<ActionReturnValue> {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.referenced,
|
||||
this.graph.editStyles.referencedLine
|
||||
this.graph.editStyles.referenced
|
||||
);
|
||||
|
||||
const originalEditor = window.activeTextEditor;
|
||||
|
@ -2,7 +2,7 @@ import { SyntaxNode } from "web-tree-sitter";
|
||||
import * as vscode from "vscode";
|
||||
import { Location } from "vscode";
|
||||
import { SymbolColor } from "./constants";
|
||||
import EditStyles from "./editStyles";
|
||||
import { EditStyles } from "./editStyles";
|
||||
import NavigationMap from "./NavigationMap";
|
||||
|
||||
/**
|
||||
@ -256,6 +256,7 @@ export type ActionType =
|
||||
| "move"
|
||||
| "outdentLines"
|
||||
| "paste"
|
||||
| "replaceWithText"
|
||||
| "scrollToBottom"
|
||||
| "scrollToCenter"
|
||||
| "scrollToTop"
|
||||
|
@ -9,17 +9,11 @@ import {
|
||||
import { runForEachEditor } from "../targetUtils";
|
||||
import update from "immutability-helper";
|
||||
import displayPendingEditDecorations from "../editDisplayUtils";
|
||||
import { performInsideOutsideAdjustment } from "../performInsideOutsideAdjustment";
|
||||
import { performOutsideAdjustment } from "../performInsideOutsideAdjustment";
|
||||
import { flatten, zip } from "lodash";
|
||||
import { Selection, TextEditorDecorationType, TextEditor, Range } from "vscode";
|
||||
import { Selection, TextEditor, Range } from "vscode";
|
||||
import { performEditsAndUpdateSelections } from "../updateSelections";
|
||||
|
||||
interface DecorationTypes {
|
||||
sourceStyle: TextEditorDecorationType;
|
||||
sourceLineStyle: TextEditorDecorationType;
|
||||
destinationStyle: TextEditorDecorationType;
|
||||
destinationLineStyle: TextEditorDecorationType;
|
||||
}
|
||||
import { getTextWithPossibleDelimiter } from "../getTextWithPossibleDelimiter";
|
||||
|
||||
interface ExtendedEdit extends Edit {
|
||||
editor: TextEditor;
|
||||
@ -37,7 +31,7 @@ interface ThatMarkEntry {
|
||||
class BringMoveSwap implements Action {
|
||||
targetPreferences: ActionPreferences[] = [
|
||||
{ insideOutsideType: null },
|
||||
{ insideOutsideType: "inside" },
|
||||
{ insideOutsideType: null },
|
||||
];
|
||||
|
||||
constructor(private graph: Graph, private type: string) {
|
||||
@ -56,25 +50,20 @@ class BringMoveSwap implements Action {
|
||||
return sources;
|
||||
}
|
||||
|
||||
private getDecorationStyles(): DecorationTypes {
|
||||
let sourceStyle, sourceLineStyle;
|
||||
private getDecorationStyles() {
|
||||
let sourceStyle;
|
||||
if (this.type === "bring") {
|
||||
sourceStyle = this.graph.editStyles.referenced;
|
||||
sourceLineStyle = this.graph.editStyles.referencedLine;
|
||||
} else if (this.type === "swap") {
|
||||
sourceStyle = this.graph.editStyles.pendingModification1;
|
||||
sourceLineStyle = this.graph.editStyles.pendingLineModification1;
|
||||
} else if (this.type === "move") {
|
||||
sourceStyle = this.graph.editStyles.pendingDelete;
|
||||
sourceLineStyle = this.graph.editStyles.pendingLineDelete;
|
||||
} else {
|
||||
throw Error(`Unknown type "${this.type}"`);
|
||||
}
|
||||
// NB this.type === "swap"
|
||||
else {
|
||||
sourceStyle = this.graph.editStyles.pendingModification1;
|
||||
}
|
||||
return {
|
||||
sourceStyle,
|
||||
sourceLineStyle,
|
||||
destinationStyle: this.graph.editStyles.pendingModification0,
|
||||
destinationLineStyle: this.graph.editStyles.pendingLineModification0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -84,15 +73,10 @@ class BringMoveSwap implements Action {
|
||||
) {
|
||||
const decorationTypes = this.getDecorationStyles();
|
||||
await Promise.all([
|
||||
displayPendingEditDecorations(
|
||||
sources,
|
||||
decorationTypes.sourceStyle,
|
||||
decorationTypes.sourceLineStyle
|
||||
),
|
||||
displayPendingEditDecorations(sources, decorationTypes.sourceStyle),
|
||||
displayPendingEditDecorations(
|
||||
destinations,
|
||||
decorationTypes.destinationStyle,
|
||||
decorationTypes.destinationLineStyle
|
||||
decorationTypes.destinationStyle
|
||||
),
|
||||
]);
|
||||
}
|
||||
@ -107,54 +91,45 @@ class BringMoveSwap implements Action {
|
||||
throw new Error("Targets must have same number of args");
|
||||
}
|
||||
|
||||
const sourceText = source.selection.editor.document.getText(
|
||||
source.selection.selection
|
||||
);
|
||||
|
||||
const { containingListDelimiter } = destination.selectionContext;
|
||||
const newText =
|
||||
containingListDelimiter == null || destination.position === "contents"
|
||||
? sourceText
|
||||
: destination.position === "after"
|
||||
? containingListDelimiter + sourceText
|
||||
: sourceText + containingListDelimiter;
|
||||
// Get text adjusting for destination position
|
||||
const text = getTextWithPossibleDelimiter(source, destination);
|
||||
|
||||
// Add destination edit
|
||||
const result = [
|
||||
{
|
||||
isSource: false,
|
||||
range: destination.selection.selection as Range,
|
||||
text,
|
||||
editor: destination.selection.editor,
|
||||
originalSelection: destination,
|
||||
range: destination.selection.selection as Range,
|
||||
text: newText,
|
||||
isSource: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Add source edit for move and swap
|
||||
// Prevent multiple instances of the same expanded source.
|
||||
if (this.type !== "bring" && !usedSources.includes(source)) {
|
||||
let newText: string;
|
||||
let text: string;
|
||||
let range: Range;
|
||||
|
||||
if (this.type === "swap") {
|
||||
newText = destination.selection.editor.document.getText(
|
||||
text = destination.selection.editor.document.getText(
|
||||
destination.selection.selection
|
||||
);
|
||||
range = source.selection.selection;
|
||||
} else {
|
||||
// NB: this.type === "move"
|
||||
newText = "";
|
||||
range = performInsideOutsideAdjustment(source, "outside").selection
|
||||
.selection;
|
||||
}
|
||||
// NB: this.type === "move"
|
||||
else {
|
||||
text = "";
|
||||
range = performOutsideAdjustment(source).selection.selection;
|
||||
}
|
||||
|
||||
usedSources.push(source);
|
||||
result.push({
|
||||
isSource: true,
|
||||
range,
|
||||
text,
|
||||
editor: source.selection.editor,
|
||||
originalSelection: source,
|
||||
range,
|
||||
text: newText,
|
||||
isSource: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -206,15 +181,13 @@ class BringMoveSwap implements Action {
|
||||
thatMark
|
||||
.filter(({ isSource }) => isSource)
|
||||
.map(({ typedSelection }) => typedSelection),
|
||||
decorationTypes.sourceStyle,
|
||||
decorationTypes.sourceLineStyle
|
||||
decorationTypes.sourceStyle
|
||||
),
|
||||
displayPendingEditDecorations(
|
||||
thatMark
|
||||
.filter(({ isSource }) => !isSource)
|
||||
.map(({ typedSelection }) => typedSelection),
|
||||
decorationTypes.destinationStyle,
|
||||
decorationTypes.destinationLineStyle
|
||||
decorationTypes.destinationStyle
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
@ -46,8 +46,7 @@ class CopyLines implements Action {
|
||||
async run([targets]: [TypedSelection[]]): Promise<ActionReturnValue> {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.referenced,
|
||||
this.graph.editStyles.referencedLine
|
||||
this.graph.editStyles.referenced
|
||||
);
|
||||
|
||||
const thatMark = flatten(
|
||||
|
@ -18,10 +18,14 @@ export class FindInFiles implements Action {
|
||||
async run([targets]: [TypedSelection[]]): Promise<ActionReturnValue> {
|
||||
ensureSingleTarget(targets);
|
||||
|
||||
const { returnValue: query, thatMark } =
|
||||
await this.graph.actions.getText.run([targets]);
|
||||
const {
|
||||
returnValue: [query],
|
||||
thatMark,
|
||||
} = await this.graph.actions.getText.run([targets]);
|
||||
|
||||
await commands.executeCommand("workbench.action.findInFiles", { query });
|
||||
await commands.executeCommand("workbench.action.findInFiles", {
|
||||
query,
|
||||
});
|
||||
|
||||
return { returnValue: null, thatMark };
|
||||
}
|
||||
|
@ -16,24 +16,21 @@ export default class GetText implements Action {
|
||||
|
||||
async run(
|
||||
[targets]: [TypedSelection[]],
|
||||
showDecorations = true
|
||||
{ showDecorations = true } = {}
|
||||
): Promise<ActionReturnValue> {
|
||||
if (showDecorations) {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.referenced,
|
||||
this.graph.editStyles.referencedLine
|
||||
this.graph.editStyles.referenced
|
||||
);
|
||||
}
|
||||
|
||||
const text = targets
|
||||
.map((target) =>
|
||||
target.selection.editor.document.getText(target.selection.selection)
|
||||
)
|
||||
.join("\n");
|
||||
const returnValue = targets.map((target) =>
|
||||
target.selection.editor.document.getText(target.selection.selection)
|
||||
);
|
||||
|
||||
return {
|
||||
returnValue: text,
|
||||
returnValue,
|
||||
thatMark: targets.map((target) => target.selection),
|
||||
};
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ class InsertEmptyLines implements Action {
|
||||
displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.referenced,
|
||||
this.graph.editStyles.referencedLine
|
||||
);
|
||||
|
||||
const edits = await runForEachEditor(
|
||||
|
@ -23,7 +23,6 @@ export default class Paste implements Action {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.pendingModification0,
|
||||
this.graph.editStyles.pendingLineModification0
|
||||
);
|
||||
|
||||
const text = await env.clipboard.readText();
|
||||
@ -78,7 +77,6 @@ export default class Paste implements Action {
|
||||
await displayPendingEditDecorations(
|
||||
thatMark.map(({ typedSelection }) => typedSelection),
|
||||
this.graph.editStyles.pendingModification0,
|
||||
this.graph.editStyles.pendingLineModification0
|
||||
);
|
||||
|
||||
return { returnValue: null, thatMark };
|
||||
|
66
src/actions/ReplaceWithText.ts
Normal file
66
src/actions/ReplaceWithText.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import {
|
||||
Action,
|
||||
ActionPreferences,
|
||||
ActionReturnValue,
|
||||
Graph,
|
||||
TypedSelection,
|
||||
} from "../Types";
|
||||
import displayPendingEditDecorations from "../editDisplayUtils";
|
||||
import { runForEachEditor } from "../targetUtils";
|
||||
import { flatten, zip } from "lodash";
|
||||
import { maybeAddDelimiter } from "../getTextWithPossibleDelimiter";
|
||||
import { performEditsAndUpdateSelections } from "../updateSelections";
|
||||
|
||||
export default class implements Action {
|
||||
targetPreferences: ActionPreferences[] = [{ insideOutsideType: null }];
|
||||
|
||||
constructor(private graph: Graph) {
|
||||
this.run = this.run.bind(this);
|
||||
}
|
||||
|
||||
async run(
|
||||
[targets]: [TypedSelection[]],
|
||||
texts: string[]
|
||||
): Promise<ActionReturnValue> {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.pendingModification0
|
||||
);
|
||||
|
||||
// Broadcast single text for each target
|
||||
if (texts.length === 1) {
|
||||
texts = Array(targets.length).fill(texts[0]);
|
||||
}
|
||||
|
||||
if (targets.length !== texts.length) {
|
||||
throw new Error("Targets and texts must have same length");
|
||||
}
|
||||
|
||||
const edits = zip(targets, texts).map(([target, text]) => ({
|
||||
editor: target!.selection.editor,
|
||||
range: target!.selection.selection,
|
||||
text: maybeAddDelimiter(text!, target!),
|
||||
}));
|
||||
|
||||
const thatMark = flatten(
|
||||
await runForEachEditor(
|
||||
edits,
|
||||
(edit) => edit.editor,
|
||||
async (editor, edits) => {
|
||||
const [updatedSelections] = await performEditsAndUpdateSelections(
|
||||
editor,
|
||||
edits,
|
||||
[targets.map((target) => target.selection.selection)]
|
||||
);
|
||||
|
||||
return updatedSelections.map((selection) => ({
|
||||
editor,
|
||||
selection,
|
||||
}));
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return { returnValue: null, thatMark };
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ class Scroll implements Action {
|
||||
|
||||
await displayDecorationsWhileRunningFunc(
|
||||
targets.map((target) => target.selection),
|
||||
this.graph.editStyles.referencedLine,
|
||||
this.graph.editStyles.referenced.line,
|
||||
scrollCallback,
|
||||
showAdditionalHighlightBeforeScroll
|
||||
);
|
||||
|
@ -41,8 +41,7 @@ export default class SetBreakpoint implements Action {
|
||||
]): Promise<ActionReturnValue> {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.referenced,
|
||||
this.graph.editStyles.referencedLine
|
||||
this.graph.editStyles.referenced
|
||||
);
|
||||
|
||||
const lines = targets.flatMap((target) => {
|
||||
|
@ -18,8 +18,9 @@ export default class Copy implements Action {
|
||||
const { returnValue, thatMark } = await this.graph.actions.getText.run([
|
||||
targets,
|
||||
]);
|
||||
const text = returnValue.join("\n");
|
||||
|
||||
await env.clipboard.writeText(returnValue);
|
||||
await env.clipboard.writeText(text);
|
||||
|
||||
return { returnValue: null, thatMark };
|
||||
}
|
||||
|
@ -5,7 +5,10 @@ import {
|
||||
Graph,
|
||||
TypedSelection,
|
||||
} from "../Types";
|
||||
import { performInsideOutsideAdjustment } from "../performInsideOutsideAdjustment";
|
||||
import {
|
||||
performInsideAdjustment,
|
||||
performOutsideAdjustment,
|
||||
} from "../performInsideOutsideAdjustment";
|
||||
|
||||
export default class Cut implements Action {
|
||||
targetPreferences: ActionPreferences[] = [{ insideOutsideType: null }];
|
||||
@ -15,14 +18,10 @@ export default class Cut implements Action {
|
||||
}
|
||||
|
||||
async run([targets]: [TypedSelection[]]): Promise<ActionReturnValue> {
|
||||
await this.graph.actions.copy.run([
|
||||
targets.map((target) => performInsideOutsideAdjustment(target, "inside")),
|
||||
]);
|
||||
await this.graph.actions.copy.run([targets.map(performInsideAdjustment)]);
|
||||
|
||||
const { thatMark } = await this.graph.actions.delete.run([
|
||||
targets.map((target) =>
|
||||
performInsideOutsideAdjustment(target, "outside")
|
||||
),
|
||||
targets.map(performOutsideAdjustment),
|
||||
]);
|
||||
|
||||
return {
|
||||
|
@ -21,7 +21,6 @@ export default class Delete implements Action {
|
||||
await displayPendingEditDecorations(
|
||||
targets,
|
||||
this.graph.editStyles.pendingDelete,
|
||||
this.graph.editStyles.pendingLineDelete
|
||||
);
|
||||
|
||||
const thatMark = flatten(
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
} from "./InsertEmptyLines";
|
||||
import GetText from "./GetText";
|
||||
import { FindInFiles } from "./Find";
|
||||
import ReplaceWithText from "./ReplaceWithText";
|
||||
import { CopyLinesUp, CopyLinesDown } from "./CopyLines";
|
||||
import SetBreakpoint from "./SetBreakpoint";
|
||||
|
||||
@ -56,6 +57,7 @@ class Actions implements ActionRecord {
|
||||
move = new Move(this.graph);
|
||||
outdentLines = new OutdentLines(this.graph);
|
||||
paste = new Paste(this.graph);
|
||||
replaceWithText = new ReplaceWithText(this.graph);
|
||||
scrollToBottom = new ScrollToBottom(this.graph);
|
||||
scrollToCenter = new ScrollToCenter(this.graph);
|
||||
scrollToTop = new ScrollToTop(this.graph);
|
||||
|
@ -51,11 +51,12 @@ export default class Wrap implements Action {
|
||||
);
|
||||
|
||||
editor.setDecorations(
|
||||
this.graph.editStyles.justAdded,
|
||||
this.graph.editStyles.justAdded.token,
|
||||
updatedSelections
|
||||
);
|
||||
await decorationSleep();
|
||||
editor.setDecorations(this.graph.editStyles.justAdded, []);
|
||||
|
||||
editor.setDecorations(this.graph.editStyles.justAdded.token, []);
|
||||
|
||||
return targets.map((target, index) => {
|
||||
const start = updatedSelections[index * 2].start;
|
||||
|
@ -3,6 +3,7 @@ import { TypedSelection, SelectionWithEditor } from "./Types";
|
||||
import { isLineSelectionType } from "./selectionType";
|
||||
import { promisify } from "util";
|
||||
import { runOnTargetsForEachEditor, runForEachEditor } from "./targetUtils";
|
||||
import { EditStyle } from "./editStyles";
|
||||
|
||||
const sleep = promisify(setTimeout);
|
||||
|
||||
@ -17,19 +18,18 @@ export async function decorationSleep() {
|
||||
|
||||
export default async function displayPendingEditDecorations(
|
||||
targets: TypedSelection[],
|
||||
tokenStyle: TextEditorDecorationType,
|
||||
lineStyle: TextEditorDecorationType
|
||||
editStyle: EditStyle
|
||||
) {
|
||||
await runOnTargetsForEachEditor(targets, async (editor, selections) => {
|
||||
editor.setDecorations(
|
||||
tokenStyle,
|
||||
editStyle.token,
|
||||
selections
|
||||
.filter((selection) => !isLineSelectionType(selection.selectionType))
|
||||
.map((selection) => selection.selection.selection)
|
||||
);
|
||||
|
||||
editor.setDecorations(
|
||||
lineStyle,
|
||||
editStyle.line,
|
||||
selections
|
||||
.filter((selection) => isLineSelectionType(selection.selectionType))
|
||||
.map((selection) => {
|
||||
@ -59,8 +59,8 @@ export default async function displayPendingEditDecorations(
|
||||
await decorationSleep();
|
||||
|
||||
await runOnTargetsForEachEditor(targets, async (editor) => {
|
||||
editor.setDecorations(tokenStyle, []);
|
||||
editor.setDecorations(lineStyle, []);
|
||||
editor.setDecorations(editStyle.token, []);
|
||||
editor.setDecorations(editStyle.line, []);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,80 +1,34 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
export default class EditStyles {
|
||||
pendingDelete: vscode.TextEditorDecorationType;
|
||||
pendingLineDelete: vscode.TextEditorDecorationType;
|
||||
referenced: vscode.TextEditorDecorationType;
|
||||
referencedLine: vscode.TextEditorDecorationType;
|
||||
pendingModification0: vscode.TextEditorDecorationType;
|
||||
pendingLineModification0: vscode.TextEditorDecorationType;
|
||||
pendingModification1: vscode.TextEditorDecorationType;
|
||||
pendingLineModification1: vscode.TextEditorDecorationType;
|
||||
justAdded: vscode.TextEditorDecorationType;
|
||||
export class EditStyle {
|
||||
token: vscode.TextEditorDecorationType;
|
||||
line: vscode.TextEditorDecorationType;
|
||||
|
||||
constructor() {
|
||||
this.pendingDelete = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor(
|
||||
"cursorless.pendingDeleteBackground"
|
||||
),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
});
|
||||
|
||||
this.pendingLineDelete = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor(
|
||||
"cursorless.pendingDeleteBackground"
|
||||
),
|
||||
constructor(colorName: string) {
|
||||
const options = {
|
||||
backgroundColor: new vscode.ThemeColor(`cursorless.${colorName}`),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
};
|
||||
this.token = vscode.window.createTextEditorDecorationType(options);
|
||||
this.line = vscode.window.createTextEditorDecorationType({
|
||||
...options,
|
||||
isWholeLine: true,
|
||||
});
|
||||
|
||||
this.justAdded = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor("cursorless.justAddedBackground"),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
});
|
||||
|
||||
this.referenced = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor("cursorless.referencedBackground"),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
});
|
||||
|
||||
this.referencedLine = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor("cursorless.referencedBackground"),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
isWholeLine: true,
|
||||
});
|
||||
|
||||
this.pendingModification0 = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor(
|
||||
"cursorless.pendingModification0Background"
|
||||
),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
});
|
||||
|
||||
this.pendingLineModification0 = vscode.window.createTextEditorDecorationType(
|
||||
{
|
||||
backgroundColor: new vscode.ThemeColor(
|
||||
"cursorless.pendingModification0Background"
|
||||
),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
isWholeLine: true,
|
||||
}
|
||||
);
|
||||
|
||||
this.pendingModification1 = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: new vscode.ThemeColor(
|
||||
"cursorless.pendingModification1Background"
|
||||
),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
});
|
||||
|
||||
this.pendingLineModification1 = vscode.window.createTextEditorDecorationType(
|
||||
{
|
||||
backgroundColor: new vscode.ThemeColor(
|
||||
"cursorless.pendingModification1Background"
|
||||
),
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
|
||||
isWholeLine: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class EditStyles {
|
||||
pendingDelete: EditStyle;
|
||||
referenced: EditStyle;
|
||||
pendingModification0: EditStyle;
|
||||
pendingModification1: EditStyle;
|
||||
justAdded: EditStyle;
|
||||
|
||||
constructor() {
|
||||
this.pendingDelete = new EditStyle("pendingDeleteBackground");
|
||||
this.justAdded = new EditStyle("justAddedBackground");
|
||||
this.referenced = new EditStyle("referencedBackground");
|
||||
this.pendingModification0 = new EditStyle("pendingModification0Background");
|
||||
this.pendingModification1 = new EditStyle("pendingModification1Background");
|
||||
}
|
||||
}
|
||||
|
29
src/getTextWithPossibleDelimiter.ts
Normal file
29
src/getTextWithPossibleDelimiter.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { TypedSelection } from "./Types";
|
||||
|
||||
/** Get text from selection. Possibly add delimiter for positions before/after */
|
||||
export function getTextWithPossibleDelimiter(
|
||||
source: TypedSelection,
|
||||
destination: TypedSelection
|
||||
) {
|
||||
const sourceText = source.selection.editor.document.getText(
|
||||
source.selection.selection
|
||||
);
|
||||
return maybeAddDelimiter(sourceText, destination);
|
||||
}
|
||||
|
||||
/** Possibly add delimiter for positions before/after */
|
||||
export function maybeAddDelimiter(
|
||||
sourceText: string,
|
||||
destination: TypedSelection
|
||||
) {
|
||||
const { insideOutsideType, position } = destination;
|
||||
const containingListDelimiter =
|
||||
destination.selectionContext.containingListDelimiter;
|
||||
return containingListDelimiter == null ||
|
||||
position === "contents" ||
|
||||
insideOutsideType === "inside"
|
||||
? sourceText
|
||||
: destination.position === "after"
|
||||
? containingListDelimiter + sourceText
|
||||
: sourceText + containingListDelimiter;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import Actions from "./actions";
|
||||
import EditStyles from "./editStyles";
|
||||
import { EditStyles } from "./editStyles";
|
||||
import { Graph } from "./Types";
|
||||
import { ConstructorMap } from "./makeGraph";
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import update from "immutability-helper";
|
||||
import { Selection } from "vscode";
|
||||
import { InsideOutsideType, TypedSelection } from "./Types";
|
||||
import { updateTypedSelectionRange } from "./selectionUtils";
|
||||
|
||||
export function performInsideOutsideAdjustment(
|
||||
selection: TypedSelection,
|
||||
@ -10,6 +9,17 @@ export function performInsideOutsideAdjustment(
|
||||
selection.insideOutsideType ?? preferredInsideOutsideType;
|
||||
|
||||
if (insideOutsideType === "outside") {
|
||||
if (selection.position !== "contents") {
|
||||
const delimiterRange =
|
||||
selection.position === "before"
|
||||
? selection.selectionContext.leadingDelimiterRange
|
||||
: selection.selectionContext.trailingDelimiterRange;
|
||||
|
||||
return delimiterRange == null
|
||||
? selection
|
||||
: updateTypedSelectionRange(selection, delimiterRange);
|
||||
}
|
||||
|
||||
const usedSelection =
|
||||
selection.selectionContext.outerSelection ??
|
||||
selection.selection.selection;
|
||||
@ -23,15 +33,16 @@ export function performInsideOutsideAdjustment(
|
||||
? usedSelection.union(delimiterRange)
|
||||
: usedSelection;
|
||||
|
||||
return update(selection, {
|
||||
selection: {
|
||||
selection: (s) =>
|
||||
s.isReversed
|
||||
? new Selection(range.end, range.start)
|
||||
: new Selection(range.start, range.end),
|
||||
},
|
||||
});
|
||||
return updateTypedSelectionRange(selection, range);
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
export function performInsideAdjustment(selection: TypedSelection) {
|
||||
return performInsideOutsideAdjustment(selection, "inside");
|
||||
}
|
||||
|
||||
export function performOutsideAdjustment(selection: TypedSelection) {
|
||||
return performInsideOutsideAdjustment(selection, "outside");
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { concat, range, zip } from "lodash";
|
||||
import update from "immutability-helper";
|
||||
import { SyntaxNode } from "web-tree-sitter";
|
||||
import * as vscode from "vscode";
|
||||
import { Selection, Range, Position } from "vscode";
|
||||
import { Selection, Range, Position, Location } from "vscode";
|
||||
import { nodeMatchers } from "./languages";
|
||||
import {
|
||||
Mark,
|
||||
@ -14,6 +13,7 @@ import {
|
||||
SelectionWithEditor,
|
||||
Target,
|
||||
TypedSelection,
|
||||
Modifier,
|
||||
} from "./Types";
|
||||
import { performInsideOutsideAdjustment } from "./performInsideOutsideAdjustment";
|
||||
import { SUBWORD_MATCHER } from "./constants";
|
||||
@ -219,7 +219,7 @@ function transformSelection(
|
||||
return [{ selection, context: {} }];
|
||||
case "containingScope":
|
||||
var node: SyntaxNode | null = context.getNodeAtLocation(
|
||||
new vscode.Location(selection.editor.document.uri, selection.selection)
|
||||
new Location(selection.editor.document.uri, selection.selection)
|
||||
);
|
||||
|
||||
const nodeMatcher =
|
||||
@ -304,7 +304,7 @@ function createTypedSelection(
|
||||
selection: SelectionWithEditor,
|
||||
selectionContext: SelectionContext
|
||||
): TypedSelection {
|
||||
const { selectionType, insideOutsideType, position } = target;
|
||||
const { selectionType, insideOutsideType, position, modifier } = target;
|
||||
const { document } = selection.editor;
|
||||
|
||||
switch (selectionType) {
|
||||
@ -314,7 +314,11 @@ function createTypedSelection(
|
||||
selectionType,
|
||||
position,
|
||||
insideOutsideType,
|
||||
selectionContext: getTokenSelectionContext(selection, selectionContext),
|
||||
selectionContext: getTokenSelectionContext(
|
||||
selection,
|
||||
modifier,
|
||||
selectionContext
|
||||
),
|
||||
};
|
||||
|
||||
case "line": {
|
||||
@ -445,6 +449,7 @@ function performPositionAdjustment(
|
||||
|
||||
function getTokenSelectionContext(
|
||||
selection: SelectionWithEditor,
|
||||
modifier: Modifier,
|
||||
selectionContext: SelectionContext
|
||||
): SelectionContext {
|
||||
if (!isSelectionContextEmpty(selectionContext)) {
|
||||
@ -482,19 +487,20 @@ function getTokenSelectionContext(
|
||||
)
|
||||
: null;
|
||||
|
||||
// Didn't find any delimiters
|
||||
if (leadingDelimiterRange == null && trailingDelimiterRange == null) {
|
||||
return selectionContext;
|
||||
if (
|
||||
leadingDelimiterRange != null ||
|
||||
trailingDelimiterRange != null ||
|
||||
modifier.type !== "subpiece"
|
||||
) {
|
||||
return {
|
||||
isInDelimitedList: true,
|
||||
containingListDelimiter: " ",
|
||||
leadingDelimiterRange,
|
||||
trailingDelimiterRange,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isInDelimitedList: true,
|
||||
containingListDelimiter: document.getText(
|
||||
(trailingDelimiterRange ?? leadingDelimiterRange)!
|
||||
),
|
||||
leadingDelimiterRange,
|
||||
trailingDelimiterRange,
|
||||
};
|
||||
return selectionContext;
|
||||
}
|
||||
|
||||
// TODO Clean this up once we have rich targets and better polymorphic
|
||||
|
31
src/selectionUtils.ts
Normal file
31
src/selectionUtils.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Range, Selection, Position } from "vscode";
|
||||
import update from "immutability-helper";
|
||||
import { TypedSelection } from "./Types";
|
||||
|
||||
export function selectionFromRange(
|
||||
range: Range,
|
||||
isReversed: boolean
|
||||
): Selection {
|
||||
const { start, end } = range;
|
||||
return isReversed ? new Selection(end, start) : new Selection(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the given typed selection so that the new selection has the new given range
|
||||
* preserving the direction of the original selection
|
||||
*
|
||||
* @param selection The original typed selection to Update
|
||||
* @param range The new range for the given selection
|
||||
* @returns The updated typed selection
|
||||
*/
|
||||
export function updateTypedSelectionRange(
|
||||
selection: TypedSelection,
|
||||
range: Range
|
||||
): TypedSelection {
|
||||
return update(selection, {
|
||||
selection: {
|
||||
selection: () =>
|
||||
selectionFromRange(range, selection.selection.selection.isReversed),
|
||||
},
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user