Skip to content

Commit 9457274

Browse files
committed
paste empty selection includes newline
1 parent 37cb706 commit 9457274

5 files changed

Lines changed: 58 additions & 9 deletions

File tree

src/vs/editor/common/config/editorOptions.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,11 @@ export interface IEditorOptions {
850850
* Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.
851851
*/
852852
inlineCompletionsAccessibilityVerbose?: boolean;
853+
/**
854+
* Configure the behaviour when copy-pasting empty selection.
855+
* Defaults to 'always'.
856+
*/
857+
pasteEmptySelectionIncludesNewline?: 'always' | 'never' | 'singleOnly' | 'multipleOnly';
853858
}
854859

855860
/**
@@ -5896,7 +5901,8 @@ export const enum EditorOption {
58965901
inlineCompletionsAccessibilityVerbose,
58975902
effectiveEditContext,
58985903
scrollOnMiddleClick,
5899-
effectiveAllowVariableFonts
5904+
effectiveAllowVariableFonts,
5905+
pasteEmptySelectionIncludesNewline,
59005906
}
59015907

59025908
export const EditorOptions = {
@@ -6804,7 +6810,21 @@ export const EditorOptions = {
68046810
wrappingIndent: register(new WrappingIndentOption()),
68056811
wrappingStrategy: register(new WrappingStrategy()),
68066812
effectiveEditContextEnabled: register(new EffectiveEditContextEnabled()),
6807-
effectiveAllowVariableFonts: register(new EffectiveAllowVariableFonts())
6813+
effectiveAllowVariableFonts: register(new EffectiveAllowVariableFonts()),
6814+
pasteEmptySelectionIncludesNewline: register(new EditorStringEnumOption(
6815+
EditorOption.pasteEmptySelectionIncludesNewline, 'pasteEmptySelectionIncludesNewline',
6816+
'always' as 'always' | 'never' | 'singleOnly' | 'multipleOnly',
6817+
['always', 'never', 'singleOnly', 'multipleOnly'] as const,
6818+
{
6819+
markdownEnumDescriptions: [
6820+
nls.localize('pasteEmptySelectionIncludesNewline.always', "Both single & multiple cursors paste with '\n' when copying empty selection"),
6821+
nls.localize('pasteEmptySelectionIncludesNewline.never', "Both single & multiple cursors trim '\n' from the end when copying empty selection"),
6822+
nls.localize('pasteEmptySelectionIncludesNewline.singleOnly', "Multiple cursors trim '\n' from the end when copying empty selection"),
6823+
nls.localize('pasteEmptySelectionIncludesNewline.multipleOnly', "Single cursor trims '\n' from the end when copying empty selection"),
6824+
],
6825+
markdownDescription: nls.localize('pasteEmptySelectionIncludesNewline', "Include '\n' character when copying empty selection")
6826+
}
6827+
)),
68086828
};
68096829

68106830
type EditorOptionsType = typeof EditorOptions;

src/vs/editor/common/cursor/cursorTypeEditOperations.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -655,11 +655,17 @@ export class PasteOperation {
655655

656656
public static getEdits(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], text: string, pasteOnNewLine: boolean[] | null, multicursorText: string[]) {
657657
const distributedPaste = this._distributePasteToCursors(config, selections, text, pasteOnNewLine, multicursorText);
658+
const [singleIncludeNewline, multipleIncludeNewline] = {
659+
'always': [true, true],
660+
'never': [false, false],
661+
'singleOnly': [true, false],
662+
'multipleOnly': [false, true]
663+
}[config.pasteEmptySelectionIncludesNewline];
658664
if (distributedPaste) {
659665
selections.sort(Range.compareRangesUsingStarts);
660-
return this._distributedPaste(config, model, selections, distributedPaste, pasteOnNewLine);
666+
return this._distributedPaste(config, model, selections, distributedPaste, pasteOnNewLine, multipleIncludeNewline);
661667
} else {
662-
return this._simplePaste(config, model, selections, text, pasteOnNewLine);
668+
return this._simplePaste(config, model, selections, text, pasteOnNewLine, singleIncludeNewline);
663669
}
664670
}
665671

@@ -688,15 +694,21 @@ export class PasteOperation {
688694
return null;
689695
}
690696

691-
private static _distributedPaste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], texts: string[], pasteOnNewLine: boolean[] | null): EditOperationResult {
697+
private static _distributedPaste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], texts: string[], pasteOnNewLine: boolean[] | null, includeNewline: boolean): EditOperationResult {
692698
if (!pasteOnNewLine) {
693699
pasteOnNewLine = [];
694700
}
695701
const commands: ICommand[] = [];
696702
for (let i = 0, len = selections.length; i < len; i++) {
697703
const selection = selections[i];
698-
const text = texts[i];
704+
let text = texts[i];
699705
const position = selection.getPosition();
706+
if (!includeNewline) {
707+
text = text.slice(0, text.length - 1);
708+
if (text[text.length - 1] === '\r') {
709+
text = text.slice(0, text.length - 1);
710+
}
711+
}
700712
if (pasteOnNewLine[i] && selection.isEmpty() && text.indexOf('\n') === text.length - 1) {
701713
// Paste entire line at the beginning of line
702714
const typeSelection = new Range(position.lineNumber, 1, position.lineNumber, 1);
@@ -713,10 +725,16 @@ export class PasteOperation {
713725
});
714726
}
715727

716-
private static _simplePaste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], text: string, pasteOnNewLine: boolean[] | null): EditOperationResult {
728+
private static _simplePaste(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], text: string, pasteOnNewLine: boolean[] | null, includeNewline: boolean): EditOperationResult {
717729
const commands: ICommand[] = [];
718730
const singleCopyPasteOnNewLine = pasteOnNewLine?.length === 1 && pasteOnNewLine[0];
719731
if (!pasteOnNewLine) { pasteOnNewLine = []; }
732+
if (!includeNewline && text[text.length - 1] === '\n') {
733+
text = text.slice(0, text.length - 1);
734+
if (text[text.length - 1] === '\r') {
735+
text = text.slice(0, text.length - 1);
736+
}
737+
}
720738
for (let i = 0, len = selections.length; i < len; i++) {
721739
const selection = selections[i];
722740
const position = selection.getPosition();

src/vs/editor/common/cursorCommon.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export class CursorConfiguration {
8080
public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean; bracket: (ch: string) => boolean; comment: (ch: string) => boolean };
8181
public readonly wordSegmenterLocales: string[];
8282
public readonly overtypeOnPaste: boolean;
83+
public readonly pasteEmptySelectionIncludesNewline: 'always' | 'never' | 'singleOnly' | 'multipleOnly';
8384

8485
private readonly _languageId: string;
8586
private _electricChars: { [key: string]: boolean } | null;
@@ -104,6 +105,7 @@ export class CursorConfiguration {
104105
|| e.hasChanged(EditorOption.readOnly)
105106
|| e.hasChanged(EditorOption.wordSegmenterLocales)
106107
|| e.hasChanged(EditorOption.overtypeOnPaste)
108+
|| e.hasChanged(EditorOption.pasteEmptySelectionIncludesNewline)
107109
);
108110
}
109111

@@ -144,6 +146,7 @@ export class CursorConfiguration {
144146
this.autoIndent = options.get(EditorOption.autoIndent);
145147
this.wordSegmenterLocales = options.get(EditorOption.wordSegmenterLocales);
146148
this.overtypeOnPaste = options.get(EditorOption.overtypeOnPaste);
149+
this.pasteEmptySelectionIncludesNewline = options.get(EditorOption.pasteEmptySelectionIncludesNewline);
147150

148151
this.surroundingPairs = {};
149152
this._electricChars = null;

src/vs/editor/common/standalone/standaloneEnums.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,8 @@ export enum EditorOption {
346346
inlineCompletionsAccessibilityVerbose = 169,
347347
effectiveEditContext = 170,
348348
scrollOnMiddleClick = 171,
349-
effectiveAllowVariableFonts = 172
349+
effectiveAllowVariableFonts = 172,
350+
pasteEmptySelectionIncludesNewline = 173,
350351
}
351352

352353
/**

src/vs/monaco.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3989,6 +3989,11 @@ declare namespace monaco.editor {
39893989
* Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.
39903990
*/
39913991
inlineCompletionsAccessibilityVerbose?: boolean;
3992+
/**
3993+
* Configure the behaviour when copy-pasting empty selection.
3994+
* Defaults to 'always'.
3995+
*/
3996+
pasteEmptySelectionIncludesNewline?: 'always' | 'never' | 'singleOnly' | 'multipleOnly';
39923997
}
39933998

