From 156fe981c77725a90eb1591baba8304f3323a3f3 Mon Sep 17 00:00:00 2001 From: Qiming Zhao Date: Mon, 15 Aug 2022 16:28:28 +0800 Subject: [PATCH] fix watchBuild not work before service start Closes #390 --- src/server/features/watchBuild.ts | 126 --------------------- src/server/index.ts | 10 ++ src/server/typescriptServiceClientHost.ts | 2 - src/server/watchBuild.ts | 129 ++++++++++++++++++++++ 4 files changed, 139 insertions(+), 128 deletions(-) create mode 100644 src/server/watchBuild.ts diff --git a/src/server/features/watchBuild.ts b/src/server/features/watchBuild.ts index 7ea12c3..e69de29 100644 --- a/src/server/features/watchBuild.ts +++ b/src/server/features/watchBuild.ts @@ -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 { - 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 { - 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 { - let { tscPath } = this.client - if (!tscPath) { - window.showMessage(`Local & global tsc not found`, 'error') - return - } - - const tsconfigPath = workspace.getConfiguration('tsserver').get('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) - } -} diff --git a/src/server/index.ts b/src/server/index.ts index 108bf75..032c0c2 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -3,6 +3,7 @@ import { Disposable, DocumentSelector, Emitter, Event } from 'vscode-languageser import { PluginManager } from '../utils/plugins' 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' @@ -43,6 +44,7 @@ export default class TsserverService implements IServiceProvider { this.selector = this.descriptions.reduce((arr, c) => { return arr.concat(c.languageIds) }, []) + console.log(111) this.registCommands() } @@ -61,6 +63,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)) diff --git a/src/server/typescriptServiceClientHost.ts b/src/server/typescriptServiceClientHost.ts index 43e591f..e081079 100644 --- a/src/server/typescriptServiceClientHost.ts +++ b/src/server/typescriptServiceClientHost.ts @@ -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 diff --git a/src/server/watchBuild.ts b/src/server/watchBuild.ts new file mode 100644 index 0000000..fdb4a64 --- /dev/null +++ b/src/server/watchBuild.ts @@ -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 { + let opts = this.options = await this.getOptions() + await this.start(opts) + } + + private async check(): Promise { + 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 { + 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 { + 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('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) + } +}