Skip to content

Commit d6e60d3

Browse files
authored
Fix paste actions breaking paste on new line (microsoft#152809)
Fixes microsoft#151326
1 parent 5437480 commit d6e60d3

File tree

1 file changed

+40
-24
lines changed

1 file changed

+40
-24
lines changed

src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import { DataTransfers } from 'vs/base/browser/dnd';
77
import { addDisposableListener } from 'vs/base/browser/dom';
88
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
9-
import { CancellationToken } from 'vs/base/common/cancellation';
109
import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer';
1110
import { Disposable } from 'vs/base/common/lifecycle';
1211
import { Mimes } from 'vs/base/common/mime';
@@ -16,8 +15,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
1615
import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService';
1716
import { EditorOption } from 'vs/editor/common/config/editorOptions';
1817
import { IRange, Range } from 'vs/editor/common/core/range';
19-
import { IEditorContribution } from 'vs/editor/common/editorCommon';
20-
import { DocumentPasteEdit, DocumentPasteEditProvider } from 'vs/editor/common/languages';
18+
import { Handler, IEditorContribution, PastePayload } from 'vs/editor/common/editorCommon';
2119
import { ITextModel } from 'vs/editor/common/model';
2220
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
2321
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
@@ -26,21 +24,12 @@ import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser';
2624
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
2725
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2826

29-
const vscodeClipboardMime = 'application/vnd.code.copyId';
27+
const vscodeClipboardMime = 'application/vnd.code.copyMetadata';
3028

31-
const defaultPasteEditProvider = new class implements DocumentPasteEditProvider {
32-
pasteMimeTypes = [Mimes.text, 'text'];
33-
34-
async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: VSDataTransfer, _token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
35-
const textDataTransfer = dataTransfer.get(Mimes.text) ?? dataTransfer.get('text');
36-
if (textDataTransfer) {
37-
const text = await textDataTransfer.asString();
38-
return { insertText: text };
39-
}
40-
41-
return undefined;
42-
}
43-
};
29+
interface CopyMetadata {
30+
readonly id?: string;
31+
readonly wasFromEmptySelection: boolean;
32+
}
4433

4534
export class CopyPasteController extends Disposable implements IEditorContribution {
4635

@@ -97,7 +86,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi
9786

9887
const ranges: IRange[] = [...selections];
9988
const primarySelection = selections[0];
100-
if (primarySelection.isEmpty()) {
89+
const wasFromEmptySelection = primarySelection.isEmpty();
90+
if (wasFromEmptySelection) {
10191
if (!this._editor.getOption(EditorOption.emptySelectionClipboard)) {
10292
return;
10393
}
@@ -106,14 +96,18 @@ export class CopyPasteController extends Disposable implements IEditorContributi
10696

10797
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model).filter(x => !!x.prepareDocumentPaste);
10898
if (!providers.length) {
99+
this.setCopyMetadata(e.clipboardData, { wasFromEmptySelection });
109100
return;
110101
}
111102

112103
const dataTransfer = toVSDataTransfer(e.clipboardData);
113104

114105
// Save off a handle pointing to data that VS Code maintains.
115106
const handle = generateUuid();
116-
e.clipboardData.setData(vscodeClipboardMime, handle);
107+
this.setCopyMetadata(e.clipboardData, {
108+
id: handle,
109+
wasFromEmptySelection,
110+
});
117111

118112
const promise = createCancelablePromise(async token => {
119113
const results = await Promise.all(providers.map(provider => {
@@ -133,6 +127,10 @@ export class CopyPasteController extends Disposable implements IEditorContributi
133127
this._currentClipboardItem = { handle: handle, dataTransferPromise: promise };
134128
}
135129

130+
private setCopyMetadata(dataTransfer: DataTransfer, metadata: CopyMetadata) {
131+
dataTransfer.setData(vscodeClipboardMime, JSON.stringify(metadata));
132+
}
133+
136134
private async handlePaste(e: ClipboardEvent) {
137135
if (!e.clipboardData || !this._editor.hasTextFocus()) {
138136
return;
@@ -148,9 +146,10 @@ export class CopyPasteController extends Disposable implements IEditorContributi
148146
return;
149147
}
150148

151-
const handle = e.clipboardData?.getData(vscodeClipboardMime);
152-
if (typeof handle !== 'string') {
153-
return;
149+
let metadata: CopyMetadata | undefined;
150+
const rawMetadata = e.clipboardData?.getData(vscodeClipboardMime);
151+
if (rawMetadata && typeof rawMetadata === 'string') {
152+
metadata = JSON.parse(rawMetadata);
154153
}
155154

156155
const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model);
@@ -167,7 +166,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
167166
try {
168167
const dataTransfer = toVSDataTransfer(e.clipboardData);
169168

170-
if (handle && this._currentClipboardItem?.handle === handle) {
169+
if (metadata?.id && this._currentClipboardItem?.handle === metadata.id) {
171170
const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise;
172171
toMergeDataTransfer.forEach((value, key) => {
173172
dataTransfer.replace(key, value);
@@ -184,7 +183,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
184183

185184
dataTransfer.delete(vscodeClipboardMime);
186185

187-
for (const provider of [...providers, defaultPasteEditProvider]) {
186+
for (const provider of providers) {
188187
if (!provider.pasteMimeTypes.some(type => {
189188
if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) {
190189
return [...dataTransfer.values()].some(item => item.asFile());
@@ -208,6 +207,23 @@ export class CopyPasteController extends Disposable implements IEditorContributi
208207
return;
209208
}
210209
}
210+
211+
// Default handler
212+
const textDataTransfer = dataTransfer.get(Mimes.text) ?? dataTransfer.get('text');
213+
if (!textDataTransfer) {
214+
return;
215+
}
216+
217+
const text = await textDataTransfer.asString();
218+
if (originalDocVersion !== model.getVersionId()) {
219+
return;
220+
}
221+
222+
this._editor.trigger('keyboard', Handler.Paste, <PastePayload>{
223+
text: text,
224+
pasteOnNewLine: metadata?.wasFromEmptySelection,
225+
multicursorText: null
226+
});
211227
} finally {
212228
tokenSource.dispose();
213229
}

0 commit comments

Comments
 (0)