From 86a558116ada85c3664974a65af420067e678f00 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 15 Jul 2021 12:53:47 +0200 Subject: [PATCH] implemented paste action --- src/actions/Paste.ts | 107 +++++++++++++++++++++++++++++++++++++++++++ src/actions/index.ts | 8 +--- 2 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 src/actions/Paste.ts diff --git a/src/actions/Paste.ts b/src/actions/Paste.ts new file mode 100644 index 0000000000..a17a10e4a6 --- /dev/null +++ b/src/actions/Paste.ts @@ -0,0 +1,107 @@ +import { + Action, + ActionPreferences, + ActionReturnValue, + Graph, + TypedSelection, +} from "../Types"; +import displayPendingEditDecorations from "../editDisplayUtils"; +import { env, Selection } from "vscode"; +import { runForEachEditor } from "../targetUtils"; +import { computeChangedOffsets } from "../computeChangedOffsets"; +import { flatten, zip } from "lodash"; +import update from "immutability-helper"; + +export default class Paste implements Action { + targetPreferences: ActionPreferences[] = [{ insideOutsideType: "inside" }]; + + constructor(private graph: Graph) { + this.run = this.run.bind(this); + } + + async run([targets]: [TypedSelection[]]): Promise { + await displayPendingEditDecorations( + targets, + this.graph.editStyles.pendingModification0, + this.graph.editStyles.pendingLineModification0 + ); + + const text = await env.clipboard.readText(); + + if (text.length === 0) { + throw new Error("Can't paste empty clipboard"); + } + + const lines = text.trim().split("\n"); + + const getText = + targets.length === lines.length + ? // Paste each line on each target + (index: number) => lines[index] + : // Paste entire clipboard on each target + () => text; + + const edits = targets.map((target, index) => ({ + editor: target.selection.editor, + range: target.selection.selection, + originalSelection: target, + newText: getText(index), + })); + + const thatMark = flatten( + await runForEachEditor( + edits, + (edit) => edit.editor, + async (editor, edits) => { + const newEdits = zip(edits, computeChangedOffsets(editor, edits)).map( + ([originalEdit, changedEdit]) => ({ + originalRange: originalEdit!.range, + originalSelection: originalEdit!.originalSelection, + newText: originalEdit!.newText, + newStartOffset: changedEdit!.startOffset, + newEndOffset: changedEdit!.endOffset, + }) + ); + + await editor.edit((editBuilder) => { + newEdits.forEach((edit) => { + if (edit.originalRange.isEmpty) { + editBuilder.insert(edit.originalRange.start, edit.newText); + } else { + editBuilder.replace(edit.originalRange, edit.newText); + } + }); + }); + + return newEdits.map((edit) => { + const start = editor.document.positionAt(edit.newStartOffset); + const end = editor.document.positionAt(edit.newEndOffset); + const isReversed = + edit.originalSelection.selection.selection.isReversed; + const selection = new Selection( + isReversed ? end : start, + isReversed ? start : end + ); + return { + editor, + typedSelection: update(edit.originalSelection, { + selection: { + selection: { $set: selection }, + }, + }), + selection, + }; + }); + } + ) + ); + + await displayPendingEditDecorations( + thatMark.map(({ typedSelection }) => typedSelection), + this.graph.editStyles.pendingModification0, + this.graph.editStyles.pendingLineModification0 + ); + + return { returnValue: null, thatMark }; + } +} diff --git a/src/actions/index.ts b/src/actions/index.ts index ba3c7663ab..647bcd2926 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -15,6 +15,7 @@ import Swap from "./swap"; import Use from "./use"; import Wrap from "./wrap"; import { ScrollToTop, ScrollToCenter, ScrollToBottom } from "./Scroll"; +import Paste from "./Paste"; class Actions implements ActionRecord { constructor(private graph: Graph) {} @@ -27,12 +28,7 @@ class Actions implements ActionRecord { fold = new Fold(this.graph); insertLineBefore = new InsertLineBefore(this.graph); insertLineAfter = new InsertLineAfter(this.graph); - paste: Action = { - run: async ([targets]) => { - throw new Error("Not implemented"); - }, - targetPreferences: [{ position: "after", insideOutsideType: "outside" }], - }; + paste = new Paste(this.graph); scrollToBottom = new ScrollToBottom(this.graph); scrollToCenter = new ScrollToCenter(this.graph); scrollToTop = new ScrollToTop(this.graph);