class: center, middle # Neovim remote JS --- class: center, middle # Neovim + Node = ♥ --- class: center, middle # Neovim ⇄ Node -- ## Communication? -- ## Socket! --- class: center, middle # MsgPack http://msgpack.org « It's like JSON, but fast and small » --- class: center, middle # MsgPack5 JS https://github.com/mcollina/msgpack5 - decode - encode --- class: middle #MsgPack5-RPC JS https://github.com/tarruda/node-msgpack5rpc ```javascript const Session = require('msgpack5rpc') const session = new Session() session.attach(process.stdout, process.stdin) session.on('request', (method, args, resp) => { resp.send('response!') console.log('received request') session.request('remote-method', ['arg1', 'arg2'], (err, res) => { console.log('received response') }) }) session.on('notification', (method, args) => { console.log('received notification') session.notify('remote-event', ['arg1', 'arg2']) }) ``` --- # Node-Client ### attach https://github.com/neovim/node-client ```javascript const cp = require('child_process') const attach = require('neovim-client') const nvimProc = cp.spawn('nvim', ['-u', 'NONE', '-N', '--embed'], {}) attach(nvimProc.stdin, nvimProc.stdout, (err, nvim) => { // … }) ``` --- # Node-Client ### request and notif events ```javascript attach(nvimProc.stdin, nvimProc.stdout, (err, nvim) => { nvim.on('request', (method, args, resp) => { // handle msgpack-rpc request }) nvim.on('notification', (method, args) => { // handle msgpack-rpc notification }) }) ``` --- # Node-Client ### command ```javascript attach(nvimProc.stdin, nvimProc.stdout, (err, nvim) => { nvim.command('vsp', (err, res) => { nvim.getWindows((err, windows) => { console.log(windows.length) // 2 console.log(windows[0] instanceof nvim.Window) // true nvim.setCurrentWindow(windows[1], (err, res) => { nvim.getCurrentWindow((err, win) => { console.log(win.equals(windows[1])) // true nvim.quit() nvim.on('disconnect', () => { console.log("Nvim exited!") }) }) }) }) }) }) ``` --- class: center, middle # Callback hell ![hell](images/hell.gif) --- # Node-Client ```javascript let attach = require('neovim-client/promise') let nvim attach(nvimProc.stdin, nvimProc.stdout) .then((_nvim) => { nvim = _nvim return nvim.command('vsp') }) .then((res) => nvim.getWindows()) .then((windows) => { console.log(windows.length) // 2 console.log(windows[0] instanceof nvim.Window) // true // … }) ``` --- # API ### created at runtime ```javascript session.request('vim_get_api_info', [], (err, res) => { // read metadata and dynamically build // Tabpage, Window and Buffer // and their associated methods }) ``` ### :h api --- class: center, middle # NyaoVim https://github.com/rhysd/NyaoVim **react** component in an **electron** using **node-client** --- class: center, middle # Mmm…OK… ### What about going back to Neovim? ![confused](images/confused.gif) --- class: center, middle # Plugins! --- # Node-Host https://github.com/neovim/node-host - Install (with vim-plug): ``` if has('nvim') Plug 'neovim/node-host' endif ``` - Then put your JS files in: `$XDG_CONFIG_HOME/nvim/rplugin/node` - Finally, register them with: `:UpdateRemotePlugins` --- # Node-Host ### command ```javascript plugin.commandSync('DanseLeMia', { range: '', nargs: '*', }, (nvim, args, range, cb) => { nvim.setCurrentLine('Chemise ouverte, chaîne en or qui brille', cb) }) ``` --- # Node-Host ### autocmd ```javascript plugin.autocmdSync('BufEnter', { pattern: '*.xml', eval: 'expand("
")' }, (nvim, filename, cb) => { nvim.setCurrentLine('YOLO', cb) }) ``` --- # Node-Host ### function ```javascript plugin.function('SplitInThirteen', (nvim, args) => { nvim.setCurrentLine('1 1 1 1 1 1 1 1 1 1 1 1 1') }) ``` --- class: center, middle # magic ![magic](images/magic.gif) --- # Node-Host - it scans the `rplugin/node` directory - it rewraps the found CommonJS modules in a sandbox - it injects a new `plugin` *global* - it uses Node-Client under the hood all of that in JS of course! --- class: center, middle # Recap - MsgPack5 - MsgPack5-RPC - Node-Client - Node-Host --- # Example ```javascript const five = require('five') Object.keys(five).forEach(k => { plugin.commandSync(`Five${k}`, { range: '', nargs: '*' }, (nvim, args, range, cb) => { try { nvim.setCurrentLine(five[k](), cb) } catch (ex) { cb(ex) } }) }) ``` --- class: center, middle # :q