Added action duplicate (#134)

* started working on action for line duplication

* unifi ranges

* added comments

* clean up

* clean up

* clean up

* calculate newmark

* Various cleanup

Co-authored-by: Pokey Rule <pokey.rule@gmail.com>
This commit is contained in:
Andreas Arvidsson 2021-07-25 02:43:32 +02:00 committed by GitHub
parent 7b24e21d5d
commit 184ea2ae3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 135 additions and 5 deletions

View File

@ -240,6 +240,8 @@ export type ActionType =
| "commentLines"
| "copy"
| "cut"
| "copyLinesDown"
| "copyLinesUp"
| "delete"
| "extractVariable"
| "editNewLineAbove"

85
src/actions/CopyLines.ts Normal file
View File

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

View File

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

View File

@ -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 {
@ -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);

28
src/unifyRanges.ts Normal file
View File

@ -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];
}

View File

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