neovim 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|