Merge branch 'neoclide:master' into master
This commit is contained in:
commit
c728c143f0
17 changed files with 260 additions and 168 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
# 1.11.0
|
||||||
|
|
||||||
|
- Add command `tsserver.goToSourceDefinition`.
|
||||||
|
|
||||||
# 1.10.5
|
# 1.10.5
|
||||||
|
|
||||||
- Fix a fold issue #380
|
- Fix a fold issue #380
|
||||||
|
|
18
package.json
18
package.json
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "coc-tsserver",
|
"name": "coc-tsserver",
|
||||||
"version": "1.10.5",
|
"version": "1.11.7",
|
||||||
"description": "tsserver extension for coc.nvim",
|
"description": "tsserver extension for coc.nvim",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"publisher": "chemzqm",
|
"publisher": "chemzqm",
|
||||||
"engines": {
|
"engines": {
|
||||||
"coc": "^0.0.80"
|
"coc": "^0.0.82"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,11 +30,10 @@
|
||||||
"onLanguage:jsx-tags",
|
"onLanguage:jsx-tags",
|
||||||
"onLanguage:jsonc",
|
"onLanguage:jsonc",
|
||||||
"onCommand:_typescript.configurePlugin",
|
"onCommand:_typescript.configurePlugin",
|
||||||
"onCommand:typescript.reloadProjects",
|
"onCommand:tsserver.reloadProjects",
|
||||||
"onCommand:javascript.reloadProjects",
|
"onCommand:tsserver.goToProjectConfig",
|
||||||
"onCommand:javascript.goToProjectConfig",
|
"onCommand:tsserver.openTsServerLog",
|
||||||
"onCommand:typescript.goToProjectConfig",
|
"onCommand:tsserver.goToSourceDefinition",
|
||||||
"onCommand:typescript.openTsServerLog",
|
|
||||||
"onCommand:tsserver.watchBuild"
|
"onCommand:tsserver.watchBuild"
|
||||||
],
|
],
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
@ -94,6 +93,11 @@
|
||||||
"category": "TSServer",
|
"category": "TSServer",
|
||||||
"command": "tsserver.findAllFileReferences"
|
"command": "tsserver.findAllFileReferences"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "tsserver.goToSourceDefinition",
|
||||||
|
"title": "Go to Source Definition",
|
||||||
|
"category": "TSServer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Run `tsc --watch` for current project by use vim's job feature.",
|
"title": "Run `tsc --watch` for current project by use vim's job feature.",
|
||||||
"category": "TSServer",
|
"category": "TSServer",
|
||||||
|
|
|
@ -218,6 +218,56 @@ export class FileReferencesCommand implements Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SourceDefinitionCommand implements Command {
|
||||||
|
public static readonly context = 'tsSupportsSourceDefinition'
|
||||||
|
public static readonly minVersion = API.v470
|
||||||
|
|
||||||
|
public readonly id = 'tsserver.goToSourceDefinition'
|
||||||
|
|
||||||
|
public constructor(private readonly service: TsserverService) {}
|
||||||
|
|
||||||
|
public async execute() {
|
||||||
|
const client = await this.service.getClientHost()
|
||||||
|
if (client.serviceClient.apiVersion.lt(SourceDefinitionCommand.minVersion)) {
|
||||||
|
window.showErrorMessage('Go to Source Definition failed. Requires TypeScript 4.7+.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { document, position } = await workspace.getCurrentState()
|
||||||
|
if (client.serviceClient.modeIds.indexOf(document.languageId) == -1) {
|
||||||
|
window.showErrorMessage('Go to Source Definition failed. Unsupported file type.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const openedFiledPath = client.serviceClient.toOpenedFilePath(document.uri)
|
||||||
|
if (!openedFiledPath) {
|
||||||
|
window.showErrorMessage('Go to Source Definition failed. Unknown file type.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await window.withProgress({ title: 'Finding source definitions' }, async (_progress, token) => {
|
||||||
|
|
||||||
|
const args = typeConverters.Position.toFileLocationRequestArgs(openedFiledPath, position)
|
||||||
|
const response = await client.serviceClient.execute('findSourceDefinition', args, token)
|
||||||
|
if (response.type === 'response' && response.body) {
|
||||||
|
const locations: Location[] = (response as Proto.DefinitionResponse).body.map(reference =>
|
||||||
|
typeConverters.Location.fromTextSpan(client.serviceClient.toResource(reference.file), reference))
|
||||||
|
|
||||||
|
if (locations.length) {
|
||||||
|
commands.executeCommand('editor.action.showReferences', document.uri, position, locations)
|
||||||
|
// if (locations.length === 1) {
|
||||||
|
// commands.executeCommand('vscode.open', locations[0].uri)
|
||||||
|
// } else {
|
||||||
|
// commands.executeCommand('editor.action.showReferences', document.uri, position, locations)
|
||||||
|
// }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.showErrorMessage('No source definitions found.')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function registCommand(cmd: Command): Disposable {
|
export function registCommand(cmd: Command): Disposable {
|
||||||
let { id, execute } = cmd
|
let { id, execute } = cmd
|
||||||
return commands.registerCommand(id as string, execute, cmd)
|
return commands.registerCommand(id as string, execute, cmd)
|
||||||
|
|
|
@ -30,21 +30,21 @@ class CloseOperation {
|
||||||
readonly type = BufferOperationType.Close;
|
readonly type = BufferOperationType.Close;
|
||||||
constructor(
|
constructor(
|
||||||
public readonly args: string
|
public readonly args: string
|
||||||
) { }
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OpenOperation {
|
class OpenOperation {
|
||||||
readonly type = BufferOperationType.Open;
|
readonly type = BufferOperationType.Open;
|
||||||
constructor(
|
constructor(
|
||||||
public readonly args: Proto.OpenRequestArgs
|
public readonly args: Proto.OpenRequestArgs
|
||||||
) { }
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChangeOperation {
|
class ChangeOperation {
|
||||||
readonly type = BufferOperationType.Change;
|
readonly type = BufferOperationType.Change;
|
||||||
constructor(
|
constructor(
|
||||||
public readonly args: Proto.FileCodeEdits
|
public readonly args: Proto.FileCodeEdits
|
||||||
) { }
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
type BufferOperation = CloseOperation | OpenOperation | ChangeOperation
|
type BufferOperation = CloseOperation | OpenOperation | ChangeOperation
|
||||||
|
@ -59,7 +59,7 @@ class SyncedBuffer {
|
||||||
public readonly filepath: string,
|
public readonly filepath: string,
|
||||||
private readonly client: ITypeScriptServiceClient,
|
private readonly client: ITypeScriptServiceClient,
|
||||||
private readonly synchronizer: BufferSynchronizer,
|
private readonly synchronizer: BufferSynchronizer,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
public open(): void {
|
public open(): void {
|
||||||
const args: Proto.OpenRequestArgs = {
|
const args: Proto.OpenRequestArgs = {
|
||||||
|
|
|
@ -225,8 +225,6 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
||||||
let { uri, position, source, name, data } = item.data
|
let { uri, position, source, name, data } = item.data
|
||||||
const filepath = this.client.toPath(uri)
|
const filepath = this.client.toPath(uri)
|
||||||
if (!filepath) return undefined
|
if (!filepath) return undefined
|
||||||
let document = workspace.getDocument(uri)
|
|
||||||
if (!document) return undefined
|
|
||||||
const args: Proto.CompletionDetailsRequestArgs = {
|
const args: Proto.CompletionDetailsRequestArgs = {
|
||||||
...typeConverters.Position.toFileLocationRequestArgs(
|
...typeConverters.Position.toFileLocationRequestArgs(
|
||||||
filepath,
|
filepath,
|
||||||
|
@ -259,11 +257,10 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
||||||
item.additionalTextEdits = additionalTextEdits
|
item.additionalTextEdits = additionalTextEdits
|
||||||
if (detail && item.insertTextFormat == InsertTextFormat.Snippet) {
|
if (detail && item.insertTextFormat == InsertTextFormat.Snippet) {
|
||||||
const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, position, token)
|
const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, position, token)
|
||||||
if (shouldCompleteFunction && !item.insertText) {
|
if (shouldCompleteFunction) {
|
||||||
this.createSnippetOfFunctionCall(item, detail)
|
this.createSnippetOfFunctionCall(item, detail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +377,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
||||||
let { displayParts } = detail
|
let { displayParts } = detail
|
||||||
const parameterListParts = getParameterListParts(displayParts)
|
const parameterListParts = getParameterListParts(displayParts)
|
||||||
const snippet = new SnippetString()
|
const snippet = new SnippetString()
|
||||||
snippet.appendText(`${item.insertText || item.label}(`)
|
snippet.appendText(`${item.insertText ?? item.label}(`)
|
||||||
appendJoinedPlaceholders(snippet, parameterListParts.parts, ', ')
|
appendJoinedPlaceholders(snippet, parameterListParts.parts, ', ')
|
||||||
if (parameterListParts.hasOptionalParameters) {
|
if (parameterListParts.hasOptionalParameters) {
|
||||||
snippet.appendTabstop()
|
snippet.appendTabstop()
|
||||||
|
|
|
@ -2,7 +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 { TextDocument } from 'coc.nvim'
|
import { LocationLink, TextDocument } from 'coc.nvim'
|
||||||
import { DefinitionProvider, CancellationToken, Definition, Location, Position, DefinitionLink, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
import { DefinitionProvider, CancellationToken, Definition, Location, Position, DefinitionLink, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
||||||
import * as Proto from '../protocol'
|
import * as Proto from '../protocol'
|
||||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||||
|
@ -17,7 +17,7 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
token: CancellationToken
|
token: CancellationToken
|
||||||
): Promise<Location[] | undefined> {
|
): Promise<Location[] | LocationLink[] | undefined> {
|
||||||
const filepath = this.client.toPath(document.uri)
|
const filepath = this.client.toPath(document.uri)
|
||||||
if (!filepath) {
|
if (!filepath) {
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -29,12 +29,21 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
const response = await this.client.execute(definitionType, args, token)
|
const response = await this.client.execute(definitionType, args, token)
|
||||||
const locations: Proto.FileSpan[] = (response.type == 'response' && response.body) || []
|
if (response.type !== 'response' || !response.body) {
|
||||||
return locations.map(location =>
|
return undefined
|
||||||
typeConverters.Location.fromTextSpan(
|
}
|
||||||
this.client.toResource(location.file),
|
const locations: Proto.FileSpanWithContext[] = (response.type == 'response' && response.body) || []
|
||||||
location
|
return locations.map(location => {
|
||||||
)
|
const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location)
|
||||||
|
if (location.contextStart && location.contextEnd) {
|
||||||
|
return {
|
||||||
|
targetRange: typeConverters.Range.fromLocations(location.contextStart, location.contextEnd),
|
||||||
|
targetUri: target.uri,
|
||||||
|
targetSelectionRange: target.range,
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
|
@ -84,14 +93,14 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
||||||
public provideTypeDefinition(
|
public provideTypeDefinition(
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
token: CancellationToken): Promise<Definition> {
|
token: CancellationToken): Promise<Definition | DefinitionLink[]> {
|
||||||
return this.getSymbolLocations('typeDefinition', document, position, token)
|
return this.getSymbolLocations('typeDefinition', document, position, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
public provideImplementation(
|
public provideImplementation(
|
||||||
document: TextDocument,
|
document: TextDocument,
|
||||||
position: Position,
|
position: Position,
|
||||||
token: CancellationToken): Promise<Definition> {
|
token: CancellationToken): Promise<Definition | DefinitionLink[]> {
|
||||||
return this.getSymbolLocations('implementation', document, position, token)
|
return this.getSymbolLocations('implementation', document, position, token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +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 { DiagnosticCollection, languages, workspace } from 'coc.nvim'
|
import { DiagnosticCollection, Uri, languages, workspace } from 'coc.nvim'
|
||||||
import { Diagnostic, DiagnosticTag } from 'vscode-languageserver-protocol'
|
import { Diagnostic, DiagnosticTag } from 'vscode-languageserver-protocol'
|
||||||
import { ResourceMap } from './resourceMap'
|
import { ResourceMap } from './resourceMap'
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
||||||
output: DocumentSymbol[],
|
output: DocumentSymbol[],
|
||||||
item: Proto.NavigationTree,
|
item: Proto.NavigationTree,
|
||||||
): boolean {
|
): boolean {
|
||||||
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)
|
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldIncludeEntry(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)
|
||||||
|
@ -129,7 +129,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
||||||
return symbolInfo
|
return symbolInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
private static shouldInclueEntry(
|
private static shouldIncludeEntry(
|
||||||
item: Proto.NavigationTree | Proto.NavigationBarItem
|
item: Proto.NavigationTree | Proto.NavigationBarItem
|
||||||
): boolean {
|
): boolean {
|
||||||
if (item.kind === PConst.Kind.alias) {
|
if (item.kind === PConst.Kind.alias) {
|
||||||
|
|
|
@ -61,11 +61,11 @@ export default class UpdateImportsOnFileRenameHandler {
|
||||||
await workspace.nvim.command(`silent ${oldDocument.bufnr}bwipeout!`)
|
await workspace.nvim.command(`silent ${oldDocument.bufnr}bwipeout!`)
|
||||||
}
|
}
|
||||||
let document = workspace.getDocument(newUri)
|
let document = workspace.getDocument(newUri)
|
||||||
if (document) {
|
if (!document) {
|
||||||
await workspace.nvim.command(`silent ${document.bufnr}bwipeout!`)
|
document = await workspace.loadFile(newUri)
|
||||||
await wait(30)
|
} else {
|
||||||
|
workspace.nvim.command('checktime', true)
|
||||||
}
|
}
|
||||||
document = await workspace.loadFile(newUri)
|
|
||||||
if (!document) return
|
if (!document) return
|
||||||
await wait(50)
|
await wait(50)
|
||||||
const edits = await this.getEditsForFileRename(
|
const edits = await this.getEditsForFileRename(
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
import { commands, Disposable, disposeAll, StatusBarItem, TaskOptions, Uri, window, workspace } from 'coc.nvim'
|
|
||||||
import path from 'path'
|
|
||||||
import TypeScriptServiceClient from '../typescriptServiceClient'
|
|
||||||
|
|
||||||
const countRegex = /Found\s+(\d+)\s+error/
|
|
||||||
const errorRegex = /^(.+)\((\d+),(\d+)\):\s(\w+)\sTS(\d+):\s*(.+)$/
|
|
||||||
|
|
||||||
export default class WatchProject implements Disposable {
|
|
||||||
private disposables: Disposable[] = []
|
|
||||||
public static readonly id: string = 'tsserver.watchBuild'
|
|
||||||
public static readonly startTexts: string[] = ['Starting compilation in watch mode', 'Starting incremental compilation']
|
|
||||||
private statusItem: StatusBarItem
|
|
||||||
private task: any
|
|
||||||
private options: TaskOptions
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
private client: TypeScriptServiceClient
|
|
||||||
) {
|
|
||||||
this.statusItem = window.createStatusBarItem(1, { progress: true })
|
|
||||||
let task = this.task = workspace.createTask('TSC')
|
|
||||||
|
|
||||||
this.disposables.push(commands.registerCommand(WatchProject.id, async () => {
|
|
||||||
let opts = this.options = await this.getOptions()
|
|
||||||
await this.start(opts)
|
|
||||||
}))
|
|
||||||
task.onExit(code => {
|
|
||||||
if (code != 0) {
|
|
||||||
window.showMessage(`TSC exit with code ${code}`, 'warning')
|
|
||||||
}
|
|
||||||
this.onStop()
|
|
||||||
})
|
|
||||||
task.onStdout(lines => {
|
|
||||||
for (let line of lines) {
|
|
||||||
this.onLine(line)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
task.onStderr(lines => {
|
|
||||||
window.showMessage(`TSC error: ` + lines.join('\n'), 'error')
|
|
||||||
})
|
|
||||||
this.disposables.push(Disposable.create(() => {
|
|
||||||
task.dispose()
|
|
||||||
}))
|
|
||||||
this.check().catch(_e => {
|
|
||||||
// noop
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private async check(): Promise<void> {
|
|
||||||
let running = await this.task.running
|
|
||||||
if (running) {
|
|
||||||
this.options = await this.getOptions()
|
|
||||||
this.statusItem.isProgress = false
|
|
||||||
this.statusItem.text = '?'
|
|
||||||
this.statusItem.show()
|
|
||||||
} else {
|
|
||||||
this.onStop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async start(options: TaskOptions): Promise<void> {
|
|
||||||
await this.task.start(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
private onStop(): void {
|
|
||||||
this.statusItem.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
private onStart(): void {
|
|
||||||
this.statusItem.text = 'compiling'
|
|
||||||
this.statusItem.isProgress = true
|
|
||||||
this.statusItem.show()
|
|
||||||
workspace.nvim.call('setqflist', [[]], true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private onLine(line: string): void {
|
|
||||||
if (countRegex.test(line)) {
|
|
||||||
let ms = line.match(countRegex)
|
|
||||||
this.statusItem.text = ms[1] == '0' ? '✓' : '✗'
|
|
||||||
this.statusItem.isProgress = false
|
|
||||||
} else if (WatchProject.startTexts.findIndex(s => line.indexOf(s) !== -1) != -1) {
|
|
||||||
this.onStart()
|
|
||||||
} else {
|
|
||||||
let ms = line.match(errorRegex)
|
|
||||||
if (!ms) return
|
|
||||||
let fullpath = path.join(this.options.cwd, ms[1])
|
|
||||||
let uri = Uri.file(fullpath).toString()
|
|
||||||
let doc = workspace.getDocument(uri)
|
|
||||||
let bufnr = doc ? doc.bufnr : null
|
|
||||||
let item = {
|
|
||||||
filename: fullpath,
|
|
||||||
lnum: Number(ms[2]),
|
|
||||||
col: Number(ms[3]),
|
|
||||||
text: `[tsc ${ms[5]}] ${ms[6]}`,
|
|
||||||
type: /error/i.test(ms[4]) ? 'E' : 'W'
|
|
||||||
} as any
|
|
||||||
if (bufnr) item.bufnr = bufnr
|
|
||||||
workspace.nvim.call('setqflist', [[item], 'a'])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getOptions(): Promise<TaskOptions> {
|
|
||||||
let { tscPath } = this.client
|
|
||||||
if (!tscPath) {
|
|
||||||
window.showMessage(`Local & global tsc not found`, 'error')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const tsconfigPath = workspace.getConfiguration('tsserver').get<string>('tsconfigPath', 'tsconfig.json')
|
|
||||||
let find = await workspace.findUp([tsconfigPath])
|
|
||||||
if (!find) {
|
|
||||||
window.showMessage(`${tsconfigPath} not found!`, 'error')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let root = path.dirname(find)
|
|
||||||
return {
|
|
||||||
cmd: tscPath,
|
|
||||||
args: ['-p', tsconfigPath, '--watch', 'true', '--pretty', 'false'],
|
|
||||||
cwd: root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
disposeAll(this.disposables)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { commands, disposeAll, IServiceProvider, ServiceStat, workspace, WorkspaceConfiguration } from 'coc.nvim'
|
import { commands, disposeAll, IServiceProvider, ServiceStat, workspace, WorkspaceConfiguration } from 'coc.nvim'
|
||||||
import { Disposable, DocumentSelector, Emitter, Event } from 'vscode-languageserver-protocol'
|
import { Disposable, DocumentSelector, Emitter, Event } from 'vscode-languageserver-protocol'
|
||||||
import { PluginManager } from '../utils/plugins'
|
import { PluginManager } from '../utils/plugins'
|
||||||
import { AutoFixCommand, Command, ConfigurePluginCommand, FileReferencesCommand, OpenTsServerLogCommand, ReloadProjectsCommand, TypeScriptGoToProjectConfigCommand } from './commands'
|
import { AutoFixCommand, Command, ConfigurePluginCommand, FileReferencesCommand, OpenTsServerLogCommand, ReloadProjectsCommand, SourceDefinitionCommand, TypeScriptGoToProjectConfigCommand } from './commands'
|
||||||
import { OrganizeImportsCommand, SourceImportsCommand } from './organizeImports'
|
import { OrganizeImportsCommand, SourceImportsCommand } from './organizeImports'
|
||||||
|
import WatchProject from './watchBuild'
|
||||||
import TypeScriptServiceClientHost from './typescriptServiceClientHost'
|
import TypeScriptServiceClientHost from './typescriptServiceClientHost'
|
||||||
import { LanguageDescription, standardLanguageDescriptions } from './utils/languageDescription'
|
import { LanguageDescription, standardLanguageDescriptions } from './utils/languageDescription'
|
||||||
|
|
||||||
|
@ -61,6 +62,14 @@ export default class TsserverService implements IServiceProvider {
|
||||||
let { id, execute } = cmd
|
let { id, execute } = cmd
|
||||||
subscriptions.push(commands.registerCommand(id as string, execute, cmd))
|
subscriptions.push(commands.registerCommand(id as string, execute, cmd))
|
||||||
}
|
}
|
||||||
|
let watchProject = new WatchProject(this)
|
||||||
|
subscriptions.push(watchProject)
|
||||||
|
registCommand({
|
||||||
|
id: WatchProject.id,
|
||||||
|
execute: () => {
|
||||||
|
return watchProject.execute()
|
||||||
|
}
|
||||||
|
})
|
||||||
registCommand(new ConfigurePluginCommand(this.pluginManager))
|
registCommand(new ConfigurePluginCommand(this.pluginManager))
|
||||||
registCommand(new AutoFixCommand(this))
|
registCommand(new AutoFixCommand(this))
|
||||||
registCommand(new ReloadProjectsCommand(this))
|
registCommand(new ReloadProjectsCommand(this))
|
||||||
|
@ -69,6 +78,7 @@ export default class TsserverService implements IServiceProvider {
|
||||||
registCommand(new TypeScriptGoToProjectConfigCommand(this))
|
registCommand(new TypeScriptGoToProjectConfigCommand(this))
|
||||||
registCommand(new OrganizeImportsCommand(this))
|
registCommand(new OrganizeImportsCommand(this))
|
||||||
registCommand(new SourceImportsCommand(this))
|
registCommand(new SourceImportsCommand(this))
|
||||||
|
registCommand(new SourceDefinitionCommand(this))
|
||||||
registCommand({
|
registCommand({
|
||||||
id: 'tsserver.restart',
|
id: 'tsserver.restart',
|
||||||
execute: (): void => {
|
execute: (): void => {
|
||||||
|
|
|
@ -42,6 +42,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
||||||
|
|
||||||
private fileConfigurationManager: FileConfigurationManager
|
private fileConfigurationManager: FileConfigurationManager
|
||||||
private pathSeparator: string
|
private pathSeparator: string
|
||||||
|
private readonly emptyAuthority = 'ts-nul-authority'
|
||||||
private tracer: Tracer
|
private tracer: Tracer
|
||||||
private _configuration: TypeScriptServiceConfiguration
|
private _configuration: TypeScriptServiceConfiguration
|
||||||
private versionProvider: TypeScriptVersionProvider
|
private versionProvider: TypeScriptVersionProvider
|
||||||
|
@ -226,7 +227,16 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
||||||
if (this.tscPathVim) currentVersion = this.versionProvider.getVersionFromTscPath(this.tscPathVim)
|
if (this.tscPathVim) currentVersion = this.versionProvider.getVersionFromTscPath(this.tscPathVim)
|
||||||
if (!currentVersion && !ignoreLocalTsserver) currentVersion = this.versionProvider.getLocalVersion()
|
if (!currentVersion && !ignoreLocalTsserver) currentVersion = this.versionProvider.getLocalVersion()
|
||||||
if (!currentVersion || !fs.existsSync(currentVersion.tsServerPath)) {
|
if (!currentVersion || !fs.existsSync(currentVersion.tsServerPath)) {
|
||||||
this.info('Local tsserver not found, using bundled tsserver with coc-tsserver.')
|
if (ignoreLocalTsserver) {
|
||||||
|
this.info(`local tsserver is ignored, try global version`)
|
||||||
|
} else {
|
||||||
|
this.info(`local tsserver is not found, try global version`)
|
||||||
|
}
|
||||||
|
currentVersion = this.versionProvider.globalVersion
|
||||||
|
if (currentVersion) this.info('Local and global tsserver not found, using global tsserver from configuration')
|
||||||
|
}
|
||||||
|
if (!currentVersion || !fs.existsSync(currentVersion.tsServerPath)) {
|
||||||
|
this.info('Local and global tsserver not found, using bundled tsserver with coc-tsserver.')
|
||||||
currentVersion = this.versionProvider.getDefaultVersion()
|
currentVersion = this.versionProvider.getDefaultVersion()
|
||||||
}
|
}
|
||||||
if (!currentVersion || !currentVersion.isValid) {
|
if (!currentVersion || !currentVersion.isValid) {
|
||||||
|
@ -432,7 +442,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
||||||
|
|
||||||
public toResource(filepath: string): string {
|
public toResource(filepath: string): string {
|
||||||
if (filepath.includes('zipfile:')) {
|
if (filepath.includes('zipfile:')) {
|
||||||
return filepath.replace(/.*zipfile:/, 'zipfile://');
|
return filepath.replace(/.*zipfile:/, 'zipfile://')
|
||||||
}
|
}
|
||||||
if (this._apiVersion.gte(API.v213)) {
|
if (this._apiVersion.gte(API.v213)) {
|
||||||
if (filepath.startsWith(this.inMemoryResourcePrefix + 'untitled:')) {
|
if (filepath.startsWith(this.inMemoryResourcePrefix + 'untitled:')) {
|
||||||
|
@ -938,7 +948,7 @@ function getDiagnosticsKind(event: Proto.Event): DiagnosticKind {
|
||||||
case 'suggestionDiag':
|
case 'suggestionDiag':
|
||||||
return DiagnosticKind.Suggestion
|
return DiagnosticKind.Suggestion
|
||||||
}
|
}
|
||||||
throw new Error('Unknown dignostics kind')
|
throw new Error('Unknown diagnostics kind')
|
||||||
}
|
}
|
||||||
|
|
||||||
const fenceCommands = new Set(['change', 'close', 'open'])
|
const fenceCommands = new Set(['change', 'close', 'open'])
|
||||||
|
|
|
@ -8,7 +8,6 @@ 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'
|
||||||
import FileConfigurationManager from './features/fileConfigurationManager'
|
import FileConfigurationManager from './features/fileConfigurationManager'
|
||||||
import WatchBuild from './features/watchBuild'
|
|
||||||
import WorkspaceSymbolProvider from './features/workspaceSymbols'
|
import WorkspaceSymbolProvider from './features/workspaceSymbols'
|
||||||
import LanguageProvider from './languageProvider'
|
import LanguageProvider from './languageProvider'
|
||||||
import * as Proto from './protocol'
|
import * as Proto from './protocol'
|
||||||
|
@ -66,7 +65,6 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
||||||
}, null, this.disposables)
|
}, null, this.disposables)
|
||||||
|
|
||||||
// features
|
// features
|
||||||
this.disposables.push(new WatchBuild(this.client))
|
|
||||||
this.disposables.push(languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.client, allModeIds)))
|
this.disposables.push(languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.client, allModeIds)))
|
||||||
this.client.onConfigDiagnosticsReceived(diag => {
|
this.client.onConfigDiagnosticsReceived(diag => {
|
||||||
let { body } = diag
|
let { body } = diag
|
||||||
|
@ -217,11 +215,11 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
||||||
language.diagnosticsReceived(
|
language.diagnosticsReceived(
|
||||||
kind,
|
kind,
|
||||||
resource,
|
resource,
|
||||||
this.createMarkerDatas(diagnostics))
|
this.createMarkerData(diagnostics))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createMarkerDatas(diagnostics: Proto.Diagnostic[]): (Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[] {
|
private createMarkerData(diagnostics: Proto.Diagnostic[]): (Diagnostic & { reportUnnecessary: any, reportDeprecated: any })[] {
|
||||||
return diagnostics.map(tsDiag => this.tsDiagnosticToLspDiagnostic(tsDiag))
|
return diagnostics.map(tsDiag => this.tsDiagnosticToLspDiagnostic(tsDiag))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ export default class API {
|
||||||
public static readonly v420 = API.fromSimpleString('4.2.0')
|
public static readonly v420 = API.fromSimpleString('4.2.0')
|
||||||
public static readonly v430 = API.fromSimpleString('4.3.0')
|
public static readonly v430 = API.fromSimpleString('4.3.0')
|
||||||
public static readonly v440 = API.fromSimpleString('4.4.0')
|
public static readonly v440 = API.fromSimpleString('4.4.0')
|
||||||
|
public static readonly v470 = API.fromSimpleString('4.7.0')
|
||||||
|
|
||||||
public static fromVersionString(versionString: string): API {
|
public static fromVersionString(versionString: string): API {
|
||||||
let version = semver.valid(versionString)
|
let version = semver.valid(versionString)
|
||||||
|
|
|
@ -102,7 +102,11 @@ export class TypeScriptVersionProvider {
|
||||||
|
|
||||||
public get globalVersion(): TypeScriptVersion | undefined {
|
public get globalVersion(): TypeScriptVersion | undefined {
|
||||||
let { globalTsdk } = this.configuration
|
let { globalTsdk } = this.configuration
|
||||||
if (globalTsdk) return new TypeScriptVersion(workspace.expand(globalTsdk))
|
let folder = workspace.expand(globalTsdk)
|
||||||
|
if (!path.isAbsolute(folder)) {
|
||||||
|
folder = path.join(workspace.root, folder)
|
||||||
|
}
|
||||||
|
if (globalTsdk) return new TypeScriptVersion(folder)
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
129
src/server/watchBuild.ts
Normal file
129
src/server/watchBuild.ts
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import { Disposable, disposeAll, StatusBarItem, TaskOptions, Uri, window, workspace } from 'coc.nvim'
|
||||||
|
import path from 'path'
|
||||||
|
import type TsserverService from '../server'
|
||||||
|
|
||||||
|
const countRegex = /Found\s+(\d+)\s+error/
|
||||||
|
const errorRegex = /^(.+)\((\d+),(\d+)\):\s(\w+)\sTS(\d+):\s*(.+)$/
|
||||||
|
|
||||||
|
export default class WatchProject implements Disposable {
|
||||||
|
private disposables: Disposable[] = []
|
||||||
|
public static readonly id: string = 'tsserver.watchBuild'
|
||||||
|
public static readonly startTexts: string[] = ['Starting compilation in watch mode', 'Starting incremental compilation']
|
||||||
|
private statusItem: StatusBarItem
|
||||||
|
private task: any
|
||||||
|
private options: TaskOptions
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private readonly service: TsserverService
|
||||||
|
) {
|
||||||
|
this.statusItem = window.createStatusBarItem(1, { progress: true })
|
||||||
|
this.disposables.push(this.statusItem)
|
||||||
|
let task = this.task = workspace.createTask('TSC')
|
||||||
|
|
||||||
|
task.onExit(code => {
|
||||||
|
if (code != 0) {
|
||||||
|
window.showMessage(`TSC exit with code ${code}`, 'warning')
|
||||||
|
}
|
||||||
|
this.onStop()
|
||||||
|
})
|
||||||
|
task.onStdout(lines => {
|
||||||
|
for (let line of lines) {
|
||||||
|
this.onLine(line)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
task.onStderr(lines => {
|
||||||
|
window.showMessage(`TSC error: ` + lines.join('\n'), 'error')
|
||||||
|
})
|
||||||
|
this.disposables.push(Disposable.create(() => {
|
||||||
|
task.dispose()
|
||||||
|
}))
|
||||||
|
this.check().catch(_e => {
|
||||||
|
// noop
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async execute(): Promise<void> {
|
||||||
|
let opts = this.options = await this.getOptions()
|
||||||
|
await this.start(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async check(): Promise<void> {
|
||||||
|
let running = await this.task.running
|
||||||
|
if (running) {
|
||||||
|
this.options = await this.getOptions()
|
||||||
|
this.statusItem.isProgress = false
|
||||||
|
this.statusItem.text = '?'
|
||||||
|
this.statusItem.show()
|
||||||
|
} else {
|
||||||
|
this.onStop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async start(options: TaskOptions): Promise<void> {
|
||||||
|
await this.task.start(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
private onStop(): void {
|
||||||
|
this.statusItem.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
private onStart(): void {
|
||||||
|
this.statusItem.text = 'compiling'
|
||||||
|
this.statusItem.isProgress = true
|
||||||
|
this.statusItem.show()
|
||||||
|
workspace.nvim.call('setqflist', [[]], true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private onLine(line: string): void {
|
||||||
|
if (countRegex.test(line)) {
|
||||||
|
let ms = line.match(countRegex)
|
||||||
|
this.statusItem.text = ms[1] == '0' ? '✓' : '✗'
|
||||||
|
this.statusItem.isProgress = false
|
||||||
|
} else if (WatchProject.startTexts.findIndex(s => line.indexOf(s) !== -1) != -1) {
|
||||||
|
this.onStart()
|
||||||
|
} else {
|
||||||
|
let ms = line.match(errorRegex)
|
||||||
|
if (!ms) return
|
||||||
|
let fullpath = path.join(this.options.cwd, ms[1])
|
||||||
|
let uri = Uri.file(fullpath).toString()
|
||||||
|
let doc = workspace.getDocument(uri)
|
||||||
|
let bufnr = doc ? doc.bufnr : null
|
||||||
|
let item = {
|
||||||
|
filename: fullpath,
|
||||||
|
lnum: Number(ms[2]),
|
||||||
|
col: Number(ms[3]),
|
||||||
|
text: `[tsc ${ms[5]}] ${ms[6]}`,
|
||||||
|
type: /error/i.test(ms[4]) ? 'E' : 'W'
|
||||||
|
} as any
|
||||||
|
if (bufnr) item.bufnr = bufnr
|
||||||
|
workspace.nvim.call('setqflist', [[item], 'a'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOptions(): Promise<TaskOptions> {
|
||||||
|
let client = await this.service.getClientHost()
|
||||||
|
let { tscPath } = client.serviceClient
|
||||||
|
if (!tscPath) {
|
||||||
|
window.showMessage(`Local & global tsc not found`, 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const tsconfigPath = workspace.getConfiguration('tsserver').get<string>('tsconfigPath', 'tsconfig.json')
|
||||||
|
let find = await workspace.findUp([tsconfigPath])
|
||||||
|
if (!find) {
|
||||||
|
window.showMessage(`${tsconfigPath} not found!`, 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let root = path.dirname(find)
|
||||||
|
return {
|
||||||
|
cmd: tscPath,
|
||||||
|
args: ['-p', tsconfigPath, '--watch', 'true', '--pretty', 'false'],
|
||||||
|
cwd: root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
disposeAll(this.disposables)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
export const file = 'file'
|
export const file = 'file'
|
||||||
export const untitled = 'untitled'
|
export const untitled = 'untitled'
|
||||||
export const git = 'git'
|
export const git = 'git'
|
||||||
|
export const fugitive = 'fugitive'
|
||||||
/** Live share scheme */
|
/** Live share scheme */
|
||||||
export const vsls = 'vsls'
|
export const vsls = 'vsls'
|
||||||
export const walkThroughSnippet = 'walkThroughSnippet'
|
export const walkThroughSnippet = 'walkThroughSnippet'
|
||||||
|
@ -21,5 +22,6 @@ export const semanticSupportedSchemes = [
|
||||||
*/
|
*/
|
||||||
export const disabledSchemes = new Set([
|
export const disabledSchemes = new Set([
|
||||||
git,
|
git,
|
||||||
|
fugitive,
|
||||||
vsls
|
vsls
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in a new issue