diff --git a/src/vs/editor/common/cursor/cursorTypeEditOperations.ts b/src/vs/editor/common/cursor/cursorTypeEditOperations.ts index e250bb74431..d1290a3e651 100644 --- a/src/vs/editor/common/cursor/cursorTypeEditOperations.ts +++ b/src/vs/editor/common/cursor/cursorTypeEditOperations.ts @@ -664,15 +664,15 @@ export class PasteOperation { } private static _distributePasteToCursors(config: CursorConfiguration, selections: Selection[], text: string, pasteOnNewLine: boolean, multicursorText: string[]): string[] | null { - if (pasteOnNewLine) { - return null; - } if (selections.length === 1) { return null; } if (multicursorText && multicursorText.length === selections.length) { return multicursorText; } + if (pasteOnNewLine) { + return null; + } if (config.multiCursorPaste === 'spread') { // Try to spread the pasted text in case the line count matches the cursor count // Remove trailing \n if present diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index f41a32c7995..284bbc487a4 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -959,33 +959,20 @@ export class ViewModel extends Disposable implements IViewModel { } } - if (!hasNonEmptyRange) { + if (!hasNonEmptyRange && !emptySelectionClipboard) { // all ranges are empty - if (!emptySelectionClipboard) { - return ''; - } - - const modelLineNumbers = modelRanges.map((r) => r.startLineNumber); - - let result = ''; - for (let i = 0; i < modelLineNumbers.length; i++) { - if (i > 0 && modelLineNumbers[i - 1] === modelLineNumbers[i]) { - continue; - } - result += this.model.getLineContent(modelLineNumbers[i]) + newLineCharacter; - } - return result; + return ''; } if (hasEmptyRange && emptySelectionClipboard) { - // mixed empty selections and non-empty selections + // some (maybe all) empty selections const result: string[] = []; let prevModelLineNumber = 0; for (const modelRange of modelRanges) { const modelLineNumber = modelRange.startLineNumber; if (modelRange.isEmpty()) { if (modelLineNumber !== prevModelLineNumber) { - result.push(this.model.getLineContent(modelLineNumber)); + result.push(this.model.getLineContent(modelLineNumber) + newLineCharacter); } } else { result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 47b7dd7b5a2..61ddc81e06d 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2239,6 +2239,37 @@ suite('Editor Controller', () => { }); }); + test('issue #256039: paste from multiple cursors with empty selections and multiCursorPaste full', () => { + usingCursor({ + text: [ + 'line1', + 'line2', + 'line3' + ], + editorOpts: { + multiCursorPaste: 'full' + } + }, (editor, model, viewModel) => { + // 2 cursors on lines 1 and 2 + viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); + + viewModel.paste( + 'line1\nline2\n', + true, + ['line1\n', 'line2\n'] + ); + + // Each cursor gets its respective line + assert.strictEqual(model.getValue(), [ + 'line1', + 'line1', + 'line2', + 'line2', + 'line3' + ].join('\n')); + }); + }); + test('issue #3071: Investigate why undo stack gets corrupted', () => { const model = createTextModel( [ diff --git a/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts b/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts index 8afa07a8b56..c5ae0e5ac9c 100644 --- a/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts +++ b/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts @@ -198,7 +198,27 @@ suite('ViewModel', () => { new Range(3, 2, 3, 2), ], true, - 'line2\nline3\n' + [ + 'line2\n', + 'line3\n' + ] + ); + }); + + test('issue #256039: getPlainTextToCopy with multiple cursors and empty selections should return array', () => { + // Bug: When copying with multiple cursors (empty selections) with emptySelectionClipboard enabled, + // the result should be an array so that pasting with "editor.multiCursorPaste": "full" + // correctly distributes each line to the corresponding cursor. + // Without the fix, this returns 'line2\nline3\n' (a single string). + // With the fix, this returns ['line2\n', 'line3\n'] (an array). + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 1, 2, 1), + new Range(3, 1, 3, 1), + ], + true, + ['line2\n', 'line3\n'] ); }); @@ -222,7 +242,7 @@ suite('ViewModel', () => { new Range(3, 2, 3, 2), ], true, - ['ine2', 'line3'] + ['ine2', 'line3\n'] ); }); @@ -259,7 +279,10 @@ suite('ViewModel', () => { new Range(3, 2, 3, 2), ], true, - 'line2\nline3\n' + [ + 'line2\n', + 'line3\n' + ] ); });