neovim 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/bin/neovim-ruby-host +1 -2
- data/lib/neovim.rb +13 -12
- data/lib/neovim/api_info.rb +1 -1
- data/lib/neovim/async_session.rb +4 -6
- data/lib/neovim/buffer.rb +2 -62
- data/lib/neovim/client.rb +0 -4
- data/lib/neovim/current.rb +9 -0
- data/lib/neovim/event_loop.rb +14 -13
- data/lib/neovim/host.rb +17 -55
- data/lib/neovim/line_range.rb +61 -0
- data/lib/neovim/manifest.rb +62 -0
- data/lib/neovim/msgpack_stream.rb +3 -6
- data/lib/neovim/notification.rb +5 -1
- data/lib/neovim/plugin.rb +77 -35
- data/lib/neovim/request.rb +5 -1
- data/lib/neovim/version.rb +1 -1
- data/spec/acceptance/neovim-ruby-host_spec.rb +15 -24
- data/spec/helper.rb +2 -0
- data/spec/neovim/async_session_spec.rb +2 -2
- data/spec/neovim/buffer_spec.rb +3 -41
- data/spec/neovim/current_spec.rb +14 -0
- data/spec/neovim/event_loop_spec.rb +54 -6
- data/spec/neovim/host_spec.rb +7 -46
- data/spec/neovim/line_range_spec.rb +68 -0
- data/spec/neovim/manifest_spec.rb +113 -0
- data/spec/neovim/msgpack_stream_spec.rb +5 -3
- data/spec/neovim/plugin_spec.rb +33 -33
- data/spec/neovim/session_spec.rb +97 -18
- data/spec/neovim_spec.rb +13 -4
- metadata +9 -3
data/spec/neovim/host_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require "tempfile"
|
|
4
4
|
module Neovim
|
5
5
|
RSpec.describe Host do
|
6
6
|
describe ".load_from_files" do
|
7
|
-
it "loads the defined plugins" do
|
7
|
+
it "loads the defined plugins into a manifest" do
|
8
8
|
plug1 = Tempfile.open("plug1") do |f|
|
9
9
|
f.write("Neovim.plugin")
|
10
10
|
f.path
|
@@ -15,8 +15,11 @@ module Neovim
|
|
15
15
|
f.path
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
manifest = Manifest.new
|
19
|
+
|
20
|
+
expect(manifest).to receive(:register).exactly(3).times
|
21
|
+
host = Host.load_from_files([plug1, plug2], manifest)
|
22
|
+
expect(host.manifest).to eq(manifest)
|
20
23
|
end
|
21
24
|
|
22
25
|
it "doesn't load plugin code into the global namespace" do
|
@@ -31,49 +34,7 @@ module Neovim
|
|
31
34
|
end
|
32
35
|
|
33
36
|
describe "#run" do
|
34
|
-
|
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
|
-
mock_async_session = double(:async_session, :request => nil)
|
44
|
-
expect(AsyncSession).to receive(:new) { mock_async_session }
|
45
|
-
|
46
|
-
mock_client = double(:client)
|
47
|
-
expect(Client).to receive(:new) { mock_client }
|
48
|
-
|
49
|
-
expect(EventLoop).to receive(:stdio) { double(:event_loop) }
|
50
|
-
expect(MsgpackStream).to receive(:new) { double(:msgpack_stream) }
|
51
|
-
expect(Session).to receive(:new) { double(:session) }
|
52
|
-
|
53
|
-
expect(mock_async_session).to receive(:run) do |req_cb, not_cb|
|
54
|
-
mock_request = double(
|
55
|
-
:request,
|
56
|
-
:method_name => :Sync,
|
57
|
-
:arguments => [1, 2]
|
58
|
-
)
|
59
|
-
|
60
|
-
mock_notification = double(
|
61
|
-
:notification,
|
62
|
-
:method_name => :Async,
|
63
|
-
:arguments => [3, 4]
|
64
|
-
)
|
65
|
-
|
66
|
-
expect(sync_cb).to receive(:call).with(mock_client, 1, 2).and_call_original
|
67
|
-
expect(mock_request).to receive(:respond).with([mock_client, 1, 2])
|
68
|
-
req_cb.call(mock_request)
|
69
|
-
|
70
|
-
expect(async_cb).to receive(:call).with(mock_client, 3, 4).and_call_original
|
71
|
-
not_cb.call(mock_notification)
|
72
|
-
end
|
73
|
-
|
74
|
-
host = Host.new([plugin])
|
75
|
-
host.run
|
76
|
-
end
|
37
|
+
# TODO: Find a way to test this without excessive mocking
|
77
38
|
end
|
78
39
|
end
|
79
40
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Neovim
|
4
|
+
RSpec.describe LineRange do
|
5
|
+
let(:client) { Neovim.attach_child(["--headless", "-n", "-u", "NONE"]) }
|
6
|
+
let(:buffer) { client.current.buffer }
|
7
|
+
let(:line_range) { LineRange.new(buffer, 0, -1) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
client.command("normal i1")
|
11
|
+
client.command("normal o2")
|
12
|
+
client.command("normal o3")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "is enumerable" do
|
16
|
+
expect(line_range).to be_an(Enumerable)
|
17
|
+
expect(line_range).to respond_to(:each)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#to_a" do
|
21
|
+
it "returns lines as an array" do
|
22
|
+
expect(line_range.to_a).to eq(["1", "2", "3"])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#[]" do
|
27
|
+
it "accepts a single index" do
|
28
|
+
expect(line_range[1]).to eq("2")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "accepts an index and length" do
|
32
|
+
expect(line_range[0, 2].to_a).to eq(["1", "2"])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "accepts a range" do
|
36
|
+
expect(line_range[0..1].to_a).to eq(["1", "2"])
|
37
|
+
expect(line_range[0...1].to_a).to eq(["1"])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#[]=" do
|
42
|
+
it "accepts a single index" do
|
43
|
+
line_range[0] = "foo"
|
44
|
+
expect(line_range.to_a).to eq(["foo", "2", "3"])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "accepts an index and length" do
|
48
|
+
line_range[0, 2] = ["foo"]
|
49
|
+
expect(line_range.to_a).to eq(["foo", "3"])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "accepts a range" do
|
53
|
+
line_range[0..1] = ["foo"]
|
54
|
+
expect(line_range.to_a).to eq(["foo", "3"])
|
55
|
+
|
56
|
+
line_range[0...1] = ["bar"]
|
57
|
+
expect(line_range.to_a).to eq(["bar", "3"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#replace" do
|
62
|
+
it "replaces all lines" do
|
63
|
+
line_range.replace(["5", "6"])
|
64
|
+
expect(line_range.to_a).to eq(["5", "6"])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require "helper"
|
2
|
+
require "neovim/manifest"
|
3
|
+
require "neovim/plugin"
|
4
|
+
|
5
|
+
module Neovim
|
6
|
+
RSpec.describe Manifest do
|
7
|
+
it "has a default poll handler" do
|
8
|
+
manifest = Manifest.new
|
9
|
+
expect(manifest.handlers["poll"]).to respond_to(:call)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has default specs" do
|
13
|
+
manifest = Manifest.new
|
14
|
+
expect(manifest.specs).to eq({})
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#register" do
|
18
|
+
it "adds specs" do
|
19
|
+
manifest = Manifest.new
|
20
|
+
|
21
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
22
|
+
plug.command(:Foo)
|
23
|
+
end
|
24
|
+
|
25
|
+
expect {
|
26
|
+
manifest.register(plugin)
|
27
|
+
}.to change { manifest.specs }.from({}).to("source" => plugin.specs)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "adds plugin handlers" do
|
31
|
+
manifest = Manifest.new
|
32
|
+
|
33
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
34
|
+
plug.command(:Foo)
|
35
|
+
end
|
36
|
+
|
37
|
+
expect {
|
38
|
+
manifest.register(plugin)
|
39
|
+
}.to change {
|
40
|
+
manifest.handlers["source:command:Foo"]
|
41
|
+
}.from(nil).to(kind_of(Proc))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#handle" do
|
46
|
+
it "calls the poll handler" do
|
47
|
+
manifest = Manifest.new
|
48
|
+
message = double(:message, :method_name => "poll", :sync? => true)
|
49
|
+
client = double(:client)
|
50
|
+
|
51
|
+
expect(message).to receive(:respond).with("ok")
|
52
|
+
manifest.handle(message, client)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "calls the specs handler" do
|
56
|
+
manifest = Manifest.new
|
57
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
58
|
+
plug.command(:Foo)
|
59
|
+
end
|
60
|
+
manifest.register(plugin)
|
61
|
+
|
62
|
+
message = double(:message, :method_name => "specs", :sync? => true, :arguments => ["source"])
|
63
|
+
|
64
|
+
expect(message).to receive(:respond).with(plugin.specs)
|
65
|
+
manifest.handle(message, double(:client))
|
66
|
+
end
|
67
|
+
|
68
|
+
it "calls a plugin sync handler" do
|
69
|
+
manifest = Manifest.new
|
70
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
71
|
+
plug.command(:Foo, :sync => true) { |client, arg| [client, arg] }
|
72
|
+
end
|
73
|
+
manifest.register(plugin)
|
74
|
+
|
75
|
+
message = double(:message, :method_name => "source:command:Foo", :sync? => true, :arguments => [:arg])
|
76
|
+
client = double(:client)
|
77
|
+
|
78
|
+
expect(message).to receive(:respond).with([client, :arg])
|
79
|
+
manifest.handle(message, client)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "calls a plugin async handler" do
|
83
|
+
manifest = Manifest.new
|
84
|
+
async_proc = Proc.new {}
|
85
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
86
|
+
plug.command(:Foo, &async_proc)
|
87
|
+
end
|
88
|
+
manifest.register(plugin)
|
89
|
+
|
90
|
+
message = double(:message, :method_name => "source:command:Foo", :sync? => false, :arguments => [:arg])
|
91
|
+
client = double(:client)
|
92
|
+
|
93
|
+
expect(async_proc).to receive(:call).with(client, :arg)
|
94
|
+
manifest.handle(message, client)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "calls a default sync handler" do
|
98
|
+
manifest = Manifest.new
|
99
|
+
message = double(:message, :method_name => "foobar", :sync? => true)
|
100
|
+
|
101
|
+
expect(message).to receive(:error).with("Unknown request foobar")
|
102
|
+
manifest.handle(message, double(:client))
|
103
|
+
end
|
104
|
+
|
105
|
+
it "calls a default async handler" do
|
106
|
+
manifest = Manifest.new
|
107
|
+
message = double(:message, :method_name => "foobar", :sync? => false)
|
108
|
+
|
109
|
+
manifest.handle(message, double(:client))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -11,13 +11,15 @@ module Neovim
|
|
11
11
|
srv_thr = Thread.new do
|
12
12
|
client = server.accept
|
13
13
|
messages << client.readpartial(1024)
|
14
|
+
|
14
15
|
client.write(MessagePack.pack([2]))
|
16
|
+
client.close
|
17
|
+
server.close
|
15
18
|
end
|
16
19
|
|
17
20
|
fiber = Fiber.new do
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
+
msg_cb = Proc.new { |msg| Fiber.yield(msg) }
|
22
|
+
stream.send([1]).run(msg_cb)
|
21
23
|
end
|
22
24
|
|
23
25
|
expect(fiber.resume).to eq([2])
|
data/spec/neovim/plugin_spec.rb
CHANGED
@@ -6,60 +6,60 @@ module Neovim
|
|
6
6
|
it "registers a command" do
|
7
7
|
cmd_block = Proc.new {}
|
8
8
|
|
9
|
-
plugin = Plugin.from_config_block do |plug|
|
9
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
10
10
|
plug.command("Foo", :range => true, :nargs => 1, &cmd_block)
|
11
11
|
end
|
12
12
|
|
13
|
-
expect(plugin.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
expect(plugin.handlers.size).to be(1)
|
14
|
+
handler = plugin.handlers.first
|
15
|
+
|
16
|
+
expect(handler.block).to eq(cmd_block)
|
17
|
+
expect(handler.qualified_name).to eq("source:command:Foo")
|
18
|
+
expect(handler.to_spec).to eq(
|
19
|
+
:type => :command,
|
20
|
+
:name => "Foo",
|
21
|
+
:sync => false,
|
22
|
+
:opts => {:range => "", :nargs => 1},
|
23
23
|
)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "registers an autocmd" do
|
27
27
|
au_block = Proc.new {}
|
28
28
|
|
29
|
-
plugin = Plugin.from_config_block do |plug|
|
29
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
30
30
|
plug.autocmd("BufEnter", :pattern => "*.rb", &au_block)
|
31
31
|
end
|
32
32
|
|
33
|
-
expect(plugin.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
expect(plugin.handlers.size).to be(1)
|
34
|
+
handler = plugin.handlers.first
|
35
|
+
|
36
|
+
expect(handler.block).to eq(au_block)
|
37
|
+
expect(handler.qualified_name).to eq("source:autocmd:BufEnter:*.rb")
|
38
|
+
expect(handler.to_spec).to eq(
|
39
|
+
:type => :autocmd,
|
40
|
+
:name => "BufEnter",
|
41
|
+
:sync => false,
|
42
|
+
:opts => {:pattern => "*.rb"},
|
43
43
|
)
|
44
44
|
end
|
45
45
|
|
46
46
|
it "registers a function" do
|
47
47
|
fun_block = Proc.new {}
|
48
48
|
|
49
|
-
plugin = Plugin.from_config_block do |plug|
|
49
|
+
plugin = Plugin.from_config_block("source") do |plug|
|
50
50
|
plug.function("Foo", :range => true, :nargs => 1, &fun_block)
|
51
51
|
end
|
52
52
|
|
53
|
-
expect(plugin.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
expect(plugin.handlers.size).to be(1)
|
54
|
+
handler = plugin.handlers.first
|
55
|
+
|
56
|
+
expect(handler.block).to eq(fun_block)
|
57
|
+
expect(handler.qualified_name).to eq("source:function:Foo")
|
58
|
+
expect(handler.to_spec).to eq(
|
59
|
+
:type => :function,
|
60
|
+
:name => "Foo",
|
61
|
+
:sync => false,
|
62
|
+
:opts => {:range => "", :nargs => 1},
|
63
63
|
)
|
64
64
|
end
|
65
65
|
end
|
data/spec/neovim/session_spec.rb
CHANGED
@@ -1,33 +1,112 @@
|
|
1
1
|
require "helper"
|
2
|
+
require "securerandom"
|
3
|
+
require "fileutils"
|
2
4
|
|
3
5
|
module Neovim
|
4
6
|
RSpec.describe Session do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
shared_context "session behavior" do
|
8
|
+
it "supports functions with async=false" do
|
9
|
+
expect(session.request(:vim_strwidth, "foobar")).to be(6)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "supports functions with async=true" do
|
13
|
+
expect(session.request(:vim_input, "jk")).to be(2)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "raises an exception when there are errors" do
|
17
|
+
expect {
|
18
|
+
session.request(:vim_strwidth, "too", "many")
|
19
|
+
}.to raise_error(/wrong number of arguments/i)
|
20
|
+
end
|
11
21
|
|
12
|
-
|
13
|
-
|
22
|
+
it "handles large data" do
|
23
|
+
large_str = Array.new(1024 * 16) { SecureRandom.hex(1) }.join
|
24
|
+
session.request(:vim_set_current_line, large_str)
|
25
|
+
expect(session.request(:vim_get_current_line)).to eq(large_str)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#api_methods_for_prefix" do
|
29
|
+
it "returns relevant functions without a prefix" do
|
30
|
+
methods = session.api_methods_for_prefix("vim_")
|
31
|
+
expect(methods).to include(:strwidth)
|
32
|
+
end
|
33
|
+
end
|
14
34
|
end
|
15
35
|
|
16
|
-
|
17
|
-
|
36
|
+
context "tcp" do
|
37
|
+
let!(:nvim_port) do
|
38
|
+
server = TCPServer.new("0.0.0.0", 0)
|
39
|
+
server.addr[1].tap { server.close }
|
40
|
+
end
|
41
|
+
|
42
|
+
let!(:nvim_pid) do
|
43
|
+
Process.spawn(
|
44
|
+
{"NVIM_LISTEN_ADDRESS" => "0.0.0.0:#{nvim_port}"},
|
45
|
+
"#{ENV.fetch("NVIM_EXECUTABLE")} --headless -n -u NONE",
|
46
|
+
[:out, :err] => "/dev/null"
|
47
|
+
).tap do
|
48
|
+
begin
|
49
|
+
TCPSocket.open("0.0.0.0", nvim_port).close
|
50
|
+
rescue Errno::ECONNREFUSED
|
51
|
+
retry
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
after do
|
57
|
+
Process.kill(:TERM, nvim_pid)
|
58
|
+
Process.waitpid(nvim_pid)
|
59
|
+
end
|
60
|
+
|
61
|
+
let(:session) do
|
62
|
+
event_loop = EventLoop.tcp("0.0.0.0", nvim_port)
|
63
|
+
stream = MsgpackStream.new(event_loop)
|
64
|
+
async = AsyncSession.new(stream)
|
65
|
+
Session.new(async)
|
66
|
+
end
|
67
|
+
|
68
|
+
include_context "session behavior"
|
18
69
|
end
|
19
70
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
71
|
+
context "unix" do
|
72
|
+
let!(:nvim_pid) do
|
73
|
+
FileUtils.rm_f("/tmp/nvim-#$$.sock")
|
74
|
+
Process.spawn(
|
75
|
+
{"NVIM_LISTEN_ADDRESS" => "/tmp/nvim-#$$.sock"},
|
76
|
+
"#{ENV.fetch("NVIM_EXECUTABLE")} --headless -n -u NONE",
|
77
|
+
[:out, :err] => "/dev/null"
|
78
|
+
).tap do
|
79
|
+
loop do
|
80
|
+
break if File.exists?("/tmp/nvim-#$$.sock")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
after do
|
86
|
+
Process.kill(:TERM, nvim_pid)
|
87
|
+
Process.waitpid(nvim_pid)
|
88
|
+
FileUtils.rm_f("/tmp/nvim-#$$.sock")
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:session) do
|
92
|
+
event_loop = EventLoop.unix("/tmp/nvim-#$$.sock")
|
93
|
+
stream = MsgpackStream.new(event_loop)
|
94
|
+
async = AsyncSession.new(stream)
|
95
|
+
Session.new(async)
|
96
|
+
end
|
97
|
+
|
98
|
+
include_context "session behavior"
|
24
99
|
end
|
25
100
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
101
|
+
context "child" do
|
102
|
+
let(:session) do
|
103
|
+
event_loop = EventLoop.child(["-n", "-u", "NONE"])
|
104
|
+
stream = MsgpackStream.new(event_loop)
|
105
|
+
async = AsyncSession.new(stream)
|
106
|
+
Session.new(async)
|
30
107
|
end
|
108
|
+
|
109
|
+
include_context "session behavior"
|
31
110
|
end
|
32
111
|
end
|
33
112
|
end
|