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
@@ -7,6 +7,20 @@ module Neovim
7
7
 
8
8
  attr_reader :manifest
9
9
 
10
+ # Load plugin definitions and instantiate a new +Host+. This temporarily
11
+ # mutates global state on the +Neovim+ module so that +Neovim.plugin+ calls
12
+ # will be registered correctly with the provided +Manifest+.
13
+ #
14
+ # @overload load_from_files(rplugin_paths)
15
+ # @param rplugin_paths [Array<String>] The remote plugin paths.
16
+ #
17
+ # @overload load_from_files(rplugin_paths, target_manifest)
18
+ # @param rplugin_paths [Array<String>] The remote plugin paths.
19
+ # @param target_manifest [Manifest] The plugin manifest.
20
+ #
21
+ # @return [Host]
22
+ # @see Neovim.start_host
23
+ # @see Neovim.plugin
10
24
  def self.load_from_files(rplugin_paths, target_manifest=Manifest.new)
11
25
  old_manifest = Neovim.__configured_plugin_manifest
12
26
  old_path = Neovim.__configured_plugin_path
@@ -26,20 +40,19 @@ module Neovim
26
40
  end
27
41
  end
28
42
 
29
- def initialize(manifest)
43
+ def initialize(manifest, session=nil)
44
+ @session = session || Session.stdio
30
45
  @manifest = manifest
31
- @event_loop = EventLoop.stdio
32
- @msgpack_stream = MsgpackStream.new(@event_loop)
33
- @async_session = AsyncSession.new(@msgpack_stream)
34
46
  end
35
47
 
48
+ # Run the event loop, passing received messages to the manifest.
49
+ #
50
+ # @return [void]
36
51
  def run
37
- callback = Proc.new do |msg|
52
+ @session.run do |msg|
38
53
  debug("received #{msg.inspect}")
39
54
  @manifest.handle(msg, client)
40
55
  end
41
-
42
- @async_session.run(callback, callback)
43
56
  rescue => e
44
57
  fatal("got unexpected error #{e}")
45
58
  debug(e.backtrace.join("\n"))
@@ -48,7 +61,7 @@ module Neovim
48
61
  private
49
62
 
50
63
  def client
51
- @client ||= Client.new(Session.new(@async_session))
64
+ @client ||= Client.new(@session.discover_api)
52
65
  end
53
66
  end
54
67
  end
@@ -1,4 +1,5 @@
1
1
  module Neovim
2
+ # Provide an enumerable interface for dealing with ranges of lines.
2
3
  class LineRange
3
4
  include Enumerable
4
5
 
@@ -8,53 +9,92 @@ module Neovim
8
9
  @end = _end
9
10
  end
10
11
 
12
+ # @return [Array<String>]
11
13
  def to_a
12
14
  @buffer.get_line_slice(@begin, @end, true, true)
13
15
  end
14
16
 
17
+ # @yield [String] The current line
18
+ # @return [Array<String>]
15
19
  def each(&block)
16
20
  to_a.each(&block)
17
21
  end
18
22
 
19
- def [](idx, len=nil)
20
- case idx
23
+ # @overload [](index)
24
+ # @param index [Fixnum]
25
+ #
26
+ # @overload [](range)
27
+ # @param range [Range]
28
+ #
29
+ # @overload [](index, length)
30
+ # @param index [Fixnum]
31
+ # @param length [Fixnum]
32
+ #
33
+ # @example Get the first line using an index
34
+ # line_range[0] # => "first"
35
+ # @example Get the first two lines using a +Range+
36
+ # line_range[0..1] # => ["first", "second"]
37
+ # @example Get the first two lines using an index and length
38
+ # line_range[0, 2] # => ["first", "second"]
39
+ def [](pos, len=nil)
40
+ case pos
21
41
  when ::Range
22
- _end = idx.exclude_end? ? idx.end - 1 : idx.end
23
- LineRange.new(@buffer, idx.begin, _end)
42
+ _end = pos.exclude_end? ? pos.end - 1 : pos.end
43
+ LineRange.new(@buffer, pos.begin, _end)
24
44
  else
25
45
  if len
26
- LineRange.new(@buffer, idx, idx + len - 1)
46
+ LineRange.new(@buffer, pos, pos + len - 1)
27
47
  else
