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",
|
"name": "coc-tsserver",
|
||||||
"version": "1.8.1",
|
"version": "1.8.5",
|
||||||
"description": "tsserver extension for coc.nvim",
|
"description": "tsserver extension for coc.nvim",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"publisher": "chemzqm",
|
"publisher": "chemzqm",
|
||||||
|
@ -265,6 +265,11 @@
|
||||||
"default": true,
|
"default": true,
|
||||||
"description": "Show unused variable hint."
|
"description": "Show unused variable hint."
|
||||||
},
|
},
|
||||||
|
"typescript.showDeprecated": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Show deprecated variable hint."
|
||||||
|
},
|
||||||
"typescript.updateImportsOnFileMove.enable": {
|
"typescript.updateImportsOnFileMove.enable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": true,
|
||||||
|
@ -473,6 +478,11 @@
|
||||||
"default": true,
|
"default": true,
|
||||||
"description": "Show unused variable hint."
|
"description": "Show unused variable hint."
|
||||||
},
|
},
|
||||||
|
"javascript.showDeprecated": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Show deprecated variable hint."
|
||||||
|
},
|
||||||
"javascript.updateImportsOnFileMove.enable": {
|
"javascript.updateImportsOnFileMove.enable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
|
@ -675,13 +685,13 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^10.12.0",
|
"@types/node": "^10.12.0",
|
||||||
"coc.nvim": "^0.0.80",
|
"coc.nvim": "^0.0.81-next.5",
|
||||||
"esbuild": "^0.8.29",
|
"esbuild": "^0.8.29",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
"vscode-languageserver-protocol": "^3.16.0",
|
"vscode-languageserver-protocol": "^3.16.0",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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.
|
* 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 path from "path"
|
||||||
import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CancellationToken, Position, SymbolTag } from 'vscode-languageserver-protocol'
|
import { CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, CancellationToken, Position, SymbolTag } from 'vscode-languageserver-protocol'
|
||||||
import type * as Proto from '../protocol'
|
import type * as Proto from '../protocol'
|
||||||
|
@ -80,9 +80,7 @@ function parseKindModifier(kindModifiers: string): Set<string> {
|
||||||
function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): CallHierarchyItem {
|
function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): CallHierarchyItem {
|
||||||
const useFileName = isSourceFileItem(item)
|
const useFileName = isSourceFileItem(item)
|
||||||
const name = useFileName ? path.basename(item.file) : item.name
|
const name = useFileName ? path.basename(item.file) : item.name
|
||||||
// TODO
|
const detail = useFileName ? path.relative(workspace.cwd, path.dirname(item.file)) : item.containerName ?? ''
|
||||||
// const detail = useFileName ? workspace.asRelativePath(path.dirname(item.file)) : item.containerName ?? ''
|
|
||||||
const detail = item.containerName || ''
|
|
||||||
const result: CallHierarchyItem = {
|
const result: CallHierarchyItem = {
|
||||||
name,
|
name,
|
||||||
detail,
|
detail,
|
||||||
|
@ -93,7 +91,7 @@ function fromProtocolCallHierarchyItem(item: Proto.CallHierarchyItem): CallHiera
|
||||||
}
|
}
|
||||||
|
|
||||||
const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined
|
const kindModifiers = item.kindModifiers ? parseKindModifier(item.kindModifiers) : undefined
|
||||||
if (kindModifiers?.has(PConst.KindModifiers.depreacted)) {
|
if (kindModifiers?.has(PConst.KindModifiers.deprecated)) {
|
||||||
result.tags = [SymbolTag.Deprecated]
|
result.tags = [SymbolTag.Deprecated]
|
||||||
}
|
}
|
||||||
return result
|
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.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* 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 Proto from '../protocol'
|
||||||
import * as PConst from '../protocol.const'
|
import * as PConst from '../protocol.const'
|
||||||
import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'
|
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)) {
|
if (!this.completeOption.importStatementSuggestions || !this.client.apiVersion.lt(API.v430)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return pre === 'import ';
|
return pre === 'import '
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { TextDocument } from 'coc.nvim'
|
import { TextDocument } from 'coc.nvim'
|
||||||
import { DefinitionProvider, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
import { DefinitionProvider, CancellationToken, Definition, Location, Position, DefinitionLink, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
||||||
import { CancellationToken, Definition, Location, Position } from 'vscode-languageserver-protocol'
|
|
||||||
import * as Proto from '../protocol'
|
import * as Proto from '../protocol'
|
||||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
|
import API from '../utils/api'
|
||||||
import * as typeConverters from '../utils/typeConverters'
|
import * as typeConverters from '../utils/typeConverters'
|
||||||
|
|
||||||
export default class TypeScriptDefinitionProvider implements DefinitionProvider, TypeDefinitionProvider, ImplementationProvider {
|
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,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<Definition | undefined> {
|
): Promise<Definition | DefinitionLink[] | undefined> {
|
||||||
return this.getSymbolLocations('definition', document, position, token)
|
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(
|
public provideTypeDefinition(
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { DiagnosticCollection, languages, workspace } from 'coc.nvim'
|
import { DiagnosticCollection, languages, workspace } from 'coc.nvim'
|
||||||
import { Diagnostic } from 'vscode-languageserver-protocol'
|
import { Diagnostic, DiagnosticTag } from 'vscode-languageserver-protocol'
|
||||||
import { ResourceMap } from './resourceMap'
|
import { ResourceMap } from './resourceMap'
|
||||||
|
|
||||||
export class DiagnosticSet {
|
export class DiagnosticSet {
|
||||||
|
@ -146,8 +146,7 @@ export class DiagnosticsManager {
|
||||||
.get(uri)
|
.get(uri)
|
||||||
.filter(x => {
|
.filter(x => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
// Still show unused
|
return x.tags && (x.tags.includes(DiagnosticTag.Unnecessary) || x.tags.includes(DiagnosticTag.Deprecated))
|
||||||
return x.code == 6133
|
|
||||||
}
|
}
|
||||||
return x.code !== 80001 // disable annoying CommonJS module warning
|
return x.code !== 80001 // disable annoying CommonJS module warning
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* 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 { CancellationToken, CompletionContext, CompletionItem, CompletionItemKind, CompletionList, Position, Range, TextDocument } from 'coc.nvim'
|
||||||
import { TextDocument } from 'coc.nvim'
|
|
||||||
import { workspace } from 'coc.nvim'
|
import { workspace } from 'coc.nvim'
|
||||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
import API from '../utils/api'
|
import API from '../utils/api'
|
||||||
|
@ -37,7 +36,7 @@ const tsDirectives390: Directive[] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export default class DirectiveCommentCompletionProvider {
|
export default class DirectiveCommentCompletionProvider {
|
||||||
constructor(private readonly client: ITypeScriptServiceClient) { }
|
constructor(private readonly client: ITypeScriptServiceClient) {}
|
||||||
|
|
||||||
public provideCompletionItems(
|
public provideCompletionItems(
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
|
@ -62,7 +61,7 @@ export default class DirectiveCommentCompletionProvider {
|
||||||
? tsDirectives390
|
? tsDirectives390
|
||||||
: tsDirectives
|
: tsDirectives
|
||||||
let items = directives.map(directive => {
|
let items = directives.map(directive => {
|
||||||
const item = CompletionItem.create(directive.value)
|
const item: CompletionItem = { label: directive.value }
|
||||||
item.kind = CompletionItemKind.Snippet
|
item.kind = CompletionItemKind.Snippet
|
||||||
item.detail = directive.description
|
item.detail = directive.description
|
||||||
item.textEdit = {
|
item.textEdit = {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { TextDocument } from 'coc.nvim'
|
import { TextDocument } from 'coc.nvim'
|
||||||
import { DocumentSymbolProvider } 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 Proto from '../protocol'
|
||||||
import * as PConst from '../protocol.const'
|
import * as PConst from '../protocol.const'
|
||||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
|
@ -82,20 +82,15 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
||||||
}
|
}
|
||||||
|
|
||||||
private static convertNavTree(
|
private static convertNavTree(
|
||||||
bucket: DocumentSymbol[],
|
output: DocumentSymbol[],
|
||||||
item: Proto.NavigationTree,
|
item: Proto.NavigationTree,
|
||||||
): boolean {
|
): boolean {
|
||||||
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)
|
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)
|
||||||
const children = new Set(item.childItems || [])
|
const children = new Set(item.childItems || [])
|
||||||
for (const span of item.spans) {
|
for (const span of item.spans) {
|
||||||
const range = typeConverters.Range.fromTextSpan(span)
|
const range = typeConverters.Range.fromTextSpan(span)
|
||||||
const symbolInfo = DocumentSymbol.create(
|
const symbolInfo = TypeScriptDocumentSymbolProvider.convertSymbol(item, range)
|
||||||
item.text,
|
if (children.size) symbolInfo.children = []
|
||||||
'',
|
|
||||||
getSymbolKind(item.kind),
|
|
||||||
range,
|
|
||||||
range)
|
|
||||||
symbolInfo.children = children.size > 0 ? [] : null
|
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (child.spans.some(span => !!containsRange(range, typeConverters.Range.fromTextSpan(span)))) {
|
if (child.spans.some(span => !!containsRange(range, typeConverters.Range.fromTextSpan(span)))) {
|
||||||
|
@ -106,11 +101,31 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldInclude) {
|
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(
|
private static shouldInclueEntry(
|
||||||
|
@ -142,3 +157,7 @@ function containsRange(range: Range, otherRange: Range): boolean {
|
||||||
}
|
}
|
||||||
return true
|
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.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* 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 { CancellationTokenSource, Disposable, disposeAll, Position, Range, snippetManager, events, workspace, InsertChange } from 'coc.nvim'
|
||||||
import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol'
|
|
||||||
import * as Proto from '../protocol'
|
import * as Proto from '../protocol'
|
||||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
import API from '../utils/api'
|
import API from '../utils/api'
|
||||||
|
@ -19,49 +18,82 @@ export default class TagClosing implements Disposable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _disposables: Disposable[] = []
|
private _disposables: Disposable[] = []
|
||||||
private _enabled: boolean = false
|
|
||||||
private _disposed = false
|
private _disposed = false
|
||||||
private _timeout: NodeJS.Timer | undefined = undefined
|
private _timeout: NodeJS.Timer | undefined = undefined
|
||||||
private _cancel: CancellationTokenSource | undefined = undefined
|
private _cancel: CancellationTokenSource | undefined = undefined
|
||||||
|
private lastInsert: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly client: ITypeScriptServiceClient,
|
private readonly client: ITypeScriptServiceClient,
|
||||||
private readonly descriptionLanguageId: string
|
private readonly descriptionLanguageId: string
|
||||||
) {
|
) {
|
||||||
workspace.onDidChangeTextDocument(
|
events.on('InsertCharPre', character => {
|
||||||
(event) =>
|
this.lastInsert = character
|
||||||
this.onDidChangeTextDocument(
|
}, null, this._disposables)
|
||||||
event.textDocument,
|
events.on('TextChangedI', this.onChange, this, this._disposables)
|
||||||
event.contentChanges
|
events.on('TextChangedP', this.onChange, this, this._disposables)
|
||||||
),
|
}
|
||||||
null,
|
|
||||||
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._cancel = new CancellationTokenSource()
|
||||||
this.updateEnabledState()
|
const response = await this.client.execute(
|
||||||
|
'jsxClosingTag',
|
||||||
workspace.registerAutocmd({
|
args,
|
||||||
event: ['BufEnter'],
|
this._cancel.token
|
||||||
request: false,
|
)
|
||||||
callback: () => this.updateEnabledState(),
|
if (response.type !== 'response' || !response.body) {
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateEnabledState(): Promise<void> {
|
|
||||||
this._enabled = false
|
|
||||||
const doc = await workspace.document
|
|
||||||
if (!doc) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const document = doc.textDocument
|
if (this._disposed) return
|
||||||
const configLang = TagClosing._configurationLanguages[document.languageId]
|
|
||||||
|
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) {
|
if (!configLang || configLang !== this.descriptionLanguageId) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if (!workspace.getConfiguration(undefined, document.uri).get<boolean>(`${configLang}.autoClosingTags`)) {
|
if (!workspace.getConfiguration(undefined, uri).get<boolean>(`${configLang}.autoClosingTags`)) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
this._enabled = true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
|
@ -82,120 +114,10 @@ export default class TagClosing implements Disposable {
|
||||||
this._disposables = []
|
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 {
|
private getTagSnippet(closingTag: Proto.TextInsertion): SnippetString {
|
||||||
const snippet = new SnippetString();
|
const snippet = new SnippetString()
|
||||||
snippet.appendPlaceholder('', 0);
|
snippet.appendPlaceholder('', 0)
|
||||||
snippet.appendText(closingTag.newText);
|
snippet.appendText(closingTag.newText)
|
||||||
return snippet;
|
return snippet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,17 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { workspace } from 'coc.nvim'
|
import { workspace } from 'coc.nvim'
|
||||||
import { WorkspaceSymbolProvider } 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 Proto from '../protocol'
|
||||||
|
import * as PConst from '../protocol.const'
|
||||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
import API from '../utils/api'
|
import API from '../utils/api'
|
||||||
import * as typeConverters from '../utils/typeConverters'
|
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 {
|
function getSymbolKind(item: Proto.NavtoItem): SymbolKind {
|
||||||
switch (item.kind) {
|
switch (item.kind) {
|
||||||
case 'method':
|
case 'method':
|
||||||
|
@ -75,7 +80,10 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo
|
||||||
getSymbolKind(item),
|
getSymbolKind(item),
|
||||||
range,
|
range,
|
||||||
this.client.toResource(item.file))
|
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)
|
result.push(symbolInfo)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -44,7 +44,7 @@ export default class LanguageProvider {
|
||||||
public client: TypeScriptServiceClient,
|
public client: TypeScriptServiceClient,
|
||||||
private readonly fileConfigurationManager: FileConfigurationManager,
|
private readonly fileConfigurationManager: FileConfigurationManager,
|
||||||
private description: LanguageDescription,
|
private description: LanguageDescription,
|
||||||
private typingsStatus: TypingsStatus
|
typingsStatus: TypingsStatus
|
||||||
) {
|
) {
|
||||||
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
|
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
|
||||||
this.configurationChanged()
|
this.configurationChanged()
|
||||||
|
@ -86,7 +86,7 @@ export default class LanguageProvider {
|
||||||
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`,
|
||||||
'TSC', languageIds, new DirectiveCommentCompletionProvider(client,), ['@']
|
'TSC', languageIds, new DirectiveCommentCompletionProvider(client), ['@']
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,17 +179,22 @@ export default class LanguageProvider {
|
||||||
public diagnosticsReceived(
|
public diagnosticsReceived(
|
||||||
diagnosticsKind: DiagnosticKind,
|
diagnosticsKind: DiagnosticKind,
|
||||||
file: Uri,
|
file: Uri,
|
||||||
diagnostics: (Diagnostic & { reportUnnecessary: any })[]
|
diagnostics: (Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[]
|
||||||
): void {
|
): void {
|
||||||
const config = workspace.getConfiguration(this.id, file.toString())
|
const config = workspace.getConfiguration(this.id, file.toString())
|
||||||
const reportUnnecessary = config.get<boolean>('showUnused', true)
|
const reportUnnecessary = config.get<boolean>('showUnused', true)
|
||||||
|
const reportDeprecated = config.get<boolean>('showDeprecated', true)
|
||||||
this.client.diagnosticsManager.diagnosticsReceived(diagnosticsKind, file.toString(), diagnostics.filter(diag => {
|
this.client.diagnosticsManager.diagnosticsReceived(diagnosticsKind, file.toString(), diagnostics.filter(diag => {
|
||||||
if (!reportUnnecessary) {
|
if (!reportUnnecessary) {
|
||||||
diag.tags = undefined
|
|
||||||
if (diag.reportUnnecessary && diag.severity === DiagnosticSeverity.Information) {
|
if (diag.reportUnnecessary && diag.severity === DiagnosticSeverity.Information) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!reportDeprecated) {
|
||||||
|
if (diag.reportDeprecated && diag.severity === DiagnosticSeverity.Hint) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ export class DiagnosticCategory {
|
||||||
|
|
||||||
export class KindModifiers {
|
export class KindModifiers {
|
||||||
public static readonly optional = 'optional'
|
public static readonly optional = 'optional'
|
||||||
public static readonly depreacted = 'deprecated'
|
public static readonly deprecated = 'deprecated'
|
||||||
public static readonly color = 'color'
|
public static readonly color = 'color'
|
||||||
|
|
||||||
public static readonly dtsFile = '.d.ts'
|
public static readonly dtsFile = '.d.ts'
|
||||||
|
@ -60,7 +60,7 @@ export class KindModifiers {
|
||||||
KindModifiers.tsxFile,
|
KindModifiers.tsxFile,
|
||||||
KindModifiers.jsFile,
|
KindModifiers.jsFile,
|
||||||
KindModifiers.jsxFile,
|
KindModifiers.jsxFile,
|
||||||
KindModifiers.jsonFile,
|
KindModifiers.jsonFile
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,3 +72,19 @@ export class DisplayPartKind {
|
||||||
public static readonly punctuation = 'punctuation'
|
public static readonly punctuation = 'punctuation'
|
||||||
public static readonly text = 'text'
|
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.
|
* 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 { 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 { flatten } from '../utils/arrays'
|
||||||
import { PluginManager } from '../utils/plugins'
|
import { PluginManager } from '../utils/plugins'
|
||||||
import { DiagnosticKind } from './features/diagnostics'
|
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))
|
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 { start, end, text } = diagnostic
|
||||||
const range = {
|
const range = {
|
||||||
start: typeConverters.Position.fromLocation(start),
|
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 {
|
return {
|
||||||
range,
|
range,
|
||||||
|
tags,
|
||||||
message: text,
|
message: text,
|
||||||
code: diagnostic.code ? diagnostic.code : null,
|
code: diagnostic.code ? diagnostic.code : null,
|
||||||
severity: this.getDiagnosticSeverity(diagnostic),
|
severity: this.getDiagnosticSeverity(diagnostic),
|
||||||
|
reportDeprecated: diagnostic.reportsDeprecated,
|
||||||
reportUnnecessary: diagnostic.reportsUnnecessary,
|
reportUnnecessary: diagnostic.reportsUnnecessary,
|
||||||
source: diagnostic.source || 'tsserver',
|
source: diagnostic.source || 'tsserver',
|
||||||
relatedInformation
|
relatedInformation
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* 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 Proto from '../protocol'
|
||||||
import * as PConst from '../protocol.const'
|
import * as PConst from '../protocol.const'
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ export function convertCompletionEntry(
|
||||||
|
|
||||||
let insertText = tsEntry.insertText
|
let insertText = tsEntry.insertText
|
||||||
let commitCharacters = getCommitCharacters(tsEntry, context)
|
let commitCharacters = getCommitCharacters(tsEntry, context)
|
||||||
|
let tags: CompletionItemTag[]
|
||||||
|
|
||||||
if (tsEntry.isImportStatementCompletion) {
|
if (tsEntry.isImportStatementCompletion) {
|
||||||
insertText = label
|
insertText = label
|
||||||
|
@ -75,6 +77,10 @@ export function convertCompletionEntry(
|
||||||
label += '?'
|
label += '?'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kindModifiers.has(PConst.KindModifiers.deprecated)) {
|
||||||
|
tags = [CompletionItemTag.Deprecated]
|
||||||
|
}
|
||||||
|
|
||||||
if (kindModifiers.has(PConst.KindModifiers.color)) {
|
if (kindModifiers.has(PConst.KindModifiers.color)) {
|
||||||
kind = CompletionItemKind.Color
|
kind = CompletionItemKind.Color
|
||||||
}
|
}
|
||||||
|
@ -97,6 +103,7 @@ export function convertCompletionEntry(
|
||||||
insertText,
|
insertText,
|
||||||
textEdit,
|
textEdit,
|
||||||
kind,
|
kind,
|
||||||
|
tags,
|
||||||
preselect,
|
preselect,
|
||||||
insertTextFormat,
|
insertTextFormat,
|
||||||
sortText,
|
sortText,
|
||||||
|
|
|
@ -5,37 +5,83 @@
|
||||||
|
|
||||||
import { MarkupContent, MarkupKind } from 'vscode-languageserver-protocol'
|
import { MarkupContent, MarkupKind } from 'vscode-languageserver-protocol'
|
||||||
import * as Proto from '../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 {
|
function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
|
||||||
if (!tag.text) {
|
if (!tag.text) {
|
||||||
return undefined
|
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) {
|
switch (tag.name) {
|
||||||
case 'example':
|
case 'example':
|
||||||
case 'default':
|
// check for caption tags, fix for #79704
|
||||||
// Convert to markdown code block if it not already one
|
const captionTagMatches = text.match(/<caption>(.*?)<\/caption>\s*(\r\n|\n)/)
|
||||||
if (tag.text.match(/^\s*[~`]{3}/g)) {
|
if (captionTagMatches && captionTagMatches.index === 0) {
|
||||||
return tag.text
|
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 {
|
function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
|
||||||
switch (tag.name) {
|
switch (tag.name) {
|
||||||
|
case 'augments':
|
||||||
|
case 'extends':
|
||||||
case 'param':
|
case 'param':
|
||||||
const body = (tag.text || '').split(/^([\w\.]+)\s*/)
|
case 'template':
|
||||||
if (body && body.length === 3) {
|
const body = (convertLinkTags(tag.text)).split(/^(\S+)\s*-?\s*/)
|
||||||
|
if (body?.length === 3) {
|
||||||
const param = body[1]
|
const param = body[1]
|
||||||
const doc = body[2]
|
const doc = body[2]
|
||||||
const label = `*@${tag.name}* \`${param}\``
|
const label = `*@${tag.name}* \`${param}\``
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return label
|
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) {
|
if (!text) {
|
||||||
return label
|
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 {
|
export function plain(parts: Proto.SymbolDisplayPart[]): string {
|
||||||
|
@ -71,3 +117,73 @@ export function markdownDocumentation(
|
||||||
value: out
|
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 => ({
|
export const toFormattingRequestArgs = (file: string, range: language.Range): Proto.FormatRequestArgs => ({
|
||||||
file,
|
file,
|
||||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -7,10 +7,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.44.tgz#3945e6b702cb6403f22b779c8ea9e5c3f44ead40"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.44.tgz#3945e6b702cb6403f22b779c8ea9e5c3f44ead40"
|
||||||
integrity sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==
|
integrity sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==
|
||||||
|
|
||||||
coc.nvim@^0.0.80:
|
coc.nvim@^0.0.81-next.5:
|
||||||
version "0.0.80"
|
version "0.0.81-next.5"
|
||||||
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.80.tgz#785145c382660db03f517f9b497900d95cbd0e4f"
|
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.81-next.5.tgz#b09bda5a2d527f7cd7bd786d4dee958160285e2b"
|
||||||
integrity sha512-/3vTcnofoAYMrdENrlQmADTzfXX4+PZ0fiM10a39UA37dTR2dpIGi9O469kcIksuunLjToqWG8S45AGx/9wV7g==
|
integrity sha512-NUTEWmjm9uKGBD8FNsj55PH1Xn4hD5uDCs7EdMoBpXWehq6/P1UoDZQfWXQv9HTq4zF/jH3g7KtZtl94VTlW4A==
|
||||||
|
|
||||||
esbuild@^0.8.29:
|
esbuild@^0.8.29:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||||
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
||||||
|
|
||||||
typescript@^4.3.2:
|
typescript@^4.3.5:
|
||||||
version "4.3.2"
|
version "4.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
|
||||||
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
|
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
||||||
|
|
||||||
vscode-jsonrpc@6.0.0:
|
vscode-jsonrpc@6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
|
|
Loading…
Reference in a new issue