neovim 0.0.5 → 0.0.6
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 +5 -2
- data/CHANGELOG.md +11 -0
- data/README.md +15 -4
- data/Rakefile +64 -4
- data/bin/j2mp +8 -0
- data/bin/mp2j +8 -0
- data/lib/neovim.rb +100 -17
- data/lib/neovim/api.rb +84 -0
- data/lib/neovim/async_session.rb +74 -21
- data/lib/neovim/buffer.rb +111 -4
- data/lib/neovim/client.rb +175 -4
- data/lib/neovim/current.rb +19 -8
- data/lib/neovim/event_loop.rb +55 -10
- data/lib/neovim/host.rb +21 -8
- data/lib/neovim/line_range.rb +55 -15
- data/lib/neovim/logging.rb +4 -0
- data/lib/neovim/manifest.rb +11 -3
- data/lib/neovim/msgpack_stream.rb +53 -19
- data/lib/neovim/notification.rb +2 -0
- data/lib/neovim/plugin.rb +12 -87
- data/lib/neovim/plugin/dsl.rb +81 -0
- data/lib/neovim/plugin/handler.rb +43 -0
- data/lib/neovim/remote_object.rb +64 -0
- data/lib/neovim/request.rb +4 -2
- data/lib/neovim/session.rb +167 -16
- data/lib/neovim/tabpage.rb +24 -2
- data/lib/neovim/version.rb +1 -1
- data/lib/neovim/window.rb +92 -2
- data/neovim.gemspec +1 -1
- data/spec/acceptance/neovim-ruby-host_spec.rb +16 -8
- data/spec/helper.rb +11 -3
- data/spec/neovim/api_spec.rb +40 -0
- data/spec/neovim/async_session_spec.rb +19 -25
- data/spec/neovim/current_spec.rb +64 -4
- data/spec/neovim/event_loop_spec.rb +18 -27
- data/spec/neovim/host_spec.rb +20 -1
- data/spec/neovim/manifest_spec.rb +0 -2
- data/spec/neovim/msgpack_stream_spec.rb +5 -6
- data/spec/neovim/{object_spec.rb → remote_object_spec.rb} +1 -4
- data/spec/neovim/session_spec.rb +18 -26
- data/spec/neovim_spec.rb +2 -3
- data/spec/support.rb +5 -5
- metadata +13 -6
- data/lib/neovim/api_info.rb +0 -27
- data/lib/neovim/object.rb +0 -40
@@ -0,0 +1,43 @@
|
|
1
|
+
module Neovim
|
2
|
+
class Plugin
|
3
|
+
# @api private
|
4
|
+
class Handler
|
5
|
+
attr_reader :block
|
6
|
+
|
7
|
+
def initialize(source, type, name, sync, options, block)
|
8
|
+
@source = source
|
9
|
+
@type = type.to_sym
|
10
|
+
@name = name.to_s
|
11
|
+
@sync = !!sync
|
12
|
+
@options = options
|
13
|
+
@block = block || Proc.new {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def qualified_name
|
17
|
+
if @type == :autocmd
|
18
|
+
pattern = @options.fetch(:pattern, "*")
|
19
|
+
"#{@source}:#{@type}:#{@name}:#{pattern}"
|
20
|
+
else
|
21
|
+
"#{@source}:#{@type}:#{@name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def sync?
|
26
|
+
@sync
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_spec
|
30
|
+
{
|
31
|
+
:type => @type,
|
32
|
+
:name => @name,
|
33
|
+
:sync => @sync,
|
34
|
+
:opts => @options,
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(*args)
|
39
|
+
@block.call(*args)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Neovim
|
2
|
+
# @abstract Superclass for all +nvim+ remote objects.
|
3
|
+
#
|
4
|
+
# @see Buffer
|
5
|
+
# @see Window
|
6
|
+
# @see Tabpage
|
7
|
+
class RemoteObject
|
8
|
+
attr_reader :index
|
9
|
+
|
10
|
+
def initialize(index, session)
|
11
|
+
@index = index
|
12
|
+
@session = session
|
13
|
+
@api = session.api
|
14
|
+
end
|
15
|
+
|
16
|
+
# Serialize object to MessagePack.
|
17
|
+
#
|
18
|
+
# @param packer [MessagePack::Packer]
|
19
|
+
# @return [String]
|
20
|
+
def to_msgpack(packer)
|
21
|
+
packer.pack(@index)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Intercept method calls and delegate to appropriate RPC methods.
|
25
|
+
def method_missing(method_name, *args)
|
26
|
+
if func = @api.function(qualify(method_name))
|
27
|
+
func.call(@session, @index, *args)
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Extend +respond_to?+ to support RPC methods.
|
34
|
+
def respond_to?(method_name)
|
35
|
+
super || rpc_methods.include?(method_name.to_sym)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extend +methods+ to include RPC methods
|
39
|
+
def methods
|
40
|
+
super | rpc_methods
|
41
|
+
end
|
42
|
+
|
43
|
+
# Extend +==+ to only look at class and index.
|
44
|
+
def ==(other)
|
45
|
+
other.class.equal?(self.class) && @index == other.index
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def rpc_methods
|
51
|
+
@api.functions_with_prefix(function_prefix).map do |func|
|
52
|
+
func.name.sub(/\A#{function_prefix}/, "").to_sym
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def function_prefix
|
57
|
+
"#{self.class.to_s.split("::").last.downcase}_"
|
58
|
+
end
|
59
|
+
|
60
|
+
def qualify(method_name)
|
61
|
+
:"#{function_prefix}#{method_name}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/neovim/request.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Neovim
|
2
|
+
# A synchronous message from +nvim+.
|
3
|
+
# @api private
|
2
4
|
class Request
|
3
5
|
attr_reader :method_name, :arguments
|
4
6
|
|
@@ -14,12 +16,12 @@ module Neovim
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def respond(value)
|
17
|
-
@msgpack_stream.
|
19
|
+
@msgpack_stream.write([1, @request_id, nil, value])
|
18
20
|
self
|
19
21
|
end
|
20
22
|
|
21
23
|
def error(message)
|
22
|
-
@msgpack_stream.
|
24
|
+
@msgpack_stream.write([1, @request_id, message, nil])
|
23
25
|
self
|
24
26
|
end
|
25
27
|
end
|
data/lib/neovim/session.rb
CHANGED
@@ -1,35 +1,186 @@
|
|
1
|
-
require "neovim/
|
1
|
+
require "neovim/api"
|
2
|
+
require "fiber"
|
2
3
|
|
3
4
|
module Neovim
|
5
|
+
# Wraps an +AsyncSession+ in a synchronous API using +Fiber+s.
|
4
6
|
class Session
|
5
|
-
|
7
|
+
# Connect to a TCP socket.
|
8
|
+
#
|
9
|
+
# @param host [String] The hostname or IP address
|
10
|
+
# @param port [Fixnum] The port
|
11
|
+
# @return [Session]
|
12
|
+
# @see EventLoop.tcp
|
13
|
+
def self.tcp(host, port)
|
14
|
+
from_event_loop(EventLoop.tcp(host, port))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Connect to a UNIX domain socket.
|
18
|
+
#
|
19
|
+
# @param socket_path [String] The socket path
|
20
|
+
# @return [Session]
|
21
|
+
# @see EventLoop.unix
|
22
|
+
def self.unix(socket_path)
|
23
|
+
from_event_loop(EventLoop.unix(socket_path))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Spawn and connect to a child +nvim+ process.
|
27
|
+
#
|
28
|
+
# @param argv [Array] The arguments to pass to the spawned process
|
29
|
+
# @return [Session]
|
30
|
+
# @see EventLoop.child
|
31
|
+
def self.child(argv)
|
32
|
+
from_event_loop(EventLoop.child(argv))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Connect to the current process's standard streams. This is used to
|
36
|
+
# promote the current process to a Ruby plugin host.
|
37
|
+
#
|
38
|
+
# @return [Session]
|
39
|
+
# @see EventLoop.stdio
|
40
|
+
def self.stdio
|
41
|
+
from_event_loop(EventLoop.stdio)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.from_event_loop(event_loop)
|
45
|
+
msgpack_stream = MsgpackStream.new(event_loop)
|
46
|
+
async_session = AsyncSession.new(msgpack_stream)
|
47
|
+
new(async_session)
|
48
|
+
end
|
49
|
+
private_class_method :from_event_loop
|
6
50
|
|
7
51
|
def initialize(async_session)
|
8
52
|
@async_session = async_session
|
9
|
-
@
|
53
|
+
@pending_messages = []
|
54
|
+
@in_handler = false
|
55
|
+
@running = false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return the +nvim+ API as described in the +vim_get_api_info+ call.
|
59
|
+
# Defaults to empty API information.
|
60
|
+
#
|
61
|
+
# @return [API]
|
62
|
+
# @see API.null
|
63
|
+
def api
|
64
|
+
@api ||= API.null
|
65
|
+
end
|
10
66
|
|
11
|
-
|
67
|
+
# Discover the +nvim+ API as described in the +vim_get_api_info+ call.
|
68
|
+
#
|
69
|
+
# @return [self]
|
70
|
+
# @see API
|
71
|
+
def discover_api
|
72
|
+
@api = API.new(request(:vim_get_api_info))
|
73
|
+
self
|
12
74
|
end
|
13
75
|
|
76
|
+
# Run the event loop, handling messages in a +Fiber+.
|
77
|
+
#
|
78
|
+
# @yield [Object]
|
79
|
+
# @return [void]
|
80
|
+
# @see AsyncSession#run
|
81
|
+
# @see MsgpackStream#run
|
82
|
+
# @see EventLoop#run
|
83
|
+
def run
|
84
|
+
@running = true
|
85
|
+
|
86
|
+
while message = @pending_messages.shift
|
87
|
+
in_handler_fiber { yield message if block_given? }
|
88
|
+
end
|
89
|
+
|
90
|
+
return unless @running
|
91
|
+
|
92
|
+
@async_session.run(self) do |message|
|
93
|
+
in_handler_fiber { yield message if block_given? }
|
94
|
+
end
|
95
|
+
ensure
|
96
|
+
stop
|
97
|
+
end
|
98
|
+
|
99
|
+
# Make an RPC request and return its response.
|
100
|
+
#
|
101
|
+
# If this method is called inside a callback, we are already inside a
|
102
|
+
# +Fiber+ handler. In that case, we write to the stream and yield the
|
103
|
+
# +Fiber+. Once the response is received, resume the +Fiber+ and
|
104
|
+
# return the result.
|
105
|
+
#
|
106
|
+
# If this method is called outside a callback, write to the stream and
|
107
|
+
# run the event loop until a response is received. Messages received
|
108
|
+
# in the meantime are enqueued to be handled later.
|
109
|
+
#
|
110
|
+
# @param method [String, Symbol] The RPC method name
|
111
|
+
# @param *args [Array] The RPC method arguments
|
112
|
+
# @return [Object] The response from the RPC call
|
113
|
+
# @raise [ArgumentError] An error returned from +nvim+
|
14
114
|
def request(method, *args)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
115
|
+
if @in_handler
|
116
|
+
err, res = running_request(method, *args)
|
117
|
+
else
|
118
|
+
err, res = stopped_request(method, *args)
|
19
119
|
end
|
20
120
|
|
21
|
-
|
22
|
-
|
121
|
+
err ? raise(ArgumentError, err) : res
|
122
|
+
end
|
123
|
+
|
124
|
+
# Make an RPC notification
|
125
|
+
#
|
126
|
+
# @param method [String, Symbol] The RPC method name
|
127
|
+
# @param *args [Array] The RPC method arguments
|
128
|
+
# @return [nil]
|
129
|
+
def notify(method, *args)
|
130
|
+
@async_session.notify(method, *args)
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Stop the event loop
|
135
|
+
#
|
136
|
+
# @return [void]
|
137
|
+
# @see EventLoop#stop
|
138
|
+
def stop
|
139
|
+
@running = false
|
140
|
+
@async_session.stop
|
141
|
+
end
|
142
|
+
|
143
|
+
# Shut down the event loop
|
144
|
+
#
|
145
|
+
# @return [void]
|
146
|
+
# @see EventLoop#shutdown
|
147
|
+
def shutdown
|
148
|
+
@running = false
|
149
|
+
@async_session.shutdown
|
23
150
|
end
|
24
151
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
152
|
+
private
|
153
|
+
|
154
|
+
def in_handler_fiber(&block)
|
155
|
+
Fiber.new do
|
156
|
+
@in_handler = true
|
157
|
+
begin
|
158
|
+
block.call
|
159
|
+
ensure
|
160
|
+
@in_handler = false
|
31
161
|
end
|
162
|
+
end.resume
|
163
|
+
end
|
164
|
+
|
165
|
+
def running_request(method, *args)
|
166
|
+
fiber = Fiber.current
|
167
|
+
@async_session.request(method, *args) do |err, res|
|
168
|
+
fiber.resume(err, res)
|
169
|
+
end
|
170
|
+
Fiber.yield
|
171
|
+
end
|
172
|
+
|
173
|
+
def stopped_request(method, *args)
|
174
|
+
error, result = nil
|
175
|
+
|
176
|
+
@async_session.request(method, *args) do |err, res|
|
177
|
+
error, result = err, res
|
178
|
+
stop
|
179
|
+
end.run(self) do |message|
|
180
|
+
@pending_messages << message
|
32
181
|
end
|
182
|
+
|
183
|
+
[error, result]
|
33
184
|
end
|
34
185
|
end
|
35
186
|
end
|
data/lib/neovim/tabpage.rb
CHANGED
@@ -1,6 +1,28 @@
|
|
1
|
-
require "neovim/
|
1
|
+
require "neovim/remote_object"
|
2
2
|
|
3
3
|
module Neovim
|
4
|
-
class Tabpage <
|
4
|
+
class Tabpage < RemoteObject
|
5
|
+
|
6
|
+
# The following methods are dynamically generated.
|
7
|
+
=begin
|
8
|
+
@method get_windows
|
9
|
+
@return [Array<Window>]
|
10
|
+
|
11
|
+
@method get_var(name)
|
12
|
+
@param [String] name
|
13
|
+
@return [Object]
|
14
|
+
|
15
|
+
@method set_var(name, value)
|
16
|
+
@param [String] name
|
17
|
+
@param [Object] value
|
18
|
+
@return [Object]
|
19
|
+
|
20
|
+
@method get_window
|
21
|
+
@return [Window]
|
22
|
+
|
23
|
+
@method is_valid
|
24
|
+
@return [Boolean]
|
25
|
+
|
26
|
+
=end
|
5
27
|
end
|
6
28
|
end
|
data/lib/neovim/version.rb
CHANGED
data/lib/neovim/window.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
require "neovim/
|
1
|
+
require "neovim/remote_object"
|
2
2
|
|
3
3
|
module Neovim
|
4
|
-
class Window <
|
4
|
+
class Window < RemoteObject
|
5
|
+
# Interface for interacting with the cursor position.
|
6
|
+
#
|
7
|
+
# @return [Cursor]
|
8
|
+
# @see Cursor
|
5
9
|
def cursor
|
6
10
|
@cursor ||= Cursor.new(self)
|
7
11
|
end
|
@@ -11,29 +15,115 @@ module Neovim
|
|
11
15
|
@window = window
|
12
16
|
end
|
13
17
|
|
18
|
+
# Get the current coordinates of the cursor.
|
19
|
+
#
|
20
|
+
# @return [Array<Fixnum>]
|
21
|
+
# @note coordinates are 1-indexed
|
14
22
|
def coordinates
|
15
23
|
@window.get_cursor
|
16
24
|
end
|
17
25
|
|
26
|
+
# Set the coordinates of the cursor.
|
27
|
+
#
|
28
|
+
# @param coords [Array<Fixnum>] The coordinates as a pair of integers
|
29
|
+
# @return [Array<Fixnum>]
|
30
|
+
# @note coordinates are 1-indexed
|
31
|
+
# @example Move the cursor to line 1, column 2
|
32
|
+
# window.cursor.coordinates = [1, 2]
|
18
33
|
def coordinates=(coords)
|
19
34
|
@window.set_cursor(coords)
|
20
35
|
end
|
21
36
|
|
37
|
+
# Get the cursor's line number.
|
38
|
+
#
|
39
|
+
# @return [Fixnum]
|
40
|
+
# @note Line numbers are 1-indexed
|
22
41
|
def line
|
23
42
|
coordinates[0]
|
24
43
|
end
|
25
44
|
|
45
|
+
# Set the cursor's line number.
|
46
|
+
#
|
47
|
+
# @param n [Fixnum]
|
48
|
+
# @return [Fixnum]
|
49
|
+
# @note Line numbers are 1-indexed
|
26
50
|
def line=(n)
|
27
51
|
self.coordinates = [n, column]
|
52
|
+
n
|
28
53
|
end
|
29
54
|
|
55
|
+
# Get the cursor's column number.
|
56
|
+
#
|
57
|
+
# @return [Fixnum]
|
58
|
+
# @note Column numbers are 1-indexed
|
30
59
|
def column
|
31
60
|
coordinates[1]
|
32
61
|
end
|
33
62
|
|
63
|
+
# Set the cursor's column number.
|
64
|
+
#
|
65
|
+
# @param n [Fixnum]
|
66
|
+
# @return [Fixnum]
|
67
|
+
# @note Column numbers are 1-indexed
|
34
68
|
def column=(n)
|
35
69
|
self.coordinates = [line, n]
|
70
|
+
n
|
36
71
|
end
|
37
72
|
end
|
73
|
+
|
74
|
+
# The following methods are dynamically generated.
|
75
|
+
=begin
|
76
|
+
@method get_buffer
|
77
|
+
@return [Buffer]
|
78
|
+
|
79
|
+
@method get_cursor
|
80
|
+
@return [Array<Fixnum>]
|
81
|
+
|
82
|
+
@method set_cursor(pos)
|
83
|
+
@param [Array<Fixnum>] pos
|
84
|
+
@return [void]
|
85
|
+
|
86
|
+
@method get_height
|
87
|
+
@return [Fixnum]
|
88
|
+
|
89
|
+
@method set_height(height)
|
90
|
+
@param [Fixnum] height
|
91
|
+
@return [void]
|
92
|
+
|
93
|
+
@method get_width
|
94
|
+
@return [Fixnum]
|
95
|
+
|
96
|
+
@method set_width(width)
|
97
|
+
@param [Fixnum] width
|
98
|
+
@return [void]
|
99
|
+
|
100
|
+
@method get_var(name)
|
101
|
+
@param [String] name
|
102
|
+
@return [Object]
|
103
|
+
|
104
|
+
@method set_var(name, value)
|
105
|
+
@param [String] name
|
106
|
+
@param [Object] value
|
107
|
+
@return [Object]
|
108
|
+
|
109
|
+
@method get_option(name)
|
110
|
+
@param [String] name
|
111
|
+
@return [Object]
|
112
|
+
|
113
|
+
@method set_option(name, value)
|
114
|
+
@param [String] name
|
115
|
+
@param [Object] value
|
116
|
+
@return [void]
|
117
|
+
|
118
|
+
@method get_position
|
119
|
+
@return [Array<Fixnum>]
|
120
|
+
|
121
|
+
@method get_tabpage
|
122
|
+
@return [Tabpage]
|
123
|
+
|
124
|
+
@method is_valid
|
125
|
+
@return [Boolean]
|
126
|
+
|
127
|
+
=end
|
38
128
|
end
|
39
129
|
end
|