-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic remote module support (#107)
See: neovim/neovim#27949 This includes a helper method for defining remote modules as well as an acceptance spec to demonstrate their usage. I chose to implement a new DSL class just for remote modules because the existing plugin DSL is far too complicated for simple RPC handling. As remote plugins are phased out, I expect to phase out and eventually deprecate the existing plugin DSL.
- Loading branch information
Showing
7 changed files
with
139 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
require "neovim/client" | ||
require "neovim/event_loop" | ||
require "neovim/logging" | ||
require "neovim/remote_module/dsl" | ||
require "neovim/session" | ||
|
||
module Neovim | ||
class RemoteModule | ||
include Logging | ||
|
||
def self.from_config_block(&block) | ||
new(DSL::new(&block).handlers) | ||
end | ||
|
||
def initialize(handlers) | ||
@handlers = handlers | ||
end | ||
|
||
def start | ||
event_loop = EventLoop.stdio | ||
session = Session.new(event_loop) | ||
client = nil | ||
|
||
session.run do |message| | ||
case message | ||
when Message::Request | ||
begin | ||
client ||= Client.from_event_loop(event_loop, session) | ||
args = message.arguments.flatten(1) | ||
|
||
@handlers[message.method_name].call(client, *args).tap do |rv| | ||
session.respond(message.id, rv, nil) if message.sync? | ||
end | ||
rescue => e | ||
log_exception(:error, e, __method__) | ||
session.respond(message.id, nil, e.message) if message.sync? | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module Neovim | ||
class RemoteModule | ||
# The DSL exposed in +Neovim.start_remote+ blocks. | ||
# | ||
# @api public | ||
class DSL < BasicObject | ||
attr_reader :handlers | ||
|
||
def initialize(&block) | ||
@handlers = ::Hash.new do |h, name| | ||
h[name] = ::Proc.new do |_, *| | ||
raise NotImplementedError, "undefined handler #{name.inspect}" | ||
end | ||
end | ||
|
||
block&.call(self) | ||
end | ||
|
||
# Define an RPC handler for use in remote modules. | ||
# | ||
# @param name [String] The handler name. | ||
# @param block [Proc] The body of the handler. | ||
def register_handler(name, &block) | ||
@handlers[name.to_s] = ::Proc.new do |client, *args| | ||
block.call(client, *args) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
let s:suite = themis#suite("Remote module") | ||
let s:expect = themis#helper("expect") | ||
|
||
call themis#helper('command').with(s:) | ||
|
||
function! s:suite.defines_commands() abort | ||
RbSetVar set_from_rb_mod foobar | ||
call s:expect(g:set_from_rb_mod).to_equal('foobar') | ||
endfunction | ||
|
||
function! s:suite.propagates_errors() abort | ||
Throws /oops/ :RbWillRaise | ||
endfunction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require "neovim" | ||
|
||
Neovim.start_remote do |mod| | ||
mod.register_handler("rb_set_var") do |nvim, name, val| | ||
nvim.set_var(name, val.to_s) | ||
end | ||
|
||
mod.register_handler("rb_will_raise") do |nvim| | ||
raise "oops" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
local chan | ||
|
||
local function ensure_job() | ||
if chan then | ||
return chan | ||
end | ||
|
||
chan = vim.fn.jobstart({ | ||
'ruby', | ||
'-I', 'lib', | ||
'spec/acceptance/runtime/example_remote_module.rb', | ||
}, { rpc = true }) | ||
|
||
return chan | ||
end | ||
|
||
vim.api.nvim_create_user_command('RbSetVar', function(args) | ||
vim.fn.rpcrequest(ensure_job(), 'rb_set_var', args.fargs) | ||
end, { nargs = '*' }) | ||
|
||
vim.api.nvim_create_user_command('RbWillRaise', function(args) | ||
vim.fn.rpcrequest(ensure_job(), 'rb_will_raise', args.fargs) | ||
end, { nargs = 0 }) |