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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +10 -0
  4. data/lib/neovim/current.rb +19 -2
  5. data/lib/neovim/host.rb +3 -11
  6. data/lib/neovim/host/loader.rb +0 -2
  7. data/lib/neovim/line_range.rb +14 -2
  8. data/lib/neovim/plugin.rb +1 -4
  9. data/lib/neovim/plugin/dsl.rb +6 -14
  10. data/lib/neovim/plugin/handler.rb +2 -9
  11. data/lib/neovim/ruby_provider.rb +9 -15
  12. data/lib/neovim/session.rb +23 -70
  13. data/lib/neovim/session/api.rb +61 -0
  14. data/lib/neovim/session/event_loop.rb +108 -0
  15. data/lib/neovim/session/notification.rb +19 -0
  16. data/lib/neovim/session/request.rb +31 -0
  17. data/lib/neovim/session/rpc.rb +95 -0
  18. data/lib/neovim/session/serializer.rb +62 -0
  19. data/lib/neovim/version.rb +1 -1
  20. data/neovim.gemspec +1 -1
  21. data/spec/acceptance/neovim-ruby-host_spec.rb +0 -5
  22. data/spec/acceptance/ruby_provider_spec.rb +35 -3
  23. data/spec/helper.rb +33 -2
  24. data/spec/neovim/host_spec.rb +1 -1
  25. data/spec/neovim/plugin_spec.rb +2 -2
  26. data/spec/neovim/session/api_spec.rb +46 -0
  27. data/spec/neovim/session/event_loop_spec.rb +99 -0
  28. data/spec/neovim/session/rpc_spec.rb +108 -0
  29. data/spec/neovim/session/serializer_spec.rb +48 -0
  30. data/spec/neovim/session_spec.rb +1 -1
  31. metadata +16 -18
  32. data/lib/neovim/api.rb +0 -80
  33. data/lib/neovim/async_session.rb +0 -119
  34. data/lib/neovim/event_loop.rb +0 -128
  35. data/lib/neovim/msgpack_stream.rb +0 -80
  36. data/lib/neovim/notification.rb +0 -17
  37. data/lib/neovim/request.rb +0 -29
  38. data/spec/neovim/api_spec.rb +0 -44
  39. data/spec/neovim/async_session_spec.rb +0 -106
  40. data/spec/neovim/event_loop_spec.rb +0 -97
  41. data/spec/neovim/msgpack_stream_spec.rb +0 -46
  42. data/spec/support.rb +0 -33
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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