neovim 0.6.2 → 0.7.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 +5 -5
- data/.gitignore +1 -1
- data/.travis.yml +2 -3
- data/CHANGELOG.md +29 -0
- data/Gemfile +0 -11
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/appveyor.yml +9 -1
- data/lib/neovim.rb +3 -3
- data/lib/neovim/client.rb +2 -3
- data/lib/neovim/connection.rb +69 -0
- data/lib/neovim/event_loop.rb +34 -34
- data/lib/neovim/host.rb +47 -51
- data/lib/neovim/host/loader.rb +1 -4
- data/lib/neovim/logging.rb +8 -8
- data/lib/neovim/message.rb +70 -0
- data/lib/neovim/plugin.rb +0 -3
- data/lib/neovim/plugin/handler.rb +6 -6
- data/lib/neovim/remote_object.rb +1 -1
- data/lib/neovim/ruby_provider.rb +25 -14
- data/lib/neovim/session.rb +36 -43
- data/lib/neovim/version.rb +1 -1
- data/neovim.gemspec +4 -0
- data/spec/acceptance/runtime/rplugin/ruby/autocmds.rb +3 -3
- data/spec/acceptance/runtime/rplugin/ruby/commands.rb +15 -15
- data/spec/acceptance/runtime/rplugin/ruby/functions.rb +5 -5
- data/spec/acceptance_spec.rb +19 -16
- data/spec/helper.rb +15 -0
- data/spec/neovim/connection_spec.rb +79 -0
- data/spec/neovim/event_loop_spec.rb +36 -50
- data/spec/neovim/host/loader_spec.rb +16 -9
- data/spec/neovim/host_spec.rb +82 -92
- data/spec/neovim/logging_spec.rb +6 -6
- data/spec/neovim/message_spec.rb +119 -0
- data/spec/neovim/plugin_spec.rb +31 -31
- data/spec/neovim/session_spec.rb +1 -1
- metadata +38 -15
- data/lib/neovim/event_loop/connection.rb +0 -78
- data/lib/neovim/event_loop/message_builder.rb +0 -127
- data/lib/neovim/event_loop/serializer.rb +0 -37
- data/spec/acceptance/runtime/rplugin.vim +0 -37
- data/spec/neovim/event_loop/connection_spec.rb +0 -89
- data/spec/neovim/event_loop/message_builder_spec.rb +0 -105
- data/spec/neovim/event_loop/serializer_spec.rb +0 -63
data/lib/neovim/host.rb
CHANGED
@@ -8,67 +8,57 @@ module Neovim
|
|
8
8
|
class Host
|
9
9
|
include Logging
|
10
10
|
|
11
|
-
|
12
|
-
# which is spawned by +nvim+ to discover and run Ruby plugins, and acts as
|
13
|
-
# the bridge between +nvim+ and the plugin.
|
14
|
-
def self.run(rplugin_paths, event_loop=EventLoop.stdio)
|
15
|
-
client = Client.from_event_loop(event_loop)
|
11
|
+
attr_reader :plugins
|
16
12
|
|
17
|
-
|
13
|
+
def self.run(rplugin_paths, event_loop=EventLoop.stdio)
|
14
|
+
new(event_loop).tap do |host|
|
18
15
|
Loader.new(host).load(rplugin_paths)
|
19
16
|
end.run
|
20
17
|
end
|
21
18
|
|
22
|
-
def initialize(
|
23
|
-
@
|
24
|
-
@session =
|
19
|
+
def initialize(event_loop)
|
20
|
+
@event_loop = event_loop
|
21
|
+
@session = Session.new(event_loop)
|
25
22
|
@handlers = {"poll" => poll_handler, "specs" => specs_handler}
|
23
|
+
@plugins = []
|
26
24
|
@specs = {}
|
27
25
|
end
|
28
26
|
|
29
|
-
# Register a +Plugin+ to receive +Host+ messages.
|
30
|
-
def register(plugin)
|
31
|
-
plugin.handlers.each do |handler|
|
32
|
-
@handlers[handler.qualified_name] = wrap_plugin_handler(handler)
|
33
|
-
end
|
34
|
-
|
35
|
-
plugin.setup(@client)
|
36
|
-
@specs[plugin.source] = plugin.specs
|
37
|
-
end
|
38
|
-
|
39
|
-
# Run the event loop, passing received messages to the appropriate handler.
|
40
27
|
def run
|
41
28
|
@session.run { |msg| handle(msg) }
|
42
|
-
ensure
|
43
|
-
@client.shutdown
|
44
|
-
@session.shutdown
|
45
29
|
end
|
46
30
|
|
47
|
-
# Handle messages received from the host. Sends a +Neovim::Client+ along
|
48
|
-
# with the message to be used in plugin callbacks.
|
49
31
|
def handle(message)
|
50
32
|
log(:debug) { message.to_h }
|
51
33
|
|
52
34
|
@handlers.
|
53
35
|
fetch(message.method_name, default_handler).
|
54
36
|
call(@client, message)
|
55
|
-
rescue
|
56
|
-
log_exception(:
|
57
|
-
|
58
|
-
|
59
|
-
|
37
|
+
rescue Exception => e
|
38
|
+
log_exception(:error, e, __method__)
|
39
|
+
|
40
|
+
if message.sync?
|
41
|
+
@session.respond(message.id, nil, e.message)
|
42
|
+
else
|
43
|
+
@client.err_writeln("Exception handling #{message.method_name}: (#{e.class}) #{e.message}")
|
44
|
+
end
|
45
|
+
|
46
|
+
raise unless StandardError === e
|
60
47
|
end
|
61
48
|
|
62
49
|
private
|
63
50
|
|
64
51
|
def poll_handler
|
65
|
-
@poll_handler ||=
|
52
|
+
@poll_handler ||= -> (_, req) {
|
53
|
+
initialize_client(req.id)
|
54
|
+
initialize_plugins
|
55
|
+
|
66
56
|
@session.respond(req.id, "ok")
|
67
|
-
|
57
|
+
}
|
68
58
|
end
|
69
59
|
|
70
60
|
def specs_handler
|
71
|
-
@specs_handler ||=
|
61
|
+
@specs_handler ||= -> (_, req) {
|
72
62
|
source = req.arguments.fetch(0)
|
73
63
|
|
74
64
|
if @specs.key?(source)
|
@@ -76,33 +66,39 @@ module Neovim
|
|
76
66
|
else
|
77
67
|
@session.respond(req.id, nil, "Unknown plugin #{source}")
|
78
68
|
end
|
79
|
-
|
69
|
+
}
|
80
70
|
end
|
81
71
|
|
82
72
|
def default_handler
|
83
|
-
@default_handler ||=
|
73
|
+
@default_handler ||= -> (_, message) {
|
84
74
|
next unless message.sync?
|
85
75
|
@session.respond(message.id, nil, "Unknown request #{message.method_name}")
|
86
|
-
|
76
|
+
}
|
87
77
|
end
|
88
78
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
if message.sync?
|
100
|
-
@session.respond(message.id, nil, e.message)
|
101
|
-
else
|
102
|
-
client.err_writeln("#{handler.qualified_name}: (#{e.class}) #{e.message}")
|
103
|
-
end
|
79
|
+
def initialize_client(request_id)
|
80
|
+
@session.request_id = request_id
|
81
|
+
@client = Client.from_event_loop(@event_loop, @session)
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize_plugins
|
85
|
+
@plugins.each do |plugin|
|
86
|
+
plugin.handlers.each do |handler|
|
87
|
+
@handlers[handler.qualified_name] = wrap_plugin_handler(handler)
|
104
88
|
end
|
89
|
+
|
90
|
+
plugin.setup(@client)
|
91
|
+
@specs[plugin.source] = plugin.specs
|
105
92
|
end
|
106
93
|
end
|
94
|
+
|
95
|
+
def wrap_plugin_handler(handler)
|
96
|
+
-> (client, message) {
|
97
|
+
args = message.arguments.flatten(1)
|
98
|
+
result = handler.call(client, *args)
|
99
|
+
|
100
|
+
@session.respond(message.id, result) if message.sync?
|
101
|
+
}
|
102
|
+
end
|
107
103
|
end
|
108
104
|
end
|
data/lib/neovim/host/loader.rb
CHANGED
@@ -8,9 +8,6 @@ module Neovim
|
|
8
8
|
@host = host
|
9
9
|
end
|
10
10
|
|
11
|
-
# Load the provided Ruby files while temporarily overriding
|
12
|
-
# +Neovim.plugin+ to expose the remote plugin DSL and register the result
|
13
|
-
# to the host.
|
14
11
|
def load(paths)
|
15
12
|
paths.each do |path|
|
16
13
|
override_plugin_method(path) do
|
@@ -27,7 +24,7 @@ module Neovim
|
|
27
24
|
|
28
25
|
Neovim.define_singleton_method(:plugin) do |&block|
|
29
26
|
plugin = Plugin.from_config_block(path, &block)
|
30
|
-
at_host.
|
27
|
+
at_host.plugins << plugin
|
31
28
|
end
|
32
29
|
|
33
30
|
yield
|
data/lib/neovim/logging.rb
CHANGED
@@ -43,16 +43,16 @@ module Neovim
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def self.json_formatter
|
46
|
-
|
46
|
+
-> (level, time, _, fields) {
|
47
47
|
require "multi_json"
|
48
48
|
|
49
49
|
MultiJson.encode(
|
50
50
|
{
|
51
|
-
:
|
52
|
-
:
|
51
|
+
_level: level,
|
52
|
+
_time: time.strftime(TIMESTAMP_FORMAT)
|
53
53
|
}.merge!(fields)
|
54
54
|
) << "\n"
|
55
|
-
|
55
|
+
}
|
56
56
|
end
|
57
57
|
private_class_method :json_formatter
|
58
58
|
|
@@ -63,8 +63,8 @@ module Neovim
|
|
63
63
|
begin
|
64
64
|
Logging.logger.public_send(level) do
|
65
65
|
{
|
66
|
-
:
|
67
|
-
:
|
66
|
+
_class: self.class,
|
67
|
+
_method: _method || block.binding.eval("__method__"),
|
68
68
|
}.merge!(block.call)
|
69
69
|
end
|
70
70
|
rescue => ex
|
@@ -76,11 +76,11 @@ module Neovim
|
|
76
76
|
|
77
77
|
def log_exception(level, ex, _method)
|
78
78
|
log(level, _method) do
|
79
|
-
{:
|
79
|
+
{exception: ex.class, message: ex.message}
|
80
80
|
end
|
81
81
|
|
82
82
|
log(:debug, _method) do
|
83
|
-
{:
|
83
|
+
{exception: ex.class, message: ex.message, backtrace: ex.backtrace}
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "neovim/logging"
|
2
|
+
|
3
|
+
module Neovim
|
4
|
+
# @api private
|
5
|
+
class Message
|
6
|
+
def self.from_array((kind, *payload))
|
7
|
+
case kind
|
8
|
+
when 0
|
9
|
+
request(*payload)
|
10
|
+
when 1
|
11
|
+
reqid, (_, error), value = payload
|
12
|
+
response(reqid, error, value)
|
13
|
+
when 2
|
14
|
+
notification(*payload)
|
15
|
+
else
|
16
|
+
raise "Unknown message type #{kind.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.request(id, method, args)
|
21
|
+
Request.new(id, method, args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.response(request_id, error, value)
|
25
|
+
Response.new(request_id, error, value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.notification(method, args)
|
29
|
+
Notification.new(method, args)
|
30
|
+
end
|
31
|
+
|
32
|
+
class Request < Struct.new(:id, :method_name, :arguments)
|
33
|
+
def to_a
|
34
|
+
[0, id, method_name, arguments]
|
35
|
+
end
|
36
|
+
|
37
|
+
def received(_, &block)
|
38
|
+
block.call(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def sync?
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Response < Struct.new(:request_id, :error, :value)
|
47
|
+
def to_a
|
48
|
+
[1, request_id, error, value]
|
49
|
+
end
|
50
|
+
|
51
|
+
def received(handlers)
|
52
|
+
handlers[request_id].call(self)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Notification < Struct.new(:method_name, :arguments)
|
57
|
+
def to_a
|
58
|
+
[2, method_name, arguments]
|
59
|
+
end
|
60
|
+
|
61
|
+
def received(_, &block)
|
62
|
+
block.call(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def sync?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/neovim/plugin.rb
CHANGED
@@ -6,7 +6,6 @@ module Neovim
|
|
6
6
|
attr_accessor :handlers, :setup_blocks
|
7
7
|
attr_reader :source
|
8
8
|
|
9
|
-
# Entrypoint to the +Neovim.plugin+ DSL.
|
10
9
|
def self.from_config_block(source)
|
11
10
|
new(source).tap do |instance|
|
12
11
|
yield DSL.new(instance) if block_given?
|
@@ -19,14 +18,12 @@ module Neovim
|
|
19
18
|
@setup_blocks = []
|
20
19
|
end
|
21
20
|
|
22
|
-
# Return specs used by +nvim+ to register plugins.
|
23
21
|
def specs
|
24
22
|
@handlers.inject([]) do |acc, handler|
|
25
23
|
handler.qualified? ? acc + [handler.to_spec] : acc
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
29
|
-
# Run all registered setup blocks.
|
30
27
|
def setup(client)
|
31
28
|
@setup_blocks.each { |bl| bl.call(client) }
|
32
29
|
end
|
@@ -5,7 +5,7 @@ module Neovim
|
|
5
5
|
attr_reader :block
|
6
6
|
|
7
7
|
def self.unqualified(name, block)
|
8
|
-
new(nil, nil, name, true, {:
|
8
|
+
new(nil, nil, name, true, {qualified: false}, block)
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(source, type, name, sync, options, block)
|
@@ -14,7 +14,7 @@ module Neovim
|
|
14
14
|
@name = name.to_s
|
15
15
|
@sync = !!sync
|
16
16
|
@options = options
|
17
|
-
@block = block ||
|
17
|
+
@block = block || -> {}
|
18
18
|
@qualified =
|
19
19
|
options.key?(:qualified) ? options.delete(:qualified) : true
|
20
20
|
end
|
@@ -40,10 +40,10 @@ module Neovim
|
|
40
40
|
|
41
41
|
def to_spec
|
42
42
|
{
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
43
|
+
type: @type,
|
44
|
+
name: @name,
|
45
|
+
sync: @sync,
|
46
|
+
opts: @options,
|
47
47
|
}
|
48
48
|
end
|
49
49
|
|
data/lib/neovim/remote_object.rb
CHANGED
data/lib/neovim/ruby_provider.rb
CHANGED
@@ -27,14 +27,6 @@ module Neovim
|
|
27
27
|
# 2. Define the +DirChanged+ event to update the provider's pwd.
|
28
28
|
def self.__define_setup(plug)
|
29
29
|
plug.__send__(:setup) do |client|
|
30
|
-
$stdout.define_singleton_method(:write) do |string|
|
31
|
-
client.out_write(string + "\n")
|
32
|
-
end
|
33
|
-
|
34
|
-
$stderr.define_singleton_method(:write) do |string|
|
35
|
-
client.err_writeln(string)
|
36
|
-
end
|
37
|
-
|
38
30
|
begin
|
39
31
|
cid = client.api.channel_id
|
40
32
|
client.command("au DirChanged * call rpcrequest(#{cid}, 'ruby_chdir', v:event)")
|
@@ -107,7 +99,9 @@ module Neovim
|
|
107
99
|
Vim.__refresh_globals(client)
|
108
100
|
|
109
101
|
__with_exception_handling(client) do
|
110
|
-
|
102
|
+
__with_std_streams(client) do
|
103
|
+
yield
|
104
|
+
end
|
111
105
|
end
|
112
106
|
nil
|
113
107
|
end
|
@@ -116,13 +110,30 @@ module Neovim
|
|
116
110
|
def self.__with_exception_handling(client)
|
117
111
|
begin
|
118
112
|
yield
|
119
|
-
rescue
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
113
|
+
rescue ScriptError, StandardError => e
|
114
|
+
msg = [e.class, e.message].join(": ")
|
115
|
+
client.err_writeln(msg)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
private_class_method :__with_exception_handling
|
119
|
+
|
120
|
+
def self.__with_std_streams(client)
|
121
|
+
old_stdout = $stdout.dup
|
122
|
+
old_stderr = $stderr.dup
|
123
|
+
|
124
|
+
$stdout, $stderr = StringIO.new, StringIO.new
|
125
|
+
|
126
|
+
begin
|
127
|
+
yield
|
128
|
+
|
129
|
+
client.out_write($stdout.string + $/) if $stdout.length > 0
|
130
|
+
client.err_writeln($stderr.string) if $stderr.length > 0
|
131
|
+
ensure
|
132
|
+
$stdout = old_stdout
|
133
|
+
$stderr = old_stderr
|
124
134
|
end
|
125
135
|
end
|
136
|
+
private_class_method :__with_std_streams
|
126
137
|
|
127
138
|
def self.__update_lines_in_chunks(buffer, start, stop, size)
|
128
139
|
(start..stop).each_slice(size) do |linenos|
|
data/lib/neovim/session.rb
CHANGED
@@ -8,28 +8,29 @@ module Neovim
|
|
8
8
|
class Session
|
9
9
|
include Logging
|
10
10
|
|
11
|
+
attr_writer :request_id
|
12
|
+
|
11
13
|
def initialize(event_loop)
|
12
14
|
@event_loop = event_loop
|
13
|
-
@pending_messages = []
|
14
15
|
@main_thread = Thread.current
|
15
16
|
@main_fiber = Fiber.current
|
17
|
+
@response_handlers = Hash.new(-> {})
|
18
|
+
@pending_messages = []
|
19
|
+
@request_id = 0
|
16
20
|
end
|
17
21
|
|
18
|
-
|
19
|
-
def run
|
22
|
+
def run(&block)
|
20
23
|
@running = true
|
21
24
|
|
22
|
-
while
|
23
|
-
Fiber.new {
|
25
|
+
while message = @pending_messages.shift
|
26
|
+
Fiber.new { message.received(@response_handlers, &block) }.resume
|
24
27
|
end
|
25
28
|
|
26
29
|
return unless @running
|
27
30
|
|
28
31
|
@event_loop.run do |message|
|
29
|
-
Fiber.new {
|
32
|
+
Fiber.new { message.received(@response_handlers, &block) }.resume
|
30
33
|
end
|
31
|
-
ensure
|
32
|
-
@event_loop.shutdown
|
33
34
|
end
|
34
35
|
|
35
36
|
# Make an RPC request and return its response.
|
@@ -44,13 +45,21 @@ module Neovim
|
|
44
45
|
# in the meantime are enqueued to be handled later.
|
45
46
|
def request(method, *args)
|
46
47
|
main_thread_only do
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
@request_id += 1
|
49
|
+
blocking = Fiber.current == @main_fiber
|
50
|
+
|
51
|
+
log(:debug) do
|
52
|
+
{
|
53
|
+
method_name: method,
|
54
|
+
request_id: @request_id,
|
55
|
+
blocking: blocking,
|
56
|
+
arguments: args
|
57
|
+
}
|
51
58
|
end
|
52
59
|
|
53
|
-
|
60
|
+
@event_loop.request(@request_id, method, *args)
|
61
|
+
response = blocking ? blocking_response : yielding_response
|
62
|
+
response.error ? raise(response.error) : response.value
|
54
63
|
end
|
55
64
|
end
|
56
65
|
|
@@ -58,7 +67,6 @@ module Neovim
|
|
58
67
|
@event_loop.respond(request_id, value, error)
|
59
68
|
end
|
60
69
|
|
61
|
-
# Make an RPC notification. +nvim+ will not block waiting for a response.
|
62
70
|
def notify(method, *args)
|
63
71
|
@event_loop.notify(method, *args)
|
64
72
|
end
|
@@ -75,43 +83,28 @@ module Neovim
|
|
75
83
|
|
76
84
|
private
|
77
85
|
|
78
|
-
def
|
79
|
-
log(:debug) do
|
80
|
-
{
|
81
|
-
:method_name => method,
|
82
|
-
:arguments => args,
|
83
|
-
}
|
84
|
-
end
|
85
|
-
|
86
|
-
fiber = Fiber.current
|
87
|
-
@event_loop.request(method, *args) do |response|
|
88
|
-
fiber.resume(response)
|
89
|
-
end
|
90
|
-
Fiber.yield
|
91
|
-
end
|
92
|
-
|
93
|
-
def blocking_request(method, *args)
|
94
|
-
log(:debug) do
|
95
|
-
{
|
96
|
-
:method_name => method,
|
97
|
-
:arguments => args,
|
98
|
-
}
|
99
|
-
end
|
100
|
-
|
86
|
+
def blocking_response
|
101
87
|
response = nil
|
102
88
|
|
103
|
-
@
|
89
|
+
@response_handlers[@request_id] = -> (res) {
|
104
90
|
response = res
|
105
91
|
stop
|
106
|
-
|
107
|
-
|
108
|
-
@event_loop.run do |message|
|
109
|
-
@pending_messages << message
|
110
|
-
end
|
92
|
+
}
|
111
93
|
|
94
|
+
run { |message| @pending_messages << message }
|
112
95
|
response
|
113
96
|
end
|
114
97
|
|
98
|
+
def yielding_response
|
99
|
+
fiber = Fiber.current
|
100
|
+
|
101
|
+
@response_handlers[@request_id] = -> (response) {
|
102
|
+
fiber.resume(response)
|
103
|
+
}
|
104
|
+
|
105
|
+
Fiber.yield
|
106
|
+
end
|
107
|
+
|
115
108
|
def main_thread_only
|
116
109
|
if Thread.current == @main_thread
|
117
110
|
yield if block_given?
|