Skip to content

Commit

Permalink
Adding documentation.refactor proposed contribution point
Browse files Browse the repository at this point in the history
For #86788
  • Loading branch information
mjbvz committed Jan 8, 2020
1 parent 74c8922 commit f033645
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 14 deletions.
10 changes: 10 additions & 0 deletions extensions/typescript-language-features/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion extensions/typescript-language-features/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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());
}
Original file line number Diff line number Diff line change
@@ -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'));
}
}
26 changes: 22 additions & 4 deletions src/vs/editor/contrib/codeAction/codeActionMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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();
Expand All @@ -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 };
Expand Down
1 change: 1 addition & 0 deletions src/vs/editor/contrib/codeAction/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -50,15 +50,15 @@ export const editorConfiguration = Object.freeze<IConfigurationNode>({
}
});

export class CodeActionWorkbenchConfigurationContribution extends Disposable implements IWorkbenchContribution {
export class CodeActionsContribution extends Disposable implements IWorkbenchContribution {

private _contributedCodeActions: CodeActionsExtensionPoint[] = [];

private readonly _onDidChangeContributions = this._register(new Emitter<void>());

constructor(
codeActionsExtensionPoint: IExtensionPoint<CodeActionsExtensionPoint[]>,
keybindingService: IKeybindingService,
@IKeybindingService keybindingService: IKeybindingService,
) {
super();

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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
};

0 comments on commit f033645

Please sign in to comment.