V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chemzqm
V2EX  ›  Vim

文件自动执行插件

  •  2
     
  •   chemzqm · 2021-03-28 20:52:12 +08:00 · 2063 次点击
    这是一个创建于 1346 天前的主题,其中的信息可能已经有所发展或是发生改变。

    coc.nvim 插件,放在 .vim/coc-extensions 目录下即可使用。

    右侧打开窗口显示执行代码的结果,保存后自动重新执行。

    • 使用异步 job 执行
    • 支持 vim 和 neovim
    • 错误高亮提示
    • 状态栏支持

    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)
      )
    }
    

    仅供参考

    3 条回复    2021-03-29 17:20:37 +08:00
    IgniteWhite
        1
    IgniteWhite  
       2021-03-28 21:26:59 +08:00
    给大佬点赞
    chemzqm
        2
    chemzqm  
    OP
       2021-03-29 00:07:08 +08:00
    多了一行 `if (!taskMap.has(doc.bufnr)) return`
    yuuko
        3
    yuuko  
       2021-03-29 17:20:37 +08:00
    我这里

    nvim.pauseNotification()
    nvim.command(`belowright vs ${bufname}`)

    要改成

    await nvim.command(`belowright vs ${bufname}`)
    nvim.pauseNotification()
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5379 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:47 · PVG 15:47 · LAX 23:47 · JFK 02:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.