neovim 0.0.1
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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +18 -0
- data/.gitmodules +4 -0
- data/.travis.yml +30 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +26 -0
- data/lib/neovim.rb +31 -0
- data/lib/neovim/api_info.rb +27 -0
- data/lib/neovim/async_session.rb +75 -0
- data/lib/neovim/client.rb +29 -0
- data/lib/neovim/current.rb +44 -0
- data/lib/neovim/event_loop.rb +75 -0
- data/lib/neovim/msgpack_stream.rb +44 -0
- data/lib/neovim/object.rb +44 -0
- data/lib/neovim/session.rb +35 -0
- data/lib/neovim/version.rb +3 -0
- data/neovim.gemspec +27 -0
- data/spec/helper.rb +26 -0
- data/spec/neovim/async_session_spec.rb +96 -0
- data/spec/neovim/buffer_spec.rb +37 -0
- data/spec/neovim/client_spec.rb +57 -0
- data/spec/neovim/current_spec.rb +78 -0
- data/spec/neovim/event_loop_spec.rb +58 -0
- data/spec/neovim/msgpack_stream_spec.rb +29 -0
- data/spec/neovim/session_spec.rb +29 -0
- data/spec/neovim/tabpage_spec.rb +37 -0
- data/spec/neovim/window_spec.rb +37 -0
- data/spec/neovim_spec.rb +54 -0
- metadata +169 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require "msgpack"
|
2
|
+
|
3
|
+
module Neovim
|
4
|
+
class MsgpackStream
|
5
|
+
def initialize(event_loop)
|
6
|
+
@event_loop = event_loop
|
7
|
+
@unpacker = MessagePack::Unpacker.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def register_session(session)
|
11
|
+
session.api_info.types.each do |type, info|
|
12
|
+
klass = Neovim.const_get(type)
|
13
|
+
id = info.fetch("id")
|
14
|
+
|
15
|
+
@unpacker.register_type(id) do |data|
|
16
|
+
klass.new(MessagePack.unpack(data), session)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def send(msg)
|
22
|
+
@event_loop.send(MessagePack.pack(msg))
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def run(&message_cb)
|
27
|
+
@event_loop.run do |data|
|
28
|
+
@unpacker.feed_each(data) do |msg|
|
29
|
+
message_cb.call(msg)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
@event_loop.stop
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def shutdown
|
40
|
+
@event_loop.shutdown
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Neovim
|
2
|
+
class Object
|
3
|
+
attr_reader :index
|
4
|
+
|
5
|
+
def initialize(index, session)
|
6
|
+
@index = index
|
7
|
+
@session = session
|
8
|
+
end
|
9
|
+
|
10
|
+
def respond_to?(method_name)
|
11
|
+
super || methods.include?(method_name.to_sym)
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args)
|
15
|
+
if methods.include?(method_name)
|
16
|
+
@session.request(qualify(method_name), @index, *args)
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_msgpack(packer)
|
23
|
+
packer.pack(@index)
|
24
|
+
end
|
25
|
+
|
26
|
+
def methods
|
27
|
+
super | @session.api_methods_for_prefix(function_prefix)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def function_prefix
|
33
|
+
"#{self.class.to_s.split("::").last.downcase}_"
|
34
|
+
end
|
35
|
+
|
36
|
+
def qualify(method_name)
|
37
|
+
:"#{function_prefix}#{method_name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Buffer = Class.new(Object)
|
42
|
+
Tabpage = Class.new(Object)
|
43
|
+
Window = Class.new(Object)
|
44
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "neovim/api_info"
|
2
|
+
|
3
|
+
module Neovim
|
4
|
+
class Session
|
5
|
+
attr_reader :api_info
|
6
|
+
|
7
|
+
def initialize(async_session)
|
8
|
+
@async_session = async_session
|
9
|
+
|
10
|
+
@api_info = APIInfo.new(request(:vim_get_api_info))
|
11
|
+
@async_session.register_session(self)
|
12
|
+
end
|
13
|
+
|
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
|
21
|
+
|
22
|
+
err ? raise(ArgumentError, err) : res
|
23
|
+
end
|
24
|
+
|
25
|
+
def api_methods_for_prefix(prefix)
|
26
|
+
@api_info.functions.inject([]) do |acc, function|
|
27
|
+
if function["name"] =~ /\A#{prefix}/
|
28
|
+
acc + [function["name"].sub(/\A#{prefix}/, "").to_sym]
|
29
|
+
else
|
30
|
+
acc
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/neovim.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "neovim/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "neovim"
|
8
|
+
spec.version = Neovim::VERSION
|
9
|
+
spec.authors = ["Alex Genco"]
|
10
|
+
spec.email = ["alexgenco@gmail.com"]
|
11
|
+
spec.summary = %q{A Ruby client for Neovim}
|
12
|
+
spec.homepage = "https://github.com/alexgenco/neovim-ruby"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "msgpack", "0.7.0dev1"
|
21
|
+
spec.add_dependency "eventmachine", "~> 1.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
spec.add_development_dependency "coveralls", "~> 0.7.2"
|
27
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
require "neovim"
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
if ENV["REPORT_COVERAGE"]
|
7
|
+
require "coveralls"
|
8
|
+
Coveralls.wear!
|
9
|
+
end
|
10
|
+
|
11
|
+
ENV["NVIM_EXECUTABLE"] = File.expand_path("../../vendor/neovim/build/bin/nvim", __FILE__)
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.expect_with :rspec do |exp|
|
15
|
+
exp.syntax = :expect
|
16
|
+
end
|
17
|
+
|
18
|
+
config.disable_monkey_patching!
|
19
|
+
config.order = :random
|
20
|
+
|
21
|
+
Kernel.srand config.seed
|
22
|
+
|
23
|
+
config.around do |spec|
|
24
|
+
Timeout.timeout(1) { spec.run }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Neovim
|
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
|
20
|
+
end
|
21
|
+
|
22
|
+
req_cb = Proc.new do |*payload|
|
23
|
+
messages << payload
|
24
|
+
async.shutdown
|
25
|
+
end
|
26
|
+
|
27
|
+
async.run(req_cb)
|
28
|
+
srv_thr.join
|
29
|
+
|
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
|
34
|
+
|
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
|
+
|
52
|
+
not_cb = Proc.new do |*payload|
|
53
|
+
messages << payload
|
54
|
+
async.shutdown
|
55
|
+
end
|
56
|
+
|
57
|
+
async.run(nil, not_cb)
|
58
|
+
srv_thr.join
|
59
|
+
|
60
|
+
expect(messages).to eq([["func", [1, 2, 3]]])
|
61
|
+
end
|
62
|
+
|
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 = []
|
69
|
+
|
70
|
+
srv_thr = Thread.new do
|
71
|
+
client = server.accept
|
72
|
+
messages << client.read_nonblock(1024)
|
73
|
+
|
74
|
+
client.write(MessagePack.pack(
|
75
|
+
[1, 0, [0, "error"], "result"]
|
76
|
+
))
|
77
|
+
|
78
|
+
client.close
|
79
|
+
server.close
|
80
|
+
end
|
81
|
+
|
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
|
86
|
+
end
|
87
|
+
|
88
|
+
async.run
|
89
|
+
srv_thr.join
|
90
|
+
|
91
|
+
expect(messages).to eq(
|
92
|
+
[MessagePack.pack([0, 0, "func", [1, 2, 3]])]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Neovim
|
4
|
+
RSpec.describe Client do
|
5
|
+
let(:client) { Neovim.attach_child(["-n", "-u", "NONE"]) }
|
6
|
+
|
7
|
+
describe "#respond_to?" do
|
8
|
+
it "returns true for vim functions" do
|
9
|
+
expect(client).to respond_to(:strwidth)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns true for Ruby functions" do
|
13
|
+
expect(client).to respond_to(:inspect)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns false otherwise" do
|
17
|
+
expect(client).not_to respond_to(:foobar)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#method_missing" do
|
22
|
+
it "enables vim_* function calls" do
|
23
|
+
expect(client.strwidth("hi")).to eq(2)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "raises exceptions for unknown methods" do
|
27
|
+
expect {
|
28
|
+
client.foobar
|
29
|
+
}.to raise_error(NoMethodError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "raises exceptions for incorrect usage" do
|
33
|
+
expect {
|
34
|
+
client.strwidth("too", "many")
|
35
|
+
}.to raise_error("Wrong number of arguments: expecting 1 but got 2")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#methods" do
|
40
|
+
it "includes builtin methods" do
|
41
|
+
expect(client.methods).to include(:inspect)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "includes api methods" do
|
45
|
+
expect(client.methods).to include(:strwidth)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#current" do
|
50
|
+
it "returns the target" do
|
51
|
+
expect(client.current.buffer).to be_a(Buffer)
|
52
|
+
expect(client.current.window).to be_a(Window)
|
53
|
+
expect(client.current.tabpage).to be_a(Tabpage)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Neovim
|
4
|
+
RSpec.describe Current do
|
5
|
+
let(:client) { Neovim.attach_child(["-n", "-u", "NONE"]) }
|
6
|
+
let(:current) { client.current }
|
7
|
+
|
8
|
+
describe "#line" do
|
9
|
+
it "returns an empty string if the current line is empty" do
|
10
|
+
expect(current.line).to eq("")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns the contents of the line" do
|
14
|
+
current.line = "New content"
|
15
|
+
expect(current.line).to eq("New content")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#line=" do
|
20
|
+
it "sets the content of the current line" do
|
21
|
+
current.line = "New content"
|
22
|
+
expect(current.line).to eq("New content")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#buffer" do
|
27
|
+
it "returns the current buffer" do
|
28
|
+
expect(current.buffer).to be_a(Buffer)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#buffer=" do
|
33
|
+
it "sets the current buffer" do
|
34
|
+
initial_index = current.buffer.index
|
35
|
+
client.command("vnew")
|
36
|
+
|
37
|
+
expect {
|
38
|
+
current.buffer = initial_index
|
39
|
+
}.to change { current.buffer.index }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#window" do
|
44
|
+
it "returns the current window" do
|
45
|
+
expect(current.window).to be_a(Window)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#window=" do
|
50
|
+
it "sets the current window" do
|
51
|
+
client.command("vsp")
|
52
|
+
expect(current.window.index).not_to eq(1)
|
53
|
+
|
54
|
+
expect {
|
55
|
+
current.window = 1
|
56
|
+
}.to change { current.window.index }.to(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#tabpage" do
|
61
|
+
it "returns the current tabpage" do
|
62
|
+
expect(current.tabpage).to be_a(Tabpage)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#tabpage=" do
|
67
|
+
it "sets the current tabpage" do
|
68
|
+
initial_index = current.tabpage.index
|
69
|
+
client.command("tabnew")
|
70
|
+
expect(current.tabpage.index).not_to eq(initial_index)
|
71
|
+
|
72
|
+
expect {
|
73
|
+
current.tabpage = initial_index
|
74
|
+
}.to change { current.tabpage.index }.to(initial_index)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|