neovim 0.7.1 → 0.9.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 (75) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/docs.yml +39 -0
  3. data/.github/workflows/tests.yml +64 -0
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +22 -0
  6. data/CODE_OF_CONDUCT.md +3 -3
  7. data/README.md +19 -20
  8. data/Rakefile +21 -11
  9. data/exe/neovim-ruby-host +5 -0
  10. data/lib/neovim/buffer.rb +120 -41
  11. data/lib/neovim/client.rb +304 -39
  12. data/lib/neovim/client_info.rb +46 -0
  13. data/lib/neovim/connection.rb +7 -2
  14. data/lib/neovim/event_loop.rb +8 -4
  15. data/lib/neovim/host/cli.rb +41 -0
  16. data/lib/neovim/host.rb +3 -0
  17. data/lib/neovim/logging.rb +1 -1
  18. data/lib/neovim/message.rb +1 -1
  19. data/lib/neovim/plugin/dsl.rb +6 -0
  20. data/lib/neovim/plugin.rb +7 -2
  21. data/lib/neovim/remote_object.rb +5 -2
  22. data/lib/neovim/ruby_provider/object_ext.rb +5 -0
  23. data/lib/neovim/ruby_provider/vim.rb +6 -5
  24. data/lib/neovim/ruby_provider.rb +29 -10
  25. data/lib/neovim/session.rb +21 -16
  26. data/lib/neovim/tabpage.rb +8 -15
  27. data/lib/neovim/version.rb +1 -1
  28. data/lib/neovim/window.rb +45 -33
  29. data/lib/neovim.rb +12 -3
  30. data/neovim.gemspec +4 -5
  31. data/script/ci/download_nvim.sh +40 -0
  32. data/script/dump_api.rb +1 -1
  33. data/script/generate_docs.rb +6 -7
  34. data/script/run_acceptance.rb +15 -11
  35. data/spec/acceptance/client_info_spec.vim +42 -0
  36. data/spec/acceptance/rplugin_command_spec.vim +2 -2
  37. data/spec/acceptance/ruby_spec.vim +18 -11
  38. data/spec/acceptance/rubydo_spec.vim +9 -0
  39. data/spec/acceptance/rubyeval_spec.vim +22 -0
  40. data/spec/acceptance/rubyfile/curbuf_ivar_get.rb +1 -1
  41. data/spec/acceptance/rubyfile/curbuf_ivar_set.rb +1 -1
  42. data/spec/acceptance/rubyfile/define_foo.rb +1 -1
  43. data/spec/acceptance/rubyfile/nested_inner.rb +1 -1
  44. data/spec/acceptance/rubyfile/set_pwd_after.rb +1 -1
  45. data/spec/acceptance/rubyfile/set_pwd_before.rb +1 -1
  46. data/spec/acceptance/rubyfile_spec.vim +19 -19
  47. data/spec/acceptance/runtime/init.vim +2 -2
  48. data/spec/acceptance/runtime/rplugin/ruby/commands.rb +2 -2
  49. data/spec/helper.rb +25 -7
  50. data/spec/neovim/api_spec.rb +1 -1
  51. data/spec/neovim/buffer_spec.rb +10 -6
  52. data/spec/neovim/client_info_spec.rb +77 -0
  53. data/spec/neovim/client_spec.rb +9 -2
  54. data/spec/neovim/connection_spec.rb +32 -4
  55. data/spec/neovim/current_spec.rb +1 -2
  56. data/spec/neovim/event_loop_spec.rb +16 -0
  57. data/spec/neovim/host/cli_spec.rb +94 -0
  58. data/spec/neovim/host_spec.rb +16 -14
  59. data/spec/neovim/line_range_spec.rb +1 -3
  60. data/spec/neovim/remote_object_spec.rb +1 -2
  61. data/spec/neovim/ruby_provider/buffer_ext_spec.rb +6 -7
  62. data/spec/neovim/ruby_provider/object_ext_spec.rb +10 -0
  63. data/spec/neovim/ruby_provider/vim_spec.rb +1 -1
  64. data/spec/neovim/ruby_provider/window_ext_spec.rb +7 -10
  65. data/spec/neovim/session_spec.rb +13 -40
  66. data/spec/neovim/window_spec.rb +1 -1
  67. data/spec/neovim_spec.rb +28 -51
  68. data/spec/support.rb +27 -1
  69. metadata +26 -44
  70. data/.coveralls.yml +0 -1
  71. data/.rubocop.yml +0 -118
  72. data/.travis.yml +0 -22
  73. data/appveyor.yml +0 -31
  74. data/bin/neovim-ruby-host +0 -18
  75. data/script/validate_docs.rb +0 -29
