Skip to content

Commit 3a514f3

Browse files
committed
Add paste as default settings and enable js/ts paste with imports by default
Fixes microsoft#184871 For microsoft#30066 Adds new settings that let you configure the default way to paste/drop. Also enables js/ts paste with imports by default for 5.7+. However will not apply by default. Instead it will be shown as an option after pasting. You can then use the `editor.pasteAs.preferences` setting to make it apply automatically or use the `javascript.updateImportsOnPaste.enabled` settings to disable the feature entirely
1 parent 84285b3 commit 3a514f3

File tree

11 files changed

+277
-96
lines changed

11 files changed

+277
-96
lines changed

extensions/typescript-language-features/package.json

+6-12
Original file line numberDiff line numberDiff line change
@@ -1506,23 +1506,17 @@
15061506
"description": "%typescript.tsserver.enableRegionDiagnostics%",
15071507
"scope": "window"
15081508
},
1509-
"javascript.experimental.updateImportsOnPaste": {
1509+
"javascript.updateImportsOnPaste.enabled": {
15101510
"scope": "window",
15111511
"type": "boolean",
1512-
"default": false,
1513-
"description": "%configuration.updateImportsOnPaste%",
1514-
"tags": [
1515-
"experimental"
1516-
]
1512+
"default": true,
1513+
"markdownDescription": "%configuration.updateImportsOnPaste%"
15171514
},
1518-
"typescript.experimental.updateImportsOnPaste": {
1515+
"typescript.updateImportsOnPaste.enabled": {
15191516
"scope": "window",
15201517
"type": "boolean",
1521-
"default": false,
1522-
"description": "%configuration.updateImportsOnPaste%",
1523-
"tags": [
1524-
"experimental"
1525-
]
1518+
"default": true,
1519+
"markdownDescription": "%configuration.updateImportsOnPaste%"
15261520
},
15271521
"typescript.experimental.expandableHover": {
15281522
"type": "boolean",

extensions/typescript-language-features/package.nls.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@
224224
"configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors": "Suppresses semantic errors on web even when project wide IntelliSense is enabled. This is always on when project wide IntelliSense is not enabled or available. See `#typescript.tsserver.web.projectWideIntellisense.enabled#`",
225225
"configuration.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web. This enables IntelliSense for imported packages. Requires `#typescript.tsserver.web.projectWideIntellisense.enabled#`. Currently not supported for Safari.",
226226
"configuration.tsserver.nodePath": "Run TS Server on a custom Node installation. This can be a path to a Node executable, or 'node' if you want VS Code to detect a Node installation.",
227-
"configuration.updateImportsOnPaste": "Automatically update imports when pasting code. Requires TypeScript 5.7+.",
227+
"configuration.updateImportsOnPaste": "Enable updating imports when pasting code. Requires TypeScript 5.7+.\n\nBy default this shows a option to update imports after pasting. You can use the `#editor.pasteAs.preferences#` setting to update imports automatically when pasting: `\"editor.pasteAs.preferences\": [{ \"kind\": \"text.jsts.pasteWithImports\" }]`.",
228228
"configuration.expandableHover": "Enable/disable expanding on hover.",
229229
"walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js",
230230
"walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.",

extensions/typescript-language-features/src/languageFeatures/copyPaste.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class CopyMetadata {
3838
}
3939
}
4040

41-
const settingId = 'experimental.updateImportsOnPaste';
41+
const enabledSettingId = 'updateImportsOnPaste.enabled';
4242

4343
class DocumentPasteProvider implements vscode.DocumentPasteEditProvider {
4444

@@ -127,6 +127,8 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider {
127127
}
128128

129129
const edit = new vscode.DocumentPasteEdit('', vscode.l10n.t("Paste with imports"), DocumentPasteProvider.kind);
130+
edit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Empty.append('text', 'plain')];
131+
130132
const additionalEdit = new vscode.WorkspaceEdit();
131133
for (const edit of response.body.edits) {
132134
additionalEdit.set(this._client.toResource(edit.fileName), edit.textChanges.map(typeConverters.TextEdit.fromCodeEdit));
@@ -146,15 +148,15 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider {
146148

147149
private isEnabled(document: vscode.TextDocument) {
148150
const config = vscode.workspace.getConfiguration(this._modeId, document.uri);
149-
return config.get(settingId, false);
151+
return config.get(enabledSettingId, false);
150152
}
151153
}
152154

153155
export function register(selector: DocumentSelector, language: LanguageDescription, client: ITypeScriptServiceClient) {
154156
return conditionalRegistration([
155157
requireSomeCapability(client, ClientCapability.Semantic),
156158
requireMinVersion(client, API.v570),
157-
requireGlobalConfiguration(language.id, settingId),
159+
requireGlobalConfiguration(language.id, enabledSettingId),
158160
], () => {
159161
return vscode.languages.registerDocumentPasteEditProvider(selector.semantic, new DocumentPasteProvider(language.id, client), {
160162
providedPasteEditKinds: [DocumentPasteProvider.kind],

src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteContribution.ts

+37-3
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';
77
import { IJSONSchema, SchemaToType } from '../../../../base/common/jsonSchema.js';
88
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
9+
import * as nls from '../../../../nls.js';
10+
import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';
11+
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
12+
import { Registry } from '../../../../platform/registry/common/platform.js';
913
import { ICodeEditor } from '../../../browser/editorBrowser.js';
1014
import { EditorAction, EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from '../../../browser/editorExtensions.js';
15+
import { editorConfigurationBaseNode } from '../../../common/config/editorConfigurationSchema.js';
1116
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
1217
import { registerEditorFeature } from '../../../common/editorFeatures.js';
13-
import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx } from './copyPasteController.js';
18+
import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx, pasteAsPreferenceConfig } from './copyPasteController.js';
1419
import { DefaultPasteProvidersFeature, DefaultTextPasteOrDropEditProvider } from './defaultProviders.js';
15-
import * as nls from '../../../../nls.js';
16-
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
1720

1821
registerEditorContribution(CopyPasteController.ID, CopyPasteController, EditorContributionInstantiation.Eager); // eager because it listens to events on the container dom node of the editor
1922
registerEditorFeature(DefaultPasteProvidersFeature);
@@ -105,3 +108,34 @@ registerEditorAction(class extends EditorAction {
105108
return CopyPasteController.get(editor)?.pasteAs({ providerId: DefaultTextPasteOrDropEditProvider.id });
106109
}
107110
});
111+
112+
export type PreferredPasteConfiguration = ReadonlyArray<{ readonly kind: string; readonly mimeType?: string }>;
113+
114+
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
115+
...editorConfigurationBaseNode,
116+
properties: {
117+
[pasteAsPreferenceConfig]: {
118+
type: 'array',
119+
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
120+
description: nls.localize('preferredDescription', "Configures the preferred type of edit to use when pasting content.\n\nThis is an ordered list of edit kinds with optional mime types for the content being pasted. The first available edit of a preferred kind will be used."),
121+
default: [],
122+
items: {
123+
type: 'object',
124+
required: ['kind'],
125+
properties: {
126+
mimeType: {
127+
type: 'string',
128+
description: nls.localize('mimeType', "The optional mime type that this preference applies to. If not provided, the preference will be used for all mime types."),
129+
},
130+
kind: {
131+
type: 'string',
132+
description: nls.localize('kind', "The kind identifier of the paste edit."),
133+
}
134+
},
135+
defaultSnippets: [
136+
{ body: { kind: '$1' } }
137+
]
138+
}
139+
},
140+
}
141+
});

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

+72-20
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,26 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { addDisposableListener, getActiveDocument } from '../../../../base/browser/dom.js';
7+
import { IAction } from '../../../../base/common/actions.js';
78
import { coalesce } from '../../../../base/common/arrays.js';
89
import { CancelablePromise, createCancelablePromise, DeferredPromise, raceCancellation } from '../../../../base/common/async.js';
910
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
10-
import { UriList, VSDataTransfer, createStringDataTransferItem, matchesMimeType } from '../../../../base/common/dataTransfer.js';
11+
import { createStringDataTransferItem, matchesMimeType, UriList, VSDataTransfer } from '../../../../base/common/dataTransfer.js';
12+
import { CancellationError, isCancellationError } from '../../../../base/common/errors.js';
1113
import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';
1214
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
1315
import { Mimes } from '../../../../base/common/mime.js';
1416
import * as platform from '../../../../base/common/platform.js';
17+
import { upcast } from '../../../../base/common/types.js';
1518
import { generateUuid } from '../../../../base/common/uuid.js';
19+
import { localize } from '../../../../nls.js';
20+
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
21+
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
22+
import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
23+
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
24+
import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';
25+
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js';
26+
import { ClipboardEventUtils } from '../../../browser/controller/editContext/textArea/textAreaEditContextInput.js';
1627
import { toExternalVSDataTransfer, toVSDataTransfer } from '../../../browser/dnd.js';
1728
import { ICodeEditor, PastePayload } from '../../../browser/editorBrowser.js';
1829
import { IBulkEditService } from '../../../browser/services/bulkEditService.js';
@@ -23,26 +34,21 @@ import { Handler, IEditorContribution } from '../../../common/editorCommon.js';
2334
import { DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteTriggerKind } from '../../../common/languages.js';
2435
import { ITextModel } from '../../../common/model.js';
2536
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
26-
import { DefaultTextPasteOrDropEditProvider } from './defaultProviders.js';
27-
import { createCombinedWorkspaceEdit, sortEditsByYieldTo } from './edit.js';
2837
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js';
2938
import { InlineProgressManager } from '../../inlineProgress/browser/inlineProgress.js';
3039
import { MessageController } from '../../message/browser/messageController.js';
31-
import { localize } from '../../../../nls.js';
32-
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
33-
import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
34-
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
35-
import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js';
36-
import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';
40+
import { PreferredPasteConfiguration } from './copyPasteContribution.js';
41+
import { DefaultTextPasteOrDropEditProvider } from './defaultProviders.js';
42+
import { createCombinedWorkspaceEdit, sortEditsByYieldTo } from './edit.js';
3743
import { PostEditWidgetManager } from './postEditWidget.js';
38-
import { CancellationError, isCancellationError } from '../../../../base/common/errors.js';
39-
import { ClipboardEventUtils } from '../../../browser/controller/editContext/textArea/textAreaEditContextInput.js';
4044

4145
export const changePasteTypeCommandId = 'editor.changePasteType';
4246

47+
export const pasteAsPreferenceConfig = 'editor.pasteAs.preferences';
48+
4349
export const pasteWidgetVisibleCtx = new RawContextKey<boolean>('pasteWidgetVisible', false, localize('pasteWidgetVisible', "Whether the paste widget is showing"));
4450

45-
const vscodeClipboardMime = 'application/vnd.code.copyMetadata';
51+
const vscodeClipboardMime = 'application/vnd.code.copymetadata';
4652

4753
interface CopyMetadata {
4854
readonly id?: string;
@@ -73,6 +79,12 @@ export class CopyPasteController extends Disposable implements IEditorContributi
7379
return editor.getContribution<CopyPasteController>(CopyPasteController.ID);
7480
}
7581

82+
public static setConfigureDefaultAction(action: IAction) {
83+
CopyPasteController._configureDefaultAction = action;
84+
}
85+
86+
private static _configureDefaultAction?: IAction;
87+
7688
/**
7789
* Global tracking the last copy operation.
7890
*
@@ -98,6 +110,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
98110
@IInstantiationService instantiationService: IInstantiationService,
99111
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
100112
@IClipboardService private readonly _clipboardService: IClipboardService,
113+
@IConfigurationService private readonly _configService: IConfigurationService,
101114
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
102115
@IQuickInputService private readonly _quickInputService: IQuickInputService,
103116
@IProgressService private readonly _progressService: IProgressService,
@@ -113,7 +126,10 @@ export class CopyPasteController extends Disposable implements IEditorContributi
113126

114127
this._pasteProgressManager = this._register(new InlineProgressManager('pasteIntoEditor', editor, instantiationService));
115128

116-
this._postPasteWidgetManager = this._register(instantiationService.createInstance(PostEditWidgetManager, 'pasteIntoEditor', editor, pasteWidgetVisibleCtx, { id: changePasteTypeCommandId, label: localize('postPasteWidgetTitle', "Show paste options...") }));
129+
this._postPasteWidgetManager = this._register(instantiationService.createInstance(PostEditWidgetManager, 'pasteIntoEditor', editor, pasteWidgetVisibleCtx,
130+
{ id: changePasteTypeCommandId, label: localize('postPasteWidgetTitle', "Show paste options...") },
131+
() => CopyPasteController._configureDefaultAction ? [CopyPasteController._configureDefaultAction] : []
132+
));
117133
}
118134

119135
public changePasteType() {
@@ -354,7 +370,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
354370

355371
if (editSession.edits.length) {
356372
const canShowWidget = editor.getOption(EditorOption.pasteAs).showPasteSelector === 'afterPaste';
357-
return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections, { activeEditIndex: 0, allEdits: editSession.edits }, canShowWidget, (edit, token) => {
373+
return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections, { activeEditIndex: this.getInitialActiveEditIndex(model, editSession.edits), allEdits: editSession.edits }, canShowWidget, (edit, token) => {
358374
return new Promise<PasteEditWithProvider>((resolve, reject) => {
359375
(async () => {
360376
try {
@@ -464,14 +480,36 @@ export class CopyPasteController extends Disposable implements IEditorContributi
464480
if (preference) {
465481
pickedEdit = editSession.edits.at(0);
466482
} else {
467-
const selected = await this._quickInputService.pick(
468-
editSession.edits.map((edit): IQuickPickItem & { edit: DocumentPasteEdit } => ({
469-
label: edit.title,
470-
description: edit.kind?.value,
471-
edit,
472-
})), {
483+
type ItemWithEdit = IQuickPickItem & { edit?: DocumentPasteEdit };
484+
const configureDefaultItem: ItemWithEdit = {
485+
id: 'editor.pasteAs.default',
486+
label: localize('pasteAsDefault', "Configure default paste action"),
487+
edit: undefined,
488+
};
489+
490+
const selected = await this._quickInputService.pick<ItemWithEdit>(
491+
[
492+
...editSession.edits.map((edit): ItemWithEdit => ({
493+
label: edit.title,
494+
description: edit.kind?.value,
495+
edit,
496+
})),
497+
...(CopyPasteController._configureDefaultAction ? [
498+
upcast<IQuickPickSeparator>({ type: 'separator' }),
499+
{
500+
label: CopyPasteController._configureDefaultAction.label,
501+
edit: undefined,
502+
}
503+
] : [])
504+
], {
473505
placeHolder: localize('pasteAsPickerPlaceholder', "Select Paste Action"),
474506
});
507+
508+
if (selected === configureDefaultItem) {
509+
CopyPasteController._configureDefaultAction?.run();
510+
return;
511+
}
512+
475513
pickedEdit = selected?.edit;
476514
}
477515

@@ -621,4 +659,18 @@ export class CopyPasteController extends Disposable implements IEditorContributi
621659
return provider.id === preference.providerId;
622660
}
623661
}
662+
663+
private getInitialActiveEditIndex(model: ITextModel, edits: readonly DocumentPasteEdit[]) {
664+
const preferredProviders = this._configService.getValue<PreferredPasteConfiguration>(pasteAsPreferenceConfig, { resource: model.uri });
665+
for (const config of Array.isArray(preferredProviders) ? preferredProviders : []) {
666+
const desiredKind = new HierarchicalKind(config.kind);
667+
const editIndex = edits.findIndex(edit =>
668+
desiredKind.contains(edit.kind)
669+
&& (!config.mimeType || (edit.handledMimeType && matchesMimeType(config.mimeType, [edit.handledMimeType]))));
670+
if (editIndex >= 0) {
671+
return editIndex;
672+
}
673+
}
674+
return 0;
675+
}
624676
}

0 commit comments

Comments
 (0)