28
- @buffer.get_line(idx)
48
+ @buffer.get_line(pos)
29
49
  end
30
50
  end
31
51
  end
32
52
  alias_method :slice, :[]
33
53
 
54
+ # @overload []=(index, string)
55
+ # @param index [Fixnum]
56
+ # @param string [String]
57
+ #
58
+ # @overload []=(index, length, strings)
59
+ # @param index [Fixnum]
60
+ # @param length [Fixnum]
61
+ # @param strings [Array<String>]
62
+ #
63
+ # @overload []=(range, strings)
64
+ # @param range [Range]
65
+ # @param strings [Array<String>]
66
+ #
67
+ # @example Replace the first line using an index
68
+ # line_range[0] = "first"
69
+ # @example Replace the first two lines using a +Range+
70
+ # line_range[0..1] = ["first", "second"]
71
+ # @example Replace the first two lines using an index and length
72
+ # line_range[0, 2] = ["first", "second"]
34
73
  def []=(*args)
35
74
  *target, val = args
36
- idx, len = target
75
+ pos, len = target
37
76
 
38
- case idx
77
+ case pos
39
78
  when ::Range
40
79
  @buffer.set_line_slice(
41
- idx.begin,
42
- idx.end,
80
+ pos.begin,
81
+ pos.end,
43
82
  true,
44
- !idx.exclude_end?,
83
+ !pos.exclude_end?,
45
84
  val
46
85
  )
47
86
  else
48
87
  if len
49
- @buffer.set_line_slice(idx, idx + len, true, false, val)
88
+ @buffer.set_line_slice(pos, pos + len, true, false, val)
50
89
  else
51
- @buffer.set_line(idx, val)
90
+ @buffer.set_line(pos, val)
52
91
  end
53
92
  end
54
93
  end
55
94
 
56
- def replace(other_ary)
57
- self[0..-1] = other_ary
95
+ # @param other [Array] The replacement lines
96
+ def replace(other)
97
+ self[0..-1] = other
58
98
  self
59
99
  end
60
100
  end
@@ -2,11 +2,15 @@ require "logger"
2
2
  require "stringio"
3
3
 
4
4
  module Neovim
5
+ # Mixed into classes for unified logging helper methods.
5
6
  module Logging
6
7
  class << self
7
8
  attr_writer :logger
8
9
  end
9
10
 
11
+ # Return the value of @logger, or construct it from the environment.
12
+ # $NVIM_RUBY_LOG_FILE specifies a file to log to (default +STDOUT+), while
13
+ # NVIM_RUBY_LOG_LEVEL specifies the level (default +WARN+)
10
14
  def self.logger
11
15
  return @logger if instance_variable_defined?(:@logger)
12
16
 
@@ -11,6 +11,9 @@ module Neovim
11
11
  @specs = {}
12
12
  end
13
13
 
14
+ # Register a +Plugin+ to receive +Host+ messages.
15
+ #
16
+ # @param plugin [Plugin]
14
17
  def register(plugin)
15
18
  plugin.handlers.each do |handler|
16
19
  wrapped_handler = handler.sync? ? wrap_sync(handler) : wrap_async(handler)
@@ -20,9 +23,14 @@ module Neovim
20
23
  @specs[plugin.source] = plugin.specs
21
24
  end
22
25
 
23
- def handle(msg, client)
24
- default_handler = msg.sync? ? default_sync_handler : default_async_handler
25
- @handlers.fetch(msg.method_name, default_handler).call(client, msg)
26
+ # Handle messages received from the host. Sends a +Neovim::Client+ along
27
+ # with the message to be used in plugin callbacks.
28
+ #
29
+ # @param message [Neovim::Request, Neovim::Notification]
30
+ # @param client [Neovim::Client]
31
+ def handle(message, client)
32
+ default_handler = message.sync? ? default_sync_handler : default_async_handler
33
+ @handlers.fetch(message.method_name, default_handler).call(client, message)
26
34
  rescue => e
27
35
  fatal("got unexpected error #{e}")
28
36
  debug(e.backtrace.join("\n"))
@@ -2,6 +2,8 @@ require "neovim/logging"
2
2
  require "msgpack"
3
3
 
4
4
  module Neovim
5
+ # Handles serializing RPC messages to MessagePack and passing them to
6
+ # the event loop
5
7
  class MsgpackStream
