Skip to content

Commit

Permalink
Add intellisense for paste/drop preference and keybindings (#233730)
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbvz authored Nov 12, 2024
1 parent 3583dd3 commit 51b15d1
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 95 deletions.
1 change: 1 addition & 0 deletions src/vs/editor/common/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2278,6 +2278,7 @@ export interface DocumentDropEditsSession {
export interface DocumentDropEditProvider {
readonly id?: string;
readonly dropMimeTypes?: readonly string[];
readonly providedDropEditKinds?: readonly HierarchicalKind[];

provideDocumentDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult<DocumentDropEditsSession>;
resolveDocumentDropEdit?(edit: DocumentDropEdit, token: CancellationToken): Promise<DocumentDropEdit>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';
import { IJSONSchema, SchemaToType } from '../../../../base/common/jsonSchema.js';
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
import * as nls from '../../../../nls.js';
import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { ICodeEditor } from '../../../browser/editorBrowser.js';
import { EditorAction, EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from '../../../browser/editorExtensions.js';
import { editorConfigurationBaseNode } from '../../../common/config/editorConfigurationSchema.js';
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
import { registerEditorFeature } from '../../../common/editorFeatures.js';
import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx, pasteAsPreferenceConfig } from './copyPasteController.js';
import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx } from './copyPasteController.js';
import { DefaultPasteProvidersFeature, DefaultTextPasteOrDropEditProvider } from './defaultProviders.js';

export const pasteAsCommandId = 'editor.action.pasteAs';

registerEditorContribution(CopyPasteController.ID, CopyPasteController, EditorContributionInstantiation.Eager); // eager because it listens to events on the container dom node of the editor
registerEditorFeature(DefaultPasteProvidersFeature);

Expand Down Expand Up @@ -55,7 +54,6 @@ registerEditorCommand(new class extends EditorCommand {
}
});


registerEditorAction(class PasteAsAction extends EditorAction {
private static readonly argsSchema = {
type: 'object',
Expand All @@ -69,7 +67,7 @@ registerEditorAction(class PasteAsAction extends EditorAction {

constructor() {
super({
id: 'editor.action.pasteAs',
id: pasteAsCommandId,
label: nls.localize2('pasteAs', "Paste As..."),
precondition: EditorContextKeys.writable,
metadata: {
Expand Down Expand Up @@ -108,19 +106,3 @@ registerEditorAction(class extends EditorAction {
});

export type PreferredPasteConfiguration = string;

Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
...editorConfigurationBaseNode,
properties: {
[pasteAsPreferenceConfig]: {
type: 'array',
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
description: nls.localize('preferredDescription', "Configures the preferred type of edit to use when pasting content.\n\nThis is an ordered list of edit kinds. The first available edit of a preferred kind will be used."),
default: [],
items: {
type: 'string',
description: nls.localize('kind', "The kind identifier of the paste edit."),
}
},
}
});
27 changes: 21 additions & 6 deletions src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,19 @@ import { IWorkspaceContextService } from '../../../../platform/workspace/common/

abstract class SimplePasteAndDropProvider implements DocumentDropEditProvider, DocumentPasteEditProvider {

abstract readonly kind: HierarchicalKind;
readonly kind: HierarchicalKind;
readonly providedDropEditKinds: HierarchicalKind[];
readonly providedPasteEditKinds: HierarchicalKind[];

abstract readonly dropMimeTypes: readonly string[] | undefined;
abstract readonly pasteMimeTypes: readonly string[];

constructor(kind: HierarchicalKind) {
this.kind = kind;
this.providedDropEditKinds = [this.kind];
this.providedPasteEditKinds = [this.kind];
}

async provideDocumentPasteEdits(_model: ITextModel, _ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, context: DocumentPasteContext, token: CancellationToken): Promise<DocumentPasteEditsSession | undefined> {
const edit = await this.getEdit(dataTransfer, token);
if (!edit) {
Expand Down Expand Up @@ -56,13 +65,15 @@ abstract class SimplePasteAndDropProvider implements DocumentDropEditProvider, D
export class DefaultTextPasteOrDropEditProvider extends SimplePasteAndDropProvider {

static readonly id = 'text';
static readonly kind = new HierarchicalKind('text.plain');

readonly id = DefaultTextPasteOrDropEditProvider.id;
readonly kind = DefaultTextPasteOrDropEditProvider.kind;
readonly dropMimeTypes = [Mimes.text];
readonly pasteMimeTypes = [Mimes.text];

constructor() {
super(HierarchicalKind.Empty.append('text', 'plain'));
}

protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, _token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
const textEntry = dataTransfer.get(Mimes.text);
if (!textEntry) {
Expand All @@ -87,10 +98,13 @@ export class DefaultTextPasteOrDropEditProvider extends SimplePasteAndDropProvid

class PathProvider extends SimplePasteAndDropProvider {

readonly kind = new HierarchicalKind('uri.absolute');
readonly dropMimeTypes = [Mimes.uriList];
readonly pasteMimeTypes = [Mimes.uriList];

constructor() {
super(HierarchicalKind.Empty.append('uri', 'absolute'));
}

protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
const entries = await extractUriList(dataTransfer);
if (!entries.length || token.isCancellationRequested) {
Expand Down Expand Up @@ -133,14 +147,13 @@ class PathProvider extends SimplePasteAndDropProvider {

class RelativePathProvider extends SimplePasteAndDropProvider {

readonly kind = new HierarchicalKind('uri.relative');
readonly dropMimeTypes = [Mimes.uriList];
readonly pasteMimeTypes = [Mimes.uriList];

constructor(
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService
) {
super();
super(HierarchicalKind.Empty.append('uri', 'relative'));
}

protected async getEdit(dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise<DocumentPasteEdit | undefined> {
Expand Down Expand Up @@ -173,6 +186,8 @@ class PasteHtmlProvider implements DocumentPasteEditProvider {

public readonly kind = new HierarchicalKind('html');

public readonly providedPasteEditKinds = [this.kind];

public readonly pasteMimeTypes = ['text/html'];

private readonly _yieldTo = [{ mimeType: Mimes.text }];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@
*--------------------------------------------------------------------------------------------*/

import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
import * as nls from '../../../../nls.js';
import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js';
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { ICodeEditor } from '../../../browser/editorBrowser.js';
import { EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorCommand, registerEditorContribution } from '../../../browser/editorExtensions.js';
import { editorConfigurationBaseNode } from '../../../common/config/editorConfigurationSchema.js';
import { registerEditorFeature } from '../../../common/editorFeatures.js';
import { DefaultDropProvidersFeature } from './defaultProviders.js';
import { DropIntoEditorController, changeDropTypeCommandId, dropAsPreferenceConfig, dropWidgetVisibleCtx } from './dropIntoEditorController.js';
import { DropIntoEditorController, changeDropTypeCommandId, dropWidgetVisibleCtx } from './dropIntoEditorController.js';

registerEditorContribution(DropIntoEditorController.ID, DropIntoEditorController, EditorContributionInstantiation.BeforeFirstInteraction);
registerEditorFeature(DefaultDropProvidersFeature);
Expand Down Expand Up @@ -54,18 +50,3 @@ registerEditorCommand(new class extends EditorCommand {

export type PreferredDropConfiguration = string;

Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
...editorConfigurationBaseNode,
properties: {
[dropAsPreferenceConfig]: {
type: 'array',
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
description: nls.localize('preferredDescription', "Configures the preferred type of edit to use when dropping content.\n\nThis is an ordered list of edit kinds. The first available edit of a preferred kind will be used."),
default: [],
items: {
type: 'string',
description: nls.localize('kind', "The kind identifier of the drop edit."),
}
},
}
});
3 changes: 3 additions & 0 deletions src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,8 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentDropEdit

readonly dropMimeTypes?: readonly string[];

readonly providedDropEditKinds: readonly HierarchicalKind[] | undefined;

readonly resolveDocumentDropEdit?: languages.DocumentDropEditProvider['resolveDocumentDropEdit'];

constructor(
Expand All @@ -1122,6 +1124,7 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentDropEdit
@IUriIdentityService private readonly _uriIdentService: IUriIdentityService
) {
this.dropMimeTypes = metadata?.dropMimeTypes ?? ['*/*'];
this.providedDropEditKinds = metadata?.providedDropKinds?.map(kind => new HierarchicalKind(kind));

if (metadata?.supportsResolve) {
this.resolveDocumentDropEdit = async (edit, token) => {
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2231,7 +2231,8 @@ export interface IPasteEditDto {
export interface IDocumentDropEditProviderMetadata {
readonly supportsResolve: boolean;

dropMimeTypes: readonly string[];
readonly dropMimeTypes: readonly string[];
readonly providedDropKinds?: readonly string[];
}

export interface IDocumentDropEditDto {
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/common/extHostLanguageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2900,6 +2900,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector, extension), isProposedApiEnabled(extension, 'documentPaste') && metadata ? {
supportsResolve: !!provider.resolveDocumentDropEdit,
dropMimeTypes: metadata.dropMimeTypes,
providedDropKinds: metadata.providedDropEditKinds?.map(x => x.value),
} : undefined);

return this._createDisposable(handle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Emitter } from '../../../../base/common/event.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js';
import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
Expand Down Expand Up @@ -123,11 +123,14 @@ export class CodeActionsContribution extends Disposable implements IWorkbenchCon
super();

// TODO: @justschen caching of code actions based on extensions loaded: https://github.com/microsoft/vscode/issues/216019
this._register(languageFeatures.codeActionProvider.onDidChange(() => {
this._allProvidedCodeActionKinds = this.getAllProvidedCodeActionKinds();
this.updateConfigurationSchema(this._allProvidedCodeActionKinds);
this._onDidChangeSchemaContributions.fire();
}, 2000));
this._register(
Event.runAndSubscribe(
Event.debounce(languageFeatures.codeActionProvider.onDidChange, () => { }, 1000),
() => {
this._allProvidedCodeActionKinds = this.getAllProvidedCodeActionKinds();
this.updateConfigurationSchema(this._allProvidedCodeActionKinds);
this._onDidChangeSchemaContributions.fire();
}));

keybindingService.registerSchemaContribution({
getSchemaAdditions: () => this.getKeybindingSchemaAdditions(),
Expand Down
45 changes: 45 additions & 0 deletions src/vs/workbench/contrib/dropOrPasteInto/browser/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { toAction } from '../../../../base/common/actions.js';
import { CopyPasteController, pasteAsPreferenceConfig } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js';
import { DropIntoEditorController, dropAsPreferenceConfig } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js';
import { localize } from '../../../../nls.js';
import { IWorkbenchContribution } from '../../../common/contributions.js';
import { IPreferencesService } from '../../../services/preferences/common/preferences.js';

export class DropOrPasteIntoCommands implements IWorkbenchContribution {
public static ID = 'workbench.contrib.dropOrPasteInto';

constructor(
@IPreferencesService private readonly _preferencesService: IPreferencesService
) {
CopyPasteController.setConfigureDefaultAction(toAction({
id: 'workbench.action.configurePreferredPasteAction',
label: localize('configureDefaultPaste.label', 'Configure preferred paste action...'),
run: () => this.configurePreferredPasteAction()
}));

DropIntoEditorController.setConfigureDefaultAction(toAction({
id: 'workbench.action.configurePreferredDropAction',
label: localize('configureDefaultDrop.label', 'Configure preferred drop action...'),
run: () => this.configurePreferredDropAction()
}));
}

private configurePreferredPasteAction() {
return this._preferencesService.openUserSettings({
jsonEditor: true,
revealSetting: { key: pasteAsPreferenceConfig, edit: true }
});
}

private configurePreferredDropAction() {
return this._preferencesService.openUserSettings({
jsonEditor: true,
revealSetting: { key: dropAsPreferenceConfig, edit: true }
});
}
}
Loading

0 comments on commit 51b15d1

Please sign in to comment.