jsDocCompletion feature
This commit is contained in:
parent
2fc8fb014a
commit
148a55d63c
5 changed files with 149 additions and 1 deletions
|
@ -269,6 +269,11 @@ for guide of coc.nvim's configuration.
|
||||||
- `javascript.format.placeOpenBraceOnNewLineForControlBlocks` default: `false`
|
- `javascript.format.placeOpenBraceOnNewLineForControlBlocks` default: `false`
|
||||||
- `javascript.inlayHints`: inlayHints related options.
|
- `javascript.inlayHints`: inlayHints related options.
|
||||||
|
|
||||||
|
### Added on 1.10.0
|
||||||
|
|
||||||
|
- `javascript.suggest.completeJSDocs` `typescript.suggest.completeJSDocs`:
|
||||||
|
Enable/disable suggestion to complete JSDoc comments. default: `true`
|
||||||
|
|
||||||
Configurations are the same as with VSCode. Install
|
Configurations are the same as with VSCode. Install
|
||||||
[coc-json](https://github.com/neoclide/coc-json) and try completion with
|
[coc-json](https://github.com/neoclide/coc-json) and try completion with
|
||||||
`tsserver`, `typescript` or `javascript` in your
|
`tsserver`, `typescript` or `javascript` in your
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
# 1.10.0
|
||||||
|
|
||||||
|
- Support jsdoc completion.
|
||||||
|
- Add configurations `javascript.suggest.completeJSDocs` and `typescript.suggest.completeJSDocs`.
|
||||||
|
|
||||||
# 1.9.15
|
# 1.9.15
|
||||||
|
|
||||||
- Fix uri for `zipfile`.
|
- Fix uri for `zipfile`.
|
||||||
|
|
10
package.json
10
package.json
|
@ -865,6 +865,16 @@
|
||||||
"insert",
|
"insert",
|
||||||
"remove"
|
"remove"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"javascript.suggest.completeJSDocs": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Enable/disable suggestion to complete JSDoc comments."
|
||||||
|
},
|
||||||
|
"typescript.suggest.completeJSDocs": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Enable/disable suggestion to complete JSDoc comments."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
121
src/server/features/jsDocCompletion.ts
Normal file
121
src/server/features/jsDocCompletion.ts
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { CancellationToken, CompletionItem, CompletionItemKind, CompletionItemProvider, InsertTextFormat, Position, Range, SnippetString, TextDocument, workspace } from 'coc.nvim'
|
||||||
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
|
import { LanguageDescription } from '../utils/languageDescription'
|
||||||
|
import * as typeConverters from '../utils/typeConverters'
|
||||||
|
import FileConfigurationManager from './fileConfigurationManager'
|
||||||
|
|
||||||
|
const defaultJsDoc = new SnippetString(`/**\n * $0\n */`)
|
||||||
|
|
||||||
|
function createCompleteItem(document: TextDocument, position: Position): CompletionItem {
|
||||||
|
const line = document.lineAt(position.line).text
|
||||||
|
const prefix = line.slice(0, position.character).match(/\/\**\s*$/)
|
||||||
|
const suffix = line.slice(position.character).match(/^\s*\**\//)
|
||||||
|
const start = Position.create(position.line, prefix ? position.character - prefix[0].length : position.character)
|
||||||
|
const range = Range.create(start, Position.create(start.line, start.character + (suffix ? suffix[0].length : 0)))
|
||||||
|
let insert = `/** */`
|
||||||
|
return {
|
||||||
|
label: insert,
|
||||||
|
kind: CompletionItemKind.Text,
|
||||||
|
insertTextFormat: InsertTextFormat.Snippet,
|
||||||
|
detail: 'JSDoc comment',
|
||||||
|
sortText: `\0`,
|
||||||
|
textEdit: {
|
||||||
|
newText: insert,
|
||||||
|
range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class JsDocCompletionProvider implements CompletionItemProvider {
|
||||||
|
constructor(
|
||||||
|
private readonly client: ITypeScriptServiceClient,
|
||||||
|
private readonly language: LanguageDescription,
|
||||||
|
private readonly fileConfigurationManager: FileConfigurationManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async provideCompletionItems(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position,
|
||||||
|
token: CancellationToken
|
||||||
|
): Promise<CompletionItem[] | undefined> {
|
||||||
|
if (!workspace.getConfiguration(this.language.id, document.uri).get('suggest.completeJSDocs')) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = this.client.toOpenedFilePath(document.uri)
|
||||||
|
if (!file) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isPotentiallyValidDocCompletionPosition(document, position)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.client.interruptGetErr(async () => {
|
||||||
|
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token)
|
||||||
|
const args = typeConverters.Position.toFileLocationRequestArgs(file, position)
|
||||||
|
return this.client.execute('docCommentTemplate', args, token)
|
||||||
|
})
|
||||||
|
if (response.type !== 'response' || !response.body) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = createCompleteItem(document, position)
|
||||||
|
|
||||||
|
// Workaround for #43619
|
||||||
|
// docCommentTemplate previously returned undefined for empty jsdoc templates.
|
||||||
|
// TS 2.7 now returns a single line doc comment, which breaks indentation.
|
||||||
|
if (response.body.newText === '/** */') {
|
||||||
|
item.textEdit.newText = defaultJsDoc.value
|
||||||
|
} else {
|
||||||
|
item.textEdit.newText = templateToSnippet(response.body.newText).value
|
||||||
|
}
|
||||||
|
|
||||||
|
return [item]
|
||||||
|
}
|
||||||
|
|
||||||
|
private isPotentiallyValidDocCompletionPosition(
|
||||||
|
document: TextDocument,
|
||||||
|
position: Position
|
||||||
|
): boolean {
|
||||||
|
// Only show the JSdoc completion when the everything before the cursor is whitespace
|
||||||
|
// or could be the opening of a comment
|
||||||
|
const line = document.lineAt(position.line).text
|
||||||
|
const prefix = line.slice(0, position.character)
|
||||||
|
if (!/^\s*$|\/\*\s*$|^\s*\/\*+\s*$/.test(prefix)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// And everything after is possibly a closing comment or more whitespace
|
||||||
|
const suffix = line.slice(position.character)
|
||||||
|
return /^\s*(\*+\/)?\s*$/.test(suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function templateToSnippet(template: string): SnippetString {
|
||||||
|
// TODO: use append placeholder
|
||||||
|
let snippetIndex = 1
|
||||||
|
template = template.replace(/\*\s$/gm, '*')
|
||||||
|
template = template.replace(/\$/g, '\\$')
|
||||||
|
template = template.replace(/^[ \t]*(?=(\/|[ ]\*))/gm, '')
|
||||||
|
template = template.replace(/^(\/\*\*\s*\*[ ]*)$/m, (x) => x + `\$0`)
|
||||||
|
template = template.replace(/\* @param([ ]\{\S+\})?\s+(\S+)[ \t]*$/gm, (_param, type, post) => {
|
||||||
|
let out = '* @param '
|
||||||
|
if (type === ' {any}' || type === ' {*}') {
|
||||||
|
out += `{\$\{${snippetIndex++}:*\}} `
|
||||||
|
} else if (type) {
|
||||||
|
out += type + ' '
|
||||||
|
}
|
||||||
|
out += post + ` \${${snippetIndex++}}`
|
||||||
|
return out
|
||||||
|
})
|
||||||
|
|
||||||
|
template = template.replace(/\* @returns[ \t]*$/gm, `* @returns \${${snippetIndex++}}`)
|
||||||
|
|
||||||
|
return new SnippetString(template)
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import SignatureHelpProvider from './features/signatureHelp'
|
||||||
import SmartSelection from './features/smartSelect'
|
import SmartSelection from './features/smartSelect'
|
||||||
import TagClosing from './features/tagClosing'
|
import TagClosing from './features/tagClosing'
|
||||||
import UpdateImportsOnFileRenameHandler from './features/updatePathOnRename'
|
import UpdateImportsOnFileRenameHandler from './features/updatePathOnRename'
|
||||||
|
import { JsDocCompletionProvider } from './features/jsDocCompletion'
|
||||||
import { OrganizeImportsCodeActionProvider } from './organizeImports'
|
import { OrganizeImportsCodeActionProvider } from './organizeImports'
|
||||||
import TypeScriptServiceClient from './typescriptServiceClient'
|
import TypeScriptServiceClient from './typescriptServiceClient'
|
||||||
import API from './utils/api'
|
import API from './utils/api'
|
||||||
|
@ -73,13 +74,19 @@ export default class LanguageProvider {
|
||||||
typingsStatus: TypingsStatus
|
typingsStatus: TypingsStatus
|
||||||
): void {
|
): void {
|
||||||
let languageIds = this.description.modeIds
|
let languageIds = this.description.modeIds
|
||||||
let clientId = `tsserver-${this.description.id}`
|
let clientId = `tsc-${this.description.id}`
|
||||||
this._register(
|
this._register(
|
||||||
languages.registerCompletionItemProvider(clientId, 'TSC', languageIds,
|
languages.registerCompletionItemProvider(clientId, 'TSC', languageIds,
|
||||||
new CompletionItemProvider(client, typingsStatus, this.fileConfigurationManager, this.description.id),
|
new CompletionItemProvider(client, typingsStatus, this.fileConfigurationManager, this.description.id),
|
||||||
CompletionItemProvider.triggerCharacters
|
CompletionItemProvider.triggerCharacters
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
this._register(
|
||||||
|
languages.registerCompletionItemProvider(`tsc-${this.description.id}-jsdoc`, 'TSC', languageIds,
|
||||||
|
new JsDocCompletionProvider(client, this.description, this.fileConfigurationManager),
|
||||||
|
['*', ' ']
|
||||||
|
)
|
||||||
|
)
|
||||||
if (this.client.apiVersion.gte(API.v230)) {
|
if (this.client.apiVersion.gte(API.v230)) {
|
||||||
this._register(languages.registerCompletionItemProvider(
|
this._register(languages.registerCompletionItemProvider(
|
||||||
`${this.description.id}-directive`,
|
`${this.description.id}-directive`,
|
||||||
|
|
Loading…
Add table
Reference in a new issue