6
8
  include Logging
7
9
 
@@ -10,35 +12,67 @@ module Neovim
10
12
  @unpacker = MessagePack::Unpacker.new
11
13
  end
12
14
 
13
- def register_session(session)
14
- session.api_info.types.each do |type, info|
15
- klass = Neovim.const_get(type)
16
- id = info.fetch("id")
17
-
18
- @unpacker.register_type(id) do |data|
19
- klass.new(MessagePack.unpack(data), session)
20
- end
21
- end
22
- end
23
-
24
- def send(msg)
25
- debug("sending #{msg.inspect}")
26
- @event_loop.send(MessagePack.pack(msg))
15
+ # Serialize an RPC message to and write it to the event loop.
16
+ #
17
+ # @param msg [Array] The RPC message
18
+ # @return [self]
19
+ # @example Write an RPC request
20
+ # msgpack_stream.write([0, 1, :vim_strwidth, ["foobar"]])
21
+ def write(msg)
22
+ debug("writing #{msg.inspect}")
23
+ @event_loop.write(MessagePack.pack(msg))
27
24
  self
28
25
  end
29
26
 
30
- def run(message_cb, setup_cb=nil)
31
- data_cb = Proc.new do |data|
27
+ # Run the event loop, yielding deserialized messages to the block.
28
+ #
29
+ # @param session [Session] Used for registering msgpack +ext+ types as
30
+ # described by the +vim_get_api_info+ call
31
+ # @return [void]
32
+ # @see EventLoop#run
33
+ def run(session=nil)
34
+ register_types(session)
35
+
36
+ @event_loop.run do |data|
32
37
  @unpacker.feed_each(data) do |msg|
33
38
  debug("received #{msg.inspect}")
34
- message_cb.call(msg)
39
+ yield msg if block_given?
35
40
  end
36
41
  end
37
-
38
- @event_loop.run(data_cb, setup_cb)
39
42
  rescue => e
40
43
  fatal("got unexpected error #{e}")
41
44
  debug(e.backtrace.join("\n"))
42
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
+ private
64
+
65
+ def register_types(session)
66
+ return unless session && session.api
67
+
68
+ session.api.types.each do |type, info|
69
+ klass = Neovim.const_get(type)
70
+ id = info.fetch("id")
71
+
72
+ @unpacker.register_type(id) do |data|
73
+ klass.new(MessagePack.unpack(data), session)
74
+ end
75
+ end
76
+ end
43
77
  end
44
78
  end
@@ -1,4 +1,6 @@
1
1
  module Neovim
2
+ # An asynchronous message from +nvim+.
3
+ # @api private
2
4
  class Notification
3
5
  attr_reader :method_name, :arguments
4
6
 
@@ -1,103 +1,28 @@
1
+ require "neovim/plugin/dsl"
2
+
1
3
  module Neovim
2
4
  class Plugin
3
- def self.from_config_block(source, &block)
5
+ attr_accessor :handlers
6
+ attr_reader :source
7
+
8
+ # Entrypoint to the +Neovim.plugin+ DSL.
9
+ #
10
+ # @param source [String] The path of the plugin file.
11
+ # @yield [DSL] The receiver of DSL methods.
12
+ def self.from_config_block(source)
4
13
  new(source).tap do |instance|
5
- block.call(DSL.new(instance)) if block
14
+ yield DSL.new(instance) if block_given?
6
15
  end
7
16
  end
8
17
 
9
- attr_accessor :handlers
10
- attr_reader :source
11
-
12
18
  def initialize(source)
13
19
  @handlers = []
14
20
  @source = source
15
21
  end
16
22
 
23
+ # @return [Array] Handler specs used by +nvim+ to register plugins.
17
24
  def specs
18
25
  @handlers.map(&:to_spec)
19
26
  end