@@ -0,0 +1,46 @@
1
+ require "neovim/version"
2
+
3
+ module Neovim
4
+ # @api private
5
+ class ClientInfo
6
+ HOST_METHOD_SPEC = {poll: {}, specs: {nargs: 1}}.freeze
7
+
8
+ ATTRIBUTES = {
9
+ website: "https://github.com/neovim/neovim-ruby",
10
+ license: "MIT"
11
+ }.freeze
12
+
13
+ def self.for_host(host)
14
+ name = host.plugins.map(&:script_host?) == [true] ?
15
+ "ruby-script-host" :
16
+ "ruby-rplugin-host"
17
+
18
+ new(name, :host, HOST_METHOD_SPEC, ATTRIBUTES)
19
+ end
20
+
21
+ def self.for_client
22
+ new("ruby-client", :remote, {}, ATTRIBUTES)
23
+ end
24
+
25
+ def initialize(name, type, method_spec, attributes)
26
+ @name = name
27
+ @type = type
28
+ @method_spec = method_spec
29
+ @attributes = attributes
30
+
31
+ @version = ["major", "minor", "patch"]
32
+ .zip(Neovim::VERSION.segments)
33
+ .to_h
34
+ end
35
+
36
+ def to_args
37
+ [
38
+ @name,
39
+ @version,
40
+ @type,
41
+ @method_spec,
42
+ @attributes
43
+ ]
44
+ end
45
+ end
46
+ end
@@ -35,12 +35,12 @@ module Neovim
35
35
 
36
36
  @unpacker = MessagePack::Unpacker.new(@rd)
37
37
  @packer = MessagePack::Packer.new(@wr)
38
- @running = false
39
38
  end
40
39
 
41
40
  def write(object)
42
41
  log(:debug) { {object: object} }
43
- @packer.write(object).flush
42
+ @packer.write(object)
43
+ self
44
44
  end
45
45
 
46
46
  def read
@@ -49,6 +49,11 @@ module Neovim
49
49
  end
50
50
  end
51
51
 
52
+ def flush
53
+ @packer.flush
54
+ self
55
+ end
56
+
52
57
  def register_type(id)
53
58
  @unpacker.register_type(id) do |data|
54
59
  index = MessagePack.unpack(data)
@@ -34,8 +34,9 @@ module Neovim
34
34
  end
35
35
 
36
36
  def shutdown
37
- stop
37
+ @running = false
38
38
  @shutdown = true
39
+ @connection.close
39
40
  end
40
41
 
41
42
  def request(request_id, method, *args)
@@ -69,20 +70,23 @@ module Neovim
69
70
 
70
71
  def run
71
72
  @running = true
73
+ last_value = nil
72
74
 
73
75
  loop do
74
76
  break unless @running
75
77
  break if @shutdown
76
78
 
77
79
  begin
78
- yield read
79
- rescue EOFError => e
80
+ last_value = yield(read)
81
+ rescue EOFError, Errno::EPIPE => e
80
82
  log_exception(:debug, e, __method__)
81
83
  shutdown
82
84
  rescue => e
83
85
  log_exception(:error, e, __method__)
84
86
  end
85
87
  end
88
+
89
+ last_value
86
90
  ensure
87
91
  @connection.close if @shutdown
88
92
  end
@@ -102,7 +106,7 @@ module Neovim
102
106
  private
103
107
 
104
108
  def read
105
- array = @connection.read
109
+ array = @connection.flush.read
106
110
  Message.from_array(array)
107
111
  end
108
112
 
