Merge branch 'neoclide:master' into master
This commit is contained in:
commit
860c97aaf0
16 changed files with 360 additions and 213 deletions
16
package.json
16
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "coc-tsserver",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.5",
|
||||
"description": "tsserver extension for coc.nvim",
|
||||
"main": "lib/index.js",
|
||||
"publisher": "chemzqm",
|
||||
|
@ -265,6 +265,11 @@
|
|||
"default": true,
|
||||
"description": "Show unused variable hint."
|
||||
},
|
||||
"typescript.showDeprecated": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Show deprecated variable hint."
|
||||
},
|
||||
"typescript.updateImportsOnFileMove.enable": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
@ -473,6 +478,11 @@
|
|||
"default": true,
|
||||
"description": "Show unused variable hint."
|
||||
},
|
||||
"javascript.showDeprecated": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Show deprecated variable hint."
|
||||
},
|
||||
"javascript.updateImportsOnFileMove.enable": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
|
@ -675,13 +685,13 @@
|
|||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^10.12.0",
|
||||
"coc.nvim": "^0.0.80",
|
||||
"coc.nvim": "^0.0.81-next.5",
|
||||
"esbuild": "^0.8.29",
|
||||
"semver": "^7.3.2",
|
||||
"vscode-languageserver-protocol": "^3.16.0",
|
||||
"which": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript": "^4.3.2"
|
||||
"typescript": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CallHierarchyProvider, TextDocument, Uri } from 'coc.nvim'
|
||||
import { CallHierarchyProvider, TextDocument, Uri, workspace } from 'coc.nvim'
|
||||
import path from "path"
|
||||
import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CancellationToken, Position, SymbolTag } from 'vscode-languageserver-protocol'
|
||||
import type * as Proto from '../protocol'
|
||||
|
@ -80,9 +80,7 @@ function parseKindModifier(kindModifiers: string): Set<string> {
|
|||
function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): CallHierarchyItem {
|
||||
const useFileName = isSourceFileItem(item)
|
||||
const name = useFileName ? path.basename(item.file) : item.name
|
||||
// TODO
|
||||
// const detail = useFileName ? workspace.asRelativePath(path.dirname(item.file)) : item.containerName ?? ''
|
||||
const detail = item.containerName || ''
|
||||
const detail = useFileName ? path.relative(workspace.cwd, path.dirname(item.file)) : item.containerName ?? ''
|
||||
const result: CallHierarchyItem = {
|
||||
name,
|
||||
detail,
|
||||
|
@ -93,7 +91,7 @@ function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): CallHiera
|
|||
}
|
||||
|
||||
const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined
|
||||
if (kindModifiers?.has(PConst.KindModifiers.depreacted)) {
|
||||
if (kindModifiers?.has(PConst.KindModifiers.deprecated)) {
|
||||
result.tags = [SymbolTag.Deprecated]
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { commands, CompletionItemProvider, TextDocument, window, workspace } from 'coc.nvim'
|
||||
import { commands, CompletionItemProvider, TextDocument, CompletionList, CompletionItem, window, workspace } from 'coc.nvim'
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { CancellationToken, Command, CompletionContext, CompletionItem, CompletionList, InsertTextFormat, MarkupContent, MarkupKind, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
|
||||
import { CancellationToken, Command, CompletionContext, InsertTextFormat, MarkupContent, MarkupKind, Position, Range, TextEdit } from 'vscode-languageserver-protocol'
|
||||
import Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'
|
||||
|
@ -344,7 +344,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
if (!this.completeOption.importStatementSuggestions || !this.client.apiVersion.lt(API.v430)) {
|
||||
return false
|
||||
}
|
||||
return pre === 'import ';
|
||||
return pre === 'import '
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { TextDocument } from 'coc.nvim'
|
||||
import { DefinitionProvider, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
||||
import { CancellationToken, Definition, Location, Position } from 'vscode-languageserver-protocol'
|
||||
import { DefinitionProvider, CancellationToken, Definition, Location, Position, DefinitionLink, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
||||
import * as Proto from '../protocol'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
import * as typeConverters from '../utils/typeConverters'
|
||||
|
||||
export default class TypeScriptDefinitionProvider implements DefinitionProvider, TypeDefinitionProvider, ImplementationProvider {
|
||||
|
@ -41,12 +41,44 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
|||
}
|
||||
}
|
||||
|
||||
public provideDefinition(
|
||||
public async provideDefinition(
|
||||
document: TextDocument,
|
||||
position: Position,
|
||||
token: CancellationToken
|
||||
): Promise<Definition | undefined> {
|
||||
return this.getSymbolLocations('definition', document, position, token)
|
||||
): Promise<Definition | DefinitionLink[] | undefined> {
|
||||
if (this.client.apiVersion.gte(API.v270)) {
|
||||
const filepath = this.client.toOpenedFilePath(document.uri)
|
||||
if (!filepath) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position)
|
||||
const response = await this.client.execute('definitionAndBoundSpan', args, token)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const span = response.body.textSpan ? typeConverters.Range.fromTextSpan(response.body.textSpan) : undefined
|
||||
return response.body.definitions
|
||||
.map((location): DefinitionLink => {
|
||||
const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location)
|
||||
if (location.contextStart && location.contextEnd) {
|
||||
return {
|
||||
originSelectionRange: span,
|
||||
targetRange: typeConverters.Range.fromLocations(location.contextStart, location.contextEnd),
|
||||
targetUri: target.uri,
|
||||
targetSelectionRange: target.range,
|
||||
}
|
||||
}
|
||||
return {
|
||||
originSelectionRange: span,
|
||||
targetRange: target.range,
|
||||
targetUri: target.uri,
|
||||
targetSelectionRange: target.range,
|
||||
}
|
||||
})
|
||||
}
|
||||
return await this.getSymbolLocations('definition', document, position, token)
|
||||
}
|
||||
|
||||
public provideTypeDefinition(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { DiagnosticCollection, languages, workspace } from 'coc.nvim'
|
||||
import { Diagnostic } from 'vscode-languageserver-protocol'
|
||||
import { Diagnostic, DiagnosticTag } from 'vscode-languageserver-protocol'
|
||||
import { ResourceMap } from './resourceMap'
|
||||
|
||||
export class DiagnosticSet {
|
||||
|
@ -146,8 +146,7 @@ export class DiagnosticsManager {
|
|||
.get(uri)
|
||||
.filter(x => {
|
||||
if (!enabled) {
|
||||
// Still show unused
|
||||
return x.code == 6133
|
||||
return x.tags && (x.tags.includes(DiagnosticTag.Unnecessary) || x.tags.includes(DiagnosticTag.Deprecated))
|
||||
}
|
||||
return x.code !== 80001 // disable annoying CommonJS module warning
|
||||
})
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { CancellationToken, CompletionContext, CompletionItem, CompletionItemKind, CompletionList, Position, Range } from 'vscode-languageserver-protocol'
|
||||
import { TextDocument } from 'coc.nvim'
|
||||
import { CancellationToken, CompletionContext, CompletionItem, CompletionItemKind, CompletionList, Position, Range, TextDocument } from 'coc.nvim'
|
||||
import { workspace } from 'coc.nvim'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
|
@ -37,7 +36,7 @@ const tsDirectives390: Directive[] = [
|
|||
]
|
||||
|
||||
export default class DirectiveCommentCompletionProvider {
|
||||
constructor(private readonly client: ITypeScriptServiceClient) { }
|
||||
constructor(private readonly client: ITypeScriptServiceClient) {}
|
||||
|
||||
public provideCompletionItems(
|
||||
document: TextDocument,
|
||||
|
@ -62,7 +61,7 @@ export default class DirectiveCommentCompletionProvider {
|
|||
? tsDirectives390
|
||||
: tsDirectives
|
||||
let items = directives.map(directive => {
|
||||
const item = CompletionItem.create(directive.value)
|
||||
const item: CompletionItem = { label: directive.value }
|
||||
item.kind = CompletionItemKind.Snippet
|
||||
item.detail = directive.description
|
||||
item.textEdit = {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
import { TextDocument } from 'coc.nvim'
|
||||
import { DocumentSymbolProvider } from 'coc.nvim'
|
||||
import { CancellationToken, DocumentSymbol, Range, SymbolKind } from 'vscode-languageserver-protocol'
|
||||
import { CancellationToken, DocumentSymbol, Range, SymbolKind, SymbolTag } from 'vscode-languageserver-protocol'
|
||||
import * as Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
|
@ -82,20 +82,15 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
|||
}
|
||||
|
||||
private static convertNavTree(
|
||||
bucket: DocumentSymbol[],
|
||||
output: DocumentSymbol[],
|
||||
item: Proto.NavigationTree,
|
||||
): boolean {
|
||||
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)
|
||||
const children = new Set(item.childItems || [])
|
||||
for (const span of item.spans) {
|
||||
const range = typeConverters.Range.fromTextSpan(span)
|
||||
const symbolInfo = DocumentSymbol.create(
|
||||
item.text,
|
||||
'',
|
||||
getSymbolKind(item.kind),
|
||||
range,
|
||||
range)
|
||||
symbolInfo.children = children.size > 0 ? [] : null
|
||||
const symbolInfo = TypeScriptDocumentSymbolProvider.convertSymbol(item, range)
|
||||
if (children.size) symbolInfo.children = []
|
||||
|
||||
for (const child of children) {
|
||||
if (child.spans.some(span => !!containsRange(range, typeConverters.Range.fromTextSpan(span)))) {
|
||||
|
@ -106,11 +101,31 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
|||
}
|
||||
|
||||
if (shouldInclude) {
|
||||
bucket.push(symbolInfo)
|
||||
output.push(symbolInfo)
|
||||
}
|
||||
}
|
||||
return shouldInclude
|
||||
}
|
||||
|
||||
return shouldInclude
|
||||
private static convertSymbol(item: Proto.NavigationTree, range: Range): DocumentSymbol {
|
||||
const selectionRange = item.nameSpan ? typeConverters.Range.fromTextSpan(item.nameSpan) : range
|
||||
let label = item.text
|
||||
switch (item.kind) {
|
||||
case PConst.Kind.memberGetAccessor: label = `(get) ${label}`; break
|
||||
case PConst.Kind.memberSetAccessor: label = `(set) ${label}`; break
|
||||
}
|
||||
const symbolInfo = DocumentSymbol.create(
|
||||
label,
|
||||
'',
|
||||
getSymbolKind(item.kind),
|
||||
range,
|
||||
containsRange(range, selectionRange) ? selectionRange : range)
|
||||
|
||||
const kindModifiers = parseKindModifier(item.kindModifiers)
|
||||
if (kindModifiers.has(PConst.KindModifiers.deprecated)) {
|
||||
symbolInfo.tags = [SymbolTag.Deprecated]
|
||||
}
|
||||
return symbolInfo
|
||||
}
|
||||
|
||||
private static shouldInclueEntry(
|
||||
|
@ -142,3 +157,7 @@ function containsRange(range: Range, otherRange: Range): boolean {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function parseKindModifier(kindModifiers: string): Set<string> {
|
||||
return new Set(kindModifiers.split(/,|\s+/g))
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { CancellationTokenSource, Disposable, disposeAll, Position, Range, snippetManager, window, workspace } from 'coc.nvim'
|
||||
import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol'
|
||||
import { CancellationTokenSource, Disposable, disposeAll, Position, Range, snippetManager, events, workspace, InsertChange } from 'coc.nvim'
|
||||
import * as Proto from '../protocol'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
|
@ -19,49 +18,82 @@ export default class TagClosing implements Disposable {
|
|||
}
|
||||
|
||||
private _disposables: Disposable[] = []
|
||||
private _enabled: boolean = false
|
||||
private _disposed = false
|
||||
private _timeout: NodeJS.Timer | undefined = undefined
|
||||
private _cancel: CancellationTokenSource | undefined = undefined
|
||||
private lastInsert: string
|
||||
|
||||
constructor(
|
||||
private readonly client: ITypeScriptServiceClient,
|
||||
private readonly descriptionLanguageId: string
|
||||
) {
|
||||
workspace.onDidChangeTextDocument(
|
||||
(event) =>
|
||||
this.onDidChangeTextDocument(
|
||||
event.textDocument,
|
||||
event.contentChanges
|
||||
),
|
||||
null,
|
||||
this._disposables
|
||||
events.on('InsertCharPre', character => {
|
||||
this.lastInsert = character
|
||||
}, null, this._disposables)
|
||||
events.on('TextChangedI', this.onChange, this, this._disposables)
|
||||
events.on('TextChangedP', this.onChange, this, this._disposables)
|
||||
}
|
||||
|
||||
private async onChange(bufnr: number, change: InsertChange): Promise<void> {
|
||||
let doc = workspace.getDocument((bufnr))
|
||||
if (!doc || !doc.attached) return
|
||||
let enabled = this.isEnabled(doc.filetype, doc.uri)
|
||||
if (!enabled) return
|
||||
let { pre, changedtick, lnum } = change
|
||||
if (!pre.endsWith('/') && !pre.endsWith('>')) return
|
||||
if (!pre.endsWith(this.lastInsert)) return
|
||||
if (pre.length > 1 && pre[pre.length - 2] == '>') return
|
||||
const filepath = this.client.toOpenedFilePath(doc.uri)
|
||||
if (!filepath) return
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
if (this._cancel) {
|
||||
this._cancel.cancel()
|
||||
this._cancel.dispose()
|
||||
this._cancel = undefined
|
||||
}
|
||||
await (doc as any).patchChange()
|
||||
this._timeout = setTimeout(async () => {
|
||||
this._timeout = undefined
|
||||
if (this._disposed) return
|
||||
if (doc.changedtick > changedtick) return
|
||||
const position = Position.create(lnum - 1, pre.length)
|
||||
const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(
|
||||
filepath,
|
||||
position
|
||||
)
|
||||
|
||||
this.updateEnabledState()
|
||||
|
||||
workspace.registerAutocmd({
|
||||
event: ['BufEnter'],
|
||||
request: false,
|
||||
callback: () => this.updateEnabledState(),
|
||||
})
|
||||
}
|
||||
|
||||
async updateEnabledState(): Promise<void> {
|
||||
this._enabled = false
|
||||
const doc = await workspace.document
|
||||
if (!doc) {
|
||||
this._cancel = new CancellationTokenSource()
|
||||
const response = await this.client.execute(
|
||||
'jsxClosingTag',
|
||||
args,
|
||||
this._cancel.token
|
||||
)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return
|
||||
}
|
||||
const document = doc.textDocument
|
||||
const configLang = TagClosing._configurationLanguages[document.languageId]
|
||||
if (this._disposed) return
|
||||
|
||||
const insertion = response.body
|
||||
if (doc.changedtick === changedtick) {
|
||||
snippetManager.insertSnippet(
|
||||
this.getTagSnippet(insertion).value,
|
||||
false,
|
||||
Range.create(position, position)
|
||||
)
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
private isEnabled(languageId: string, uri: string): boolean {
|
||||
const configLang = TagClosing._configurationLanguages[languageId]
|
||||
if (!configLang || configLang !== this.descriptionLanguageId) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
if (!workspace.getConfiguration(undefined, document.uri).get<boolean>(`${configLang}.autoClosingTags`)) {
|
||||
return
|
||||
if (!workspace.getConfiguration(undefined, uri).get<boolean>(`${configLang}.autoClosingTags`)) {
|
||||
return false
|
||||
}
|
||||
this._enabled = true
|
||||
return true
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
|
@ -82,120 +114,10 @@ export default class TagClosing implements Disposable {
|
|||
this._disposables = []
|
||||
}
|
||||
|
||||
private async onDidChangeTextDocument(
|
||||
documentEvent: {
|
||||
uri: string,
|
||||
version: number,
|
||||
},
|
||||
changes: readonly TextDocumentContentChangeEvent[]
|
||||
) {
|
||||
if (!this._enabled) {
|
||||
return
|
||||
}
|
||||
const document = await workspace.document
|
||||
if (!document) {
|
||||
return
|
||||
}
|
||||
const activeDocument = document.textDocument
|
||||
if (activeDocument.uri !== documentEvent.uri || changes.length === 0) {
|
||||
return
|
||||
}
|
||||
const filepath = this.client.toOpenedFilePath(documentEvent.uri)
|
||||
if (!filepath) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof this._timeout !== 'undefined') {
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
|
||||
if (this._cancel) {
|
||||
this._cancel.cancel()
|
||||
this._cancel.dispose()
|
||||
this._cancel = undefined
|
||||
}
|
||||
|
||||
const lastChange = changes[changes.length - 1]
|
||||
if (!Range.is(lastChange['range']) || !lastChange.text) {
|
||||
return
|
||||
}
|
||||
|
||||
const lastCharacter = lastChange.text[lastChange.text.length - 1]
|
||||
if (lastCharacter !== '>' && lastCharacter !== '/') {
|
||||
return
|
||||
}
|
||||
|
||||
const version = documentEvent.version
|
||||
|
||||
const rangeStart = lastChange['range'].start
|
||||
const priorCharacter =
|
||||
lastChange['range'].start.character > 0
|
||||
? activeDocument.getText(
|
||||
Range.create(
|
||||
Position.create(rangeStart.line, rangeStart.character - 1),
|
||||
rangeStart
|
||||
)
|
||||
)
|
||||
: ''
|
||||
if (priorCharacter === '>') {
|
||||
return
|
||||
}
|
||||
|
||||
this._timeout = setTimeout(async () => {
|
||||
this._timeout = undefined
|
||||
|
||||
if (this._disposed) {
|
||||
return
|
||||
}
|
||||
|
||||
const addedLines = lastChange.text.split(/\r\n|\n/g)
|
||||
const position =
|
||||
addedLines.length <= 1
|
||||
? Position.create(
|
||||
rangeStart.line,
|
||||
rangeStart.character + lastChange.text.length
|
||||
)
|
||||
: Position.create(
|
||||
rangeStart.line + addedLines.length - 1,
|
||||
addedLines[addedLines.length - 1].length
|
||||
)
|
||||
|
||||
const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(
|
||||
filepath,
|
||||
position
|
||||
)
|
||||
this._cancel = new CancellationTokenSource()
|
||||
const response = await this.client.execute(
|
||||
'jsxClosingTag',
|
||||
args,
|
||||
this._cancel.token
|
||||
)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._disposed) {
|
||||
return
|
||||
}
|
||||
|
||||
const insertion = response.body;
|
||||
if (
|
||||
documentEvent.uri === activeDocument.uri &&
|
||||
activeDocument.version === version
|
||||
) {
|
||||
snippetManager.insertSnippet(
|
||||
this.getTagSnippet(insertion).value,
|
||||
false,
|
||||
Range.create(position, position)
|
||||
)
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private getTagSnippet(closingTag: Proto.TextInsertion): SnippetString {
|
||||
const snippet = new SnippetString();
|
||||
snippet.appendPlaceholder('', 0);
|
||||
snippet.appendText(closingTag.newText);
|
||||
return snippet;
|
||||
const snippet = new SnippetString()
|
||||
snippet.appendPlaceholder('', 0)
|
||||
snippet.appendText(closingTag.newText)
|
||||
return snippet
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,17 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
import { workspace } from 'coc.nvim'
|
||||
import { WorkspaceSymbolProvider } from 'coc.nvim'
|
||||
import { CancellationToken, Range, SymbolInformation, SymbolKind } from 'vscode-languageserver-protocol'
|
||||
import { CancellationToken, Range, SymbolInformation, SymbolKind, SymbolTag } from 'vscode-languageserver-protocol'
|
||||
import * as Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
import * as typeConverters from '../utils/typeConverters'
|
||||
|
||||
function parseKindModifier(kindModifiers: string): Set<string> {
|
||||
return new Set(kindModifiers.split(/,|\s+/g))
|
||||
}
|
||||
|
||||
function getSymbolKind(item: Proto.NavtoItem): SymbolKind {
|
||||
switch (item.kind) {
|
||||
case 'method':
|
||||
|
@ -75,7 +80,10 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo
|
|||
getSymbolKind(item),
|
||||
range,
|
||||
this.client.toResource(item.file))
|
||||
|
||||
const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined
|
||||
if (kindModifiers?.has(PConst.KindModifiers.deprecated)) {
|
||||
symbolInfo.tags = [SymbolTag.Deprecated]
|
||||
}
|
||||
result.push(symbolInfo)
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class LanguageProvider {
|
|||
public client: TypeScriptServiceClient,
|
||||
private readonly fileConfigurationManager: FileConfigurationManager,
|
||||
private description: LanguageDescription,
|
||||
private typingsStatus: TypingsStatus
|
||||
typingsStatus: TypingsStatus
|
||||
) {
|
||||
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
|
||||
this.configurationChanged()
|
||||
|
@ -86,7 +86,7 @@ export default class LanguageProvider {
|
|||
if (this.client.apiVersion.gte(API.v230)) {
|
||||
this._register(languages.registerCompletionItemProvider(
|
||||
`${this.description.id}-directive`,
|
||||
'TSC', languageIds, new DirectiveCommentCompletionProvider(client,), ['@']
|
||||
'TSC', languageIds, new DirectiveCommentCompletionProvider(client), ['@']
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -179,17 +179,22 @@ export default class LanguageProvider {
|
|||
public diagnosticsReceived(
|
||||
diagnosticsKind: DiagnosticKind,
|
||||
file: Uri,
|
||||
diagnostics: (Diagnostic & { reportUnnecessary: any })[]
|
||||
diagnostics: (Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[]
|
||||
): void {
|
||||
const config = workspace.getConfiguration(this.id, file.toString())
|
||||
const reportUnnecessary = config.get<boolean>('showUnused', true)
|
||||
const reportDeprecated = config.get<boolean>('showDeprecated', true)
|
||||
this.client.diagnosticsManager.diagnosticsReceived(diagnosticsKind, file.toString(), diagnostics.filter(diag => {
|
||||
if (!reportUnnecessary) {
|
||||
diag.tags = undefined
|
||||
if (diag.reportUnnecessary && diag.severity === DiagnosticSeverity.Information) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (!reportDeprecated) {
|
||||
if (diag.reportDeprecated && diag.severity === DiagnosticSeverity.Hint) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export class DiagnosticCategory {
|
|||
|
||||
export class KindModifiers {
|
||||
public static readonly optional = 'optional'
|
||||
public static readonly depreacted = 'deprecated'
|
||||
public static readonly deprecated = 'deprecated'
|
||||
public static readonly color = 'color'
|
||||
|
||||
public static readonly dtsFile = '.d.ts'
|
||||
|
@ -60,7 +60,7 @@ export class KindModifiers {
|
|||
KindModifiers.tsxFile,
|
||||
KindModifiers.jsFile,
|
||||
KindModifiers.jsxFile,
|
||||
KindModifiers.jsonFile,
|
||||
KindModifiers.jsonFile
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -72,3 +72,19 @@ export class DisplayPartKind {
|
|||
public static readonly punctuation = 'punctuation'
|
||||
public static readonly text = 'text'
|
||||
}
|
||||
|
||||
export enum EventName {
|
||||
syntaxDiag = 'syntaxDiag',
|
||||
semanticDiag = 'semanticDiag',
|
||||
suggestionDiag = 'suggestionDiag',
|
||||
configFileDiag = 'configFileDiag',
|
||||
telemetry = 'telemetry',
|
||||
projectLanguageServiceState = 'projectLanguageServiceState',
|
||||
projectsUpdatedInBackground = 'projectsUpdatedInBackground',
|
||||
beginInstallTypes = 'beginInstallTypes',
|
||||
endInstallTypes = 'endInstallTypes',
|
||||
typesInstallerInitializationFailed = 'typesInstallerInitializationFailed',
|
||||
surveyReady = 'surveyReady',
|
||||
projectLoadingStart = 'projectLoadingStart',
|
||||
projectLoadingFinish = 'projectLoadingFinish'
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { disposeAll, languages, TextDocument, Uri, workspace } from 'coc.nvim'
|
||||
import { CancellationToken, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, Disposable, Position, Range } from 'vscode-languageserver-protocol'
|
||||
import { CancellationToken, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Disposable, Position, Range } from 'vscode-languageserver-protocol'
|
||||
import { flatten } from '../utils/arrays'
|
||||
import { PluginManager } from '../utils/plugins'
|
||||
import { DiagnosticKind } from './features/diagnostics'
|
||||
|
@ -217,11 +217,11 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private createMarkerDatas(diagnostics: Proto.Diagnostic[]): (Diagnostic & { reportUnnecessary: any })[] {
|
||||
private createMarkerDatas(diagnostics: Proto.Diagnostic[]): (Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[] {
|
||||
return diagnostics.map(tsDiag => this.tsDiagnosticToLspDiagnostic(tsDiag))
|
||||
}
|
||||
|
||||
private tsDiagnosticToLspDiagnostic(diagnostic: Proto.Diagnostic): (Diagnostic & { reportUnnecessary: any }) {
|
||||
private tsDiagnosticToLspDiagnostic(diagnostic: Proto.Diagnostic): (Diagnostic & { reportUnnecessary: any, reportDeprecated: any }) {
|
||||
const { start, end, text } = diagnostic
|
||||
const range = {
|
||||
start: typeConverters.Position.fromLocation(start),
|
||||
|
@ -237,11 +237,22 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
}
|
||||
})
|
||||
}
|
||||
let tags: DiagnosticTag[] | undefined = []
|
||||
if (diagnostic.reportsUnnecessary) {
|
||||
tags.push(DiagnosticTag.Unnecessary)
|
||||
}
|
||||
if (diagnostic.reportsDeprecated) {
|
||||
tags.push(DiagnosticTag.Deprecated)
|
||||
}
|
||||
tags = tags.length ? tags : undefined
|
||||
|
||||
return {
|
||||
range,
|
||||
tags,
|
||||
message: text,
|
||||
code: diagnostic.code ? diagnostic.code : null,
|
||||
severity: this.getDiagnosticSeverity(diagnostic),
|
||||
reportDeprecated: diagnostic.reportsDeprecated,
|
||||
reportUnnecessary: diagnostic.reportsUnnecessary,
|
||||
source: diagnostic.source || 'tsserver',
|
||||
relatedInformation
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { Range, CompletionItem, CompletionItemKind, InsertTextFormat, Position, TextEdit } from 'vscode-languageserver-protocol'
|
||||
import { Range, CompletionItem, CompletionItemKind, InsertTextFormat, Position, TextEdit } from 'coc.nvim'
|
||||
import { CompletionItemTag } from 'vscode-languageserver-protocol'
|
||||
import * as Proto from '../protocol'
|
||||
import * as PConst from '../protocol.const'
|
||||
|
||||
|
@ -52,6 +53,7 @@ export function convertCompletionEntry(
|
|||
|
||||
let insertText = tsEntry.insertText
|
||||
let commitCharacters = getCommitCharacters(tsEntry, context)
|
||||
let tags: CompletionItemTag[]
|
||||
|
||||
if (tsEntry.isImportStatementCompletion) {
|
||||
insertText = label
|
||||
|
@ -75,6 +77,10 @@ export function convertCompletionEntry(
|
|||
label += '?'
|
||||
}
|
||||
|
||||
if (kindModifiers.has(PConst.KindModifiers.deprecated)) {
|
||||
tags = [CompletionItemTag.Deprecated]
|
||||
}
|
||||
|
||||
if (kindModifiers.has(PConst.KindModifiers.color)) {
|
||||
kind = CompletionItemKind.Color
|
||||
}
|
||||
|
@ -97,6 +103,7 @@ export function convertCompletionEntry(
|
|||
insertText,
|
||||
textEdit,
|
||||
kind,
|
||||
tags,
|
||||
preselect,
|
||||
insertTextFormat,
|
||||
sortText,
|
||||
|
|
|
@ -5,37 +5,83 @@
|
|||
|
||||
import { MarkupContent, MarkupKind } from 'vscode-languageserver-protocol'
|
||||
import * as Proto from '../protocol'
|
||||
import { Uri } from 'coc.nvim'
|
||||
|
||||
function toResource(filepath: string): Uri {
|
||||
return Uri.file(filepath)
|
||||
}
|
||||
|
||||
function replaceLinks(text: string): string {
|
||||
return text
|
||||
// Http(s) links
|
||||
.replace(/\{@(link|linkplain|linkcode) (https?:\/\/[^ |}]+?)(?:[| ]([^{}\n]+?))?\}/gi, (_, tag: string, link: string, text?: string) => {
|
||||
switch (tag) {
|
||||
case 'linkcode':
|
||||
return `[\`${text ? text.trim() : link}\`](${link})`
|
||||
|
||||
default:
|
||||
return `[${text ? text.trim() : link}](${link})`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function processInlineTags(text: string): string {
|
||||
return replaceLinks(text)
|
||||
}
|
||||
|
||||
function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
if (!tag.text) {
|
||||
return undefined
|
||||
}
|
||||
// Convert to markdown code block if it is not already one
|
||||
function makeCodeblock(text: string): string {
|
||||
if (text.match(/^\s*[~`]{3}/g)) {
|
||||
return text
|
||||
}
|
||||
return '```\n' + text + '\n```'
|
||||
}
|
||||
|
||||
const text = convertLinkTags(tag.text)
|
||||
switch (tag.name) {
|
||||
case 'example':
|
||||
case 'default':
|
||||
// Convert to markdown code block if it not already one
|
||||
if (tag.text.match(/^\s*[~`]{3}/g)) {
|
||||
return tag.text
|
||||
// check for caption tags, fix for #79704
|
||||
const captionTagMatches = text.match(/<caption>(.*?)<\/caption>\s*(\r\n|\n)/)
|
||||
if (captionTagMatches && captionTagMatches.index === 0) {
|
||||
return captionTagMatches[1] + '\n\n' + makeCodeblock(text.substr(captionTagMatches[0].length))
|
||||
} else {
|
||||
return makeCodeblock(text)
|
||||
}
|
||||
return '```\n' + tag.text + '\n```'
|
||||
case 'author':
|
||||
// fix obsucated email address, #80898
|
||||
const emailMatch = text.match(/(.+)\s<([-.\w]+@[-.\w]+)>/)
|
||||
|
||||
if (emailMatch === null) {
|
||||
return text
|
||||
} else {
|
||||
return `${emailMatch[1]} ${emailMatch[2]}`
|
||||
}
|
||||
case 'default':
|
||||
return makeCodeblock(text)
|
||||
}
|
||||
|
||||
return tag.text
|
||||
return processInlineTags(text)
|
||||
}
|
||||
|
||||
function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
|
||||
switch (tag.name) {
|
||||
case 'augments':
|
||||
case 'extends':
|
||||
case 'param':
|
||||
const body = (tag.text || '').split(/^([\w\.]+)\s*/)
|
||||
if (body && body.length === 3) {
|
||||
case 'template':
|
||||
const body = (convertLinkTags(tag.text)).split(/^(\S+)\s*-?\s*/)
|
||||
if (body?.length === 3) {
|
||||
const param = body[1]
|
||||
const doc = body[2]
|
||||
const label = `*@${tag.name}* \`${param}\``
|
||||
if (!doc) {
|
||||
return label
|
||||
}
|
||||
return label + (doc.match(/\r\n|\n/g) ? '\n' + doc : ` — ${doc}`)
|
||||
return label + (doc.match(/\r\n|\n/g) ? ' \n' + processInlineTags(doc) : ` — ${processInlineTags(doc)}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +91,7 @@ function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
|
|||
if (!text) {
|
||||
return label
|
||||
}
|
||||
return label + (text.match(/\r\n|\n/g) ? '\n' + text : ` — ${text}`)
|
||||
return label + (text.match(/\r\n|\n/g) ? ' \n' + text : ` — ${text}`)
|
||||
}
|
||||
|
||||
export function plain(parts: Proto.SymbolDisplayPart[]): string {
|
||||
|
@ -71,3 +117,73 @@ export function markdownDocumentation(
|
|||
value: out
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert `@link` inline tags to markdown links
|
||||
*/
|
||||
function convertLinkTags(
|
||||
parts: readonly Proto.SymbolDisplayPart[] | string | undefined
|
||||
): string {
|
||||
if (!parts) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (typeof parts === 'string') {
|
||||
return parts
|
||||
}
|
||||
|
||||
const out: string[] = []
|
||||
let currentLink: { name?: string, target?: Proto.FileSpan, text?: string } | undefined
|
||||
for (const part of parts) {
|
||||
switch (part.kind) {
|
||||
case 'link':
|
||||
if (currentLink) {
|
||||
const text = currentLink.text ?? currentLink.name
|
||||
if (currentLink.target) {
|
||||
const link = toResource(currentLink.target.file)
|
||||
.with({
|
||||
fragment: `L${currentLink.target.start.line},${currentLink.target.start.offset}`
|
||||
})
|
||||
|
||||
out.push(`[${text}](${link.toString()})`)
|
||||
} else {
|
||||
if (text) {
|
||||
if (/^https?:/.test(text)) {
|
||||
const parts = text.split(' ')
|
||||
if (parts.length === 1) {
|
||||
out.push(parts[0])
|
||||
} else if (parts.length > 1) {
|
||||
out.push(`[${parts.slice(1).join(' ')}](${parts[0]})`)
|
||||
}
|
||||
} else {
|
||||
out.push(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
currentLink = undefined
|
||||
} else {
|
||||
currentLink = {}
|
||||
}
|
||||
break
|
||||
|
||||
case 'linkName':
|
||||
if (currentLink) {
|
||||
currentLink.name = part.text
|
||||
// TODO: remove cast once we pick up TS 4.3
|
||||
currentLink.target = (part as any as Proto.JSDocLinkDisplayPart).target
|
||||
}
|
||||
break
|
||||
|
||||
case 'linkText':
|
||||
if (currentLink) {
|
||||
currentLink.text = part.text
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
out.push(part.text)
|
||||
break
|
||||
}
|
||||
}
|
||||
return processInlineTags(out.join(''))
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@ export namespace Range {
|
|||
}
|
||||
}
|
||||
}
|
||||
export const fromLocations = (start: Proto.Location, end: Proto.Location): language.Range =>
|
||||
language.Range.create(
|
||||
Math.max(0, start.line - 1), Math.max(start.offset - 1, 0),
|
||||
Math.max(0, end.line - 1), Math.max(0, end.offset - 1))
|
||||
|
||||
|
||||
export const toFormattingRequestArgs = (file: string, range: language.Range): Proto.FormatRequestArgs => ({
|
||||
file,
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -7,10 +7,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.44.tgz#3945e6b702cb6403f22b779c8ea9e5c3f44ead40"
|
||||
integrity sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==
|
||||
|
||||
coc.nvim@^0.0.80:
|
||||
version "0.0.80"
|
||||
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.80.tgz#785145c382660db03f517f9b497900d95cbd0e4f"
|
||||
integrity sha512-/3vTcnofoAYMrdENrlQmADTzfXX4+PZ0fiM10a39UA37dTR2dpIGi9O469kcIksuunLjToqWG8S45AGx/9wV7g==
|
||||
coc.nvim@^0.0.81-next.5:
|
||||
version "0.0.81-next.5"
|
||||
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.81-next.5.tgz#b09bda5a2d527f7cd7bd786d4dee958160285e2b"
|
||||
integrity sha512-NUTEWmjm9uKGBD8FNsj55PH1Xn4hD5uDCs7EdMoBpXWehq6/P1UoDZQfWXQv9HTq4zF/jH3g7KtZtl94VTlW4A==
|
||||
|
||||
esbuild@^0.8.29:
|
||||
version "0.8.29"
|
||||
|
@ -27,10 +27,10 @@ semver@^7.3.2:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
||||
|
||||
typescript@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
||||
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
|
||||
typescript@^4.3.5:
|
||||
version "4.3.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
|
||||
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
||||
|
||||
vscode-jsonrpc@6.0.0:
|
||||
version "6.0.0"
|
||||
|
|
Loading…
Reference in a new issue