20
-
21
- class Handler
22
- attr_reader :block
23
-
24
- def initialize(source, type, name, sync, options, block)
25
- @source = source
26
- @type = type.to_sym
27
- @name = name.to_s
28
- @sync = !!sync
29
- @options = options
30
- @block = block || ::Proc.new {}
31
- end
32
-
33
- def qualified_name
34
- if @type == :autocmd
35
- pattern = @options.fetch(:pattern, "*")
36
- "#{@source}:#{@type}:#{@name}:#{pattern}"
37
- else
38
- "#{@source}:#{@type}:#{@name}"
39
- end
40
- end
41
-
42
- def sync?
43
- @sync
44
- end
45
-
46
- def to_spec
47
- {
48
- :type => @type,
49
- :name => @name,
50
- :sync => @sync,
51
- :opts => @options,
52
- }
53
- end
54
-
55
- def call(*args)
56
- @block.call(*args)
57
- end
58
- end
59
-
60
- class DSL < BasicObject
61
- def initialize(plugin)
62
- @plugin = plugin
63
- end
64
-
65
- def command(name, options={}, &block)
66
- register_handler(:command, name, options, block)
67
- end
68
-
69
- def function(name, options={}, &block)
70
- register_handler(:function, name, options, block)
71
- end
72
-
73
- def autocmd(name, options={}, &block)
74
- register_handler(:autocmd, name, options, block)
75
- end
76
-
77
- private
78
-
79
- def register_handler(type, name, _options, block)
80
- if type == :autocmd
81
- options = _options.dup
82
- else
83
- options = standardize_range(_options.dup)
84
- end
85
-
86
- sync = options.delete(:sync)
87
-
88
- @plugin.handlers.push(
89
- Handler.new(@plugin.source, type, name, sync, options, block)
90
- )
91
- end
92
-
93
- def standardize_range(options)
94
- if options.key?(:range)
95
- options[:range] = "" if options[:range] == true
96
- options[:range] = ::Kernel.String(options[:range])
97
- end
98
-
99
- options
100
- end
101
- end
102
27
  end
103
28
  end
@@ -0,0 +1,81 @@
1
+ require "neovim/plugin/handler"
2
+
3
+ module Neovim
4
+ class Plugin
5
+ # The DSL exposed in +Neovim.plugin+ blocks.
6
+ class DSL < BasicObject
7
+ def initialize(plugin)
8
+ @plugin = plugin
9
+ end
10
+
11
+ # Register an +nvim+ command.
12
+ #
13
+ # @param name [String]
14
+ # @param options [Hash]
15
+ # @param &block [Proc, nil]
16
+ #
17
+ # @option options [Fixnum] :nargs
18
+ # @option options [Fixnum] :count
19
+ # @option options [String] :eval
20
+ # @option options [Boolean] :sync (false)
21
+ # @option options [Boolean] :bang
22
+ # @option options [Boolean] :register
23
+ # @option options [Boolean] :complete
24
+ # @option options [String, Boolean] :range
25
+ def command(name, options={}, &block)
26
+ register_handler(:command, name, options, block)
27
+ end
28
+
29
+ # Register an +nvim+ function.
30
+ #
31
+ # @param name [String]
32
+ # @param options [Hash]
33
+ # @param &block [Proc, nil]
34
+ #
35
+ # @option options [String] :eval
36
+ # @option options [Boolean] :sync (false)
37
+ # @option options [String, Boolean] :range
38
+ def function(name, options={}, &block)
39
+ register_handler(:function, name, options, block)
40
+ end
41
+
42
+ # Register an +nvim+ autocmd.
43
+ #
44
+ # @param event [String]
45
+ # @param options [Hash]
46
+ # @param &block [Proc, nil]
47
+ #
48
+ # @option options [String] :pattern
49
+ # @option options [String] :eval
50
+ # @option options [Boolean] :sync (false)
51
+ def autocmd(event, options={}, &block)
52
+ register_handler(:autocmd, event, options, block)
53
+ end
54
+
55
+ private
56
+
57
+ def register_handler(type, name, _options, block)
58
+ if type == :autocmd
59
+ options = _options.dup
60
+ else
61
+ options = standardize_range(_options.dup)
62
+ end
63
+
64
+ sync = options.delete(:sync)
65
+
66
+ @plugin.handlers.push(
67
+ Handler.new(@plugin.source, type, name, sync, options, block)
68
+ )
69
+ end
70
+
71
+ def standardize_range(options)
72
+ if options.key?(:range)
73
+ options[:range] = "" if options[:range] == true
74
+ options[:range] = ::Kernel.String(options[:range])
75
+ end
76
+
77
+ options
78
+ end
79
+ end
80
+ end
81
+ end