use vim to run watchBuild

This commit is contained in:
chemzqm 2019-04-12 16:33:19 +08:00
parent 845812f3b3
commit 898bf1585f
3 changed files with 104 additions and 90 deletions

View file

@ -5,7 +5,7 @@
"main": "lib/index.js",
"publisher": "chemzqm",
"engines": {
"coc": "^0.0.35"
"coc": "^0.0.65"
},
"keywords": [
"coc.nvim",
@ -446,7 +446,7 @@
"@types/fast-diff": "^1.2.0",
"@types/find-up": "^2.1.1",
"@types/node": "^11.13.0",
"coc.nvim": "0.0.62",
"coc.nvim": "^0.0.65",
"rimraf": "^2.6.3",
"tslint": "^5.15.0"
},

View file

@ -1,5 +1,5 @@
import { ChildProcess, spawn } from 'child_process'
import { disposeAll, StatusBarItem, workspace } from 'coc.nvim'
import { disposeAll, StatusBarItem, workspace, TaskOptions } from 'coc.nvim'
import { Command, CommandManager } from 'coc.nvim/lib/commands'
import findUp from 'find-up'
import fs from 'fs'
@ -9,6 +9,7 @@ import { Disposable, Location } from 'vscode-languageserver-protocol'
import Uri from 'vscode-uri'
import which from 'which'
import { resolveRoot } from '../utils/fs'
import Task from 'coc.nvim/lib/model/task'
const TSC = './node_modules/.bin/tsc'
const countRegex = /Found\s+(\d+)\s+error/
@ -27,20 +28,64 @@ enum TscStatus {
ERROR,
}
class WatchCommand implements Command {
public readonly id: string = 'tsserver.watchBuild'
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 isRunning = false
private process: ChildProcess
private task: Task
private options: TaskOptions
constructor() {
public constructor(
commandManager: CommandManager
) {
this.statusItem = workspace.createStatusBarItem(1, { progress: true })
let task = this.task = workspace.createTask('TSC')
this.options = this.getOptions()
this.disposables.push(commandManager.registerCommand(WatchProject.id, async () => {
await this.start(this.options)
}))
task.onExit(code => {
if (code != 0) {
workspace.showMessage(`TSC exit with code ${code}`, 'warning')
}
this.onStop()
})
task.onStdout(lines => {
for (let line of lines) {
this.onLine(line)
}
})
task.onStderr(lines => {
workspace.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.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 {
let { nvim } = workspace
this.isRunning = false
nvim.setVar('Tsc_running', 0, true)
this.statusItem.hide()
}
@ -51,60 +96,43 @@ class WatchCommand implements Command {
workspace.nvim.call('setqflist', [[], 'r'], true)
}
private async start(cmd: string, args: string[], cwd: string): Promise<void> {
if (this.isRunning) {
this.process.kill()
await wait(200)
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'])
}
this.isRunning = true
workspace.nvim.setVar('Tsc_running', 1, true)
this.process = spawn(cmd, args, { cwd })
this.process.on('error', e => {
workspace.showMessage(e.message, 'error')
})
const rl = readline.createInterface(this.process.stdout)
this.process.on('exit', () => {
this.onStop()
rl.close()
})
this.process.stderr.on('data', chunk => {
workspace.showMessage(chunk.toString('utf8'), 'error')
})
const startTexts = ['Starting compilation in watch mode', 'Starting incremental compilation']
rl.on('line', line => {
if (countRegex.test(line)) {
let ms = line.match(countRegex)
this.statusItem.text = ms[1] == '0' ? '✓' : '✗'
this.statusItem.isProgress = false
} else if (startTexts.findIndex(s => line.indexOf(s) !== -1) != -1) {
this.onStart()
} else {
let ms = line.match(errorRegex)
if (!ms) return
let fullpath = path.join(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 execute(): Promise<void> {
public getOptions(): TaskOptions {
let docs = workspace.documents
let idx = docs.findIndex(doc => doc.uri.indexOf(TSC) !== -1)
if (idx !== -1) return
let document = await workspace.document
let fsPath = Uri.parse(document.uri).fsPath
let cwd = path.dirname(fsPath)
let doc = workspace.getDocument(workspace.bufnr)
let cwd: string
if (doc && doc.schema == 'file') {
cwd = path.dirname(Uri.parse(doc.uri).fsPath)
} else {
cwd = workspace.cwd
}
let res = findUp.sync(['node_modules'], { cwd })
let cmd: string
let root: string
@ -130,26 +158,11 @@ class WatchCommand implements Command {
return
}
let configPath = path.relative(root, path.join(configRoot, 'tsconfig.json'))
this.start(cmd, ['-p', configPath, '--watch', 'true', '--pretty', 'false'], root)
}
}
export default class WatchProject implements Disposable {
private disposables: Disposable[] = []
public constructor(
commandManager: CommandManager
) {
let cmd = new WatchCommand()
commandManager.register(cmd)
let { nvim } = workspace
nvim.getVar('Tsc_running').then(running => {
if (running) {
cmd.execute().catch(e => {
workspace.showMessage('TSC:' + e.message, 'error')
})
}
})
return {
cmd,
args: ['-p', configPath, '--watch', 'true', '--pretty', 'false'],
cwd: root
}
}
public dispose(): void {

View file

@ -85,6 +85,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
binary-search@1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.5.tgz#479ad009589e0273cf54e5d74ab1546c489078ce"
integrity sha512-RHFP0AdU6KAB0CCZsRMU2CJTk2EpL8GLURT+4gilpjr1f/7M91FgUMnXuQLmf3OKLet34gjuNFwO7e4agdX5pw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -125,22 +130,23 @@ chalk@^2.3.0:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
coc.nvim@0.0.62:
version "0.0.62"
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.62.tgz#49557e9bc95ce3627df66914b787be982374d986"
integrity sha512-DlaLXlRELXwu39DkXvZ+kfZdKImLiq9mRkFOnzzwMD/xftN/Ytj2UlEndMr9gl2hmM5Z6VzZC1wvn6JYU9Zj7A==
coc.nvim@^0.0.65:
version "0.0.65"
resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.65.tgz#6c802a30beb63b02a71f2bdd07db50542c77a46f"
integrity sha512-CPvc4g0PJMlo/0A8kXvIAOWGqaDJKOpe6OvMhY4SJhWZRL03AExjByiLLGcBD+tOONBJNyDpPBnfMUqkOD0Vmg==
dependencies:
"@chemzqm/neovim" "4.4.1"
binary-search "1.3.5"
debounce "^1.2.0"
fast-diff "^1.2.0"
fb-watchman "^2.0.0"
find-up "^3.0.0"
glob "^7.1.3"
isuri "^2.0.3"
jsonc-parser "^2.0.3"
log4js "^4.0.2"
jsonc-parser "^2.1.0"
log4js "^4.1.0"
minimatch "^3.0.4"
semver "^5.6.0"
semver "^6.0.0"
tslib "^1.9.3"
uuid "^3.3.2"
vscode-languageserver-protocol "^3.15.0-next.1"
@ -339,7 +345,7 @@ js-yaml@^3.13.0:
argparse "^1.0.7"
esprima "^4.0.0"
jsonc-parser@^2.0.3:
jsonc-parser@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.0.tgz#eb0d0c7a3c33048524ce3574c57c7278fb2f1bf3"
integrity sha512-n9GrT8rrr2fhvBbANa1g+xFmgGK5X91KFeDwlKQ3+SJfmH5+tKv/M/kahx/TXOMflfWHKGKqKyfHQaLKTNzJ6w==
@ -364,7 +370,7 @@ lodash@^4.17.10, lodash@^4.17.11:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
log4js@^4.0.2:
log4js@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-4.1.0.tgz#57983c6a443546a8c8607e9cb045d2a117c27644"
integrity sha512-eDa+zZPeVEeK6QGJAePyXM6pg4P3n3TO5rX9iZMVY48JshsTyLJZLIL5HipI1kQ2qLsSyOpUqNND/C5H4WhhiA==
@ -484,11 +490,6 @@ semver@^5.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==
semver@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
semver@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65"