neovim 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c69f05bcd8852ed00d741fa9c86b4453a34acea8
4
- data.tar.gz: fdf05283ab34d8f5692dc8cb95d3335d2b105bda
3
+ metadata.gz: ff45b0d6aaf7cab8f5c5092d9865c05d08d65dd4
4
+ data.tar.gz: 37296e52f00a0f027edb8035c7d35e869782cf65
5
5
  SHA512:
6
- metadata.gz: 3f6d303d8fd49a35c9ba809e0be985533a80e26a20940e565797821a3eefe968e3c579a3ae760f0f739eadf478ae7325e720553e60dcfd059bf470b82b2d0803
7
- data.tar.gz: 14a9f4aa0be233fedac15c11e1064d8c54903fed3d868472c600063be30f7f8a692db1993da8ac0b77bc5622ccc848c99f39ac9a7d808cbd47b8c382d71f0929
6
+ metadata.gz: d09d6c4f10345ebaa35d9d1e3eef431dab85d827c7a4045bcbbce56437b03a0598e5c4b683051b3fd992ccd902deb820500ee1101b7de0fddd2e3738daeede7a
7
+ data.tar.gz: bc3e6b3734c26b344a88b38fae3c12063fe5e3418c0e23142b226f8c1da6f7a0dee93526a3a77400208a915264365479a8597c8a02fa72416babdc44bc95b0a3
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # 0.0.2
2
+
3
+ - Add Neovim.plugin DSL for defining plugins
4
+ - Add neovim-ruby-host executable for spawning plugins
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Neovim Ruby
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/neovim.svg)](https://badge.fury.io/rb/neovim)
3
4
  [![Travis](https://travis-ci.org/alexgenco/neovim-ruby.svg?branch=master)](https://travis-ci.org/alexgenco/neovim-ruby)
4
5
  [![Coverage Status](https://coveralls.io/repos/alexgenco/neovim-ruby/badge.png)](https://coveralls.io/r/alexgenco/neovim-ruby)
5
6
 
@@ -38,6 +39,38 @@ client = Neovim.attach_unix("/tmp/nvim.sock")
38
39
 
39
40
  The interface of the client is generated at runtime from the `vim_get_api_info` RPC call. For now, you can refer to the Node client's auto-generated [API description](https://github.com/neovim/node-client/blob/master/index.d.ts). Note that methods will be in `snake_case` rather than `camelCase`.
40
41
 
42
+ The `neovim-ruby-host` executable can be used to spawn Ruby plugins via the `rpcstart` command. A plugin can be defined like this:
43
+
44
+ ```ruby
45
+ # my_plugin.rb
46
+
47
+ Neovim.plugin do |plug|
48
+ # Define a command called "Add" which returns the sum of two numbers
49
+ # The `:sync => true` option tells nvim to wait for a response.
50
+ # The result of the block will be returned to nvim.
51
+ plug.command(:Add, :nargs => 2, :sync => true) do |nvim, x, y|
52
+ x + y
53
+ end
54
+
55
+ # Define a command called "SetLine" which sets the current line
56
+ # This command is asynchronous, so nvim won't wait for a response.
57
+ plug.command(:SetLine, :nargs => 1) do |nvim, str|
58
+ nvim.current.line = str
59
+ end
60
+ end
61
+ ```
62
+
63
+ In your `nvim`, you can start this plugin by running `let host = rpcstart("neovim-ruby-host", ["./my_plugin.rb"])`. You can use this channel id to communicate with the host, e.g.
64
+ ```viml
65
+ let result = rpcrequest(host, "Add", 1, 2)
66
+ let result " # => 3
67
+
68
+ call rpcnotify(host, "SetLine", "Foo")
69
+ " Current line is set to "Foo"
70
+ ```
71
+
72
+ Plugin functionality is very limited right now. Besides `command`, the plugin DSL exposes the `function` and `autocmd` directives, however they are functionally identical to `command`. Their purpose is to define a manifest that nvim can load via the `UpdateRemotePlugins` command, which will generate the actual `command`, `function`, and `autocmd` definitions. This piece has not yet been implemented.
73
+
41
74
  ## Contributing
42
75
 
43
76
  1. Fork it (http://github.com/alexgenco/neovim-ruby/fork)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require "neovim"
3
+ Neovim.start_host(ARGV)
data/lib/neovim.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  require "neovim/async_session"
2
2
  require "neovim/client"
3
3
  require "neovim/event_loop"
4
+ require "neovim/host"
4
5
  require "neovim/msgpack_stream"
5
6
  require "neovim/session"
7
+ require "neovim/plugin"
6
8
 
7
9
  module Neovim
10
+ @__configured_plugins = []
11
+
8
12
  def self.attach_tcp(host, port)
9
13
  attach_event_loop(EventLoop.tcp(host, port))
10
14
  end
@@ -17,7 +21,19 @@ module Neovim
17
21
  attach_event_loop(EventLoop.child(argv))
18
22
  end
19
23
 
24
+ def self.start_host(rplugin_paths)
25
+ Host.load_from_files(rplugin_paths).run
26
+ end
27
+
28
+ def self.plugin(&block)
29
+ Plugin.from_config_block(&block).tap do |plugin|
30
+ __configured_plugins << plugin
31
+ end
32
+ end
33
+
20
34
  class << self
35
+ attr_accessor :__configured_plugins
36
+
21
37
  private
22
38
 
23
39
  def attach_event_loop(event_loop)
@@ -1,3 +1,6 @@
1
+ require "neovim/request"
2
+ require "neovim/notification"
3
+
1
4
  module Neovim
2
5
  class AsyncSession
3
6
  def initialize(msgpack_stream)
@@ -34,13 +37,13 @@ module Neovim
34
37
  case kind
35
38
  when 0
36
39
  reqid, method, args = rest
37
- request_cb.call(method, args, Responder.new(@msgpack_stream, reqid))
40
+ request_cb.call(Request.new(method, args, @msgpack_stream, reqid))
38
41
  when 1
39
42
  reqid, (_, error), result = rest
40
43
  @pending_requests.fetch(reqid).call(error, result)
41
44
  when 2
42
- event, args = rest
43
- notification_cb.call(event, args)
45
+ method, args = rest
46
+ notification_cb.call(Notification.new(method, args))
44
47
  end
45
48
  end
46
49
  end
@@ -49,27 +52,5 @@ module Neovim
49
52
  @msgpack_stream.stop
50
53
  self
51
54
  end
52
-
53
- def shutdown
54
- @msgpack_stream.shutdown
55
- self
56
- end
57
-
58
- class Responder
59
- def initialize(msgpack_stream, request_id)
60
- @msgpack_stream = msgpack_stream
61
- @request_id = request_id
62
- end
63
-
64
- def send(value)
65
- @msgpack_stream.send([1, @request_id, nil, value])
66
- self
67
- end
68
-
69
- def error(value)
70
- @msgpack_stream.send([1, @request_id, value, nil])
71
- self
72
- end
73
- end
74
55
  end
75
56
  end
data/lib/neovim/client.rb CHANGED
@@ -25,5 +25,9 @@ module Neovim
25
25
  def current
26
26
  Current.new(@session)
27
27
  end
28
+
29
+ def stop
30
+ @session.stop
31
+ end
28
32
  end
29
33
  end
@@ -1,4 +1,3 @@
1
- require "eventmachine"
2
1
  require "socket"
3
2
 
4
3
  module Neovim
@@ -24,52 +23,33 @@ module Neovim
24
23
  end
25
24
 
26
25
  def initialize(rd, wr)
27
- @read_stream, @write_stream = rd, wr
26
+ @rd, @wr = rd, wr
27
+ @running = false
28
28
  end
29
29
 
30
30
  def send(data)
31
- EM.schedule do
32
- @write_conn.send_data(data)
33
- end
31
+ @wr.write_nonblock(data)
34
32
  self
33
+ rescue IO::WaitWritable
34
+ IO.select(nil, [@wr])
35
+ retry
35
36
  end
36
37
 
37
38
  def run(&message_callback)
39
+ @running = true
38
40
  message_callback ||= Proc.new {}
39
41
 
40
- EM.run do
41
- @read_conn = EM.watch(@read_stream, Connection)
42
- @write_conn = EM.watch(@write_stream, Connection) unless @write_stream == @read_stream
43
- @write_conn ||= @read_conn
44
-
45
- @read_conn.notify_readable = true
46
- @read_conn.message_callback = message_callback
42
+ loop do
43
+ break unless @running
44
+ message_callback.call(@rd.readpartial(1024 * 16))
47
45
  end
46
+ rescue EOFError
47
+ stop
48
48
  end
49
49
 
50
50
  def stop
51
- EM.stop_event_loop
52
- self
53
- end
54
-
55
- def shutdown
56
- stop
51
+ @running = false
57
52
  self
58
- ensure
59
- @read_conn.close if @read_conn.respond_to?(:close)
60
- @write_conn.close if @write_conn.respond_to?(:close)
61
- end
62
-
63
- class Connection < EM::Connection
64
- attr_writer :message_callback
65
-
66
- def send_data(data)
67
- @io.write_nonblock(data)
68
- end
69
-
70
- def notify_readable
71
- @message_callback.call(@io.readpartial(1024 * 16))
72
- end
73
53
  end
74
54
  end
75
55
  end
@@ -0,0 +1,70 @@
1
+ module Neovim
2
+ class Host
3
+ def self.load_from_files(rplugin_paths)
4
+ plugins_before = Neovim.__configured_plugins
5
+ captured_plugins = []
6
+
7
+ begin
8
+ Neovim.__configured_plugins = captured_plugins
9
+
10
+ rplugin_paths.each do |rplugin_path|
11
+ Kernel.load(rplugin_path, true)
12
+ end
13
+
14
+ new(captured_plugins)
15
+ ensure
16
+ Neovim.__configured_plugins = plugins_before
17
+ end
18
+ end
19
+
20
+ attr_reader :plugins
21
+
22
+ def initialize(plugins)
23
+ @plugins = plugins
24
+ @handlers = compile_handlers(plugins)
25
+ end
26
+
27
+ def run
28
+ event_loop = EventLoop.stdio
29
+ msgpack_stream = MsgpackStream.new(event_loop)
30
+ async_session = AsyncSession.new(msgpack_stream)
31
+ session = Session.new(async_session)
32
+ client = Client.new(session)
33
+
34
+ notification_callback = Proc.new do |notif|
35
+ @handlers[:notification][notif.method_name].call(client, notif)
36
+ end
37
+
38
+ request_callback = Proc.new do |request|
39
+ @handlers[:request][request.method_name].call(client, request)
40
+ end
41
+
42
+ async_session.run(request_callback, notification_callback)
43
+ end
44
+
45
+ private
46
+
47
+ def compile_handlers(plugins)
48
+ base = {
49
+ :request => Hash.new(Proc.new {}),
50
+ :notification => Hash.new(Proc.new {})
51
+ }
52
+
53
+ plugins.inject(base) do |handlers, plugin|
54
+ plugin.specs.each do |spec|
55
+ if spec[:sync]
56
+ handlers[:request][spec[:name]] = lambda do |client, request|
57
+ request.respond(spec[:proc].call(client, *request.arguments))
58
+ end
59
+ else
60
+ handlers[:notification][spec[:name]] = lambda do |client, notification|
61
+ spec[:proc].call(client, *notification.arguments)
62
+ end
63
+ end
64
+ end
65
+
66
+ handlers
67
+ end
68
+ end
69
+ end
70
+ end
@@ -35,10 +35,5 @@ module Neovim
35
35
  @event_loop.stop
36
36
  self
37
37
  end
38
-
39
- def shutdown
40
- @event_loop.shutdown
41
- self
42
- end
43
38
  end
44
39
  end
@@ -0,0 +1,10 @@
1
+ module Neovim
2
+ class Notification
3
+ attr_reader :method_name, :arguments
4
+
5
+ def initialize(method_name, args)
6
+ @method_name = method_name.to_sym
7
+ @arguments = args
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,61 @@
1
+ module Neovim
2
+ class Plugin
3
+ def self.from_config_block(&block)
4
+ new.tap do |instance|
5
+ block.call(DSL.new(instance)) if block
6
+ end
7
+ end
8
+
9
+ attr_accessor :specs
10
+
11
+ def initialize
12
+ @specs = []
13
+ end
14
+
15
+ class DSL < BasicObject
16
+ def initialize(plugin)
17
+ @plugin = plugin
18
+ end
19
+
20
+ def command(name, _options={}, &block)
21
+ options = _options.dup
22
+ options[:range] = "" if options[:range] == true
23
+ options[:range] = ::Kernel.String(options[:range])
24
+
25
+ @plugin.specs.push(
26
+ :type => :command,
27
+ :name => name.to_sym,
28
+ :sync => !!options.delete(:sync),
29
+ :opts => options,
30
+ :proc => block || ::Proc.new {}
31
+ )
32
+ end
33
+
34
+ def function(name, _options, &block)
35
+ options = _options.dup
36
+ options[:range] = "" if options[:range] == true
37
+ options[:range] = ::Kernel.String(options[:range])
38
+
39
+ @plugin.specs.push(
40
+ :type => :function,
41
+ :name => name.to_sym,
42
+ :sync => !!options.delete(:sync),
43
+ :opts => options,
44
+ :proc => block || ::Proc.new {}
45
+ )
46
+ end
47
+
48
+ def autocmd(name, _options={}, &block)
49
+ options = _options.dup
50
+
51
+ @plugin.specs.push(
52
+ :type => :autocmd,
53
+ :name => name.to_sym,
54
+ :sync => !!options.delete(:sync),
55
+ :opts => options,
56
+ :proc => block || ::Proc.new {}
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ module Neovim
2
+ class Request
3
+ attr_reader :method_name, :arguments
4
+
5
+ def initialize(method_name, args, msgpack_stream, request_id)
6
+ @method_name = method_name.to_sym
7
+ @arguments = args
8
+ @msgpack_stream = msgpack_stream
9
+ @request_id = request_id
10
+ end
11
+
12
+ def respond(value)
13
+ @msgpack_stream.send([1, @request_id, nil, value])
14
+ self
15
+ end
16
+
17
+ def error(message)
18
+ @msgpack_stream.send([1, @request_id, message, nil])
19
+ self
20
+ end
21
+ end
22
+ end
@@ -12,14 +12,14 @@ module Neovim
12
12
  end
13
13
 
14
14
  def request(method, *args)
15
- err = res = nil
16
-
17
- @async_session.request(method, *args) do |error, response|
18
- err, res = error, response
19
- @async_session.stop
20
- end.run
15
+ fiber = Fiber.new do
16
+ @async_session.request(method, *args) do |err, res|
17
+ Fiber.yield(err, res)
18
+ end.run
19
+ end
21
20
 
22
- err ? raise(ArgumentError, err) : res
21
+ error, response = fiber.resume
22
+ error ? raise(ArgumentError, error) : response
23
23
  end
24
24
 
25
25
  def api_methods_for_prefix(prefix)
@@ -1,3 +1,3 @@
1
1
  module Neovim
2
- VERSION = Gem::Version.new("0.0.1")
2
+ VERSION = Gem::Version.new("0.0.2")
3
3
  end
data/neovim.gemspec CHANGED
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency "msgpack", "0.7.0dev1"
21
- spec.add_dependency "eventmachine", "~> 1.0"
22
21
 
23
22
  spec.add_development_dependency "bundler"
24
23
  spec.add_development_dependency "rake"
@@ -0,0 +1,57 @@
1
+ require "helper"
2
+ require "tempfile"
3
+
4
+ RSpec.describe "neovim-ruby-host" do
5
+ let(:lib_path) { File.expand_path("../../../lib", __FILE__) }
6
+ let(:bin_path) { File.expand_path("../../../bin/neovim-ruby-host", __FILE__) }
7
+ let(:nvim_path) { File.expand_path("../../../vendor/neovim/build/bin/nvim", __FILE__) }
8
+
9
+ specify do
10
+ plugin1 = Tempfile.open("plug1") do |f|
11
+ f.write(<<-RUBY)
12
+ Neovim.plugin do |plug|
13
+ plug.command(:SyncAdd, :args => 2, :sync => true) do |nvim, x, y|
14
+ x + y
15
+ end
16
+ end
17
+ RUBY
18
+ f.path
19
+ end
20
+
21
+ plugin2 = Tempfile.open("plug2") do |f|
22
+ f.write(<<-RUBY)
23
+ Neovim.plugin do |plug|
24
+ plug.command(:AsyncSetLine, :args => 1) do |nvim, str|
25
+ nvim.current.line = str
26
+ end
27
+ end
28
+ RUBY
29
+ f.path
30
+ end
31
+
32
+ output = Tempfile.new("output").tap(&:close).path
33
+ host_nvim = Neovim.attach_child(["--headless", "-u", "NONE", "-N", "-n"])
34
+
35
+ # Start the remote host
36
+ host_nvim.command(%{let g:chan = rpcstart("#{bin_path}", ["#{plugin1}", "#{plugin2}"])})
37
+ sleep 0.4 # TODO figure out if/why this is necessary
38
+
39
+ # Make a request to the synchronous SyncAdd method and store the results
40
+ host_nvim.command(%{let g:res = rpcrequest(g:chan, "SyncAdd", 1, 2)})
41
+
42
+ # Write the results to the buffer
43
+ host_nvim.command("put =g:res")
44
+ host_nvim.command("normal o")
45
+
46
+ # Set the current line content via the AsyncSetLine method
47
+ host_nvim.command(%{call rpcnotify(g:chan, "AsyncSetLine", "foo")})
48
+
49
+ # Ensure notification callback has completed
50
+ host_nvim.eval("0")
51
+
52
+ # Save the contents of the buffer
53
+ host_nvim.command("write! #{output}")
54
+
55
+ expect(File.read(output)).to eq("\n3\nfoo\n")
56
+ end
57
+ end
data/spec/helper.rb CHANGED
@@ -21,6 +21,6 @@ RSpec.configure do |config|
21
21
  Kernel.srand config.seed
22
22
 
23
23
  config.around do |spec|
24
- Timeout.timeout(1) { spec.run }
24
+ Timeout.timeout(5) { spec.run }
25
25
  end
26
26
  end
@@ -2,95 +2,109 @@ require "helper"
2
2
 
3
3
  module Neovim
4
4
  RSpec.describe AsyncSession do
5
- it "receives requests" do
6
- server = TCPServer.new("0.0.0.0", 0)
7
- event_loop = EventLoop.tcp("0.0.0.0", server.addr[1])
8
- stream = MsgpackStream.new(event_loop)
9
- async = AsyncSession.new(stream)
10
- messages = []
11
-
12
- srv_thr = Thread.new do
13
- client = server.accept
14
- client.write(MessagePack.pack(
15
- [0, 123, "func", [1, 2, 3]]
16
- ))
17
-
18
- client.close
19
- server.close
5
+ shared_context "async session behavior" do
6
+ it "receives requests" do
7
+ stream = MsgpackStream.new(event_loop)
8
+ async = AsyncSession.new(stream)
9
+
10
+ srv_thr = Thread.new do
11
+ client = server.accept
12
+ IO.select(nil, [client])
13
+ client.write(MessagePack.pack(
14
+ [0, 123, "func", [1, 2, 3]]
15
+ ))
16
+ end
17
+
18
+ req_cb = Proc.new do |request|
19
+ Fiber.yield(request)
20
+ end
21
+
22
+ fiber = Fiber.new do
23
+ async.run(req_cb)
24
+ end
25
+
26
+ request = fiber.resume
27
+
28
+ srv_thr.join
29
+
30
+ expect(request).to be_a(Request)
31
+ expect(request.method_name).to eq(:func)
32
+ expect(request.arguments).to eq([1, 2, 3])
20
33
  end
21
34
 
22
- req_cb = Proc.new do |*payload|
23
- messages << payload
24
- async.shutdown
25
- end
35
+ it "receives notifications" do
36
+ stream = MsgpackStream.new(event_loop)
37
+ async = AsyncSession.new(stream)
26
38
 
27
- async.run(req_cb)
28
- srv_thr.join
39
+ srv_thr = Thread.new do
40
+ client = server.accept
41
+ IO.select(nil, [client])
42
+ client.write(MessagePack.pack(
43
+ [2, "func", [1, 2, 3]]
44
+ ))
45
+ end
29
46
 
30
- expect(messages.first.size).to eq(3)
31
- expect(messages.first[0..1]).to eq(["func", [1, 2, 3]])
32
- expect(messages.first[2]).to be_a(AsyncSession::Responder)
33
- end
47
+ not_cb = Proc.new do |notification|
48
+ Fiber.yield(notification)
49
+ end
34
50
 
35
- it "receives notifications" do
36
- server = TCPServer.new("0.0.0.0", 0)
37
- event_loop = EventLoop.tcp("0.0.0.0", server.addr[1])
38
- stream = MsgpackStream.new(event_loop)
39
- async = AsyncSession.new(stream)
40
- messages = []
41
-
42
- srv_thr = Thread.new do
43
- client = server.accept
44
- client.write(MessagePack.pack(
45
- [2, "func", [1, 2, 3]]
46
- ))
47
-
48
- client.close
49
- server.close
50
- end
51
+ fiber = Fiber.new do
52
+ async.run(nil, not_cb)
53
+ end
51
54
 
52
- not_cb = Proc.new do |*payload|
53
- messages << payload
54
- async.shutdown
55
+ notification = fiber.resume
56
+
57
+ srv_thr.join
58
+
59
+ expect(notification).to be_a(Notification)
60
+ expect(notification.method_name).to eq(:func)
61
+ expect(notification.arguments).to eq([1, 2, 3])
55
62
  end
56
63
 
57
- async.run(nil, not_cb)
58
- srv_thr.join
64
+ it "receives responses to requests" do
65
+ stream = MsgpackStream.new(event_loop)
66
+ async = AsyncSession.new(stream)
67
+ messages = []
59
68
 
60
- expect(messages).to eq([["func", [1, 2, 3]]])
61
- end
69
+ srv_thr = Thread.new do
70
+ client = server.accept
71
+ messages << client.readpartial(1024)
62
72
 
63
- it "receives responses to requests" do
64
- server = TCPServer.new("0.0.0.0", 0)
65
- event_loop = EventLoop.tcp("0.0.0.0", server.addr[1])
66
- stream = MsgpackStream.new(event_loop)
67
- async = AsyncSession.new(stream)
68
- messages = []
73
+ client.write(MessagePack.pack(
74
+ [1, 0, [0, "error"], "result"]
75
+ ))
76
+ end
69
77
 
70
- srv_thr = Thread.new do
71
- client = server.accept
72
- messages << client.read_nonblock(1024)
78
+ fiber = Fiber.new do
79
+ async.request("func", 1, 2, 3) do |error, result|
80
+ Fiber.yield(error, result)
81
+ end.run
82
+ end
73
83
 
74
- client.write(MessagePack.pack(
75
- [1, 0, [0, "error"], "result"]
76
- ))
84
+ expect(fiber.resume).to eq(["error", "result"])
77
85
 
78
- client.close
79
- server.close
80
- end
86
+ srv_thr.join
81
87
 
82
- async.request("func", 1, 2, 3) do |error, result|
83
- expect(error).to eq("error")
84
- expect(result).to eq("result")
85
- async.shutdown
88
+ expect(messages).to eq(
89
+ [MessagePack.pack([0, 0, "func", [1, 2, 3]])]
90
+ )
86
91
  end
92
+ end
93
+
94
+ context "tcp" do
95
+ let!(:server) { TCPServer.new("0.0.0.0", 0) }
96
+ let!(:event_loop) { EventLoop.tcp("0.0.0.0", server.addr[1]) }
97
+
98
+ include_context "async session behavior"
99
+ end
87
100
 
88
- async.run
89
- srv_thr.join
101
+ context "unix" do
102
+ before { FileUtils.rm_f("/tmp/#$$.sock") }
103
+ after { FileUtils.rm_f("/tmp/#$$.sock") }
104
+ let!(:server) { UNIXServer.new("/tmp/#$$.sock") }
105
+ let!(:event_loop) { EventLoop.unix("/tmp/#$$.sock") }
90
106
 
91
- expect(messages).to eq(
92
- [MessagePack.pack([0, 0, "func", [1, 2, 3]])]
93
- )
107
+ include_context "async session behavior"
94
108
  end
95
109
  end
96
110
  end
@@ -5,24 +5,24 @@ require "fileutils"
5
5
 
6
6
  module Neovim
7
7
  RSpec.describe EventLoop do
8
- shared_context "socket behaviors" do
8
+ shared_context "socket behavior" do
9
9
  it "sends and receives data" do
10
10
  messages = []
11
11
 
12
12
  srv_thr = Thread.new do
13
13
  client = server.accept
14
- messages << client.read_nonblock(1024)
14
+ messages << client.readpartial(1024)
15
15
 
16
- client.write("from server")
17
- client.close
18
- server.close
16
+ client.write("OK")
19
17
  end
20
18
 
21
- event_loop.send("data").run do |msg|
22
- expect(msg).to eq("from server")
23
- event_loop.shutdown
19
+ fiber = Fiber.new do
20
+ event_loop.send("data").run do |msg|
21
+ Fiber.yield(msg)
22
+ end
24
23
  end
25
24
 
25
+ expect(fiber.resume).to eq("OK")
26
26
  srv_thr.join
27
27
  expect(messages).to eq(["data"])
28
28
  end
@@ -32,15 +32,16 @@ module Neovim
32
32
  let!(:server) { TCPServer.new("0.0.0.0", 0) }
33
33
  let!(:event_loop) { EventLoop.tcp("0.0.0.0", server.addr[1]) }
34
34
 
35
- include_context "socket behaviors"
35
+ include_context "socket behavior"
36
36
  end
37
37
 
38
38
  context "unix" do
39
39
  before { FileUtils.rm_f("/tmp/#$$.sock") }
40
+ after { FileUtils.rm_f("/tmp/#$$.sock") }
40
41
  let!(:server) { UNIXServer.new("/tmp/#$$.sock") }
41
42
  let!(:event_loop) { EventLoop.unix("/tmp/#$$.sock") }
42
43
 
43
- include_context "socket behaviors"
44
+ include_context "socket behavior"
44
45
  end
45
46
 
46
47
  context "child" do
@@ -48,10 +49,13 @@ module Neovim
48
49
  event_loop = EventLoop.child(["-n", "-u", "NONE"])
49
50
  message = MessagePack.pack([0, 0, :vim_strwidth, ["hi"]])
50
51
 
51
- event_loop.send(message).run do |msg|
52
- expect(msg).to eq(MessagePack.pack([1, 0, nil, 2]))
53
- event_loop.shutdown
52
+ fiber = Fiber.new do
53
+ event_loop.send(message).run do |msg|
54
+ Fiber.yield(msg)
55
+ end
54
56
  end
57
+
58
+ expect(fiber.resume).to eq(MessagePack.pack([1, 0, nil, 2]))
55
59
  end
56
60
  end
57
61
  end
@@ -0,0 +1,80 @@
1
+ require "helper"
2
+ require "tempfile"
3
+
4
+ module Neovim
5
+ RSpec.describe Host do
6
+ describe ".load_from_files" do
7
+ it "loads the defined plugins" do
8
+ plug1 = Tempfile.open("plug1") do |f|
9
+ f.write("Neovim.plugin")
10
+ f.path
11
+ end
12
+
13
+ plug2 = Tempfile.open("plug2") do |f|
14
+ f.write("Neovim.plugin; Neovim.plugin")
15
+ f.path
16
+ end
17
+
18
+ host = Host.load_from_files([plug1, plug2])
19
+ expect(host.plugins.size).to eq(3)
20
+ end
21
+
22
+ it "doesn't load plugin code into the global namespace" do
23
+ plug = Tempfile.open("plug") do |f|
24
+ f.write("class FooClass; end")
25
+ f.path
26
+ end
27
+
28
+ host = Host.load_from_files([plug])
29
+ expect(Kernel.const_defined?("FooClass")).to be(false)
30
+ end
31
+ end
32
+
33
+ describe "#run" do
34
+ it "runs an async client with the plugins as callbacks" do
35
+ sync_cb = lambda { |nvim, x, y| [nvim, x, y] }
36
+ async_cb = lambda { |nvim, x, y| [nvim, x, y] }
37
+
38
+ plugin = Plugin.from_config_block do |plug|
39
+ plug.command(:Sync, :nargs => 2, :sync => true, &sync_cb)
40
+ plug.command(:Async, :nargs => 2, &async_cb)
41
+ end
42
+
43
+ host = Host.new([plugin])
44
+
45
+ mock_async_session = double(:async_session, :request => nil)
46
+ expect(AsyncSession).to receive(:new) { mock_async_session }
47
+
48
+ mock_client = double(:client)
49
+ expect(Client).to receive(:new) { mock_client }
50
+
51
+ expect(EventLoop).to receive(:stdio) { double(:event_loop) }
52
+ expect(MsgpackStream).to receive(:new) { double(:msgpack_stream) }
53
+ expect(Session).to receive(:new) { double(:session) }
54
+
55
+ expect(mock_async_session).to receive(:run) do |req_cb, not_cb|
56
+ mock_request = double(
57
+ :request,
58
+ :method_name => :Sync,
59
+ :arguments => [1, 2]
60
+ )
61
+
62
+ mock_notification = double(
63
+ :notification,
64
+ :method_name => :Async,
65
+ :arguments => [3, 4]
66
+ )
67
+
68
+ expect(sync_cb).to receive(:call).with(mock_client, 1, 2).and_call_original
69
+ expect(mock_request).to receive(:respond).with([mock_client, 1, 2])
70
+ req_cb.call(mock_request)
71
+
72
+ expect(async_cb).to receive(:call).with(mock_client, 3, 4).and_call_original
73
+ not_cb.call(mock_notification)
74
+ end
75
+
76
+ host.run
77
+ end
78
+ end
79
+ end
80
+ end
@@ -3,27 +3,42 @@ require "helper"
3
3
 
4
4
  module Neovim
5
5
  RSpec.describe MsgpackStream do
6
- it "sends and receives msgpack" do
7
- server = TCPServer.new("0.0.0.0", 0)
8
- event_loop = EventLoop.tcp("0.0.0.0", server.addr[1])
9
- stream = MsgpackStream.new(event_loop)
10
- messages = []
11
-
12
- srv_thr = Thread.new do
13
- client = server.accept
14
- messages << client.read_nonblock(1024)
15
-
16
- client.write(MessagePack.pack([2]))
17
- client.close
18
- server.close
19
- end
6
+ shared_context "msgpack stream behavior" do
7
+ it "sends and receives data" do
8
+ stream = MsgpackStream.new(event_loop)
9
+ messages = []
10
+
11
+ srv_thr = Thread.new do
12
+ client = server.accept
13
+ messages << client.readpartial(1024)
14
+ client.write(MessagePack.pack([2]))
15
+ end
20
16
 
21
- stream.send([1]).run do |msg|
22
- expect(msg).to eq([2])
23
- stream.shutdown
17
+ fiber = Fiber.new do
18
+ stream.send([1]).run do |msg|
19
+ Fiber.yield(msg)
20
+ end
21
+ end
22
+
23
+ expect(fiber.resume).to eq([2])
24
+ expect(messages).to eq([MessagePack.pack([1])])
24
25
  end
26
+ end
27
+
28
+ context "tcp" do
29
+ let!(:server) { TCPServer.new("0.0.0.0", 0) }
30
+ let!(:event_loop) { EventLoop.tcp("0.0.0.0", server.addr[1]) }
31
+
32
+ include_context "msgpack stream behavior"
33
+ end
34
+
35
+ context "unix" do
36
+ before { FileUtils.rm_f("/tmp/#$$.sock") }
37
+ after { FileUtils.rm_f("/tmp/#$$.sock") }
38
+ let!(:server) { UNIXServer.new("/tmp/#$$.sock") }
39
+ let!(:event_loop) { EventLoop.unix("/tmp/#$$.sock") }
25
40
 
26
- expect(messages).to eq([MessagePack.pack([1])])
41
+ include_context "msgpack stream behavior"
27
42
  end
28
43
  end
29
44
  end
@@ -0,0 +1,107 @@
1
+ require "helper"
2
+
3
+ module Neovim
4
+ RSpec.describe Object do
5
+ context Window do
6
+ let(:window) { Neovim.attach_child(["-n", "-u", "NONE"]).current.window }
7
+
8
+ describe "#respond_to?" do
9
+ it "returns true for Window functions" do
10
+ expect(window).to respond_to(:get_cursor)
11
+ end
12
+
13
+ it "returns true for Ruby functions" do
14
+ expect(window).to respond_to(:inspect)
15
+ end
16
+
17
+ it "returns false otherwise" do
18
+ expect(window).not_to respond_to(:foobar)
19
+ end
20
+ end
21
+
22
+ describe "#method_missing" do
23
+ it "enables window_* function calls" do
24
+ expect(window.get_cursor).to eq([1, 0])
25
+ end
26
+ end
27
+
28
+ describe "#methods" do
29
+ it "returns builtin methods" do
30
+ expect(window.methods).to include(:inspect)
31
+ end
32
+
33
+ it "returns api methods" do
34
+ expect(window.methods).to include(:get_height)
35
+ end
36
+ end
37
+ end
38
+
39
+ context Tabpage do
40
+ let(:tabpage) { Neovim.attach_child(["-n", "-u", "NONE"]).current.tabpage }
41
+
42
+ describe "#respond_to?" do
43
+ it "returns true for Tabpage functions" do
44
+ expect(tabpage).to respond_to(:is_valid)
45
+ end
46
+
47
+ it "returns true for Ruby functions" do
48
+ expect(tabpage).to respond_to(:inspect)
49
+ end
50
+
51
+ it "returns false otherwise" do
52
+ expect(tabpage).not_to respond_to(:foobar)
53
+ end
54
+ end
55
+
56
+ describe "#method_missing" do
57
+ it "enables tabpage_* function calls" do
58
+ expect(tabpage.is_valid).to be(true)
59
+ end
60
+ end
61
+
62
+ describe "#methods" do
63
+ it "returns builtin methods" do
64
+ expect(tabpage.methods).to include(:inspect)
65
+ end
66
+
67
+ it "returns api methods" do
68
+ expect(tabpage.methods).to include(:get_windows)
69
+ end
70
+ end
71
+ end
72
+
73
+ context Buffer do
74
+ let(:buffer) { Neovim.attach_child(["-n", "-u", "NONE"]).current.buffer }
75
+
76
+ describe "#respond_to?" do
77
+ it "returns true for Buffer functions" do
78
+ expect(buffer).to respond_to(:line_count)
79
+ end
80
+
81
+ it "returns true for Ruby functions" do
82
+ expect(buffer).to respond_to(:inspect)
83
+ end
84
+
85
+ it "returns false otherwise" do
86
+ expect(buffer).not_to respond_to(:foobar)
87
+ end
88
+ end
89
+
90
+ describe "#method_missing" do
91
+ it "enables buffer_* function calls" do
92
+ expect(buffer.line_count).to be(1)
93
+ end
94
+ end
95
+
96
+ describe "#methods" do
97
+ it "returns builtin methods" do
98
+ expect(buffer.methods).to include(:inspect)
99
+ end
100
+
101
+ it "returns api methods" do
102
+ expect(buffer.methods).to include(:get_mark)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,67 @@
1
+ require "helper"
2
+
3
+ module Neovim
4
+ RSpec.describe Plugin do
5
+ describe ".from_config_block" do
6
+ it "registers a command" do
7
+ cmd_block = Proc.new {}
8
+
9
+ plugin = Plugin.from_config_block do |plug|
10
+ plug.command("Foo", :range => true, :nargs => 1, &cmd_block)
11
+ end
12
+
13
+ expect(plugin.specs).to eq(
14
+ [
15
+ {
16
+ :type => :command,
17
+ :name => :Foo,
18
+ :sync => false,
19
+ :opts => {:range => "", :nargs => 1},
20
+ :proc => cmd_block
21
+ }
22
+ ]
23
+ )
24
+ end
25
+
26
+ it "registers an autocmd" do
27
+ au_block = Proc.new {}
28
+
29
+ plugin = Plugin.from_config_block do |plug|
30
+ plug.autocmd("BufEnter", :pattern => "*.rb", &au_block)
31
+ end
32
+
33
+ expect(plugin.specs).to eq(
34
+ [
35
+ {
36
+ :type => :autocmd,
37
+ :name => :BufEnter,
38
+ :sync => false,
39
+ :opts => {:pattern => "*.rb"},
40
+ :proc => au_block
41
+ }
42
+ ]
43
+ )
44
+ end
45
+
46
+ it "registers a function" do
47
+ fun_block = Proc.new {}
48
+
49
+ plugin = Plugin.from_config_block do |plug|
50
+ plug.function("Foo", :range => true, :nargs => 1, &fun_block)
51
+ end
52
+
53
+ expect(plugin.specs).to eq(
54
+ [
55
+ {
56
+ :type => :function,
57
+ :name => :Foo,
58
+ :sync => false,
59
+ :opts => {:range => "", :nargs => 1},
60
+ :proc => fun_block
61
+ }
62
+ ]
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
data/spec/neovim_spec.rb CHANGED
@@ -45,10 +45,18 @@ RSpec.describe Neovim do
45
45
  end
46
46
  end
47
47
 
48
- describe "attach_child" do
48
+ describe ".attach_child" do
49
49
  it "spawns and attaches to a child process" do
50
50
  nvim = Neovim.attach_child(nvim_argv)
51
51
  expect(nvim.strwidth("hi")).to eq(2)
52
52
  end
53
53
  end
54
+
55
+ describe ".plugin" do
56
+ it "adds to Neovim.__configured_plugins" do
57
+ expect {
58
+ Neovim.plugin
59
+ }.to change { Neovim.__configured_plugins.size }.by(1)
60
+ end
61
+ end
54
62
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neovim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Genco
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-16 00:00:00.000000000 Z
11
+ date: 2015-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.7.0dev1
27
- - !ruby/object:Gem::Dependency
28
- name: eventmachine
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: bundler
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -97,7 +83,8 @@ dependencies:
97
83
  description:
98
84
  email:
99
85
  - alexgenco@gmail.com
100
- executables: []
86
+ executables:
87
+ - neovim-ruby-host
101
88
  extensions: []
102
89
  extra_rdoc_files: []
103
90
  files:
@@ -105,31 +92,38 @@ files:
105
92
  - ".gitignore"
106
93
  - ".gitmodules"
107
94
  - ".travis.yml"
95
+ - CHANGELOG.md
108
96
  - Gemfile
109
97
  - LICENSE.txt
110
98
  - README.md
111
99
  - Rakefile
100
+ - bin/neovim-ruby-host
112
101
  - lib/neovim.rb
113
102
  - lib/neovim/api_info.rb
114
103
  - lib/neovim/async_session.rb
115
104
  - lib/neovim/client.rb
116
105
  - lib/neovim/current.rb
117
106
  - lib/neovim/event_loop.rb
107
+ - lib/neovim/host.rb
118
108
  - lib/neovim/msgpack_stream.rb
109
+ - lib/neovim/notification.rb
119
110
  - lib/neovim/object.rb
111
+ - lib/neovim/plugin.rb
112
+ - lib/neovim/request.rb
120
113
  - lib/neovim/session.rb
121
114
  - lib/neovim/version.rb
122
115
  - neovim.gemspec
116
+ - spec/acceptance/neovim-ruby-host_spec.rb
123
117
  - spec/helper.rb
124
118
  - spec/neovim/async_session_spec.rb
125
- - spec/neovim/buffer_spec.rb
126
119
  - spec/neovim/client_spec.rb
127
120
  - spec/neovim/current_spec.rb
128
121
  - spec/neovim/event_loop_spec.rb
122
+ - spec/neovim/host_spec.rb
129
123
  - spec/neovim/msgpack_stream_spec.rb
124
+ - spec/neovim/object_spec.rb
125
+ - spec/neovim/plugin_spec.rb
130
126
  - spec/neovim/session_spec.rb
131
- - spec/neovim/tabpage_spec.rb
132
- - spec/neovim/window_spec.rb
133
127
  - spec/neovim_spec.rb
134
128
  homepage: https://github.com/alexgenco/neovim-ruby
135
129
  licenses:
@@ -156,14 +150,15 @@ signing_key:
156
150
  specification_version: 4
157
151
  summary: A Ruby client for Neovim
158
152
  test_files:
153
+ - spec/acceptance/neovim-ruby-host_spec.rb
159
154
  - spec/helper.rb
160
155
  - spec/neovim/async_session_spec.rb
161
- - spec/neovim/buffer_spec.rb
162
156
  - spec/neovim/client_spec.rb
163
157
  - spec/neovim/current_spec.rb
164
158
  - spec/neovim/event_loop_spec.rb
159
+ - spec/neovim/host_spec.rb
165
160
  - spec/neovim/msgpack_stream_spec.rb
161
+ - spec/neovim/object_spec.rb
162
+ - spec/neovim/plugin_spec.rb
166
163
  - spec/neovim/session_spec.rb
167
- - spec/neovim/tabpage_spec.rb
168
- - spec/neovim/window_spec.rb
169
164
  - spec/neovim_spec.rb
@@ -1,37 +0,0 @@
1
- require "helper"
2
-
3
- module Neovim
4
- RSpec.describe Buffer do
5
- let(:buffer) { Neovim.attach_child(["-n", "-u", "NONE"]).current.buffer }
6
-
7
- describe "#respond_to?" do
8
- it "returns true for Buffer functions" do
9
- expect(buffer).to respond_to(:line_count)
10
- end
11
-
12
- it "returns true for Ruby functions" do
13
- expect(buffer).to respond_to(:inspect)
14
- end
15
-
16
- it "returns false otherwise" do
17
- expect(buffer).not_to respond_to(:foobar)
18
- end
19
- end
20
-
21
- describe "#method_missing" do
22
- it "enables buffer_* function calls" do
23
- expect(buffer.line_count).to be(1)
24
- end
25
- end
26
-
27
- describe "#methods" do
28
- it "returns builtin methods" do
29
- expect(buffer.methods).to include(:inspect)
30
- end
31
-
32
- it "returns api methods" do
33
- expect(buffer.methods).to include(:get_mark)
34
- end
35
- end
36
- end
37
- end
@@ -1,37 +0,0 @@
1
- require "helper"
2
-
3
- module Neovim
4
- RSpec.describe Tabpage do
5
- let(:tabpage) { Neovim.attach_child(["-n", "-u", "NONE"]).current.tabpage }
6
-
7
- describe "#respond_to?" do
8
- it "returns true for Tabpage functions" do
9
- expect(tabpage).to respond_to(:is_valid)
10
- end
11
-
12
- it "returns true for Ruby functions" do
13
- expect(tabpage).to respond_to(:inspect)
14
- end
15
-
16
- it "returns false otherwise" do
17
- expect(tabpage).not_to respond_to(:foobar)
18
- end
19
- end
20
-
21
- describe "#method_missing" do
22
- it "enables tabpage_* function calls" do
23
- expect(tabpage.is_valid).to be(true)
24
- end
25
- end
26
-
27
- describe "#methods" do
28
- it "returns builtin methods" do
29
- expect(tabpage.methods).to include(:inspect)
30
- end
31
-
32
- it "returns api methods" do
33
- expect(tabpage.methods).to include(:get_windows)
34
- end
35
- end
36
- end
37
- end
@@ -1,37 +0,0 @@
1
- require "helper"
2
-
3
- module Neovim
4
- RSpec.describe Window do
5
- let(:window) { Neovim.attach_child(["-n", "-u", "NONE"]).current.window }
6
-
7
- describe "#respond_to?" do
8
- it "returns true for Window functions" do
9
- expect(window).to respond_to(:get_cursor)
10
- end
11
-
12
- it "returns true for Ruby functions" do
13
- expect(window).to respond_to(:inspect)
14
- end
15
-
16
- it "returns false otherwise" do
17
- expect(window).not_to respond_to(:foobar)
18
- end
19
- end
20
-
21
- describe "#method_missing" do
22
- it "enables window_* function calls" do
23
- expect(window.get_cursor).to eq([1, 0])
24
- end
25
- end
26
-
27
- describe "#methods" do
28
- it "returns builtin methods" do
29
- expect(window.methods).to include(:inspect)
30
- end
31
-
32
- it "returns api methods" do
33
- expect(window.methods).to include(:get_height)
34
- end
35
- end
36
- end
37
- end