Autofill for JSDocs (#20625)

* Add autofill for JSDocs

Adds prototype of autofill for jsdoc comments. For code like:

```js
    /**|
    function mul(x, y) {
        return x * y;
    }
```

When you press return, we will complete it to:

```js
    /**
     *
     * @param x
     * @param y
     */
    function mul(x, y) {
        return x * y;
    }
```

In Typescript and to:

```js
    /**
     *
     * @param {any} x
     * @param {any} y
     */
    function mul(x, y) {
        return x + y;
    }
```

In JavaScript

* Handle revert case better

* Disable on enter when suggestion is visible
This commit is contained in:
Matt Bierner
2017-02-15 14:04:36 -08:00
committed by GitHub
parent e0354d386a
commit fe742d3800
4 changed files with 131 additions and 1 deletions

View File

@@ -40,6 +40,7 @@ import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import JsDocCompletionHelper from './utils/JsDocCompletionHelper';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
@@ -101,6 +102,20 @@ export function activate(context: ExtensionContext): void {
client.onVersionStatusClicked();
}));
const jsDocCompletionHelper = new JsDocCompletionHelper(client);
context.subscriptions.push(commands.registerCommand('_typescript.tryCompleteJsDoc', () => {
const editor = window.activeTextEditor;
if (!editor || !editor.selection.isEmpty) {
return commands.executeCommand('type', { text: '\n' });
}
return jsDocCompletionHelper.tryCompleteJsDoc(editor, editor.selection.active).then(didCompleteComment => {
if (didCompleteComment) {
return;
}
return commands.executeCommand('type', { text: '\n' });
});
}));
const goToProjectConfig = (isTypeScript: boolean) => {
const editor = window.activeTextEditor;
if (editor) {

View File

@@ -103,6 +103,7 @@ export interface ITypescriptServiceClient {
execute(command: 'navtree', args: Proto.FileRequestArgs, token?: CancellationToken): Promise<Proto.NavTreeResponse>;
execute(command: 'getCodeFixes', args: Proto.CodeFixRequestArgs, token?: CancellationToken): Promise<Proto.GetCodeFixesResponse>;
execute(command: 'getSupportedCodeFixes', args: null, token?: CancellationToken): Promise<Proto.GetSupportedCodeFixesResponse>;
execute(command: 'docCommentTemplate', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.DocCommandTemplateResponse>;
// execute(command: 'compileOnSaveAffectedFileList', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise<Proto.CompileOnSaveAffectedFileListResponse>;
// execute(command: 'compileOnSaveEmitFile', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise<any>;
execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise<any>;

View File

@@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TextEditor, Position, Range, Selection } from 'vscode';
import { ITypescriptServiceClient } from '../typescriptService';
import { FileLocationRequestArgs } from '../protocol';
export default class JsDocCompletionHelper {
constructor(
private client: ITypescriptServiceClient,
) { }
public tryCompleteJsDoc(editor: TextEditor, position: Position): Thenable<boolean> {
const file = this.client.normalizePath(editor.document.uri);
if (!file) {
return Promise.resolve(false);
}
const line = editor.document.lineAt(position.line).text;
// Ensure line starts with '/**' then cursor
const prefix = line.slice(0, position.character).match(/^\s*(\/\*\*+)$/);
if (!prefix) {
return Promise.resolve(false);
}
// Ensure there is no content after the cursor besides possibly the end of the comment
const suffix = line.slice(position.character).match(/^\s*\**\/?$/);
if (!suffix) {
return Promise.resolve(false);
}
const start = position.translate(0, -prefix[1].length);
return editor.edit(
edits => {
edits.delete(new Range(start, new Position(start.line, line.length)));
}, {
undoStopBefore: true,
undoStopAfter: false
}
).then(removedComment => {
if (!removedComment) {
// Edit failed, nothing to revert.
return false;
}
let cancelled = false;
const timer = setTimeout(() => {
cancelled = true;
}, 250);
const args: FileLocationRequestArgs = {
file: file,
line: start.line + 1,
offset: start.character + 1
};
return this.client.execute('docCommentTemplate', args)
.then(res => {
clearTimeout(timer);
if (cancelled || !res || !res.body) {
return false;
}
const commentText = res.body.newText;
return editor.edit(
edits => edits.insert(start, commentText),
{ undoStopBefore: false, undoStopAfter: true });
}, () => {
clearTimeout(timer);
return false;
})
.then(didInsertComment => {
if (didInsertComment) {
const newCursorPosition = new Position(start.line + 1, editor.document.lineAt(start.line + 1).text.length);
editor.selection = new Selection(newCursorPosition, newCursorPosition);
return true;
}
// Revert to the original line content and restore position
return editor.edit(
edits => {
edits.insert(start, prefix[1] + suffix[0]);
}, {
undoStopBefore: false,
undoStopAfter: true
}
).then(() => {
editor.selection = new Selection(position, position);
return false;
});
});
});
}
}