@@ -0,0 +1,41 @@
1
+ require "neovim/connection"
2
+ require "neovim/event_loop"
3
+ require "neovim/host"
4
+ require "neovim/version"
5
+ require "optparse"
6
+
7
+ module Neovim
8
+ class Host
9
+ # @api private
10
+ class CLI
11
+ def self.run(path, argv, inn, out, err)
12
+ cmd = File.basename(path)
13
+
14
+ OptionParser.new do |opts|
15
+ opts.on("-V", "--version") do
16
+ out.puts Neovim::VERSION
17
+ exit(0)
18
+ end
19
+
20
+ opts.on("-h", "--help") do
21
+ out.puts "Usage: #{cmd} [-hV] rplugin_path ..."
22
+ exit(0)
23
+ end
24
+ end.order!(argv)
25
+
26
+ if inn.tty?
27
+ err.puts("Can't run #{cmd} interactively.")
28
+ exit(1)
29
+ else
30
+ conn = Connection.new(inn, out)
31
+ event_loop = EventLoop.new(conn)
32
+
33
+ Host.run(argv, event_loop)
34
+ end
35
+ rescue OptionParser::InvalidOption => e
36
+ err.puts(e.message)
37
+ exit(1)
38
+ end
39
+ end
40
+ end
41
+ end
data/lib/neovim/host.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "neovim"
2
2
  require "neovim/client"
3
+ require "neovim/client_info"
3
4
  require "neovim/event_loop"
4
5
  require "neovim/host/loader"
5
6
 
@@ -78,6 +79,8 @@ module Neovim
78
79
 
79
80
  def initialize_client(request_id)
80
81
  @session.request_id = request_id
82
+ @session.notify(:nvim_set_client_info, *ClientInfo.for_host(self).to_args)
83
+
81
84
  @client = Client.from_event_loop(@event_loop, @session)
82
85
  end
83
86
 
@@ -18,7 +18,7 @@ module Neovim
18
18
 
19
19
  @logger = Logger.new(env_file || STDERR)
20
20
 
21
- if env_level
21
+ if /\S+/.match?(env_level)
22
22
  begin
23
23
  @logger.level = Integer(env_level)
24
24
  rescue ArgumentError
@@ -49,7 +49,7 @@ module Neovim
49
49
  end
50
50
 
51
51
  def received(handlers)
52
- handlers[request_id].call(self)
52
+ handlers.delete(request_id).call(self)
53
53
  end
54
54
  end
55
55
 
@@ -68,6 +68,12 @@ module Neovim
68
68
 
69
69
  private
70
70
 
71
+ # Mark this plugin as the Ruby script host started by nvim. Should only
72
+ # be used in +Neovim::RubyProvider+.
73
+ def script_host!
74
+ @plugin.script_host = true
75
+ end
76
+
71
77
  # Register a setup block to run once before the host starts. The block
72
78
  # should expect to receive a single argument, a +Neovim::Client+.
73
79
  #
data/lib/neovim/plugin.rb CHANGED
@@ -3,7 +3,7 @@ require "neovim/plugin/dsl"
3
3
  module Neovim
4
4
  # @api private
5
5
  class Plugin
6
- attr_accessor :handlers, :setup_blocks
6
+ attr_accessor :handlers, :setup_blocks, :script_host
7
7
  attr_reader :source
8
8
 
9
9
  def self.from_config_block(source)
@@ -13,9 +13,10 @@ module Neovim
13
13
  end
14
14
 
15
15
  def initialize(source)
16
- @handlers = []
17
16
  @source = source
17
+ @handlers = []
18
18
  @setup_blocks = []
19
+ @script_host = false
19
20
  end
20
21
 
21
22
  def specs
@@ -27,5 +28,9 @@ module Neovim
27
28
  def setup(client)
28
29
  @setup_blocks.each { |bl| bl.call(client) }
29
30
  end
31
+
32
+ def script_host?
33
+ !!@script_host
34
+ end
30
35
  end
31
36
  end
@@ -1,3 +1,5 @@
1
+ require "set"
2
+
1
3
  module Neovim
2
4
  # @abstract Superclass for all +nvim+ remote objects.
3
5
  #
@@ -37,7 +39,7 @@ module Neovim
37
39
 
38
40
  # Extend +methods+ to include RPC methods
39
41
  def methods(*args)
40
- super | rpc_methods
42
+ super | rpc_methods.to_a
41
43
  end
42
44
 
43
45
  # Extend +==+ to only look at class and index.
@@ -48,7 +50,8 @@ module Neovim
48
50
  private
