Adding documentation.refactor proposed contribution point

For #86788
This commit is contained in:
Matt Bierner 2020-01-07 15:40:20 -08:00
parent 74c8922d31
commit f0336455ed
11 changed files with 220 additions and 14 deletions

View file

@ -45,10 +45,20 @@
"onCommand:typescript.openTsServerLog",
"onCommand:workbench.action.tasks.runTask",
"onCommand:_typescript.configurePlugin",
"onCommand:_typescript.learnMoreAboutRefactorings",
"onLanguage:jsonc"
],
"main": "./out/extension",
"contributes": {
"documentation": {
"refactoring": [
{
"title": "%documentation.refactoring.title%",
"when": "typescript.isManagedFile",
"command": "_typescript.learnMoreAboutRefactorings"
}
]
},
"jsonValidation": [
{
"fileMatch": "package.json",

View file

@ -97,5 +97,6 @@
"codeActions.refactor.rewrite.parameters.toDestructured.title": "Convert parameters to destructured object",
"codeActions.refactor.rewrite.property.generateAccessors.title": "Generate accessors",
"codeActions.refactor.rewrite.property.generateAccessors.description": "Generate 'get' and 'set' accessors",
"codeActions.source.organizeImports.title": "Organize imports"
"codeActions.source.organizeImports.title": "Organize imports",
"documentation.refactoring.title": "Learn more about JS/TS refactorings"
}

View file

@ -13,6 +13,7 @@ import { OpenTsServerLogCommand } from './openTsServerLog';
import { ReloadJavaScriptProjectsCommand, ReloadTypeScriptProjectsCommand } from './reloadProject';
import { RestartTsServerCommand } from './restartTsServer';
import { SelectTypeScriptVersionCommand } from './selectTypeScriptVersion';
import { LearnMoreAboutRefactoringsCommand } from './learnMoreAboutRefactorings';
export function registerCommands(
commandManager: CommandManager,
@ -27,4 +28,5 @@ export function registerCommands(
commandManager.register(new TypeScriptGoToProjectConfigCommand(lazyClientHost));
commandManager.register(new JavaScriptGoToProjectConfigCommand(lazyClientHost));
commandManager.register(new ConfigurePluginCommand(pluginManager));
}
commandManager.register(new LearnMoreAboutRefactoringsCommand());
}

View file

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { Command } from '../utils/commandManager';
export class LearnMoreAboutRefactoringsCommand implements Command {
public readonly id = '_typescript.learnMoreAboutRefactorings';
public execute() {
vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=2114477'));
}
}

View file

@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Action } from 'vs/base/common/actions';
import { Action, IAction } from 'vs/base/common/actions';
import { canceled } from 'vs/base/common/errors';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { Lazy } from 'vs/base/common/lazy';
@ -14,7 +15,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { CodeAction } from 'vs/editor/common/modes';
import { CodeActionSet, refactorCommandId, sourceActionCommandId, codeActionCommandId, organizeImportsCommandId, fixAllCommandId } from 'vs/editor/contrib/codeAction/codeAction';
import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@ -83,8 +84,7 @@ export class CodeActionMenu extends Disposable {
this._visible = true;
this._showingActions.value = codeActions;
const menuActions = actionsToShow.map(action =>
new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action)));
const menuActions = this.getMenuActions(actionsToShow);
const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 };
const resolver = this._keybindingResolver.getResolver();
@ -101,6 +101,24 @@ export class CodeActionMenu extends Disposable {
});
}
private getMenuActions(actionsToShow: readonly CodeAction[]): IAction[] {
const allActions = actionsToShow
.map(action => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action)));
// Treat documentation actions as special
const result: IAction[] = allActions
.filter(action => !action.action.kind || !CodeActionKind.RefactorDocumentation.contains(new CodeActionKind(action.action.kind)));
const documentationActions = allActions
.filter(action => action.action.kind && CodeActionKind.RefactorDocumentation.contains(new CodeActionKind(action.action.kind)));
if (documentationActions.length) {
result.push(new Separator(), ...documentationActions);
}
return result;
}
private _toCoords(position: IPosition): { x: number, y: number } {
if (!this._editor.hasModel()) {
return { x: 0, y: 0 };

View file

@ -14,6 +14,7 @@ export class CodeActionKind {
public static readonly Empty = new CodeActionKind('');
public static readonly QuickFix = new CodeActionKind('quickfix');
public static readonly Refactor = new CodeActionKind('refactor');
public static readonly RefactorDocumentation = new CodeActionKind('refactor.documentation');
public static readonly Source = new CodeActionKind('source');
public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports');
public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll');

View file

@ -4,24 +4,28 @@
*--------------------------------------------------------------------------------------------*/
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { CodeActionWorkbenchConfigurationContribution, editorConfiguration } from 'vs/workbench/contrib/codeActions/common/configuration';
import { CodeActionsExtensionPoint, codeActionsExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/extensionPoint';
import { CodeActionsContribution, editorConfiguration } from 'vs/workbench/contrib/codeActions/common/codeActionsContribution';
import { CodeActionsExtensionPoint, codeActionsExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint';
import { CodeActionDocumentationContribution } from 'vs/workbench/contrib/codeActions/common/documentationContribution';
import { DocumentationExtensionPoint, documentationExtensionPointDescriptor } from 'vs/workbench/contrib/codeActions/common/documentationExtensionPoint';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
const codeActionsExtensionPoint = ExtensionsRegistry.registerExtensionPoint<CodeActionsExtensionPoint[]>(codeActionsExtensionPointDescriptor);
const documentationExtensionPoint = ExtensionsRegistry.registerExtensionPoint<DocumentationExtensionPoint>(documentationExtensionPointDescriptor);
Registry.as<IConfigurationRegistry>(Extensions.Configuration)
.registerConfiguration(editorConfiguration);
class WorkbenchConfigurationContribution {
constructor(
@IKeybindingService keybindingsService: IKeybindingService,
@IInstantiationService instantiationService: IInstantiationService,
) {
new CodeActionWorkbenchConfigurationContribution(codeActionsExtensionPoint, keybindingsService);
instantiationService.createInstance(CodeActionsContribution, codeActionsExtensionPoint);
instantiationService.createInstance(CodeActionDocumentationContribution, documentationExtensionPoint);
}
}

View file

@ -15,7 +15,7 @@ import { Extensions, IConfigurationNode, IConfigurationRegistry, ConfigurationSc
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { CodeActionsExtensionPoint, ContributedCodeAction } from 'vs/workbench/contrib/codeActions/common/extensionPoint';
import { CodeActionsExtensionPoint, ContributedCodeAction } from 'vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint';
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { editorConfigurationBaseNode } from 'vs/editor/common/config/commonEditorConfig';
@ -50,7 +50,7 @@ export const editorConfiguration = Object.freeze<IConfigurationNode>({
}
});
export class CodeActionWorkbenchConfigurationContribution extends Disposable implements IWorkbenchContribution {
export class CodeActionsContribution extends Disposable implements IWorkbenchContribution {
private _contributedCodeActions: CodeActionsExtensionPoint[] = [];
@ -58,7 +58,7 @@ export class CodeActionWorkbenchConfigurationContribution extends Disposable imp
constructor(
codeActionsExtensionPoint: IExtensionPoint<CodeActionsExtensionPoint[]>,
keybindingService: IKeybindingService,
@IKeybindingService keybindingService: IKeybindingService,
) {
super();

View file

@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { Disposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model';
import { CodeAction, CodeActionContext, CodeActionList, CodeActionProvider, CodeActionProviderRegistry } from 'vs/editor/common/modes';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { DocumentationExtensionPoint } from './documentationExtensionPoint';
export class CodeActionDocumentationContribution extends Disposable implements IWorkbenchContribution, CodeActionProvider {
private contributions: {
title: string;
when: ContextKeyExpr;
command: string;
}[] = [];
constructor(
extensionPoint: IExtensionPoint<DocumentationExtensionPoint>,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super();
CodeActionProviderRegistry.register('*', this);
extensionPoint.setHandler(points => {
this.contributions = [];
for (const documentation of points) {
if (!documentation.value.refactoring) {
continue;
}
for (const contribution of documentation.value.refactoring) {
const precondition = ContextKeyExpr.deserialize(contribution.when);
if (!precondition) {
continue;
}
this.contributions.push({
title: contribution.title,
when: precondition,
command: contribution.command
});
}
}
});
}
async provideCodeActions(_model: ITextModel, _range: Range | Selection, context: CodeActionContext, _token: CancellationToken): Promise<CodeActionList> {
if (!context.only || !CodeActionKind.Refactor.contains(new CodeActionKind(context.only))) {
return {
actions: [],
dispose: () => { }
};
}
const actions: CodeAction[] = [];
for (const contribution of this.contributions) {
if (!this.contextKeyService.contextMatchesRules(contribution.when)) {
continue;
}
actions.push({
title: contribution.title,
kind: CodeActionKind.RefactorDocumentation.value,
command: {
id: contribution.command,
title: contribution.title
}
});
}
return {
actions,
dispose: () => { }
};
}
public readonly providedCodeActionKinds = [CodeActionKind.RefactorDocumentation.value] as const;
}

View file

@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchModeService';
export enum DocumentationExtensionPointFields {
when = 'when',
title = 'title',
command = 'command',
}
export interface RefactoringDocumentationExtensionPoint {
readonly [DocumentationExtensionPointFields.title]: string;
readonly [DocumentationExtensionPointFields.when]: string;
readonly [DocumentationExtensionPointFields.command]: string;
}
export interface DocumentationExtensionPoint {
readonly refactoring?: readonly RefactoringDocumentationExtensionPoint[];
}
const documentationExtensionPointSchema = Object.freeze<IConfigurationPropertySchema>({
type: 'object',
description: nls.localize('contributes.documentation', "Contributed documentation."),
properties: {
'refactoring': {
type: 'array',
description: nls.localize('contributes.documentation.refactorings', "Contributed documentation for refactorings."),
items: {
type: 'object',
description: nls.localize('contributes.documentation.refactoring', "Contributed documentation for refactoring."),
required: [
DocumentationExtensionPointFields.title,
DocumentationExtensionPointFields.when,
DocumentationExtensionPointFields.command
],
properties: {
[DocumentationExtensionPointFields.title]: {
type: 'string',
description: nls.localize('contributes.documentation.refactoring.title', "Label for the documentation used in the UI."),
},
[DocumentationExtensionPointFields.when]: {
type: 'string',
description: nls.localize('contributes.documentation.refactoring.when', "When clause."),
},
[DocumentationExtensionPointFields.command]: {
type: 'string',
description: nls.localize('contributes.documentation.refactoring.command', "Command executed."),
},
},
}
}
}
});
export const documentationExtensionPointDescriptor = {
extensionPoint: 'documentation',
deps: [languagesExtPoint],
jsonSchema: documentationExtensionPointSchema
};