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
|
||||
|
||||
- Fix a fold issue #380
|
||||
|
|
18
package.json
18
package.json
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"name": "coc-tsserver",
|
||||
"version": "1.10.5",
|
||||
"version": "1.11.7",
|
||||
"description": "tsserver extension for coc.nvim",
|
||||
"main": "lib/index.js",
|
||||
"publisher": "chemzqm",
|
||||
"engines": {
|
||||
"coc": "^0.0.80"
|
||||
"coc": "^0.0.82"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -30,11 +30,10 @@
|
|||
"onLanguage:jsx-tags",
|
||||
"onLanguage:jsonc",
|
||||
"onCommand:_typescript.configurePlugin",
|
||||
"onCommand:typescript.reloadProjects",
|
||||
"onCommand:javascript.reloadProjects",
|
||||
"onCommand:javascript.goToProjectConfig",
|
||||
"onCommand:typescript.goToProjectConfig",
|
||||
"onCommand:typescript.openTsServerLog",
|
||||
"onCommand:tsserver.reloadProjects",
|
||||
"onCommand:tsserver.goToProjectConfig",
|
||||
"onCommand:tsserver.openTsServerLog",
|
||||
"onCommand:tsserver.goToSourceDefinition",
|
||||
"onCommand:tsserver.watchBuild"
|
||||
],
|
||||
"contributes": {
|
||||
|
@ -94,6 +93,11 @@
|
|||
"category": "TSServer",
|
||||
"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.",
|
||||
"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 {
|
||||
let { id, execute } = cmd
|
||||
return commands.registerCommand(id as string, execute, cmd)
|
||||
|
|
|
@ -30,21 +30,21 @@ class CloseOperation {
|
|||
readonly type = BufferOperationType.Close;
|
||||
constructor(
|
||||
public readonly args: string
|
||||
) { }
|
||||
) {}
|
||||
}
|
||||
|
||||
class OpenOperation {
|
||||
readonly type = BufferOperationType.Open;
|
||||
constructor(
|
||||
public readonly args: Proto.OpenRequestArgs
|
||||
) { }
|
||||
) {}
|
||||
}
|
||||
|
||||
class ChangeOperation {
|
||||
readonly type = BufferOperationType.Change;
|
||||
constructor(
|
||||
public readonly args: Proto.FileCodeEdits
|
||||
) { }
|
||||
) {}
|
||||
}
|
||||
|
||||
type BufferOperation = CloseOperation | OpenOperation | ChangeOperation
|
||||
|
@ -59,7 +59,7 @@ class SyncedBuffer {
|
|||
public readonly filepath: string,
|
||||
private readonly client: ITypeScriptServiceClient,
|
||||
private readonly synchronizer: BufferSynchronizer,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
public open(): void {
|
||||
const args: Proto.OpenRequestArgs = {
|
||||
|
|
|
@ -225,8 +225,6 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
let { uri, position, source, name, data } = item.data
|
||||
const filepath = this.client.toPath(uri)
|
||||
if (!filepath) return undefined
|
||||
let document = workspace.getDocument(uri)
|
||||
if (!document) return undefined
|
||||
const args: Proto.CompletionDetailsRequestArgs = {
|
||||
...typeConverters.Position.toFileLocationRequestArgs(
|
||||
filepath,
|
||||
|
@ -259,11 +257,10 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
item.additionalTextEdits = additionalTextEdits
|
||||
if (detail && item.insertTextFormat == InsertTextFormat.Snippet) {
|
||||
const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, position, token)
|
||||
if (shouldCompleteFunction && !item.insertText) {
|
||||
if (shouldCompleteFunction) {
|
||||
this.createSnippetOfFunctionCall(item, detail)
|
||||
}
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
|
@ -380,7 +377,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
|
|||
let { displayParts } = detail
|
||||
const parameterListParts = getParameterListParts(displayParts)
|
||||
const snippet = new SnippetString()
|
||||
snippet.appendText(`${item.insertText || item.label}(`)
|
||||
snippet.appendText(`${item.insertText ?? item.label}(`)
|
||||
appendJoinedPlaceholders(snippet, parameterListParts.parts, ', ')
|
||||
if (parameterListParts.hasOptionalParameters) {
|
||||
snippet.appendTabstop()
|
||||
|
|
|
@ -2,7 +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 { TextDocument } from 'coc.nvim'
|
||||
import { LocationLink, TextDocument } from 'coc.nvim'
|
||||
import { DefinitionProvider, CancellationToken, Definition, Location, Position, DefinitionLink, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim'
|
||||
import * as Proto from '../protocol'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
|
@ -17,7 +17,7 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
|||
document: TextDocument,
|
||||
position: Position,
|
||||
token: CancellationToken
|
||||
): Promise<Location[] | undefined> {
|
||||
): Promise<Location[] | LocationLink[] | undefined> {
|
||||
const filepath = this.client.toPath(document.uri)
|
||||
if (!filepath) {
|
||||
return undefined
|
||||
|
@ -29,12 +29,21 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
|||
)
|
||||
try {
|
||||
const response = await this.client.execute(definitionType, args, token)
|
||||
const locations: Proto.FileSpan[] = (response.type == 'response' && response.body) || []
|
||||
return locations.map(location =>
|
||||
typeConverters.Location.fromTextSpan(
|
||||
this.client.toResource(location.file),
|
||||
location
|
||||
)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return undefined
|
||||
}
|
||||
const locations: Proto.FileSpanWithContext[] = (response.type == 'response' && response.body) || []
|
||||
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 {
|
||||
return []
|
||||
|
@ -84,14 +93,14 @@ export default class TypeScriptDefinitionProvider implements DefinitionProvider,
|
|||
public provideTypeDefinition(
|
||||
document: TextDocument,
|
||||
position: Position,
|
||||
token: CancellationToken): Promise<Definition> {
|
||||
token: CancellationToken): Promise<Definition | DefinitionLink[]> {
|
||||
return this.getSymbolLocations('typeDefinition', document, position, token)
|
||||
}
|
||||
|
||||
public provideImplementation(
|
||||
document: TextDocument,
|
||||
position: Position,
|
||||
token: CancellationToken): Promise<Definition> {
|
||||
token: CancellationToken): Promise<Definition | DefinitionLink[]> {
|
||||
return this.getSymbolLocations('implementation', document, position, token)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +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 { DiagnosticCollection, languages, workspace } from 'coc.nvim'
|
||||
import { DiagnosticCollection, Uri, languages, workspace } from 'coc.nvim'
|
||||
import { Diagnostic, DiagnosticTag } from 'vscode-languageserver-protocol'
|
||||
import { ResourceMap } from './resourceMap'
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
|||
output: DocumentSymbol[],
|
||||
item: Proto.NavigationTree,
|
||||
): boolean {
|
||||
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)
|
||||
let shouldInclude = TypeScriptDocumentSymbolProvider.shouldIncludeEntry(item)
|
||||
const children = new Set(item.childItems || [])
|
||||
for (const span of item.spans) {
|
||||
const range = typeConverters.Range.fromTextSpan(span)
|
||||
|
@ -129,7 +129,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP
|
|||
return symbolInfo
|
||||
}
|
||||
|
||||
private static shouldInclueEntry(
|
||||
private static shouldIncludeEntry(
|
||||
item: Proto.NavigationTree | Proto.NavigationBarItem
|
||||
): boolean {
|
||||
if (item.kind === PConst.Kind.alias) {
|
||||
|
|
|
@ -61,11 +61,11 @@ export default class UpdateImportsOnFileRenameHandler {
|
|||
await workspace.nvim.command(`silent ${oldDocument.bufnr}bwipeout!`)
|
||||
}
|
||||
let document = workspace.getDocument(newUri)
|
||||
if (document) {
|
||||
await workspace.nvim.command(`silent ${document.bufnr}bwipeout!`)
|
||||
await wait(30)
|
||||
if (!document) {
|
||||
document = await workspace.loadFile(newUri)
|
||||
} else {
|
||||
workspace.nvim.command('checktime', true)
|
||||
}
|
||||
document = await workspace.loadFile(newUri)
|
||||
if (!document) return
|
||||
await wait(50)
|
||||
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 { Disposable, DocumentSelector, Emitter, Event } from 'vscode-languageserver-protocol'
|
||||
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 WatchProject from './watchBuild'
|
||||
import TypeScriptServiceClientHost from './typescriptServiceClientHost'
|
||||
import { LanguageDescription, standardLanguageDescriptions } from './utils/languageDescription'
|
||||
|
||||
|
@ -61,6 +62,14 @@ export default class TsserverService implements IServiceProvider {
|
|||
let { id, 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 AutoFixCommand(this))
|
||||
registCommand(new ReloadProjectsCommand(this))
|
||||
|
@ -69,6 +78,7 @@ export default class TsserverService implements IServiceProvider {
|
|||
registCommand(new TypeScriptGoToProjectConfigCommand(this))
|
||||
registCommand(new OrganizeImportsCommand(this))
|
||||
registCommand(new SourceImportsCommand(this))
|
||||
registCommand(new SourceDefinitionCommand(this))
|
||||
registCommand({
|
||||
id: 'tsserver.restart',
|
||||
execute: (): void => {
|
||||
|
|
|
@ -42,6 +42,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
|||
|
||||
private fileConfigurationManager: FileConfigurationManager
|
||||
private pathSeparator: string
|
||||
private readonly emptyAuthority = 'ts-nul-authority'
|
||||
private tracer: Tracer
|
||||
private _configuration: TypeScriptServiceConfiguration
|
||||
private versionProvider: TypeScriptVersionProvider
|
||||
|
@ -226,7 +227,16 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
|||
if (this.tscPathVim) currentVersion = this.versionProvider.getVersionFromTscPath(this.tscPathVim)
|
||||
if (!currentVersion && !ignoreLocalTsserver) currentVersion = this.versionProvider.getLocalVersion()
|
||||
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()
|
||||
}
|
||||
if (!currentVersion || !currentVersion.isValid) {
|
||||
|
@ -432,7 +442,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient
|
|||
|
||||
public toResource(filepath: string): string {
|
||||
if (filepath.includes('zipfile:')) {
|
||||
return filepath.replace(/.*zipfile:/, 'zipfile://');
|
||||
return filepath.replace(/.*zipfile:/, 'zipfile://')
|
||||
}
|
||||
if (this._apiVersion.gte(API.v213)) {
|
||||
if (filepath.startsWith(this.inMemoryResourcePrefix + 'untitled:')) {
|
||||
|
@ -938,7 +948,7 @@ function getDiagnosticsKind(event: Proto.Event): DiagnosticKind {
|
|||
case 'suggestionDiag':
|
||||
return DiagnosticKind.Suggestion
|
||||
}
|
||||
throw new Error('Unknown dignostics kind')
|
||||
throw new Error('Unknown diagnostics kind')
|
||||
}
|
||||
|
||||
const fenceCommands = new Set(['change', 'close', 'open'])
|
||||
|
|
|
@ -8,7 +8,6 @@ import { flatten } from '../utils/arrays'
|
|||
import { PluginManager } from '../utils/plugins'
|
||||
import { DiagnosticKind } from './features/diagnostics'
|
||||
import FileConfigurationManager from './features/fileConfigurationManager'
|
||||
import WatchBuild from './features/watchBuild'
|
||||
import WorkspaceSymbolProvider from './features/workspaceSymbols'
|
||||
import LanguageProvider from './languageProvider'
|
||||
import * as Proto from './protocol'
|
||||
|
@ -66,7 +65,6 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
}, null, this.disposables)
|
||||
|
||||
// features
|
||||
this.disposables.push(new WatchBuild(this.client))
|
||||
this.disposables.push(languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(this.client, allModeIds)))
|
||||
this.client.onConfigDiagnosticsReceived(diag => {
|
||||
let { body } = diag
|
||||
|
@ -217,11 +215,11 @@ export default class TypeScriptServiceClientHost implements Disposable {
|
|||
language.diagnosticsReceived(
|
||||
kind,
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ export default class API {
|
|||
public static readonly v420 = API.fromSimpleString('4.2.0')
|
||||
public static readonly v430 = API.fromSimpleString('4.3.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 {
|
||||
let version = semver.valid(versionString)
|
||||
|
|
|
@ -102,7 +102,11 @@ export class TypeScriptVersionProvider {
|
|||
|
||||
public get globalVersion(): TypeScriptVersion | undefined {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
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 untitled = 'untitled'
|
||||
export const git = 'git'
|
||||
export const fugitive = 'fugitive'
|
||||
/** Live share scheme */
|
||||
export const vsls = 'vsls'
|
||||
export const walkThroughSnippet = 'walkThroughSnippet'
|
||||
|
@ -21,5 +22,6 @@ export const semanticSupportedSchemes = [
|
|||
*/
|
||||
export const disabledSchemes = new Set([
|
||||
git,
|
||||
fugitive,
|
||||
vsls
|
||||
])
|
||||
|
|
Loading…
Reference in a new issue