neovim 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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