49
51
 
50
52
  def rpc_methods
51
- @api.functions_for_object(self).map(&:method_name)
53
+ @rpc_methods ||=
54
+ @api.functions_for_object(self).map(&:method_name).to_set
52
55
  end
53
56
  end
54
57
  end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def to_msgpack(packer)
3
+ packer.pack(to_s)
4
+ end
5
+ end
@@ -15,7 +15,7 @@ module Vim
15
15
 
16
16
  # Delegate all method calls to the underlying +Neovim::Client+ object.
17
17
  def self.method_missing(method, *args, &block)
18
- if @__client.respond_to?(method)
18
+ if @__client
19
19
  @__client.public_send(method, *args, &block).tap do
20
20
  __refresh_globals(@__client)
21
21
  end
@@ -33,13 +33,14 @@ module Vim
33
33
  end
34
34
 
35
35
  def self.__refresh_globals(client)
36
- bufnr = client.evaluate("bufnr('%')")
36
+ bufid, winid = client.evaluate("[nvim_get_current_buf(), nvim_get_current_win()]")
37
+ session, api = client.session, client.api
37
38
 
38
- $curbuf = @__buffer_cache.fetch(bufnr) do
39
- @__buffer_cache[bufnr] = client.get_current_buf
39
+ $curbuf = @__buffer_cache.fetch(bufid) do
40
+ @__buffer_cache[bufid] = Buffer.new(bufid, session, api)
40
41
  end
41
42
 
42
- $curwin = client.get_current_win
43
+ $curwin = Window.new(winid, session, api)
43
44
  end
44
45
  end
45
46
 
@@ -1,4 +1,5 @@
1
1
  require "neovim/ruby_provider/vim"
2
+ require "neovim/ruby_provider/object_ext"
2
3
  require "neovim/ruby_provider/buffer_ext"
3
4
  require "neovim/ruby_provider/window_ext"
4
5
  require "stringio"
@@ -14,8 +15,11 @@ module Neovim
14
15
  Thread.abort_on_exception = true
15
16
 
16
17
  Neovim.plugin do |plug|
18
+ plug.__send__(:script_host!)
19
+
17
20
  __define_setup(plug)
18
21
  __define_ruby_execute(plug)
22
+ __define_ruby_eval(plug)
19
23
  __define_ruby_execute_file(plug)
20
24
  __define_ruby_do_range(plug)
21
25
  __define_ruby_chdir(plug)
@@ -42,12 +46,26 @@ module Neovim
42
46
  def self.__define_ruby_execute(plug)
43
47
  plug.__send__(:rpc, :ruby_execute) do |nvim, ruby|
44
48
  __wrap_client(nvim) do
45
- eval(ruby, TOPLEVEL_BINDING, "eval")
49
+ eval(ruby, TOPLEVEL_BINDING, "ruby_execute")
46
50
  end
51
+ nil
47
52
  end
48
53
  end
49
54
  private_class_method :__define_ruby_execute
50
55
 
56
+ # Evaluate the provided Ruby code, exposing the +Vim+ constant for
57
+ # interactions with the editor and returning the value.
58
+ #
59
+ # This is used by the +:rubyeval+ command.
60
+ def self.__define_ruby_eval(plug)
61
+ plug.__send__(:rpc, :ruby_eval) do |nvim, ruby|
62
+ __wrap_client(nvim) do
63
+ eval(ruby, TOPLEVEL_BINDING, "ruby_eval")
64
+ end
65
+ end
66
+ end
67
+ private_class_method :__define_ruby_eval
68
+
51
69
  # Evaluate the provided Ruby file, exposing the +Vim+ constant for
52
70
  # interactions with the editor.
53
71
  #
@@ -55,6 +73,7 @@ module Neovim
55
73
  def self.__define_ruby_execute_file(plug)
56
74
  plug.__send__(:rpc, :ruby_execute_file) do |nvim, path|
57
75
  __wrap_client(nvim) { load(path) }
76
+ nil
58
77
  end
59
78
  end
60
79
  private_class_method :__define_ruby_execute_file
@@ -73,14 +92,15 @@ module Neovim
73
92
  __start, __stop, __ruby = __args
74
93
  __buffer = __nvim.get_current_buf
