coc.nvim 插件,放在 .vim/coc-extensions
目录下即可使用。
右侧打开窗口显示执行代码的结果,保存后自动重新执行。
stderr 和 stdout 出现顺序可能会错,暂时不支持 ansi 解析,可通过使用 strip-ansi 模块去掉。
const {Uri, commands, workspace, window, Mutex} = require('coc.nvim')
const path = require('path')
const programMap = {
javascript: 'node',
typescript: 'ts-node',
python: 'python'
}
let global_id = 0
exports.activate = async context => {
const {nvim, cwd} = workspace
// bufnr => Task
const taskMap = new Map()
let statusItem = window.createStatusBarItem(0, {progress: true})
context.subscriptions.push(statusItem)
const executeFile = async (doc, create) => {
let uri = doc.uri
let relPath = path.relative(cwd, Uri.parse(uri).fsPath)
let bufname = `__coc_execute_${doc.bufnr}__`
let task = taskMap.get(doc.bufnr)
if (task) {
task.dispose()
taskMap.delete(doc.bufnr)
}
statusItem.hide()
let winnr = await nvim.call('bufwinnr', [bufname])
if (winnr == -1 && !create) return
if (winnr == -1) {
nvim.pauseNotification()
nvim.command(`belowright vs ${bufname}`)
nvim.command(`setl buftype=nofile`)
nvim.command(`setl conceallevel=0`)
nvim.command(`setl norelativenumber`)
await nvim.resumeNotification()
winnr = await nvim.call('winnr', [])
await nvim.command('wincmd p')
} else {
// clear buffer
await nvim.command(`silent call deletebufline('${bufname}', 1, '$')`)
}
let bufId = await nvim.call('bufnr', [bufname])
let buf = nvim.createBuffer(bufId)
let t = workspace.createTask(`execute-${global_id}`)
global_id = global_id + 1
let cmd = programMap[doc.filetype]
// start with options
let succeed = await t.start({cwd, cmd, args: [relPath]})
if (!succeed) {
window.showErrorMessage(`Command failed to start: ${cmd} ${relPath}`)
return
}
statusItem.text = `${cmd} ${relPath}`
statusItem.show()
taskMap.set(doc.bufnr, t)
t.onExit(code => {
statusItem.hide()
taskMap.delete(doc.bufnr)
if (code != 0) {
window.showErrorMessage(`${cmd} exit with code: ${code}`)
}
})
let empty = true
let appendLines = async lines => {
if (empty) {
empty = false
await buf.setLines(lines, {start: 0, end: -1, strictIndexing: false})
} else {
await nvim.call('appendbufline', [buf.id, '$', lines])
}
}
let mutex = new Mutex()
t.onStderr(async lines => {
let replace = empty
let release = await mutex.acquire()
try {
let len = await buf.length
await appendLines(lines)
await buf.highlightRanges('coc-execute', 'WarningMsg', [{
start: {line: (replace ? len - 1 : len), character: 0},
end: {line: len + lines.length, character: 0}
}])
if (workspace.isVim) nvim.command('redraw', true)
} catch (e) {
window.showErrorMessage(e.message)
}
release()
})
t.onStdout(async lines => {
let release = await mutex.acquire()
try {
await appendLines(lines)
if (workspace.isVim) nvim.command('redraw', true)
} catch (e) {
window.showErrorMessage(e.message)
}
release()
})
}
const execute = async () => {
let doc = await workspace.document
let program = programMap[doc.filetype]
if (!program) {
window.showErrorMessage(`filetype not supported`)
return
}
await executeFile(doc, true)
}
context.subscriptions.push(workspace.onDidSaveTextDocument(async e => {
let doc = workspace.getDocument(e.uri)
if (!taskMap.has(doc.bufnr)) return
await executeFile(doc, false)
}))
context.subscriptions.push({
dispose: () => {
for (let task of taskMap.values()) {
task.dispose()
}
}
})
context.subscriptions.push(
commands.registerCommand('execute.currentFile', execute)
)
}
仅供参考
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.