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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -2
  3. data/CHANGELOG.md +11 -0
  4. data/README.md +15 -4
  5. data/Rakefile +64 -4
  6. data/bin/j2mp +8 -0
  7. data/bin/mp2j +8 -0
  8. data/lib/neovim.rb +100 -17
  9. data/lib/neovim/api.rb +84 -0
  10. data/lib/neovim/async_session.rb +74 -21
  11. data/lib/neovim/buffer.rb +111 -4
  12. data/lib/neovim/client.rb +175 -4
  13. data/lib/neovim/current.rb +19 -8
  14. data/lib/neovim/event_loop.rb +55 -10
  15. data/lib/neovim/host.rb +21 -8
  16. data/lib/neovim/line_range.rb +55 -15
  17. data/lib/neovim/logging.rb +4 -0
  18. data/lib/neovim/manifest.rb +11 -3
  19. data/lib/neovim/msgpack_stream.rb +53 -19
  20. data/lib/neovim/notification.rb +2 -0
  21. data/lib/neovim/plugin.rb +12 -87
  22. data/lib/neovim/plugin/dsl.rb +81 -0
  23. data/lib/neovim/plugin/handler.rb +43 -0
  24. data/lib/neovim/remote_object.rb +64 -0
  25. data/lib/neovim/request.rb +4 -2
  26. data/lib/neovim/session.rb +167 -16
  27. data/lib/neovim/tabpage.rb +24 -2
  28. data/lib/neovim/version.rb +1 -1
  29. data/lib/neovim/window.rb +92 -2
  30. data/neovim.gemspec +1 -1
  31. data/spec/acceptance/neovim-ruby-host_spec.rb +16 -8
  32. data/spec/helper.rb +11 -3
  33. data/spec/neovim/api_spec.rb +40 -0
  34. data/spec/neovim/async_session_spec.rb +19 -25
  35. data/spec/neovim/current_spec.rb +64 -4
  36. data/spec/neovim/event_loop_spec.rb +18 -27
  37. data/spec/neovim/host_spec.rb +20 -1
  38. data/spec/neovim/manifest_spec.rb +0 -2
  39. data/spec/neovim/msgpack_stream_spec.rb +5 -6
  40. data/spec/neovim/{object_spec.rb → remote_object_spec.rb} +1 -4
  41. data/spec/neovim/session_spec.rb +18 -26
  42. data/spec/neovim_spec.rb +2 -3
  43. data/spec/support.rb +5 -5
  44. metadata +13 -6
  45. data/lib/neovim/api_info.rb +0 -27
  46. 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
@@ -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.send([1, @request_id, nil, value])
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.send([1, @request_id, message, nil])
24
+ @msgpack_stream.write([1, @request_id, message, nil])
23
25
  self
24
26
  end
25
27
  end
@@ -1,35 +1,186 @@
1
- require "neovim/api_info"
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
- attr_reader :api_info
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
- @api_info = APIInfo.new(request(:vim_get_api_info))
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
- @async_session.register_session(self)
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
- fiber = Fiber.new do
16
- @async_session.request(method, *args) do |err, res|
17
- Fiber.yield(err, res)
18
- end.run
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
- error, response = fiber.resume
22
- error ? raise(ArgumentError, error) : response
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
- 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
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
@@ -1,6 +1,28 @@
1
- require "neovim/object"
1
+ require "neovim/remote_object"
2
2
 
3
3
  module Neovim
4
- class Tabpage < Neovim::Object
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
@@ -1,3 +1,3 @@
1
1
  module Neovim
2
- VERSION = Gem::Version.new("0.0.5")
2
+ VERSION = Gem::Version.new("0.0.6")
3
3
  end
@@ -1,7 +1,11 @@
1
- require "neovim/object"
1
+ require "neovim/remote_object"
2
2
 
3
3
  module Neovim
4
- class Window < Neovim::Object
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