Skip to content
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
2 changes: 2 additions & 0 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ export type ActionType =
| "commentLines"
| "copy"
| "cut"
| "copyLinesDown"
| "copyLinesUp"
| "delete"
| "extractVariable"
| "editNewLineAbove"
Expand Down
85 changes: 85 additions & 0 deletions src/actions/CopyLines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
Action,
ActionPreferences,
ActionReturnValue,
Graph,
TypedSelection,
} from "../Types";
import { Range, TextEditor } from "vscode";
import { performEditsAndUpdateSelections } from "../updateSelections";
import displayPendingEditDecorations from "../editDisplayUtils";
import { runOnTargetsForEachEditor } from "../targetUtils";
import { flatten } from "lodash";
import unifyRanges from "../unifyRanges";
import expandToContainingLine from "./expandToContainingLine";

class CopyLines implements Action {
targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }];

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

private getRanges(editor: TextEditor, targets: TypedSelection[]) {
const ranges = targets.map((target) =>
expandToContainingLine(editor, target.selection.selection)
);

return unifyRanges(ranges);
}

private getEdits(editor: TextEditor, ranges: Range[]) {
return ranges.map((range) => {
let text = editor.document.getText(range);
text = this.isUp ? `${text}\n` : `\n${text}`;
const newRange = this.isUp
? new Range(range.start, range.start)
: new Range(range.end, range.end);
return {
editor,
range: newRange,
text,
};
});
}

async run([targets]: [TypedSelection[]]): Promise<ActionReturnValue> {
await displayPendingEditDecorations(
targets,
this.graph.editStyles.referenced,
this.graph.editStyles.referencedLine
);

const thatMark = flatten(
await runOnTargetsForEachEditor(targets, async (editor, targets) => {
const ranges = this.getRanges(editor, targets);
const edits = this.getEdits(editor, ranges);

const [updatedSelections] = await performEditsAndUpdateSelections(
editor,
edits,
[targets.map((target) => target.selection.selection)]
);

return updatedSelections.map((selection) => ({
editor,
selection,
}));
})
);

return { returnValue: null, thatMark };
}
}

export class CopyLinesUp extends CopyLines {
constructor(graph: Graph) {
super(graph, false);
}
}

export class CopyLinesDown extends CopyLines {
constructor(graph: Graph) {
super(graph, true);
}
}
10 changes: 10 additions & 0 deletions src/actions/expandToContainingLine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Range, TextEditor } from "vscode";

export default function expandToContainingLine(
editor: TextEditor,
range: Range
) {
const start = range.start.with({ character: 0 });
const end = editor.document.lineAt(range.end).range.end;
return new Range(start, end);
}
3 changes: 3 additions & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "./InsertEmptyLines";
import GetText from "./GetText";
import { FindInFiles } from "./Find";
import { CopyLinesUp, CopyLinesDown } from "./CopyLines";
import SetBreakpoint from "./SetBreakpoint";

class Actions implements ActionRecord {
Expand All @@ -38,6 +39,8 @@ class Actions implements ActionRecord {
clear = new Clear(this.graph);
commentLines = new CommentLines(this.graph);
copy = new Copy(this.graph);
copyLinesDown = new CopyLinesDown(this.graph);
copyLinesUp = new CopyLinesUp(this.graph);
cut = new Cut(this.graph);
delete = new Delete(this.graph);
extractVariable = new ExtractVariable(this.graph);
Expand Down
28 changes: 28 additions & 0 deletions src/unifyRanges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Range } from "vscode";

/** Unifies overlapping/intersecting ranges */
export default function unifyRanges(ranges: Range[]): Range[] {
let run = true;
while (run) {
[ranges, run] = onePass(ranges);
}
return ranges;
}

function onePass(ranges: Range[]): [Range[], boolean] {
const result: Range[] = [];
let madeChanges = false;
ranges.forEach((range) => {
const index = result.findIndex((r) => r.intersection(range) != null);
// Update existing intersecting range
if (index > -1) {
result[index] = result[index].union(range);
madeChanges = true;
}
// Add new range
else {
result.push(range);
}
});
return [result, madeChanges];
}
12 changes: 7 additions & 5 deletions src/updateSelections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ function updateSelectionInfoMatrix(

selectionInfoMatrix.forEach((selectionInfos) => {
selectionInfos.forEach((selectionInfo) => {
// Change is before selection. Move entire selection.
if (change.range.start.isBefore(selectionInfo.range.start)) {
selectionInfo.startOffset += offsetDelta;
// Change is selection. Move just end to match.
if (change.range.isEqual(selectionInfo.range)) {
selectionInfo.endOffset += offsetDelta;
}
// Change is selection. Move just end to match.
else if (change.range.isEqual(selectionInfo.range)) {
// Change is before selection. Move entire selection.
else if (
change.range.start.isBeforeOrEqual(selectionInfo.range.start)
) {
selectionInfo.startOffset += offsetDelta;
selectionInfo.endOffset += offsetDelta;
}
});
Expand Down