39943999
export interface IDiffEditorBaseOptions {
@@ -5236,7 +5241,8 @@ declare namespace monaco.editor {
52365241
inlineCompletionsAccessibilityVerbose = 169,
52375242
effectiveEditContext = 170,
52385243
scrollOnMiddleClick = 171,
5239-
effectiveAllowVariableFonts = 172
5244+
effectiveAllowVariableFonts = 172,
5245+
pasteEmptySelectionIncludesNewline = 173
52405246
}
52415247

52425248
export const EditorOptions: {
@@ -5413,6 +5419,7 @@ declare namespace monaco.editor {
54135419
wrappingStrategy: IEditorOption<EditorOption.wrappingStrategy, 'simple' | 'advanced'>;
54145420
effectiveEditContextEnabled: IEditorOption<EditorOption.effectiveEditContext, boolean>;
54155421
effectiveAllowVariableFonts: IEditorOption<EditorOption.effectiveAllowVariableFonts, boolean>;
5422+
pasteEmptySelectionIncludesNewline: IEditorOption<EditorOption.pasteEmptySelectionIncludesNewline, 'always' | 'never' | 'singleOnly' | 'multipleOnly'>;
54165423
};
54175424

54185425
type EditorOptionsType = typeof EditorOptions;

0 commit comments

Comments
 (0)