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