neovim 0.2.5 → 0.3.0
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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +10 -0
- data/lib/neovim/current.rb +19 -2
- data/lib/neovim/host.rb +3 -11
- data/lib/neovim/host/loader.rb +0 -2
- data/lib/neovim/line_range.rb +14 -2
- data/lib/neovim/plugin.rb +1 -4
- data/lib/neovim/plugin/dsl.rb +6 -14
- data/lib/neovim/plugin/handler.rb +2 -9
- data/lib/neovim/ruby_provider.rb +9 -15
- data/lib/neovim/session.rb +23 -70
- data/lib/neovim/session/api.rb +61 -0
- data/lib/neovim/session/event_loop.rb +108 -0
- data/lib/neovim/session/notification.rb +19 -0
- data/lib/neovim/session/request.rb +31 -0
- data/lib/neovim/session/rpc.rb +95 -0
- data/lib/neovim/session/serializer.rb +62 -0
- data/lib/neovim/version.rb +1 -1
- data/neovim.gemspec +1 -1
- data/spec/acceptance/neovim-ruby-host_spec.rb +0 -5
- data/spec/acceptance/ruby_provider_spec.rb +35 -3
- data/spec/helper.rb +33 -2
- data/spec/neovim/host_spec.rb +1 -1
- data/spec/neovim/plugin_spec.rb +2 -2
- data/spec/neovim/session/api_spec.rb +46 -0
- data/spec/neovim/session/event_loop_spec.rb +99 -0
- data/spec/neovim/session/rpc_spec.rb +108 -0
- data/spec/neovim/session/serializer_spec.rb +48 -0
- data/spec/neovim/session_spec.rb +1 -1
- metadata +16 -18
- data/lib/neovim/api.rb +0 -80
- data/lib/neovim/async_session.rb +0 -119
- data/lib/neovim/event_loop.rb +0 -128
- data/lib/neovim/msgpack_stream.rb +0 -80
- data/lib/neovim/notification.rb +0 -17
- data/lib/neovim/request.rb +0 -29
- data/spec/neovim/api_spec.rb +0 -44
- data/spec/neovim/async_session_spec.rb +0 -106
- data/spec/neovim/event_loop_spec.rb +0 -97
- data/spec/neovim/msgpack_stream_spec.rb +0 -46
- data/spec/support.rb +0 -33
data/lib/neovim/api.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
module Neovim
|
2
|
-
# @api private
|
3
|
-
class API
|
4
|
-
attr_reader :channel_id
|
5
|
-
|
6
|
-
# Represents an unknown API. Used as a stand-in when the API hasn't been
|
7
|
-
# discovered yet via the +vim_get_api_info+ RPC call.
|
8
|
-
#
|
9
|
-
# @return [API]
|
10
|
-
def self.null
|
11
|
-
new([nil, {"functions" => [], "types" => []}])
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize(api_info)
|
15
|
-
@channel_id, @api_info = api_info
|
16
|
-
end
|
17
|
-
|
18
|
-
# Return all functions defined by the API.
|
19
|
-
#
|
20
|
-
# @return [Hash{String => Function}] A +Hash+ mapping function names to
|
21
|
-
# +Function+ objects.
|
22
|
-
# @see Function
|
23
|
-
def functions
|
24
|
-
@functions ||= @api_info.fetch("functions").inject({}) do |acc, func|
|
25
|
-
name, async = func.values_at("name", "async")
|
26
|
-
acc.merge(name => Function.new(name, async))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Return information about +nvim+ types. Used for registering MessagePack
|
31
|
-
# +ext+ types.
|
32
|
-
#
|
33
|
-
# @return [Hash]
|
34
|
-
def types
|
35
|
-
@types ||= @api_info.fetch("types")
|
36
|
-
end
|
37
|
-
|
38
|
-
# Return a list of functions with the given name prefix.
|
39
|
-
#
|
40
|
-
# @param prefix [String] The function prefix
|
41
|
-
# @return [Array<Function>]
|
42
|
-
def functions_with_prefix(prefix)
|
43
|
-
functions.inject([]) do |acc, (name, function)|
|
44
|
-
name =~ /\A#{prefix}/ ? acc.push(function) : acc
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Find a function with the given name.
|
49
|
-
#
|
50
|
-
# @param name [String] The name of the function
|
51
|
-
# @return [Function, nil]
|
52
|
-
def function(name)
|
53
|
-
functions[name.to_s]
|
54
|
-
end
|
55
|
-
|
56
|
-
# Truncate the output of inspect so console sessions are more pleasant.
|
57
|
-
#
|
58
|
-
# @return [String]
|
59
|
-
def inspect
|
60
|
-
"#<#{self.class}:0x%x @types={...} @functions={...}>" % (object_id << 1)
|
61
|
-
end
|
62
|
-
|
63
|
-
# Encapsulate an RPC function.
|
64
|
-
class Function < Struct.new(:name, :async)
|
65
|
-
# Apply this function to a running RPC session. Sends either a request if
|
66
|
-
# +async+ is +false+ or a notification if +async+ is +true+.
|
67
|
-
#
|
68
|
-
# @param session [Session] The session to apply the function to.
|
69
|
-
# @param *args [Array] Arguments to the function.
|
70
|
-
# @return [Object, nil]
|
71
|
-
def call(session, *args)
|
72
|
-
if async
|
73
|
-
session.notify(name, *args)
|
74
|
-
else
|
75
|
-
session.request(name, *args)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
data/lib/neovim/async_session.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
require "neovim/logging"
|
2
|
-
require "neovim/request"
|
3
|
-
require "neovim/notification"
|
4
|
-
|
5
|
-
module Neovim
|
6
|
-
# Handles formatting RPC requests and writing them to the
|
7
|
-
# +MsgpackStream+. This exposes an asynchronous API, in which responses
|
8
|
-
# are handled in callbacks.
|
9
|
-
#
|
10
|
-
# @api private
|
11
|
-
class AsyncSession
|
12
|
-
include Logging
|
13
|
-
|
14
|
-
attr_reader :msgpack_stream
|
15
|
-
|
16
|
-
def initialize(msgpack_stream)
|
17
|
-
@msgpack_stream = msgpack_stream
|
18
|
-
@request_id = 0
|
19
|
-
@pending_requests = {}
|
20
|
-
end
|
21
|
-
|
22
|
-
# Send an RPC request and enqueue it's callback to be called when a
|
23
|
-
# response is received.
|
24
|
-
#
|
25
|
-
# @param method [Symbol, String] The RPC method name
|
26
|
-
# @param *args [Array] The arguments to the RPC method
|
27
|
-
# @return [self]
|
28
|
-
# @example
|
29
|
-
# async_session.request(:vim_strwidth, "foobar") do |response|
|
30
|
-
# $stderr.puts("Got a response #{response}")
|
31
|
-
# end
|
32
|
-
def request(method, *args, &response_cb)
|
33
|
-
reqid = @request_id
|
34
|
-
@request_id += 1
|
35
|
-
|
36
|
-
@msgpack_stream.write([0, reqid, method, args])
|
37
|
-
@pending_requests[reqid] = response_cb || Proc.new {}
|
38
|
-
self
|
39
|
-
end
|
40
|
-
|
41
|
-
# Send an RPC notification. Notifications don't receive a response
|
42
|
-
# from +nvim+.
|
43
|
-
#
|
44
|
-
# @param method [Symbol, String] The RPC method name
|
45
|
-
# @param *args [Array] The arguments to the RPC method
|
46
|
-
# @return [self]
|
47
|
-
# @example
|
48
|
-
# async_session.notify(:vim_input, "jk")
|
49
|
-
def notify(method, *args)
|
50
|
-
@msgpack_stream.write([2, method, args])
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
|
-
# Run the event loop, yielding received RPC messages to the block. RPC
|
55
|
-
# requests and notifications from +nvim+ will be wrapped in +Request+
|
56
|
-
# and +Notification+ objects, respectively, and responses will be
|
57
|
-
# passed to their callbacks with optional errors.
|
58
|
-
#
|
59
|
-
# @param session [Session] The current session
|
60
|
-
# @yield [Object]
|
61
|
-
# @return [void]
|
62
|
-
# @see MsgpackStream#run
|
63
|
-
# @see EventLoop#run
|
64
|
-
def run(&callback)
|
65
|
-
@msgpack_stream.run do |msg|
|
66
|
-
debug("received #{msg.inspect}")
|
67
|
-
kind, *payload = msg
|
68
|
-
|
69
|
-
case kind
|
70
|
-
when 0
|
71
|
-
handle_request(payload, callback)
|
72
|
-
when 1
|
73
|
-
handle_response(payload)
|
74
|
-
when 2
|
75
|
-
handle_notification(payload, callback)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
rescue => e
|
79
|
-
fatal("got unexpected error #{e.inspect}")
|
80
|
-
debug(e.backtrace.join("\n"))
|
81
|
-
end
|
82
|
-
|
83
|
-
# Stop the event loop.
|
84
|
-
#
|
85
|
-
# @return [void]
|
86
|
-
# @see EventLoop#stop
|
87
|
-
def stop
|
88
|
-
@msgpack_stream.stop
|
89
|
-
end
|
90
|
-
|
91
|
-
# Shut down the event loop.
|
92
|
-
#
|
93
|
-
# @return [void]
|
94
|
-
# @see EventLoop#shutdown
|
95
|
-
def shutdown
|
96
|
-
@msgpack_stream.shutdown
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
def handle_request(payload, callback)
|
102
|
-
callback ||= Proc.new {}
|
103
|
-
reqid, method, args = payload
|
104
|
-
callback.call(Request.new(method, args, @msgpack_stream, reqid))
|
105
|
-
end
|
106
|
-
|
107
|
-
def handle_response(payload)
|
108
|
-
reqid, (_, error), result = payload
|
109
|
-
callback = @pending_requests.delete(reqid) || Proc.new {}
|
110
|
-
callback.call(error, result)
|
111
|
-
end
|
112
|
-
|
113
|
-
def handle_notification(payload, callback)
|
114
|
-
callback ||= Proc.new {}
|
115
|
-
method, args = payload
|
116
|
-
callback.call(Notification.new(method, args))
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
data/lib/neovim/event_loop.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
require "neovim/logging"
|
2
|
-
require "socket"
|
3
|
-
|
4
|
-
module Neovim
|
5
|
-
# The lowest level interface to reading from and writing to +nvim+.
|
6
|
-
#
|
7
|
-
# @api private
|
8
|
-
class EventLoop
|
9
|
-
include Logging
|
10
|
-
|
11
|
-
private_class_method :new
|
12
|
-
|
13
|
-
# Connect to a TCP socket.
|
14
|
-
#
|
15
|
-
# @param host [String] The hostname or IP address
|
16
|
-
# @param port [Fixnum] The port
|
17
|
-
# @return [EventLoop]
|
18
|
-
def self.tcp(host, port)
|
19
|
-
socket = TCPSocket.new(host, port)
|
20
|
-
new(socket)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Connect to a UNIX domain socket.
|
24
|
-
#
|
25
|
-
# @param path [String] The socket path
|
26
|
-
# @return [EventLoop]
|
27
|
-
def self.unix(path)
|
28
|
-
socket = UNIXSocket.new(path)
|
29
|
-
new(socket)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Spawn and connect to a child +nvim+ process.
|
33
|
-
#
|
34
|
-
# @param argv [Array] The arguments to pass to the spawned process
|
35
|
-
# @return [EventLoop]
|
36
|
-
def self.child(argv)
|
37
|
-
io = IO.popen(argv | ["--embed"], "rb+").tap do |_io|
|
38
|
-
Process.detach(_io.pid)
|
39
|
-
end
|
40
|
-
|
41
|
-
new(io)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Connect to the current process's standard streams. This is used to
|
45
|
-
# promote the current process to a Ruby plugin host.
|
46
|
-
#
|
47
|
-
# @return [EventLoop]
|
48
|
-
def self.stdio
|
49
|
-
new(STDIN, STDOUT)
|
50
|
-
end
|
51
|
-
|
52
|
-
def initialize(rd, wr=rd)
|
53
|
-
@rd, @wr = rd, wr
|
54
|
-
@running = false
|
55
|
-
end
|
56
|
-
|
57
|
-
# Write data to the underlying +IO+. This will block until all the
|
58
|
-
# data has been written.
|
59
|
-
#
|
60
|
-
# @param data [String] The data to write (typically message-packed)
|
61
|
-
# @return [self]
|
62
|
-
def write(data)
|
63
|
-
start = 0
|
64
|
-
size = data.size
|
65
|
-
debug("writing #{data.inspect}")
|
66
|
-
|
67
|
-
begin
|
68
|
-
while start < size
|
69
|
-
start += @wr.write_nonblock(data[start..-1])
|
70
|
-
end
|
71
|
-
self
|
72
|
-
rescue IO::WaitWritable
|
73
|
-
IO.select(nil, [@wr], nil, 1)
|
74
|
-
retry
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# Run the event loop, reading from the underlying +IO+ and yielding
|
79
|
-
# received messages to the block.
|
80
|
-
#
|
81
|
-
# @yield [String]
|
82
|
-
# @return [void]
|
83
|
-
def run
|
84
|
-
@running = true
|
85
|
-
|
86
|
-
loop do
|
87
|
-
break unless @running
|
88
|
-
message = @rd.readpartial(1024 * 16)
|
89
|
-
debug("received #{message.inspect}")
|
90
|
-
yield message if block_given?
|
91
|
-
end
|
92
|
-
rescue EOFError
|
93
|
-
info("got EOFError")
|
94
|
-
rescue => e
|
95
|
-
fatal("got unexpected error #{e.inspect}")
|
96
|
-
debug(e.backtrace.join("\n"))
|
97
|
-
end
|
98
|
-
|
99
|
-
# Stop the event loop.
|
100
|
-
#
|
101
|
-
# @return [void]
|
102
|
-
def stop
|
103
|
-
@running = false
|
104
|
-
end
|
105
|
-
|
106
|
-
# Stop the event loop and close underlying +IO+s.
|
107
|
-
#
|
108
|
-
# @return [void]
|
109
|
-
def shutdown
|
110
|
-
stop
|
111
|
-
|
112
|
-
[@rd, @wr].each do |io|
|
113
|
-
begin
|
114
|
-
io.close
|
115
|
-
rescue IOError
|
116
|
-
end
|
117
|
-
|
118
|
-
begin
|
119
|
-
if pid = io.pid
|
120
|
-
Process.kill(:TERM, pid)
|
121
|
-
Process.waitpid(pid)
|
122
|
-
end
|
123
|
-
rescue IOError, Errno::ESRCH, Errno::ECHILD
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require "neovim/logging"
|
2
|
-
require "msgpack"
|
3
|
-
|
4
|
-
module Neovim
|
5
|
-
# Handles serializing RPC messages to MessagePack and passing them to
|
6
|
-
# the event loop.
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
class MsgpackStream
|
10
|
-
include Logging
|
11
|
-
|
12
|
-
def initialize(event_loop)
|
13
|
-
@event_loop = event_loop
|
14
|
-
@unpacker = MessagePack::Unpacker.new
|
15
|
-
end
|
16
|
-
|
17
|
-
# Serialize an RPC message to and write it to the event loop.
|
18
|
-
#
|
19
|
-
# @param msg [Array] The RPC message
|
20
|
-
# @return [self]
|
21
|
-
# @example Write an RPC request
|
22
|
-
# msgpack_stream.write([0, 1, :vim_strwidth, ["foobar"]])
|
23
|
-
def write(msg)
|
24
|
-
debug("writing #{msg.inspect}")
|
25
|
-
@event_loop.write(MessagePack.pack(msg))
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
# Run the event loop, yielding deserialized messages to the block.
|
30
|
-
#
|
31
|
-
# @param session [Session] Used for registering msgpack +ext+ types as
|
32
|
-
# described by the +vim_get_api_info+ call
|
33
|
-
# @return [void]
|
34
|
-
# @see EventLoop#run
|
35
|
-
def run
|
36
|
-
@event_loop.run do |data|
|
37
|
-
@unpacker.feed_each(data) do |msg|
|
38
|
-
debug("received #{msg.inspect}")
|
39
|
-
yield msg if block_given?
|
40
|
-
end
|
41
|
-
end
|
42
|
-
rescue => e
|
43
|
-
fatal("got unexpected error #{e.inspect}")
|
44
|
-
debug(e.backtrace.join("\n"))
|
45
|
-
end
|
46
|
-
|
47
|
-
# Stop the event loop.
|
48
|
-
#
|
49
|
-
# @return [void]
|
50
|
-
# @see EventLoop#stop
|
51
|
-
def stop
|
52
|
-
@event_loop.stop
|
53
|
-
end
|
54
|
-
|
55
|
-
# Shut down the event loop.
|
56
|
-
#
|
57
|
-
# @return [void]
|
58
|
-
# @see EventLoop#shutdown
|
59
|
-
def shutdown
|
60
|
-
@event_loop.shutdown
|
61
|
-
end
|
62
|
-
|
63
|
-
# Register msgpack ext types using the provided API and session
|
64
|
-
#
|
65
|
-
# @param api [API]
|
66
|
-
# @param session [Session]
|
67
|
-
# @see Session#discover_api
|
68
|
-
def register_types(api, session)
|
69
|
-
info("registering msgpack ext types")
|
70
|
-
api.types.each do |type, info|
|
71
|
-
klass = Neovim.const_get(type)
|
72
|
-
id = info.fetch("id")
|
73
|
-
|
74
|
-
@unpacker.register_type(id) do |data|
|
75
|
-
klass.new(MessagePack.unpack(data), session)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
data/lib/neovim/notification.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module Neovim
|
2
|
-
# An asynchronous message from +nvim+.
|
3
|
-
#
|
4
|
-
# @api private
|
5
|
-
class Notification
|
6
|
-
attr_reader :method_name, :arguments
|
7
|
-
|
8
|
-
def initialize(method_name, args)
|
9
|
-
@method_name = method_name.to_s
|
10
|
-
@arguments = args
|
11
|
-
end
|
12
|
-
|
13
|
-
def sync?
|
14
|
-
false
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/neovim/request.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
module Neovim
|
2
|
-
# A synchronous message from +nvim+.
|
3
|
-
#
|
4
|
-
# @api private
|
5
|
-
class Request
|
6
|
-
attr_reader :method_name, :arguments
|
7
|
-
|
8
|
-
def initialize(method_name, args, msgpack_stream, request_id)
|
9
|
-
@method_name = method_name.to_s
|
10
|
-
@arguments = args
|
11
|
-
@msgpack_stream = msgpack_stream
|
12
|
-
@request_id = request_id
|
13
|
-
end
|
14
|
-
|
15
|
-
def sync?
|
16
|
-
true
|
17
|
-
end
|
18
|
-
|
19
|
-
def respond(value)
|
20
|
-
@msgpack_stream.write([1, @request_id, nil, value])
|
21
|
-
self
|
22
|
-
end
|
23
|
-
|
24
|
-
def error(message)
|
25
|
-
@msgpack_stream.write([1, @request_id, message, nil])
|
26
|
-
self
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|