75
94
 
76
- __update_lines_in_chunks(__buffer, __start, __stop, 5000) do |__lines|
95
+ __update_lines_in_chunks(__buffer, __start, __stop, 1_000) do |__lines|
77
96
  __lines.map do |__line|
78
97
  $_ = __line
79
- eval(__ruby, binding, "eval")
98
+ eval(__ruby, binding, "ruby_do_range")
80
99
  $_
81
100
  end
82
101
  end
83
102
  end
103
+ nil
84
104
  end
85
105
  end
86
106
  private_class_method :__define_ruby_do_range
@@ -101,7 +121,6 @@ module Neovim
101
121
  yield
102
122
  end
103
123
  end
104
- nil
105
124
  end
106
125
  private_class_method :__wrap_client
107
126
 
@@ -120,10 +139,10 @@ module Neovim
120
139
  $stdout, $stderr = StringIO.new, StringIO.new
121
140
 
122
141
  begin
123
- yield
124
-
125
- client.out_write($stdout.string + $/) if $stdout.size > 0
126
- client.err_writeln($stderr.string) if $stderr.size > 0
142
+ yield.tap do
143
+ client.out_write($stdout.string + $/) if $stdout.size > 0
144
+ client.err_writeln($stderr.string) if $stderr.size > 0
145
+ end
127
146
  ensure
128
147
  $stdout = old_stdout
129
148
  $stderr = old_stderr
@@ -134,9 +153,9 @@ module Neovim
134
153
  def self.__update_lines_in_chunks(buffer, start, stop, size)
135
154
  (start..stop).each_slice(size) do |linenos|
136
155
  start, stop = linenos[0] - 1, linenos[-1]
137
- lines = buffer.get_lines(start, stop, true)
156
+ lines = buffer.get_lines(start, stop, false)
138
157
 
139
- buffer.set_lines(start, stop, true, yield(lines))
158
+ buffer.set_lines(start, stop, false, yield(lines))
140
159
  end
141
160
  end
142
161
  private_class_method :__update_lines_in_chunks
@@ -11,6 +11,13 @@ module Neovim
11
11
 
12
12
  attr_writer :request_id
13
13
 
14
+ # @api private
15
+ class Disconnected < RuntimeError
16
+ def initialize
17
+ super("Disconnected from nvim process")
18
+ end
19
+ end
20
+
14
21
  def initialize(event_loop)
15
22
  @event_loop = event_loop
16
23
  @main_thread = Thread.current
@@ -21,19 +28,21 @@ module Neovim
21
28
  end
22
29
 
23
30
  def run(&block)
24
- @running = true
31
+ block ||= ->(msg) { @pending_messages << msg }
25
32
 
26
- while (pending = @pending_messages.shift)
27
- Fiber.new { pending.received(@response_handlers, &block) }.resume
28
- end
29
-
30
- return unless @running
33
+ @running = true
31
34
 
32
35
  @event_loop.run do |message|
33
36
  Fiber.new { message.received(@response_handlers, &block) }.resume
34
37
  end
35
38
  end
36
39
 
40
+ def next
41
+ return @pending_messages.shift if @pending_messages.any?
42
+
43
+ run { |msg| stop; msg }
44
+ end
45
+
37
46
  # Make an RPC request and return its response.
38
47
  #
39
48
  # If this method is called inside a callback, we are already inside a
@@ -60,7 +69,10 @@ module Neovim
60
69
 
61
70
  @event_loop.request(@request_id, method, *args)
62
71
  response = blocking ? blocking_response : yielding_response
63
- response.error ? raise(response.error) : response.value
72
+
73
+ raise(Disconnected) if response.nil?
74
+ raise(response.error) if response.error
75
+ response.value
64
76
  end
65
77
  end
66
78
 
@@ -85,15 +97,8 @@ module Neovim
85
97
  private
86
98
 
87
99
  def blocking_response
88
- response = nil
89
-
90
- @response_handlers[@request_id] = lambda do |res|
91
- response = res
92
- stop
93
- end
94
-
95
- run { |message| @pending_messages << message }
96
- response
100
+ @response_handlers[@request_id] = ->(res) { stop; res }
101
+ run
97
102
  end
98
103
 
99
104
  def yielding_response