neovim-0.8.1/0000755000004100000410000000000013506362344013053 5ustar www-datawww-dataneovim-0.8.1/.travis.yml0000644000004100000410000000121513506362344015163 0ustar www-datawww-datalanguage: ruby cache: bundler sudo: false dist: trusty branches: only: master rvm: - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - ruby-head before_install: - eval "$(curl --connect-timeout 30 --retry 3 -Ss https://raw.githubusercontent.com/neovim/bot-ci/master/scripts/travis-setup.sh) nightly-x64" - gem update --system --conservative || (gem install "rubygems-update:~>2.7" --no-document && update_rubygems) - gem install --remote bundler || gem install --remote bundler -v "1.17.3" - gem install --user-install executable-hooks - bundle --version env: NVIM_RUBY_LOG_LEVEL=DEBUG NVIM_RUBY_LOG_FILE=ci.log script: bundle exec rake --trace neovim-0.8.1/README.md0000644000004100000410000000707413506362344014342 0ustar www-datawww-data# Neovim Ruby [![Travis](https://travis-ci.org/neovim/neovim-ruby.svg?branch=master)](https://travis-ci.org/neovim/neovim-ruby) [![Build status](https://ci.appveyor.com/api/projects/status/y44k25tcdvvuq0ee?svg=true)](https://ci.appveyor.com/project/alexgenco/neovim-ruby) [![Gem Version](https://badge.fury.io/rb/neovim.svg)](https://badge.fury.io/rb/neovim) [![Inline docs](http://inch-ci.org/github/neovim/neovim-ruby.svg?branch=master)](http://inch-ci.org/github/neovim/neovim-ruby) Ruby bindings for [Neovim](https://github.com/neovim/neovim). *Warning*: This project follows [Semantic Versioning](http://semver.org/), thus its API should be considered unstable until it reaches v1.0.0 ([spec](http://semver.org/#spec-item-4)). ## Installation Add this line to your application's Gemfile: gem "neovim" And then execute: $ bundle Or install it yourself as: $ gem install neovim ## Usage You can control a running `nvim` process by connecting to `$NVIM_LISTEN_ADDRESS`. For example, to connect to `nvim` over a UNIX domain socket, start it up like this: ```shell $ NVIM_LISTEN_ADDRESS=/tmp/nvim.sock nvim ``` You can then connect to that socket path to get a `Neovim::Client`: ```ruby require "neovim" client = Neovim.attach_unix("/tmp/nvim.sock") ``` Refer to the [`Neovim` docs](http://www.rubydoc.info/github/neovim/neovim-ruby/master/Neovim) for other ways to connect to `nvim`, and the [`Neovim::Client` docs](http://www.rubydoc.info/github/neovim/neovim-ruby/master/Neovim/Client) for a summary of the client interface. ### Plugins Plugins are Ruby files loaded from the `$VIMRUNTIME/rplugin/ruby/` directory. Here's an example plugin: ```ruby # ~/.config/nvim/rplugin/ruby/example_plugin.rb Neovim.plugin do |plug| # Define a command called "SetLine" which sets the contents of the current # line. This command is executed asynchronously, so the return value is # ignored. plug.command(:SetLine, nargs: 1) do |nvim, str| nvim.current.line = str end # Define a function called "Sum" which adds two numbers. This function is # executed synchronously, so the result of the block will be returned to nvim. plug.function(:Sum, nargs: 2, sync: true) do |nvim, x, y| x + y end # Define an autocmd for the BufEnter event on Ruby files. plug.autocmd(:BufEnter, pattern: "*.rb") do |nvim| nvim.command("echom 'Ruby file, eh?'") end end ``` When you add or update a plugin, you will need to call `:UpdateRemotePlugins` to update the remote plugin manifest. See `:help remote-plugin-manifest` for more information. Refer to the [`Neovim::Plugin::DSL` docs](http://www.rubydoc.info/github/neovim/neovim-ruby/master/Neovim/Plugin/DSL) for a more complete overview of the `Neovim.plugin` DSL. ### Vim Plugin Support The Neovim gem also acts as a compatibility layer for Ruby plugins written for `vim`. The `:ruby`, `:rubyfile`, and `:rubydo` commands are intended to match their original behavior, and their documentation can be found [here](https://neovim.io/doc/user/if_ruby.html). ## Links * Source: * Bugs: * CI: * Documentation: * Latest Gem: * Master: ## Contributing 1. Fork it (http://github.com/neovim/neovim-ruby/fork) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request neovim-0.8.1/spec/0000755000004100000410000000000013506362344014005 5ustar www-datawww-dataneovim-0.8.1/spec/neovim/0000755000004100000410000000000013506362344015302 5ustar www-datawww-dataneovim-0.8.1/spec/neovim/event_loop_spec.rb0000644000004100000410000000354313506362344021020 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe EventLoop do let!(:push_pipe) { IO.pipe } let!(:pull_pipe) { IO.pipe } let(:client_rd) { push_pipe[0] } let(:client_wr) { pull_pipe[1] } let(:server_rd) { pull_pipe[0] } let(:server_wr) { push_pipe[1] } let(:connection) { Connection.new(client_rd, client_wr) } let(:event_loop) { EventLoop.new(connection) } describe "#request" do it "writes a msgpack request" do event_loop.request(1, :method, 1, 2) connection.flush message = server_rd.readpartial(1024) expect(message).to eq(MessagePack.pack([0, 1, "method", [1, 2]])) end end describe "#respond" do it "writes a msgpack response" do event_loop.respond(2, "value", "error") connection.flush message = server_rd.readpartial(1024) expect(message).to eq(MessagePack.pack([1, 2, "error", "value"])) end end describe "#notify" do it "writes a msgpack notification" do event_loop.notify(:method, 1, 2) connection.flush message = server_rd.readpartial(1024) expect(message).to eq(MessagePack.pack([2, "method", [1, 2]])) end end describe "#run" do it "yields received messages to the block" do server_wr.write(MessagePack.pack([0, 1, :foo_method, []])) server_wr.flush message = nil event_loop.run do |req| message = req event_loop.stop end expect(message.sync?).to eq(true) expect(message.method_name).to eq("foo_method") end it "shuts down after receiving EOFError" do run_thread = Thread.new do event_loop.run end server_wr.close run_thread.join expect(client_rd).to be_closed expect(client_wr).to be_closed end end end end neovim-0.8.1/spec/neovim/line_range_spec.rb0000644000004100000410000001124613506362344020750 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe LineRange do let(:client) { Neovim.attach_child(Support.child_argv) } let(:buffer) { client.current.buffer } let(:line_range) { LineRange.new(buffer) } before do buffer.set_lines(0, -1, true, ["1", "2", "3", "4"]) end after { client.shutdown } describe "#each" do it "yields each line" do yielded = [] line_range.each { |line| yielded << line } expect(yielded).to eq(["1", "2", "3", "4"]) end it "yields a large number of lines" do lines = Array.new(6000, "x") buffer.set_lines(0, -1, true, lines) yielded = [] line_range.each { |line| yielded << line } expect(yielded).to eq(lines) end end describe "#to_a" do it "returns lines as an array" do expect(line_range.to_a).to eq(["1", "2", "3", "4"]) end it "returns a large number of lines as an array" do lines = Array.new(6000, "x") buffer.set_lines(0, -1, true, lines) expect(line_range.to_a).to eq(lines) end end describe "#==" do it "compares line contents" do client.command("new") buffer2 = client.current.buffer expect(buffer2.lines == buffer.lines).to eq(false) buffer2.set_lines(0, -1, true, ["1", "2", "3", "4"]) expect(buffer2.lines == buffer.lines).to eq(true) end end describe "#[]" do it "accepts a single index" do expect(line_range[1]).to eq("2") expect(line_range[-1]).to eq("4") expect(line_range[-2]).to eq("3") end it "accepts an index and length" do expect(line_range[0, 2]).to eq(["1", "2"]) expect(line_range[-2, 2]).to eq(["3", "4"]) expect(line_range[-2, 3]).to eq(["3", "4"]) expect do line_range[2, 3] end.to raise_error(/out of bounds/) end it "accepts a range" do expect(line_range[0..1]).to eq(["1", "2"]) expect(line_range[0...1]).to eq(["1"]) expect(line_range[0..-1]).to eq(["1", "2", "3", "4"]) expect(line_range[0..-2]).to eq(["1", "2", "3"]) expect(line_range[-3..-2]).to eq(["2", "3"]) expect(line_range[0..-5]).to eq([]) expect(line_range[0...-4]).to eq([]) expect(line_range[-2..-3]).to eq([]) expect do line_range[2..4] end.to raise_error(/out of bounds/) end end describe "#[]=" do it "accepts a single index" do expect(line_range[0] = "foo").to eq("foo") expect(line_range.to_a).to eq(["foo", "2", "3", "4"]) expect(line_range[-1] = "bar").to eq("bar") expect(line_range.to_a).to eq(["foo", "2", "3", "bar"]) expect do line_range[-5] = "foo" end.to raise_error(/out of bounds/) end it "accepts an index and length" do expect(line_range[0, 2] = ["foo"]).to eq(["foo"]) expect(line_range.to_a).to eq(["foo", "3", "4"]) expect(line_range[-2, 2] = ["bar"]).to eq(["bar"]) expect(line_range.to_a).to eq(["foo", "bar"]) expect(line_range[0, 2] = "baz").to eq("baz") expect(line_range.to_a).to eq(["baz"]) expect do line_range[0, 5] = "foo" end.to raise_error(/out of bounds/) end it "accepts a range" do expect(line_range[0..1] = ["foo"]).to eq(["foo"]) expect(line_range.to_a).to eq(["foo", "3", "4"]) expect(line_range[0...1] = ["bar"]).to eq(["bar"]) expect(line_range.to_a).to eq(["bar", "3", "4"]) expect(line_range[0..-2] = ["baz"]).to eq(["baz"]) expect(line_range.to_a).to eq(["baz", "4"]) expect(line_range[0...2] = "qux").to eq("qux") expect(line_range.to_a).to eq(["qux"]) end end describe "#replace" do it "replaces all lines" do line_range.replace(["4", "5"]) expect(line_range.to_a).to eq(["4", "5"]) end end describe "#delete" do it "deletes the line at the given index" do expect do line_range.delete(0) end.to change { line_range.to_a }.to(["2", "3", "4"]) expect do line_range.delete(-1) end.to change { line_range.to_a }.to(["2", "3"]) expect do line_range.delete(-2) end.to change { line_range.to_a }.to(["3"]) end it "returns the line deleted" do expect(line_range.delete(0)).to eq("1") expect(line_range.delete(-1)).to eq("4") end it "returns nil if provided a non-integer" do expect do expect(line_range.delete(:foo)).to eq(nil) end.not_to change { line_range.to_a } end end end end neovim-0.8.1/spec/neovim/api_spec.rb0000644000004100000410000000347213506362344017420 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe API do let(:client) { Neovim.attach_child(Support.child_argv) } let(:api) do API.new( [ nil, { "functions" => [ {"name" => "nvim_func"}, {"name" => "nvim_buf_func"}, {"name" => "nvim_win_func"}, {"name" => "nvim_tabpage_func"} ] } ] ) end describe "#functions_for_object_method" do it "returns relevant functions" do function = api.function_for_object_method(client, :func) expect(function.name).to eq("nvim_func") function = api.function_for_object_method(client.get_current_buf, :func) expect(function.name).to eq("nvim_buf_func") function = api.function_for_object_method(client.get_current_win, :func) expect(function.name).to eq("nvim_win_func") function = api.function_for_object_method(client.get_current_tabpage, :func) expect(function.name).to eq("nvim_tabpage_func") end end describe "#functions_for_object" do it "returns relevant functions" do functions = api.functions_for_object(client) expect(functions.size).to be(1) expect(functions.first.name).to eq("nvim_func") functions = api.functions_for_object(client.get_current_buf) expect(functions.size).to be(1) expect(functions.first.name).to eq("nvim_buf_func") functions = api.functions_for_object(client.get_current_win) expect(functions.size).to be(1) expect(functions.first.name).to eq("nvim_win_func") functions = api.functions_for_object(client.get_current_tabpage) expect(functions.size).to be(1) expect(functions.first.name).to eq("nvim_tabpage_func") end end end end neovim-0.8.1/spec/neovim/plugin_spec.rb0000644000004100000410000000746413506362344020152 0ustar www-datawww-datarequire "helper" require "neovim/plugin" module Neovim RSpec.describe Plugin do describe ".from_config_block" do it "registers a command" do cmd_block = -> {} plugin = Plugin.from_config_block("source") do |plug| plug.command( "Foo", nargs: 1, range: true, bang: true, register: true, &cmd_block ) end expect(plugin.handlers.size).to be(1) handler = plugin.handlers.first expect(handler.sync?).to be(false) expect(handler.qualified?).to be(true) expect(handler.block).to eq(cmd_block) expect(handler.qualified_name).to eq("source:command:Foo") expect(handler.to_spec).to eq( type: :command, name: "Foo", sync: false, opts: { nargs: 1, range: "", bang: "", register: "" } ) end it "registers an autocmd" do au_block = -> {} plugin = Plugin.from_config_block("source") do |plug| plug.autocmd("BufEnter", pattern: "*.rb", &au_block) end expect(plugin.handlers.size).to be(1) handler = plugin.handlers.first expect(handler.sync?).to be(false) expect(handler.qualified?).to be(true) expect(handler.block).to eq(au_block) expect(handler.qualified_name).to eq("source:autocmd:BufEnter:*.rb") expect(handler.to_spec).to eq( type: :autocmd, name: "BufEnter", sync: false, opts: {pattern: "*.rb"} ) end it "registers a function" do fun_block = -> {} plugin = Plugin.from_config_block("source") do |plug| plug.function("Foo", range: true, nargs: 1, &fun_block) end expect(plugin.handlers.size).to be(1) handler = plugin.handlers.first expect(handler.sync?).to be(false) expect(handler.qualified?).to be(true) expect(handler.block).to eq(fun_block) expect(handler.qualified_name).to eq("source:function:Foo") expect(handler.to_spec).to eq( type: :function, name: "Foo", sync: false, opts: {range: "", nargs: 1} ) end it "registers setup callbacks" do yielded = [] plugin = Plugin.from_config_block("source") do |plug| plug.__send__(:setup) do |client| yielded << client end plug.__send__(:setup) do |_| yielded << :other end end expect do plugin.setup(:client) end.to change { yielded }.from([]).to([:client, :other]) end it "registers a top level RPC" do cmd_block = -> {} plugin = Plugin.from_config_block("source") do |plug| plug.__send__(:rpc, "Foo", &cmd_block) end expect(plugin.handlers.size).to be(1) handler = plugin.handlers.first expect(handler.sync?).to be(true) expect(handler.qualified?).to be(false) expect(handler.block).to eq(cmd_block) expect(handler.qualified_name).to eq("Foo") end end describe "#specs" do it "returns specs for plugin handlers" do plugin = Plugin.from_config_block("source") do |plug| plug.command("Foo", sync: true, nargs: 2) end expect(plugin.specs).to eq( [ { type: :command, name: "Foo", sync: true, opts: {nargs: 2} } ] ) end it "doesn't include specs for top-level RPCs" do plugin = Plugin.from_config_block("source") do |plug| plug.__send__(:rpc, "Foo") end expect(plugin.specs).to eq([]) end end end end neovim-0.8.1/spec/neovim/current_spec.rb0000644000004100000410000000700613506362344020326 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Current do let(:client) { Neovim.attach_child(Support.child_argv) } let(:current) { client.current } after { client.shutdown } describe "#line" do it "returns an empty string if the current line is empty" do expect(current.line).to eq("") end it "returns the contents of the line" do current.line = "New content" expect(current.line).to eq("New content") end end describe "#line=" do it "sets the content of the current line" do current.line = "New content" expect(current.line).to eq("New content") end end describe "#buffer" do it "returns the current buffer" do expect(current.buffer).to be_a(Buffer) end end describe "#buffer=" do it "sets the current buffer from an integer" do initial_index = current.buffer.index client.command("vnew") expect do current.buffer = initial_index end.to change { current.buffer.index }.to(initial_index) end it "sets the current buffer from a Buffer" do b0 = current.buffer client.command("vnew") b1 = current.buffer expect do current.buffer = b0 end.to change { current.buffer }.from(b1).to(b0) end it "returns an integer" do index = current.buffer.index expect(current.buffer = index).to eq(index) end it "returns a Buffer" do buffer = current.buffer expect(current.buffer = buffer).to eq(buffer) end end describe "#window" do it "returns the current window" do expect(current.window).to be_a(Window) end end describe "#window=" do it "sets the current window from an integer" do start_index = current.window.index client.command("vsp") expect do current.window = start_index end.to change { current.window.index }.to(start_index) end it "sets the current window from a Window" do w0 = current.window client.command("vsp") w1 = current.window expect do current.window = w0 end.to change { current.window }.from(w1).to(w0) end it "returns an integer" do index = current.window.index expect(current.window = index).to eq(index) end it "returns a Window" do w0 = current.window expect(current.window = w0).to eq(w0) end end describe "#tabpage" do it "returns the current tabpage" do expect(current.tabpage).to be_a(Tabpage) end end describe "#tabpage=" do it "sets the current tabpage from an integer" do initial_index = current.tabpage.index client.command("tabnew") expect(current.tabpage.index).not_to eq(initial_index) expect do current.tabpage = initial_index end.to change { current.tabpage.index }.to(initial_index) end it "sets the current tabpage from a Tabpage" do tp0 = current.tabpage client.command("tabnew") tp1 = current.tabpage expect do current.tabpage = tp0 end.to change { current.tabpage }.from(tp1).to(tp0) end it "returns an integer" do index = current.tabpage.index expect(current.tabpage = index).to eq(index) end it "returns a Tabpage" do tp0 = current.tabpage expect(current.tabpage = tp0).to eq(tp0) end end end end neovim-0.8.1/spec/neovim/buffer_spec.rb0000644000004100000410000001073513506362344020120 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Buffer do let(:client) { Neovim.attach_child(Support.child_argv) } let(:buffer) { client.current.buffer } before do client.command("normal ione") client.command("normal otwo") client.command("normal gg") end after { client.shutdown } describe "#lines" do it "returns a LineRange" do expect(buffer.lines).to be_a(LineRange) end end describe "#lines=" do it "updates the buffer's lines" do expect do buffer.lines = ["two", "three"] end.to change { buffer.lines.to_a }.to(["two", "three"]) end end describe "#set_name", "#name" do it "updates the buffer name" do expect do buffer.set_name("test_buf") end.to change { buffer.name }.to(/test_buf$/) end end describe "#number" do it "returns the buffer number" do expect(buffer.number).to eq(1) client.command("new") expect(client.get_current_buf.number).to eq(2) end end describe "#count" do it "returns the number of lines" do expect do buffer.append(0, "zero") end.to change { buffer.count }.from(2).to(3) end end describe "#length" do it "returns the number of lines" do expect do buffer.append(0, "zero") end.to change { buffer.length }.from(2).to(3) end end describe "#[]" do it "returns the line at the line number" do expect(buffer[1]).to eq("one") end it "raises on out of bounds" do expect do buffer[-1] end.to raise_error(/out of bounds/) expect do buffer[4] end.to raise_error(/out of bounds/) end end describe "#[]=" do it "sets the line at the line number" do expect do buffer[1] = "first" end.to change { buffer[1] }.from("one").to("first") end it "raises on out of bounds" do expect do buffer[4] = "line" end.to raise_error(/out of bounds/) expect do buffer[-1] = "line" end.to raise_error(/out of bounds/) end end describe "#delete" do it "deletes at the line number" do expect do buffer.delete(2) end.to change { buffer.lines.to_a }.to(["one"]) end it "raises on out of bounds" do expect do buffer.delete(-1) end.to raise_error(/out of bounds/) expect do buffer.delete(4) end.to raise_error(/out of bounds/) end end describe "#append" do it "appends after the line" do expect do buffer.append(2, "last") end.to change { buffer.lines.to_a }.to(["one", "two", "last"]) end it "inserts before the first line" do expect do buffer.append(0, "first") end.to change { buffer.lines.to_a }.to(["first", "one", "two"]) end it "allows newlines" do expect do buffer.append(0, "first\nsecond") end.to change { buffer.lines.to_a }.to(["first", "second", "one", "two"]) end it "doesn't move the cursor" do expect do buffer.append(0, "first") end.not_to change { client.get_current_win.cursor } end it "raises on out of bounds" do expect do buffer.append(-1, "line") end.to raise_error(/out of bounds/) expect do buffer.append(4, "line") end.to raise_error(/out of bounds/) end end describe "#line_number" do it "returns the current line number" do expect do client.command("normal j") end.to change { buffer.line_number }.from(1).to(2) end it "returns nil on inactive buffers" do expect do client.command("new") end.to change { buffer.line_number }.from(1).to(nil) end end describe "#line" do before { buffer.lines = ["one", "two"] } it "returns the current line" do expect do client.command("normal j") end.to change { buffer.line }.from("one").to("two") end it "returns nil for inactive buffers" do client.command("new") expect(buffer.line).to eq(nil) end end describe "#line=" do it "updates the current line" do expect do buffer.line = "first" end.to change { buffer.line }.to("first") end end end end neovim-0.8.1/spec/neovim/connection_spec.rb0000644000004100000410000000520713506362344021004 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Connection do let(:nil_io) { StringIO.new } describe "#write" do it "writes msgpack to the underlying file descriptor" do rd, wr = IO.pipe Connection.new(nil_io, wr).write("some data").flush data = rd.readpartial(1024) expect(MessagePack.unpack(data)).to eq("some data") end end describe "#flush" do it "flushes writes to the underlying file descriptor" do rd, wr = IO.pipe connection = Connection.new(nil_io, wr).write("some data") expect { rd.read_nonblock(16) }.to raise_error(IO::WaitReadable) connection.flush expect(rd.read_nonblock(16)).to eq(MessagePack.pack("some data")) end it "throws an exception when the file is closed" do _, wr = IO.pipe connection = Connection.new(nil_io, wr).write("some data") wr.close expect { connection.flush }.to raise_error(IOError) end end describe "#read" do it "reads msgpack from the underlying file descriptor" do rd, wr = IO.pipe wr.write(MessagePack.pack("some data")) wr.flush connection = Connection.new(rd, nil_io) expect(connection.read).to eq("some data") end it "throws an exception when the file is closed" do rd, wr = IO.pipe wr.close connection = Connection.new(rd, nil_io) expect { connection.read }.to raise_error(EOFError) end end describe "#register_type" do it "registers a msgpack ext type" do ext_class = Struct.new(:id) do def self.from_msgpack_ext(data) new(data.unpack("N")[0]) end def to_msgpack_ext [id].pack("C") end end client_rd, server_wr = IO.pipe _, client_wr = IO.pipe connection = Connection.new(client_rd, client_wr) connection.register_type(42) do |id| ext_class.new(id) end factory = MessagePack::Factory.new factory.register_type(42, ext_class) obj = ext_class.new(1) factory.packer(server_wr).write(obj).flush expect(connection.read).to eq(obj) end end describe "#close" do it "closes IO handles" do rd, wr = ::IO.pipe Connection.new(rd, wr).close expect(rd).to be_closed expect(wr).to be_closed end it "kills spawned processes" do io = ::IO.popen("cat", "rb+") pid = io.pid thr = Process.detach(pid) Connection.new(io, nil_io).close expect(thr.join.pid).to eq(pid) end end end end neovim-0.8.1/spec/neovim/session_spec.rb0000644000004100000410000000531013506362344020323 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Session do let(:event_loop) { EventLoop.child(Support.child_argv) } let!(:session) { Session.new(event_loop) } after { session.shutdown } describe "#request" do it "synchronously returns a result" do expect(session.request(:nvim_strwidth, "foobar")).to be(6) end it "raises an exception when there are errors" do expect do session.request(:nvim_strwidth, "too", "many") end.to raise_error(/wrong number of arguments/i) end it "handles large data" do large_str = Array.new(1024 * 17) { SecureRandom.hex(1) }.join session.request(:nvim_set_current_line, large_str) expect(session.request(:nvim_get_current_line)).to eq(large_str) end it "raises an exception when a command causes nvim to exit" do expect do session.request(:nvim_command, "qa!") end.to raise_error(Neovim::Session::Exited, /exited/) end it "fails outside of the main thread", :silence_thread_exceptions do expect do Thread.new { session.request(:nvim_strwidth, "foo") }.join end.to raise_error(/outside of the main thread/) end end describe "#notify" do it "doesn't raise exceptions" do expect do session.notify(:nvim_strwidth, "too", "many") end.not_to raise_error end it "handles large data" do large_str = Array.new(1024 * 17) { SecureRandom.hex(1) }.join session.notify(:nvim_set_current_line, large_str) expect(session.request(:nvim_get_current_line)).to eq(large_str) end it "succeeds outside of the main thread" do expect do Thread.new { session.notify(:nvim_set_current_line, "foo") }.join end.not_to raise_error end end describe "#run" do it "enqueues messages received during blocking requests" do session.request(:nvim_subscribe, "my_event") session.request(:nvim_command, "call rpcnotify(0, 'my_event', 'foo')") message = nil session.run do |msg| message = msg session.shutdown end expect(message.sync?).to eq(false) expect(message.method_name).to eq("my_event") expect(message.arguments).to eq(["foo"]) end it "supports requests within callbacks" do session.request(:nvim_subscribe, "my_event") session.request(:nvim_command, "call rpcnotify(0, 'my_event', 'foo')") result = nil session.run do |msg| result = session.request(:nvim_strwidth, msg.arguments.first) session.shutdown end expect(result).to be(3) end end end end neovim-0.8.1/spec/neovim/ruby_provider/0000755000004100000410000000000013506362344020175 5ustar www-datawww-dataneovim-0.8.1/spec/neovim/ruby_provider/vim_spec.rb0000644000004100000410000000306513506362344022333 0ustar www-datawww-datarequire "helper" require "neovim/ruby_provider/vim" RSpec.describe Vim do around do |spec| client = Vim.instance_variable_get(:@__client) buffer_cache = Vim.instance_variable_get(:@__buffer_cache) curbuf = $curbuf curwin = $curwin begin Vim.__client = nil Vim.instance_variable_set(:@__buffer_cache, {}) $curbuf = nil $curwin = nil spec.run ensure Vim.__client = client Vim.instance_variable_set(:@__buffer_cache, buffer_cache) $curbuf = curbuf $curwin = curwin end end describe Vim::Buffer do it "refers to Neovim::Buffer" do expect(Vim::Buffer).to be(Neovim::Buffer) end end describe Vim::Window do it "refers to Neovim::Window" do expect(Vim::Window).to be(Neovim::Window) end end describe VIM do it "is an alias for the Vim module" do expect(VIM).to be(Vim) end end describe "#method_missing" do it "delegates method calls to @__client" do client = double(:client) expect(Vim).to receive(:__refresh_globals).with(client) expect(client).to receive(:foo).with(1, 2) Vim.__client = client Vim.foo(1, 2) end it "refreshes global variables" do client = Neovim.attach_child(Support.child_argv) client.command("vs foo") Vim.__client = client Vim.__refresh_globals(client) expect do Vim.command("wincmd n") end.to change { $curwin.index }.by(1) expect do Vim.command("vs bar") end.to change { $curbuf.index }.by(1) end end end neovim-0.8.1/spec/neovim/ruby_provider/window_ext_spec.rb0000644000004100000410000000232613506362344023726 0ustar www-datawww-datarequire "helper" require "neovim/ruby_provider/window_ext" module Neovim RSpec.describe Window do let!(:nvim) do Neovim.attach_child(Support.child_argv).tap do |nvim| stub_const("::Vim", nvim) end end after { nvim.shutdown } describe ".current" do it "returns the current window from the global Vim client" do expect(Window.current).to eq(nvim.get_current_win) end end describe ".count" do it "returns the current window count from the global Vim client" do expect do nvim.command("new") end.to change { Window.count }.by(1) end it "only includes windows within a tabpage" do expect do nvim.command("tabnew") end.not_to change { Window.count }.from(1) end end describe ".[]" do it "returns the window at the given index" do expect do nvim.command("new") end.to change { Window[1] }.from(nil).to(kind_of(Window)) end it "only includes windows within a tabpage" do nvim.command("new") expect do nvim.command("tabnew") end.to change { Window[1] }.from(kind_of(Window)).to(nil) end end end end neovim-0.8.1/spec/neovim/ruby_provider/buffer_ext_spec.rb0000644000004100000410000000161713506362344023672 0ustar www-datawww-datarequire "helper" require "neovim/ruby_provider/buffer_ext" module Neovim RSpec.describe Buffer do let!(:nvim) do Neovim.attach_child(Support.child_argv).tap do |nvim| stub_const("::Vim", nvim) end end after { nvim.shutdown } describe ".current" do it "returns the current buffer from the global Vim client" do expect(Buffer.current).to eq(nvim.get_current_buf) end end describe ".count" do it "returns the current buffer count from the global Vim client" do expect do nvim.command("new") end.to change { Buffer.count }.by(1) end end describe ".[]" do it "returns the buffer from the global Vim client at the given index" do expect(Buffer[0]).to eq(nvim.get_current_buf) nvim.command("new") expect(Buffer[1]).to eq(nvim.get_current_buf) end end end end neovim-0.8.1/spec/neovim/remote_object_spec.rb0000644000004100000410000000545413506362344021472 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe RemoteObject do let(:client) { Neovim.attach_child(Support.child_argv) } after { client.shutdown } context Window do let(:window) { client.current.window } describe "#respond_to?" do it "returns true for Window functions" do expect(window).to respond_to(:get_cursor) end it "returns true for Ruby functions" do expect(window).to respond_to(:inspect) end it "returns false otherwise" do expect(window).not_to respond_to(:foobar) end end describe "#method_missing" do it "enables nvim_win_* function calls" do expect(window.get_cursor).to eq([1, 0]) end it "falls back to super" do expect { window.foobar }.to raise_error(NoMethodError) end end describe "#methods" do it "returns builtin methods" do expect(window.methods).to include(:inspect) end it "returns api methods" do expect(window.methods).to include(:get_height) end end end context Tabpage do let(:tabpage) { client.current.tabpage } describe "#respond_to?" do it "returns true for Tabpage functions" do expect(tabpage).to respond_to(:is_valid) end it "returns true for Ruby functions" do expect(tabpage).to respond_to(:inspect) end it "returns false otherwise" do expect(tabpage).not_to respond_to(:foobar) end end describe "#method_missing" do it "enables nvim_tabpage_* function calls" do expect(tabpage.is_valid).to be(true) end end describe "#methods" do it "returns builtin methods" do expect(tabpage.methods).to include(:inspect) end it "returns api methods" do expect(tabpage.methods).to include(:list_wins) end end end context Buffer do let(:buffer) { client.current.buffer } describe "#respond_to?" do it "returns true for Buffer functions" do expect(buffer).to respond_to(:line_count) end it "returns true for Ruby functions" do expect(buffer).to respond_to(:inspect) end it "returns false otherwise" do expect(buffer).not_to respond_to(:foobar) end end describe "#method_missing" do it "enables nvim_buf_* function calls" do expect(buffer.line_count).to be(1) end end describe "#methods" do it "returns builtin methods" do expect(buffer.methods).to include(:inspect) end it "returns api methods" do expect(buffer.methods).to include(:get_mark) end end end end end neovim-0.8.1/spec/neovim/window_spec.rb0000644000004100000410000000204013506362344020144 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Window do let(:client) { Neovim.attach_child(Support.child_argv) } let(:window) { client.current.window } before do client.command("normal ione") client.command("normal otwo") client.command("normal gg") client.command("vsplit") end describe "#buffer" do it "returns the window's buffer" do expect(window.buffer).to eq(client.get_current_buf) end end describe "#height", "#height=" do it "adjusts the window height" do expect do window.height -= 1 end.to change { window.height }.by(-1) end end describe "#width", "#width=" do it "adjusts the window width" do expect do window.width -= 1 end.to change { window.width }.by(-1) end end describe "#cursor", "#cursor=" do it "adjusts the window cursor" do expect do window.cursor = [2, 0] end.to change { window.cursor }.to([2, 0]) end end end end neovim-0.8.1/spec/neovim/client_spec.rb0000644000004100000410000000427513506362344020127 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Client do let(:client) { Neovim.attach_child(Support.child_argv) } after { client.shutdown } describe "#set_option" do it "sets an option from two arguments" do expect do client.set_option("makeprg", "rake") end.to change { client.evaluate("&makeprg") }.to("rake") end it "sets an option from a string" do expect do client.set_option("timeoutlen=0") end.to change { client.evaluate("&timeoutlen") }.to(0) end it "sets a boolean option" do expect do client.set_option("expandtab") end.to change { client.evaluate("&expandtab") }.to(1) end end describe "#shutdown" do it "causes nvim to exit" do client.shutdown expect { client.strwidth("hi") }.to raise_error(Neovim::Session::Exited) end end describe "#respond_to?" do it "returns true for vim functions" do expect(client).to respond_to(:strwidth) end it "returns true for Ruby functions" do expect(client).to respond_to(:inspect) end it "returns false otherwise" do expect(client).not_to respond_to(:foobar) end end describe "#method_missing" do it "enables nvim_* function calls" do expect(client.strwidth("hi")).to eq(2) end it "raises exceptions for unknown methods" do expect do client.foobar end.to raise_error(NoMethodError) end it "raises exceptions for incorrect usage" do expect do client.strwidth("too", "many") end.to raise_error("Wrong number of arguments: expecting 1 but got 2") end end describe "#methods" do it "includes builtin methods" do expect(client.methods).to include(:inspect) end it "includes api methods" do expect(client.methods).to include(:strwidth) end end describe "#current" do it "returns the target" do expect(client.current.buffer).to be_a(Buffer) expect(client.current.window).to be_a(Window) expect(client.current.tabpage).to be_a(Tabpage) end end end end neovim-0.8.1/spec/neovim/logging_spec.rb0000644000004100000410000000761313506362344020276 0ustar www-datawww-datarequire "helper" require "multi_json" module Neovim RSpec.describe Logging do around do |spec| old_logger = Logging.logger begin Logging.send(:remove_instance_variable, :@logger) spec.run ensure Logging.logger = old_logger end end describe ".logger" do it "fetches the output from $NVIM_RUBY_LOG_FILE" do logger = instance_double(Logger, :level= => nil, :formatter= => nil) expect(Logger).to receive(:new).with("/tmp/nvim.log").and_return(logger) Logging.logger("NVIM_RUBY_LOG_FILE" => "/tmp/nvim.log") expect(Logging.logger).to be(logger) end it "defaults the output to STDERR" do logger = instance_double(Logger, :level= => nil, :formatter= => nil) expect(Logger).to receive(:new).with(STDERR).and_return(logger) Logging.logger({}) expect(Logging.logger).to be(logger) end it "fetches the level from $NVIM_RUBY_LOG_LEVEL as a string" do logger = instance_double(Logger, :formatter= => nil) expect(Logger).to receive(:new).and_return(logger) expect(logger).to receive(:level=).with(Logger::DEBUG) Logging.logger("NVIM_RUBY_LOG_LEVEL" => "DEBUG") expect(Logging.logger).to be(logger) end it "fetches the level from $NVIM_RUBY_LOG_LEVEL as an integer" do logger = instance_double(Logger, :formatter= => nil) expect(Logger).to receive(:new).and_return(logger) expect(logger).to receive(:level=).with(0) Logging.logger("NVIM_RUBY_LOG_LEVEL" => "0") expect(Logging.logger).to be(logger) end it "defaults the level to WARN" do logger = instance_double(Logger, :formatter= => nil) expect(Logger).to receive(:new).and_return(logger) expect(logger).to receive(:level=).with(Logger::WARN) Logging.logger({}) expect(Logging.logger).to be(logger) end end describe Logging::Helpers do let!(:log) do StringIO.new.tap do |io| logger = Logger.new(io) logger.level = Logger::DEBUG Neovim.logger = logger end end let(:klass) do Class.new do include Logging def public_log(level, fields) log(level) { fields } end def public_log_exception(*args) log_exception(*args) end end end let(:obj) { klass.new } describe "#log" do it "logs JSON at the specified level" do obj.public_log(:info, foo: "bar") logged = MultiJson.decode(log.string) expect(logged).to match( "_level" => "INFO", "_class" => klass.to_s, "_method" => "public_log", "_time" => /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+/, "foo" => "bar" ) end end describe "#log_exception" do it "logs JSON at the specified level and debugs the backtrace" do ex = RuntimeError.new("BOOM") ex.set_backtrace(["one", "two"]) obj.public_log_exception(:fatal, ex, :some_method) lines = log.string.lines.to_a fatal = MultiJson.decode(lines[0]) debug = MultiJson.decode(lines[1]) expect(fatal).to match( "_level" => "FATAL", "_class" => klass.to_s, "_method" => "some_method", "_time" => /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+/, "exception" => "RuntimeError", "message" => "BOOM" ) expect(debug).to match( "_level" => "DEBUG", "_class" => klass.to_s, "_method" => "some_method", "_time" => /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+/, "exception" => "RuntimeError", "message" => "BOOM", "backtrace" => ["one", "two"] ) end end end end end neovim-0.8.1/spec/neovim/host_spec.rb0000644000004100000410000001007713506362344017623 0ustar www-datawww-datarequire "helper" require "neovim/host" module Neovim RSpec.describe Host do describe ".run" do let!(:push_pipe) { IO.pipe } let!(:pull_pipe) { IO.pipe } let(:host_rd) { push_pipe[0] } let(:host_wr) { pull_pipe[1] } let(:nvim_rd) { MessagePack::Unpacker.new(pull_pipe[0]) } let(:nvim_wr) { MessagePack::Packer.new(push_pipe[1]) } let(:plugin_path) do Support.file_path("my_plugin").tap do |path| File.write(path, <<-PLUGIN) Neovim.plugin do |plug| plug.command(:Echo, nargs: 1, sync: true) do |client, arg| arg end plug.command(:Boom, sync: true) do |client| raise "BOOM" end plug.command(:BoomAsync) do |client| raise "BOOM ASYNC" end end PLUGIN end end let!(:host_thread) do connection = Connection.new(host_rd, host_wr) event_loop = EventLoop.new(connection) Thread.new do Host.run([plugin_path], event_loop) end end after do host_thread.kill while host_thread.alive? host_thread.join end context "poll" do it "initializes a client, sets client info, and responds 'ok'" do nvim_wr.write([0, 1, :poll, []]).flush expect(nvim_rd.read).to match([2, "nvim_set_client_info", duck_type(:to_ary)]) type, reqid, method = nvim_rd.read expect([type, reqid, method]).to match([0, duck_type(:to_int), "nvim_get_api_info"]) api_info = [0, {"types" => {}, "functions" => {}}] nvim_wr.write([1, reqid, nil, api_info]).flush expect(nvim_rd.read).to eq([1, 1, nil, "ok"]) end end context "after poll" do before do nvim_wr.write([0, 1, :poll, []]).flush expect(nvim_rd.read[1]).to eq("nvim_set_client_info") _, reqid, method = nvim_rd.read expect(method).to eq("nvim_get_api_info") session = Session.new(EventLoop.child(Support.child_argv)) api_info = session.request(:nvim_get_api_info) session.shutdown nvim_wr.write([1, reqid, nil, api_info]).flush expect(nvim_rd.read[3]).to eq("ok") end it "responds with specs to the 'specs' request" do nvim_wr.write([0, 2, :specs, [plugin_path]]).flush expect(nvim_rd.read).to eq( [ 1, 2, nil, [ { "type" => "command", "name" => "Echo", "sync" => true, "opts" => {"nargs" => 1} }, { "type" => "command", "name" => "Boom", "sync" => true, "opts" => {} }, { "type" => "command", "name" => "BoomAsync", "sync" => false, "opts" => {} } ] ] ) end it "delegates to plugin handlers" do nvim_wr.write([0, 0, "#{plugin_path}:command:Echo", ["hi"]]).flush expect(nvim_rd.read).to eq([1, 0, nil, "hi"]) end it "handles exceptions in sync plugin handlers" do nvim_wr.write([0, 0, "#{plugin_path}:command:Boom", ["hi"]]).flush expect(nvim_rd.read).to eq([1, 0, "BOOM", nil]) end it "handles exceptions in async plugin handlers" do nvim_wr.write([2, "#{plugin_path}:command:BoomAsync", ["hi"]]).flush expect(nvim_rd.read).to match_array( [ 0, duck_type(:to_int), "nvim_err_writeln", [/my_plugin:command:BoomAsync: \(RuntimeError\) BOOM ASYNC/] ] ) end it "handles unknown requests" do nvim_wr.write([0, 0, "foobar", []]).flush expect(nvim_rd.read).to eq([1, 0, "Unknown request foobar", nil]) end end end end end neovim-0.8.1/spec/neovim/message_spec.rb0000644000004100000410000000647713506362344020303 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Message do describe ".request" do it "builds a request" do request = Message.request(1, :method, [1, 2]) expect(request).to be_a(Message::Request) expect(request.sync?).to eq(true) expect(request.id).to eq(1) expect(request.method_name).to eq(:method) expect(request.arguments).to eq([1, 2]) end end describe ".response" do it "builds a response" do response = Message.response(1, "error", "value") expect(response).to be_a(Message::Response) expect(response.request_id).to eq(1) expect(response.error).to eq("error") expect(response.value).to eq("value") end end describe ".notification" do it "builds a notification" do notification = Message.notification(:method, [1, 2]) expect(notification).to be_a(Message::Notification) expect(notification.sync?).to eq(false) expect(notification.method_name).to eq(:method) expect(notification.arguments).to eq([1, 2]) end end describe ".from_array" do it "returns a request" do request = Message.from_array([0, 1, :method, [1, 2]]) expect(request).to be_a(Message::Request) expect(request.id).to eq(1) expect(request.method_name).to eq(:method) expect(request.arguments).to eq([1, 2]) end it "returns a response" do response = Message.from_array([1, 1, [1, "error"], "value"]) expect(response).to be_a(Message::Response) expect(response.request_id).to eq(1) expect(response.error).to eq("error") expect(response.value).to eq("value") end it "returns a notification" do notification = Message.from_array([2, :method, [1, 2]]) expect(notification).to be_a(Message::Notification) expect(notification.method_name).to eq(:method) expect(notification.arguments).to eq([1, 2]) end end describe Message::Request do subject { described_class.new(1, :method, [1, 2]) } specify "#to_a" do expect(subject.to_a).to eq([0, 1, :method, [1, 2]]) end describe "#received" do it "yields itself to the block" do request = Message::Request.new(1, :method, [1, 2]) expect do |block| request.received({}, &block) end.to yield_with_args(request) end end end describe Message::Response do subject { described_class.new(2, "error", "result") } specify "#to_a" do expect(subject.to_a).to eq([1, 2, "error", "result"]) end describe "#received" do it "yields itself to the response handler" do response = Message::Response.new(1, nil, "value") expect do |block| response.received(1 => block.to_proc) end.to yield_with_args(response) end end end describe Message::Notification do subject { described_class.new(:method, [1, 2]) } specify "#to_a" do expect(subject.to_a).to eq([2, :method, [1, 2]]) end describe "#received" do it "yields itself to the block" do expect do |block| subject.received({}, &block) end.to yield_with_args(subject) end end end end end neovim-0.8.1/spec/neovim/host/0000755000004100000410000000000013506362344016257 5ustar www-datawww-dataneovim-0.8.1/spec/neovim/host/loader_spec.rb0000644000004100000410000000304013506362344021061 0ustar www-datawww-datarequire "helper" require "neovim/host" module Neovim class Host RSpec.describe Loader do describe "#load" do let(:plugin_path) { Support.file_path("plug.rb") } let(:host) { instance_double(Host, plugins: []) } let(:loader) { Loader.new(host) } before do File.write(plugin_path, "Neovim.plugin") end it "registers plugins defined in the provided files" do expect do loader.load([plugin_path]) end.to change { host.plugins.size }.by(1) end it "registers multiple plugins defined in the provided files" do File.write(plugin_path, "Neovim.plugin; Neovim.plugin") expect do loader.load([plugin_path]) end.to change { host.plugins.size }.by(2) end it "doesn't register plugins when none are defined" do File.write(plugin_path, "class FooClass; end") expect do loader.load([plugin_path]) end.not_to change { host.plugins.size } end it "doesn't leak constants defined in plugins" do File.write(plugin_path, "class FooClass; end") expect do loader.load([plugin_path]) end.not_to change { Kernel.const_defined?(:FooClass) }.from(false) end it "doesn't leak the overidden Neovim.plugin method" do loader.load([plugin_path]) expect do Neovim.plugin end.to raise_error(/outside of a plugin host/) end end end end end neovim-0.8.1/spec/neovim/executable_spec.rb0000644000004100000410000000152113506362344020761 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Executable do describe ".from_env" do it "respects NVIM_EXECUTABLE" do executable = Executable.from_env("NVIM_EXECUTABLE" => "/foo/nvim") expect(executable.path).to eq("/foo/nvim") end it "returns a default path" do executable = Executable.from_env({}) expect(executable.path).to eq("nvim") end end describe "#version" do it "returns the current nvim version" do executable = Executable.from_env expect(executable.version).to match(/^\d+\.\d+\.\d+/) end it "raises with an invalid executable path" do executable = Executable.new(File::NULL) expect do executable.version end.to raise_error(Executable::Error, Regexp.new(File::NULL)) end end end end neovim-0.8.1/spec/neovim/client_info_spec.rb0000644000004100000410000000366313506362344021142 0ustar www-datawww-datarequire "neovim/client_info" require "neovim/host" require "neovim/plugin" module Neovim RSpec.describe ClientInfo do describe "#to_args" do context ".for_host" do it "returns script-host info" do plugin = double(Plugin, script_host?: true) host = double(Host, plugins: [plugin]) info = ClientInfo.for_host(host) expect(info.to_args).to match( [ "ruby-script-host", { "major" => duck_type(:to_int), "minor" => duck_type(:to_int), "patch" => duck_type(:to_int) }, :host, { poll: {}, specs: {nargs: 1} }, duck_type(:to_hash) ] ) end it "returns rplugin info" do plugin = double(Plugin, script_host?: false) host = double(Host, plugins: [plugin]) info = ClientInfo.for_host(host) expect(info.to_args).to match( [ "ruby-rplugin-host", { "major" => duck_type(:to_int), "minor" => duck_type(:to_int), "patch" => duck_type(:to_int) }, :host, { poll: {}, specs: {nargs: 1} }, duck_type(:to_hash) ] ) end end context ".for_client" do it "returns remote client info" do info = ClientInfo.for_client expect(info.to_args).to match( [ "ruby-client", { "major" => duck_type(:to_int), "minor" => duck_type(:to_int), "patch" => duck_type(:to_int) }, :remote, {}, duck_type(:to_hash) ] ) end end end end end neovim-0.8.1/spec/helper.rb0000644000004100000410000000200513506362344015606 0ustar www-datawww-datarequire "bundler/setup" require "fileutils" require "msgpack" require "neovim" require "pry" require "rubygems" require "securerandom" require "stringio" require "timeout" require File.expand_path("support.rb", __dir__) RSpec.configure do |config| config.expect_with :rspec do |exp| exp.syntax = :expect end config.disable_monkey_patching! config.order = :random config.color = true config.around(:example, :silence_thread_exceptions) do |spec| if Thread.respond_to?(:report_on_exception) original = Thread.report_on_exception begin Thread.report_on_exception = false spec.run ensure Thread.report_on_exception = original end else spec.run end end config.around(:example) do |spec| Support.setup_workspace timeout = spec.metadata.fetch(:timeout, 3) begin Timeout.timeout(timeout) { spec.run } ensure Support.teardown_workspace end end Kernel.srand config.seed end Thread.abort_on_exception = true neovim-0.8.1/spec/acceptance/0000755000004100000410000000000013506362344016073 5ustar www-datawww-dataneovim-0.8.1/spec/acceptance/rplugin_function_spec.vim0000644000004100000410000000174013506362344023211 0ustar www-datawww-datalet s:suite = themis#suite("Remote plugin function") let s:expect = themis#helper("expect") function! s:suite.before_each() abort 1,$delete call append(0, ["one", "two", "three"]) endfunction function! s:suite.has_nvim() abort call s:expect(has("nvim")).to_equal(1) endfunction function! s:suite.supports_arguments() abort call s:expect(RPluginFunctionArgs(1, 2)).to_equal([1, 2]) endfunction function! s:suite.supports_line_range() abort 3,4call RPluginFunctionRange() call s:expect(g:rplugin_function_range).to_equal([3, 4]) endfunction function! s:suite.supports_eval() abort let g:to_eval = {'a': 42} call s:expect(RPluginFunctionEval()).to_equal({"a": 42, "b": 43}) endfunction function! s:suite.supports_asynchronous_functions() abort call RPluginFunctionAsync() sleep 50m call s:expect(g:rplugin_function_async).to_equal(v:true) endfunction function! s:suite.supports_recursion() abort call s:expect(RPluginFunctionRecursive(0)).to_equal(10) endfunction neovim-0.8.1/spec/acceptance/client_info_spec.vim0000644000004100000410000000305013506362344022111 0ustar www-datawww-datalet s:suite = themis#suite("Client info") let s:expect = themis#helper("expect") function! s:suite.before_each() abort call RPluginFunctionArgs(1, 2) let s:client_chans = map( \ filter(nvim_list_chans(), "has_key(v:val, 'client')"), \ "v:val.client") endfunction function! s:suite.get_script_host_client_info() abort let client_info = s:client_chans[1] call s:expect(sort(keys(client_info))).to_equal( \ ["attributes", "methods", "name", "type", "version"]) call s:expect(client_info.attributes).to_be_dict() call s:expect(client_info.methods).to_equal({"specs": {"nargs": 1}, "poll": {}}) call s:expect(client_info.name).to_equal("ruby-script-host") call s:expect(client_info.type).to_equal("host") call s:expect(client_info.version.major).to_be_number() call s:expect(client_info.version.minor).to_be_number() call s:expect(client_info.version.patch).to_be_number() endfunction function! s:suite.get_rplugin_client_info() abort let client_info = s:client_chans[0] call s:expect(sort(keys(client_info))).to_equal( \ ["attributes", "methods", "name", "type", "version"]) call s:expect(client_info.attributes).to_be_dict() call s:expect(client_info.methods).to_equal({"specs": {"nargs": 1}, "poll": {}}) call s:expect(client_info.name).to_equal("ruby-rplugin-host") call s:expect(client_info.type).to_equal("host") call s:expect(client_info.version.major).to_be_number() call s:expect(client_info.version.minor).to_be_number() call s:expect(client_info.version.patch).to_be_number() endfunction neovim-0.8.1/spec/acceptance/rplugin_autocmd_spec.vim0000644000004100000410000000150513506362344023017 0ustar www-datawww-datalet s:suite = themis#suite("Remote plugin autocmd") let s:expect = themis#helper("expect") function! s:suite.has_nvim() abort call s:expect(has("nvim")).to_equal(1) endfunction function! s:suite.triggers_for_matched_pattern() abort silent split file.rb call s:expect(b:rplugin_autocmd_BufEnter).to_equal(v:true) endfunction function! s:suite.doesnt_trigger_for_unmatched_pattern() abort silent split file.py call s:expect(exists('b:rplugin_autocmd_BufEnter')).to_equal(0) endfunction function! s:suite.supports_eval() abort let g:to_eval = {'a': 42} silent split file.c call s:expect(g:rplugin_autocmd_BufEnter_eval).to_equal({'a': 42, 'b': 43}) endfunction function! s:suite.supports_async() abort silent split file.async sleep 50m call s:expect(g:rplugin_autocmd_BufEnter_async).to_equal(v:true) endfunction neovim-0.8.1/spec/acceptance/runtime/0000755000004100000410000000000013506362344017556 5ustar www-datawww-dataneovim-0.8.1/spec/acceptance/runtime/rplugin/0000755000004100000410000000000013506362344021236 5ustar www-datawww-dataneovim-0.8.1/spec/acceptance/runtime/rplugin/ruby/0000755000004100000410000000000013506362344022217 5ustar www-datawww-dataneovim-0.8.1/spec/acceptance/runtime/rplugin/ruby/autocmds.rb0000644000004100000410000000072213506362344024364 0ustar www-datawww-dataNeovim.plugin do |plug| plug.autocmd(:BufEnter, pattern: "*.rb", sync: true) do |nvim| nvim.get_current_buf.set_var("rplugin_autocmd_BufEnter", true) end plug.autocmd(:BufEnter, pattern: "*.c", eval: "g:to_eval", sync: true) do |nvim, to_eval| nvim.set_var("rplugin_autocmd_BufEnter_eval", to_eval.merge(b: 43)) end plug.autocmd(:BufEnter, pattern: "*.async") do |nvim, to_eval| nvim.set_var("rplugin_autocmd_BufEnter_async", true) end end neovim-0.8.1/spec/acceptance/runtime/rplugin/ruby/functions.rb0000644000004100000410000000123613506362344024556 0ustar www-datawww-dataNeovim.plugin do |plug| plug.function(:RPluginFunctionArgs, sync: true) do |nvim, *args| args end plug.function(:RPluginFunctionRange, range: true, sync: true) do |nvim, start, stop| nvim.set_var("rplugin_function_range", [start, stop]) end plug.function(:RPluginFunctionEval, eval: "g:to_eval", sync: true) do |nvim, to_eval| to_eval.merge(b: 43) end plug.function(:RPluginFunctionAsync) do |nvim| nvim.set_var("rplugin_function_async", true) end plug.function(:RPluginFunctionRecursive, sync: true, nargs: 1) do |nvim, n| if n >= 10 n else nvim.evaluate("RPluginFunctionRecursive(#{n + 1})") end end end neovim-0.8.1/spec/acceptance/runtime/rplugin/ruby/commands.rb0000644000004100000410000000434013506362344024346 0ustar www-datawww-dataNeovim.plugin do |plug| plug.command(:RPluginCommandNargs0, sync: true) do |nvim| nvim.set_var("rplugin_command_nargs_0", true) end plug.command(:RPluginCommandNargs1, nargs: 1, sync: true) do |nvim, arg| nvim.set_var("rplugin_command_nargs_1", arg) end plug.command(:RPluginCommandNargsN, nargs: "*", sync: true) do |nvim, *args| nvim.set_var("rplugin_command_nargs_n", args) end plug.command(:RPluginCommandNargsQ, nargs: "?", sync: true) do |nvim, arg| nvim.set_var("rplugin_command_nargs_q", arg) end plug.command(:RPluginCommandNargsP, nargs: "+", sync: true) do |nvim, *args| nvim.set_var("rplugin_command_nargs_p", args) end plug.command(:RPluginCommandRange, range: true, sync: true) do |nvim, *range| nvim.set_var("rplugin_command_range", range) end plug.command(:RPluginCommandRangeP, range: "%", sync: true) do |nvim, *range| nvim.set_var("rplugin_command_range_p", range) end plug.command(:RPluginCommandRangeN, range: 1, sync: true) do |nvim, *range| nvim.set_var("rplugin_command_range_n", range) end plug.command(:RPluginCommandCountN, count: 1, sync: true) do |nvim, *count| nvim.set_var("rplugin_command_count_n", count) end plug.command(:RPluginCommandBang, bang: true, sync: true) do |nvim, bang| nvim.set_var("rplugin_command_bang", bang) end plug.command(:RPluginCommandRegister, register: true, sync: true) do |nvim, reg| nvim.set_var("rplugin_command_register", reg) end plug.command(:RPluginCommandCompletion, complete: "buffer", sync: true) do |nvim| attrs = nvim.command_output("silent command RPluginCommandCompletion") compl = attrs.split($/).last.split[2] nvim.set_var("rplugin_command_completion", compl) end plug.command(:RPluginCommandEval, eval: "g:to_eval", sync: true) do |nvim, to_eval| nvim.set_var("rplugin_command_eval", to_eval.merge(b: 43)) to_eval.merge(b: 43) end plug.command(:RPluginCommandAsync) do |nvim| nvim.set_var("rplugin_command_async", true) end plug.command(:RPluginCommandRecursive, nargs: 1, sync: true) do |nvim, n| if Integer(n) >= 10 nvim.set_var("rplugin_command_recursive", n) else nvim.command("RPluginCommandRecursive #{n.succ}") end end end neovim-0.8.1/spec/acceptance/runtime/init.vim0000644000004100000410000000052013506362344021233 0ustar www-datawww-datalet s:lib_path = getcwd() . "/lib" let s:exe_path = getcwd() . "/exe/neovim-ruby-host" let g:acceptance_rtp = getcwd() . "/spec/acceptance/runtime" let g:ruby_host_prog = printf("ruby -I %s %s", s:lib_path, s:exe_path) ruby require "rspec/expectations" ruby include ::RSpec::Matchers.dup set rtp=./spec/acceptance/runtime,$VIMRUNTIME neovim-0.8.1/spec/acceptance/ruby_spec.vim0000644000004100000410000000404513506362344020606 0ustar www-datawww-datalet s:suite = themis#suite(":ruby") let s:expect = themis#helper("expect") function! s:suite.before_each() abort 1,$delete call append(0, ["one", "two"]) unlet! s:var endfunction function! s:suite.has_nvim() abort call s:expect(has("nvim")).to_equal(1) endfunction function! s:suite.defines_a_ruby_method() abort ruby def foo; Vim.command("let s:var = 1"); end ruby foo call s:expect(s:var).to_equal(1) endfunction function! s:suite.persists_curbuf_state() abort ruby $curbuf.instance_variable_set(:@foo, 123) ruby Vim.command("let s:var = #{$curbuf.instance_variable_get(:@foo)}") call s:expect(s:var).to_equal(123) endfunction function! s:suite.updates_working_directory() abort cd / ruby Vim.command("let s:var = '#{Dir.pwd.sub(/^C:/, "")}'") cd - call s:expect(s:var).to_equal("/") endfunction function! s:suite.updates_working_directory_implicitly() abort split | lcd / ruby Vim.command("let s:var = ['#{Dir.pwd}']") wincmd p ruby Vim.command("call add(s:var, '#{Dir.pwd}')") wincmd p | lcd - call s:expect(len(s:var)).to_equal(2) call s:expect(s:var[0]).not.to_equal(s:var[1]) endfunction function! s:suite.supports_nesting() abort ruby Vim.command("ruby Vim.command('let s:var = 123')") call s:expect(s:var).to_equal(123) endfunction function! s:suite.handles_standard_error() abort try ruby raise "BOOM" throw "Nothing raised" catch /BOOM/ endtry call s:suite.defines_a_ruby_method() endfunction function! s:suite.handles_syntax_error() abort try ruby puts[ throw "Nothing raised" catch /SyntaxError/ endtry call s:suite.defines_a_ruby_method() endfunction function! s:suite.exposes_Vim() abort ruby expect(Vim).to eq(VIM) ruby expect(Vim.strwidth("hi")).to eq(2) endfunction function! s:suite.exposes_Buffer() abort ruby expect($curbuf).to be_a(Neovim::Buffer) ruby expect(Vim::Buffer.current).to eq($curbuf) endfunction function! s:suite.exposes_Window() abort ruby expect($curwin).to be_a(Neovim::Window) ruby expect(Vim::Window.current).to eq($curwin) endfunction neovim-0.8.1/spec/acceptance/rubyfile_spec.vim0000644000004100000410000000413413506362344021445 0ustar www-datawww-datalet s:suite = themis#suite(":rubyfile") let s:expect = themis#helper("expect") function! s:suite.before() abort let s:pwd = getcwd() cd spec/acceptance/rubyfile unlet! s:var endfunction function! s:suite.after() abort execute("cd " . s:pwd) endfunction function! s:suite.before_each() abort 1,$delete call append(0, ["one", "two"]) endfunction function! s:suite.has_nvim() abort call s:expect(has("nvim")).to_equal(1) endfunction function! s:suite.defines_a_ruby_method() abort rubyfile ./define_foo.rb rubyfile ./call_foo.rb call s:expect(s:var).to_equal(1) endfunction function! s:suite.persists_curbuf_state() abort rubyfile ./curbuf_ivar_set.rb rubyfile ./curbuf_ivar_get.rb call s:expect(s:var).to_equal(123) endfunction function! s:suite.updates_working_directory() abort let s:rubyfile = getcwd() . "/set_pwd_before.rb" cd / exec "rubyfile " . s:rubyfile cd - call s:expect(s:var).to_equal(["/"]) endfunction function! s:suite.updates_working_directory_implicitly() abort let s:before_file = getcwd() . "/set_pwd_before.rb" let s:after_file = getcwd() . "/set_pwd_after.rb" split | lcd / exec "rubyfile " . s:before_file wincmd p exec "rubyfile " . s:after_file wincmd p | lcd - call s:expect(len(s:var)).to_equal(2) call s:expect(s:var[0]).not.to_equal(s:var[1]) endfunction function! s:suite.supports_nesting() abort rubyfile ./nested.rb call s:expect(s:var).to_equal(123) endfunction function! s:suite.handles_standard_error() abort try rubyfile ./raise_standard_error.rb throw "Nothing raised" catch /BOOM/ endtry call s:suite.defines_a_ruby_method() endfunction function! s:suite.handles_load_error() abort try rubyfile /foo/bar/baz throw "Nothing raised" catch /LoadError/ endtry call s:suite.defines_a_ruby_method() endfunction function! s:suite.handles_syntax_error() abort try rubyfile ./raise_syntax_error.rb throw "Nothing raised" catch /SyntaxError/ endtry call s:suite.defines_a_ruby_method() endfunction function! s:suite.exposes_constants() abort rubyfile ./ruby_interface.rb endfunction neovim-0.8.1/spec/acceptance/rubydo_spec.vim0000644000004100000410000000326113506362344021130 0ustar www-datawww-datalet s:suite = themis#suite(":rubydo") let s:expect = themis#helper("expect") function! s:suite.before_each() abort 1,$delete call append(0, ["one", "two", "three", "four"]) endfunction function! s:suite.has_nvim() abort call s:expect(has("nvim")).to_equal(1) endfunction function! s:suite.updates_one_line() abort 2rubydo $_.upcase! call s:expect(getline(1, 4)).to_equal(["one", "TWO", "three", "four"]) endfunction function! s:suite.updates_line_range() abort 2,3rubydo $_.upcase! call s:expect(getline(1, 4)).to_equal(["one", "TWO", "THREE", "four"]) endfunction function! s:suite.updates_large_line_range() abort 1,$delete for _ in range(0, 6000) call append(0, "x") endfor %rubydo $_.succ! call s:expect(getline(1)).to_equal("y") call s:expect(getline(6001)).to_equal("y") call s:expect(getline(6002)).to_equal("") endfunction function! s:suite.updates_all_lines() abort %rubydo $_.upcase! call s:expect(getline(1, 4)).to_equal(["ONE", "TWO", "THREE", "FOUR"]) endfunction function! s:suite.ignores_line_deletion() abort " Just ensure `Index out of bounds` exception isn't raised. " " Deleting or adding lines inside `:rubydo` is documented as not supported. " Therefore this will remain inconsistent with Vim, which deletes all but " the first line (?) %rubydo Vim.command("%d") endfunction function! s:suite.handles_standard_error() abort try 1rubydo raise "BOOM" throw "Nothing raised" catch /BOOM/ endtry call s:suite.updates_one_line() endfunction function! s:suite.handles_syntax_error() abort try 1rubydo puts[ throw "Nothing raised" catch /SyntaxError/ endtry call s:suite.updates_one_line() endfunction neovim-0.8.1/spec/acceptance/rubyfile/0000755000004100000410000000000013506362344017714 5ustar www-datawww-dataneovim-0.8.1/spec/acceptance/rubyfile/nested.rb0000644000004100000410000000005213506362344021520 0ustar www-datawww-dataVim.command("rubyfile ./nested_inner.rb") neovim-0.8.1/spec/acceptance/rubyfile/nested_inner.rb0000644000004100000410000000003713506362344022716 0ustar www-datawww-dataVim.command("let s:var = 123") neovim-0.8.1/spec/acceptance/rubyfile/raise_syntax_error.rb0000644000004100000410000000000713506362344024160 0ustar www-datawww-dataputs [ neovim-0.8.1/spec/acceptance/rubyfile/define_foo.rb0000644000004100000410000000005313506362344022334 0ustar www-datawww-datadef foo Vim.command("let s:var = 1") end neovim-0.8.1/spec/acceptance/rubyfile/set_pwd_after.rb0000644000004100000410000000005513506362344023067 0ustar www-datawww-dataVim.command("call add(s:var, '#{Dir.pwd}')") neovim-0.8.1/spec/acceptance/rubyfile/ruby_interface.rb0000644000004100000410000000034313506362344023242 0ustar www-datawww-dataexpect(Vim).to eq(VIM) expect(Vim.strwidth("hi")).to eq(2) expect($curbuf).to be_a(Neovim::Buffer) expect(Vim::Buffer.current).to eq($curbuf) expect($curwin).to be_a(Neovim::Window) expect(Vim::Window.current).to eq($curwin) neovim-0.8.1/spec/acceptance/rubyfile/set_pwd_before.rb0000644000004100000410000000007113506362344023226 0ustar www-datawww-dataVim.command("let s:var = ['#{Dir.pwd.sub(/^C:/, '')}']") neovim-0.8.1/spec/acceptance/rubyfile/curbuf_ivar_set.rb0000644000004100000410000000005213506362344023420 0ustar www-datawww-data$curbuf.instance_variable_set(:@var, 123) neovim-0.8.1/spec/acceptance/rubyfile/curbuf_ivar_get.rb0000644000004100000410000000010313506362344023401 0ustar www-datawww-dataVim.command("let s:var = #{$curbuf.instance_variable_get(:@var)}") neovim-0.8.1/spec/acceptance/rubyfile/call_foo.rb0000644000004100000410000000000413506362344022011 0ustar www-datawww-datafoo neovim-0.8.1/spec/acceptance/rubyfile/raise_standard_error.rb0000644000004100000410000000001513506362344024431 0ustar www-datawww-dataraise "BOOM" neovim-0.8.1/spec/acceptance/rplugin_command_spec.vim0000644000004100000410000000535213506362344023005 0ustar www-datawww-datalet s:suite = themis#suite("Remote plugin command") let s:expect = themis#helper("expect") function! s:suite.before_each() abort 1,$delete call append(0, ["one", "two", "three"]) normal gg endfunction function! s:suite.has_nvim() abort call s:expect(has("nvim")).to_equal(1) endfunction function! s:suite.supports_arguments() abort RPluginCommandNargs0 RPluginCommandNargs1 1 RPluginCommandNargsN RPluginCommandNargsN 1 RPluginCommandNargsN 1 2 RPluginCommandNargsQ RPluginCommandNargsQ 1 RPluginCommandNargsP 1 RPluginCommandNargsP 1 2 call s:expect(g:rplugin_command_nargs_0).to_equal(v:true) call s:expect(g:rplugin_command_nargs_1).to_equal("1") call s:expect(g:rplugin_command_nargs_n).to_equal(["1", "2"]) call s:expect(g:rplugin_command_nargs_q).to_equal("1") call s:expect(g:rplugin_command_nargs_p).to_equal(["1", "2"]) endfunction function! s:suite.supports_line_range() abort RPluginCommandRange call s:expect(g:rplugin_command_range).to_equal([1, 1]) 1,2RPluginCommandRange call s:expect(g:rplugin_command_range).to_equal([1, 2]) %RPluginCommandRange call s:expect(g:rplugin_command_range).to_equal([1, 4]) RPluginCommandRangeP call s:expect(g:rplugin_command_range_p).to_equal([1, 4]) 1,2RPluginCommandRangeP call s:expect(g:rplugin_command_range_p).to_equal([1, 2]) %RPluginCommandRangeP call s:expect(g:rplugin_command_range_p).to_equal([1, 4]) RPluginCommandRangeN call s:expect(g:rplugin_command_range_n).to_equal([1]) 2RPluginCommandRangeN call s:expect(g:rplugin_command_range_n).to_equal([2]) endfunction function! s:suite.supports_count() abort RPluginCommandCountN call s:expect(g:rplugin_command_count_n).to_equal([1]) 2RPluginCommandCountN call s:expect(g:rplugin_command_count_n).to_equal([2]) endfunction function! s:suite.supports_bang() abort RPluginCommandBang call s:expect(g:rplugin_command_bang).to_equal(0) RPluginCommandBang! call s:expect(g:rplugin_command_bang).to_equal(1) endfunction function! s:suite.supports_register() abort RPluginCommandRegister a call s:expect(g:rplugin_command_register).to_equal("a") endfunction function! s:suite.supports_completion() abort RPluginCommandCompletion call s:expect(g:rplugin_command_completion).to_equal("buffer") endfunction function! s:suite.supports_eval() abort let g:to_eval = {'a': 42} RPluginCommandEval call s:expect(g:rplugin_command_eval).to_equal({'a': 42, 'b': 43}) endfunction function! s:suite.supports_asynchronous_commands() abort RPluginCommandAsync sleep 50m call s:expect(g:rplugin_command_async).to_equal(v:true) endfunction function! s:suite.supports_recursion() abort RPluginCommandRecursive 0 call s:expect(g:rplugin_command_recursive).to_equal("10") endfunction neovim-0.8.1/spec/support.rb0000644000004100000410000000164613506362344016055 0ustar www-datawww-datamodule Support class << self attr_accessor :nvim_version end def self.workspace File.expand_path("workspace", __dir__) end def self.socket_path file_path("nvim.sock") end def self.tcp_port server = TCPServer.new("127.0.0.1", 0) begin server.addr[1] ensure server.close end end def self.file_path(name) File.join(workspace, name) end def self.setup_workspace FileUtils.mkdir_p(workspace) end def self.teardown_workspace FileUtils.rm_rf(workspace) end def self.child_argv [Neovim.executable.path, "--headless", "-i", "NONE", "-u", "NONE", "-n"] end def self.windows? Gem.win_platform? end def self.kill(pid) windows? ? Process.kill(:KILL, pid) : Process.kill(:TERM, pid) Process.waitpid(pid) end begin self.nvim_version = Neovim.executable.version rescue => e abort("Failed to load nvim: #{e}") end end neovim-0.8.1/spec/neovim_spec.rb0000644000004100000410000000440213506362344016641 0ustar www-datawww-datarequire "helper" RSpec.describe Neovim do shared_context "attached client" do it "establishes an RPC connection" do expect(client.strwidth("hi")).to eq(2) end it "sets appropriate client info" do chan_info = client.evaluate("nvim_get_chan_info(#{client.channel_id})") expect(chan_info).to match( "client" => { "name" => "ruby-client", "version" => { "major" => duck_type(:to_int), "minor" => duck_type(:to_int), "patch" => duck_type(:to_int) }, "type" => "remote", "methods" => {}, "attributes" => duck_type(:to_hash) }, "id" => duck_type(:to_int), "mode" => "rpc", "stream" => stream ) end end describe ".attach_tcp" do include_context "attached client" do let(:port) { Support.tcp_port } let(:stream) { "socket" } let!(:nvim_pid) do env = {"NVIM_LISTEN_ADDRESS" => "127.0.0.1:#{port}"} Process.spawn(env, *Support.child_argv, [:out, :err] => File::NULL) end let(:client) do begin Neovim.attach_tcp("127.0.0.1", port) rescue Errno::ECONNREFUSED retry end end after { Support.kill(nvim_pid) } end end describe ".attach_unix" do before do skip("Not supported on this platform") if Support.windows? end include_context "attached client" do let(:socket_path) { Support.socket_path } let(:stream) { "socket" } let!(:nvim_pid) do env = {"NVIM_LISTEN_ADDRESS" => socket_path} Process.spawn(env, *Support.child_argv, [:out, :err] => File::NULL) end let(:client) do begin Neovim.attach_unix(socket_path) rescue Errno::ENOENT, Errno::ECONNREFUSED retry end end after { Support.kill(nvim_pid) } end end describe ".attach_child" do include_context "attached client" do let(:stream) { "stdio" } let(:client) do Neovim.attach_child(Support.child_argv) end end after { client.shutdown } end describe ".executable" do it "returns the current executable" do expect(Neovim.executable).to be_a(Neovim::Executable) end end end neovim-0.8.1/CHANGELOG.md0000644000004100000410000001130213506362344014661 0ustar www-datawww-data# 0.8.1 - Set client info on host and client startup - Add `Client#channel_id` # 0.8.0 - Allow `Buffer#append` to take a string with newlines - Use non-strict line indexing in `:rubydo` to prevent line deletions from throwing exceptions - Performance optimizations: - Cache RPC method lookups and store them in a set - Only flush writes before reading in the event loop - Delete request handlers after invoking them - Refresh provider globals in a single RPC request # 0.7.1 - Fix `uninitialized constant Neovim::RubyProvider::StringIO` - Various backwards-compatible style changes to satisfy Rubocop rules # 0.7.0 - Drop support for Ruby < 2.2.0, update syntax accordingly - Use msgpack gem for all reading/writing - Make provider std stream capturing more robust - Lazily instantiate Host client on 'poll' request - Fix windows by setting all IOs to binmode - Refactor/simplify session and event loop logic # 0.6.2 - Put IOs into binary mode (fixes windows bugs) - Various build fixes for appveyor - Update generated docs to v0.2.2 # 0.6.1 - Add `multi_json` dependency to fix load error in certain envs # 0.6.0 - Refactor: consolidate "run" logic into EventLoop class to simplify middleware layers - Add JSON structured logging - Regenerated docs for nvim 0.2.1 # 0.5.1 - Convert vader.vim from submodule to subtree so it is included in gem installations # 0.5.0 - Breaking API changes: - Update generated methods to map to `nvim_` RPC functions, rather than the deprecated `vim_` ones - Remove `Current#range` API, simplifying `LineRange` interface - Regenerate docs to reflect nvim 0.2.0 - Fix support for `:bang` and `:register` plugin DSL options # 0.4.0 - Add `Neovim.executable` for accessing `nvim` info - Fix bug where `$curwin` and `$curbuf` got out of sync after `Vim.command` invocations - Use vader.vim for running vimscript acceptance tests # 0.3.3 - Hotfix older nvim clients' inability to hook into DirChanged # 0.3.2 - Fix directory tracking in Ruby provider # 0.3.1 - Remove window caching to fix incompatibilities with command-t - Add `Vim` module alias - Fix `Window.count` and `Window.[]` to work with tabpages - Fix `EventLoop.child` bug with repeated arguments - Fix `Window#cursor=` incompatibilities - Make `Neovim.attach_child` have default argv of `["nvim"]` # 0.3.0 - Mark `Plugin::DSL#rpc` private - Rename Session constants: - `Neovim::EventLoop` -> `Neovim::Session::EventLoop` - `Neovim::MsgpackStream` -> `Neovim::Session::Serializer` - `Neovim::AsyncSession` -> `Neovim::Session::RPC` - `Neovim::API` -> `Neovim::Session::API` - `Neovim::Request` -> `Neovim::Session::Request` - `Neovim::Notification` -> `Neovim::Session::Notification` # 0.2.5 - Optimize remote function lookup - Fix bug where $curbuf and $curwin weren't persisting instance state between requests # 0.2.4 - Maintain cursor position on Buffer#append for compatibility with vim - Fix excessive fetching of API metadata # 0.2.3 - Detach child processes in `Neovim::EventLoop.child` - Improve performance/compatibility of `Buffer#append` - Various improvements around `Host` loading # 0.2.2 - Make `VIM` constant a module instead of a class - Make `Client#set_option` accept a single string argument # 0.2.1 - Fix race condition in Fiber handling - General improvements to ruby\_provider.rb # 0.2.0 - Backwards incompatible, but we're pre-1.0.0 so going with minor bump instead - Make vim ruby functions 1-indexed - Add Client#evaluate and Client#message - Make ruby functions affect global scope - Add VIM::{Buffer,Window}.{count,index} - Add minor debug logging to Session and AsyncSession - Remove race condition in Session fiber handling # 0.1.0 - Add --version, -V to neovim-ruby-host executable - Update object interfaces to be compatible with Vim :ruby API - `NVIM_RUBY_LOG_LEVEL` now takes strings, e.g. `DEBUG` - Add `rpc` plugin DSL method for exposing top-level functions - Add `ruby_provider.rb` for Vim :ruby API compatibility - Remove Cursor class - Remove vendored `neovim` # 0.0.6 - Update Session with improved Fiber coordination - Documentation - Rename APIInfo -> API - Rename Object -> RemoteObject # 0.0.5 - Various fixes for Ruby remote plugins - Move Current#range and #range= methods to Buffer - Add better logging # 0.0.4 - Add support for loading Ruby remote plugins from nvim - Add Current#range to return a LineRange enumerable object - Support sending large messages - Remove unnecessary #stop methods - Add setup callback support to event loop # 0.0.3 - Add Buffer#lines enumerable interface - Add Window#cursor interface - Fix race condition when loading plugins from host # 0.0.2 - Add Neovim.plugin DSL for defining plugins - Add neovim-ruby-host executable for spawning plugins neovim-0.8.1/.rubocop.yml0000644000004100000410000000434613506362344015334 0ustar www-datawww-dataAllCops: Exclude: - _neovim/**/* - spec/acceptance/**/* - spec/workspace/**/* - vendor/**/* - script/**/* - bin/**/* Layout/EndOfLine: EnforcedStyle: lf Layout/MultilineMethodCallIndentation: EnforcedStyle: indented Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space Layout/SpaceInsideHashLiteralBraces: EnforcedStyle: no_space Lint/AmbiguousBlockAssociation: Exclude: - spec/**/* Lint/HandleExceptions: Exclude: - lib/neovim/ruby_provider.rb - lib/neovim/logging.rb - lib/neovim/connection.rb - lib/neovim/line_range.rb Lint/RescueException: Exclude: - lib/neovim/host.rb Lint/UnderscorePrefixedVariableName: Exclude: - lib/neovim/ruby_provider.rb Lint/UselessAccessModifier: Exclude: - lib/neovim/buffer.rb - lib/neovim/client.rb Lint/Void: Exclude: - lib/neovim/window.rb Metrics/AbcSize: Max: 20 Metrics/BlockLength: Exclude: - spec/**/* Metrics/ClassLength: Max: 500 Metrics/LineLength: Max: 125 Metrics/MethodLength: Max: 50 Metrics/ModuleLength: Exclude: - spec/**/* Metrics/ParameterLists: Max: 6 Naming/AccessorMethodName: Exclude: - lib/neovim/client.rb Naming/UncommunicativeMethodParamName: Enabled: false Security/Eval: Exclude: - lib/neovim/ruby_provider.rb Style/BlockComments: Exclude: - lib/neovim/buffer.rb - lib/neovim/client.rb - lib/neovim/tabpage.rb - lib/neovim/window.rb Style/ConditionalAssignment: EnforcedStyle: assign_inside_condition IncludeTernaryExpressions: false Style/DoubleNegation: Enabled: false Style/FormatStringToken: EnforcedStyle: unannotated Style/GlobalVars: Exclude: - lib/neovim/ruby_provider/vim.rb - spec/neovim/ruby_provider/vim_spec.rb Style/MultilineTernaryOperator: Enabled: false Style/ParallelAssignment: Enabled: false Style/RescueStandardError: EnforcedStyle: implicit Style/SpecialGlobalVars: EnforcedStyle: use_perl_names Style/StringLiterals: EnforcedStyle: double_quotes Style/StringLiteralsInInterpolation: EnforcedStyle: double_quotes Style/SymbolArray: EnforcedStyle: brackets Style/WordArray: EnforcedStyle: brackets Style/ZeroLengthPredicate: Exclude: - lib/neovim/ruby_provider.rb neovim-0.8.1/VimFlavor0000644000004100000410000000005513506362344014703 0ustar www-datawww-dataflavor "thinca/vim-themis", branch: "master" neovim-0.8.1/.gitignore0000644000004100000410000000045013506362344015042 0ustar www-datawww-data*.gem *.rbc .swp .bundle .config .yardoc Gemfile.lock VimFlavor.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp spec/workspace spec/acceptance/runtime/flavors spec/acceptance/runtime/rplugin_manifest.vim vendor/bundle bin .vim-flavor neovim-0.8.1/CODE_OF_CONDUCT.md0000644000004100000410000000622013506362344015652 0ustar www-datawww-data# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at alexgenco@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ neovim-0.8.1/script/0000755000004100000410000000000013506362344014357 5ustar www-datawww-dataneovim-0.8.1/script/mp2j.rb0000755000004100000410000000026413506362344015561 0ustar www-datawww-data#!/usr/bin/env ruby # # Convert STDIN from MessagePack to JSON require "json" require "msgpack" ARGF.binmode MessagePack::Unpacker.new(ARGF).each { |data| print JSON.dump(data) } neovim-0.8.1/script/validate_docs.rb0000755000004100000410000000125313506362344017511 0ustar www-datawww-data#!/usr/bin/env ruby require "json" require "open-uri" url = "https://api.github.com/repos/neovim/neovim/releases/latest" begin response = open(url) { |res| JSON.parse(res.read) } rescue SocketError, OpenURI::HTTPError => e puts "Request failed: #{e}\n" exit end release_version = response["name"][/NVIM v?(.+)$/, 1] client_file = File.read( File.expand_path("../lib/neovim/client.rb", __dir__) ) docs_version = client_file[ /The methods documented here were generated using NVIM v?(.+)$/, 1 ] if docs_version == release_version puts "Documentation is up-to-date." else abort "Documentation is out of date: expected #{release_version}, got #{docs_version}." end neovim-0.8.1/script/generate_docs.rb0000755000004100000410000000505013506362344017511 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.expand_path("../lib", __dir__) require "neovim" require "pathname" nvim_docs = [] buffer_docs = [] window_docs = [] tabpage_docs = [] nvim_exe = ENV.fetch("NVIM_EXECUTABLE", "nvim") nvim_vrs = %x(#{nvim_exe} --version).split("\n").first event_loop = Neovim::EventLoop.child([nvim_exe, "-u", "NONE", "-n"]) session = Neovim::Session.new(event_loop) nvim_defs = Neovim::Client.instance_methods(false) buffer_defs = Neovim::Buffer.instance_methods(false) tabpage_defs = Neovim::Tabpage.instance_methods(false) window_defs = Neovim::Window.instance_methods(false) session.request(:nvim_get_api_info)[1]["functions"].each do |func| func_name = func["name"] params = func["parameters"] case func_name when /^nvim_buf_(.+)/ method_name = $1 params.shift next if buffer_defs.include?(method_name.to_sym) when /^nvim_win_(.+)/ method_name = $1 params.shift next if window_defs.include?(method_name.to_sym) when /^nvim_tabpage_(.+)/ method_name = $1 params.shift next if tabpage_defs.include?(method_name.to_sym) when /^nvim_(.+)/ method_name = $1 next if nvim_defs.include?(method_name.to_sym) else next end return_type = func["return_type"] param_names = params.map(&:last) param_str = params.empty? ? "" : "(#{param_names.join(", ")})" method_decl = "@method #{method_name}#{param_str}" method_desc = " See +:h #{func_name}()+" param_docs = params.map do |type, name| " @param [#{type}] #{name}" end return_doc = " @return [#{return_type}]\n" method_doc = [method_decl, method_desc, *param_docs, return_doc].join("\n") method_doc.gsub!(/ArrayOf\((\w+)[^)]*\)/, 'Array<\1>') method_doc.gsub!(/Dictionary/, "Hash") case func_name when /nvim_buf_(.+)/ buffer_docs << method_doc when /nvim_win_(.+)/ window_docs << method_doc when /nvim_tabpage_(.+)/ tabpage_docs << method_doc when /nvim_(.+)/ nvim_docs << method_doc else raise "Unexpected function #{func_name.inspect}" end end lib_dir = Pathname.new(File.expand_path("../lib/neovim", __dir__)) { "client.rb" => nvim_docs, "buffer.rb" => buffer_docs, "tabpage.rb" => tabpage_docs, "window.rb" => window_docs, }.each do |filename, docs| path = lib_dir.join(filename) contents = File.read(path) doc_str = ["=begin", *docs, "=end"].join("\n") contents.sub!(/=begin.+=end/m, doc_str) contents.sub!( /# The methods documented here were generated using .+$/, "# The methods documented here were generated using #{nvim_vrs}" ) File.write(path, contents) end neovim-0.8.1/script/run_acceptance.rb0000755000004100000410000000144313506362344017663 0ustar www-datawww-data#!/usr/bin/env ruby require "fileutils" ENV.delete("VIM") ENV.delete("VIMRUNTIME") acceptance_root = File.expand_path("../spec/acceptance", __dir__) themis_rtp = File.join(acceptance_root, "runtime") themis_home = File.join(themis_rtp, "flavors/thinca_vim-themis") manifest = File.join(themis_rtp, "rplugin_manifest.vim") vimrc = File.join(themis_rtp, "init.vim") themis_exe = Gem.win_platform? ? File.join(themis_home, "bin/themis.bat") : File.join(themis_home, "bin/themis") env = { "NVIM_RPLUGIN_MANIFEST" => manifest, "THEMIS_VIM" => "nvim", "THEMIS_HOME" => themis_home, "THEMIS_ARGS" => "-e -s --headless -u #{vimrc}" } FileUtils.rm_f(manifest) system( env, "nvim", "-e", "-s", "--headless", "-u", vimrc, "+UpdateRemotePlugins", "+qa!" ) exec(env, themis_exe, *ARGV) neovim-0.8.1/script/dump_api.rb0000755000004100000410000000052113506362344016503 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.expand_path("../lib", __dir__) require "neovim" require "json" require "pp" nvim_exe = ENV.fetch("NVIM_EXECUTABLE", "nvim") event_loop = Neovim::EventLoop.child([nvim_exe, "-u", "NONE", "-n"]) session = Neovim::Session.new(event_loop) puts JSON.pretty_generate(session.request(:nvim_get_api_info)) neovim-0.8.1/script/j2mp.rb0000755000004100000410000000024313506362344015556 0ustar www-datawww-data#!/usr/bin/env ruby # # Convert STDIN from JSON to MessagePack. require "json" require "msgpack" ARGF.binmode print MessagePack.pack(JSON.parse(ARGF.read.strip)) neovim-0.8.1/Rakefile0000644000004100000410000000226013506362344014520 0ustar www-datawww-datarequire "bundler/gem_tasks" require "rspec/core/rake_task" require "rubocop/rake_task" RuboCop::RakeTask.new(:style) namespace :spec do desc "Run functional specs" RSpec::Core::RakeTask.new(:functional) desc "Run acceptance specs" task acceptance: "acceptance:deps" do run_script(:run_acceptance, "--reporter", "dot", "spec/acceptance") end namespace :acceptance do desc "Install acceptance spec dependencies" task :deps do Bundler.with_clean_env do sh "bundle exec vim-flavor update --vimfiles-path=spec/acceptance/runtime" end end end end namespace :docs do desc "Generate Neovim remote API docs" task :generate do run_script(:generate_docs) end desc "Validate generated documentation is up-to-date" task :validate do run_script(:validate_docs) end end namespace :scripts do desc "Validate script syntax" task :validate do sh "ruby -c #{File.expand_path("script/*.rb", __dir__)}" end end task default: [ :style, :"docs:validate", :"scripts:validate", :"spec:functional", :"spec:acceptance" ] def run_script(script_name, *args) ruby File.expand_path("script/#{script_name}.rb", __dir__), *args end neovim-0.8.1/lib/0000755000004100000410000000000013506362344013621 5ustar www-datawww-dataneovim-0.8.1/lib/neovim/0000755000004100000410000000000013506362344015116 5ustar www-datawww-dataneovim-0.8.1/lib/neovim/session.rb0000644000004100000410000000573113506362344017134 0ustar www-datawww-datarequire "neovim/logging" require "fiber" require "thread" module Neovim # Wraps an event loop in a synchronous API using +Fiber+s. # # @api private class Session include Logging attr_writer :request_id # @api private class Exited < RuntimeError def initialize super("nvim process exited") end end def initialize(event_loop) @event_loop = event_loop @main_thread = Thread.current @main_fiber = Fiber.current @response_handlers = Hash.new(-> {}) @pending_messages = [] @request_id = 0 end def run(&block) @running = true while (pending = @pending_messages.shift) Fiber.new { pending.received(@response_handlers, &block) }.resume end return unless @running @event_loop.run do |message| Fiber.new { message.received(@response_handlers, &block) }.resume end end # Make an RPC request and return its response. # # If this method is called inside a callback, we are already inside a # +Fiber+ handler. In that case, we write to the stream and yield the # +Fiber+. Once the response is received, resume the +Fiber+ and # return the result. # # If this method is called outside a callback, write to the stream and # run the event loop until a response is received. Messages received # in the meantime are enqueued to be handled later. def request(method, *args) main_thread_only do @request_id += 1 blocking = Fiber.current == @main_fiber log(:debug) do { method_name: method, request_id: @request_id, blocking: blocking, arguments: args } end @event_loop.request(@request_id, method, *args) response = blocking ? blocking_response : yielding_response raise(Exited) if response.nil? raise(response.error) if response.error response.value end end def respond(request_id, value, error=nil) @event_loop.respond(request_id, value, error) end def notify(method, *args) @event_loop.notify(method, *args) end def shutdown @running = false @event_loop.shutdown end def stop @running = false @event_loop.stop end private def blocking_response response = nil @response_handlers[@request_id] = lambda do |res| response = res stop end run { |message| @pending_messages << message } response end def yielding_response fiber = Fiber.current @response_handlers[@request_id] = ->(response) { fiber.resume(response) } Fiber.yield end def main_thread_only if Thread.current == @main_thread yield if block_given? else raise( "A Ruby plugin attempted to call neovim outside of the main thread, " \ "which is not yet supported by the neovim gem." ) end end end end neovim-0.8.1/lib/neovim/current.rb0000644000004100000410000000315513506362344017131 0ustar www-datawww-datarequire "neovim/buffer" require "neovim/tabpage" require "neovim/window" module Neovim # Support for +Client#current+ chaining. # # @see Client#current class Current def initialize(session) @session = session end # Get the line under the cursor. # # @return [String] def line @session.request(:nvim_get_current_line) end # Set the line under the cursor. # # @param line [String] The target line contents. # @return [String] def line=(line) @session.request(:nvim_set_current_line, line) end # Get the active buffer. # # @return [Buffer] def buffer @session.request(:nvim_get_current_buf) end # Set the active buffer. # # @param buffer [Buffer, Integer] The target buffer or index. # @return [Buffer, Integer] def buffer=(buffer) @session.request(:nvim_set_current_buf, buffer) end # Get the active window. # # @return [Window] def window @session.request(:nvim_get_current_win) end # Set the active window. # # @param window [Window, Integer] The target window or index. # @return [Window, Integer] def window=(window) @session.request(:nvim_set_current_win, window) end # Get the active tabpage. # # @return [Tabpage] def tabpage @session.request(:nvim_get_current_tabpage) end # Set the active tabpage. # # @param tabpage [Tabpage, Integer] The target tabpage or index. # @return [Tabpage, Integer] def tabpage=(tabpage) @session.request(:nvim_set_current_tabpage, tabpage) end end end neovim-0.8.1/lib/neovim/buffer.rb0000644000004100000410000001333613506362344016722 0ustar www-datawww-datarequire "neovim/remote_object" require "neovim/line_range" module Neovim # Class representing an +nvim+ buffer. # # The methods documented here were generated using NVIM v0.3.7 class Buffer < RemoteObject attr_reader :lines def initialize(*args) super @lines = LineRange.new(self) end # Replace all the lines of the buffer. # # @param strs [Array] The replacement lines # @return [Array] def lines=(strs) @lines[0..-1] = strs end # Get the buffer name. # # @return [String] def name get_name end # Get the buffer index. # # @return [Integer] def number get_number end # Get the number of lines. # # @return [Integer] def count line_count end # Get the number of lines. # # @return [Integer] def length count end # Get the given line (1-indexed). # # @param index [Integer] # @return [String] def [](index) check_index(index) @lines[index - 1] end # Set the given line (1-indexed). # # @param index [Integer] # @param str [String] # @return [String] def []=(index, str) check_index(index) @lines[index - 1] = str end # Delete the given line (1-indexed). # # @param index [Integer] # @return [void] def delete(index) check_index(index) @lines.delete(index - 1) nil end # Append a line after the given line (1-indexed). # # To maintain backwards compatibility with +vim+, the cursor is forced back # to its previous position after inserting the line. # # @param index [Integer] # @param str [String] # @return [String] def append(index, str) check_index(index) window = @session.request(:nvim_get_current_win) cursor = window.cursor set_lines(index, index, true, [*str.split($/)]) window.set_cursor(cursor) str end # Get the current line of an active buffer. # # @return [String, nil] def line @session.request(:nvim_get_current_line) if active? end # Set the current line of an active buffer. # # @param str [String] # @return [String, nil] def line=(str) @session.request(:nvim_set_current_line, str) if active? end # Get the current line number of an active buffer. # # @return [Integer, nil] def line_number @session.request(:nvim_get_current_win).get_cursor[0] if active? end # Determine if the buffer is active. # # @return [Boolean] def active? @session.request(:nvim_get_current_buf) == self end private def check_index(index) raise ArgumentError, "Index out of bounds" if index < 0 end public # The following methods are dynamically generated. =begin @method line_count See +:h nvim_buf_line_count()+ @return [Integer] @method attach(send_buffer, opts) See +:h nvim_buf_attach()+ @param [Boolean] send_buffer @param [Hash] opts @return [Boolean] @method detach See +:h nvim_buf_detach()+ @return [Boolean] @method get_lines(start, end, strict_indexing) See +:h nvim_buf_get_lines()+ @param [Integer] start @param [Integer] end @param [Boolean] strict_indexing @return [Array] @method set_lines(start, end, strict_indexing, replacement) See +:h nvim_buf_set_lines()+ @param [Integer] start @param [Integer] end @param [Boolean] strict_indexing @param [Array] replacement @return [void] @method get_offset(index) See +:h nvim_buf_get_offset()+ @param [Integer] index @return [Integer] @method get_var(name) See +:h nvim_buf_get_var()+ @param [String] name @return [Object] @method get_changedtick See +:h nvim_buf_get_changedtick()+ @return [Integer] @method get_keymap(mode) See +:h nvim_buf_get_keymap()+ @param [String] mode @return [Array] @method get_commands(opts) See +:h nvim_buf_get_commands()+ @param [Hash] opts @return [Hash] @method set_var(name, value) See +:h nvim_buf_set_var()+ @param [String] name @param [Object] value @return [void] @method del_var(name) See +:h nvim_buf_del_var()+ @param [String] name @return [void] @method get_option(name) See +:h nvim_buf_get_option()+ @param [String] name @return [Object] @method set_option(name, value) See +:h nvim_buf_set_option()+ @param [String] name @param [Object] value @return [void] @method get_number See +:h nvim_buf_get_number()+ @return [Integer] @method get_name See +:h nvim_buf_get_name()+ @return [String] @method set_name(name) See +:h nvim_buf_set_name()+ @param [String] name @return [void] @method is_loaded See +:h nvim_buf_is_loaded()+ @return [Boolean] @method is_valid See +:h nvim_buf_is_valid()+ @return [Boolean] @method get_mark(name) See +:h nvim_buf_get_mark()+ @param [String] name @return [Array] @method add_highlight(ns_id, hl_group, line, col_start, col_end) See +:h nvim_buf_add_highlight()+ @param [Integer] ns_id @param [String] hl_group @param [Integer] line @param [Integer] col_start @param [Integer] col_end @return [Integer] @method clear_namespace(ns_id, line_start, line_end) See +:h nvim_buf_clear_namespace()+ @param [Integer] ns_id @param [Integer] line_start @param [Integer] line_end @return [void] @method clear_highlight(ns_id, line_start, line_end) See +:h nvim_buf_clear_highlight()+ @param [Integer] ns_id @param [Integer] line_start @param [Integer] line_end @return [void] @method set_virtual_text(ns_id, line, chunks, opts) See +:h nvim_buf_set_virtual_text()+ @param [Integer] ns_id @param [Integer] line @param [Array] chunks @param [Hash] opts @return [Integer] =end end end neovim-0.8.1/lib/neovim/version.rb0000644000004100000410000000007013506362344017125 0ustar www-datawww-datamodule Neovim VERSION = Gem::Version.new("0.8.1") end neovim-0.8.1/lib/neovim/line_range.rb0000644000004100000410000000653213506362344017554 0ustar www-datawww-datamodule Neovim # Provide an enumerable interface for dealing with ranges of lines. class LineRange include Enumerable def initialize(buffer) @buffer = buffer end # Satisfy the +Enumerable+ interface by yielding each line. # # @yieldparam line [String] def each(&block) (0...@buffer.count).each_slice(5000) do |linenos| start, stop = linenos[0], linenos[-1] + 1 @buffer.get_lines(start, stop, true).each(&block) end end # Resolve to an array of lines as strings. # # @return [Array] def to_a map { |line| line } end # Override +#==+ to compare contents of lines. # # @return Boolean def ==(other) to_a == other.to_a end # Access a line or line range. # # @overload [](index) # @param index [Integer] # # @overload [](range) # @param range [Range] # # @overload [](index, length) # @param index [Integer] # @param length [Integer] # # @example Get the first line using an index # line_range[0] # => "first" # @example Get the first two lines using a +Range+ # line_range[0..1] # => ["first", "second"] # @example Get the first two lines using an index and length # line_range[0, 2] # => ["first", "second"] def [](pos, len=nil) if pos.is_a?(Range) @buffer.get_lines(*range_indices(pos), true) else start, stop = length_indices(pos, len || 1) lines = @buffer.get_lines(start, stop, true) len ? lines : lines.first end end alias slice [] # Set a line or line range. # # @overload []=(index, string) # @param index [Integer] # @param string [String] # # @overload []=(index, length, strings) # @param index [Integer] # @param length [Integer] # @param strings [Array] # # @overload []=(range, strings) # @param range [Range] # @param strings [Array] # # @example Replace the first line using an index # line_range[0] = "first" # @example Replace the first two lines using a +Range+ # line_range[0..1] = ["first", "second"] # @example Replace the first two lines using an index and length # line_range[0, 2] = ["first", "second"] def []=(*args) *target, val = args pos, len = target if pos.is_a?(Range) @buffer.set_lines(*range_indices(pos), true, Array(val)) else start, stop = length_indices(pos, len || 1) @buffer.set_lines(start, stop, true, Array(val)) end end # Replace the range of lines. # # @param other [Array] The replacement lines def replace(other) self[0..-1] = other.to_ary self end # Delete the line at the given index within the range. # # @param index [Integer] def delete(index) i = Integer(index) self[i].tap { self[i, 1] = [] } rescue TypeError end private def range_indices(range) start = adjust_index(range.begin) stop = adjust_index(range.end) stop += 1 unless range.exclude_end? [start, stop] end def length_indices(index, len) start = adjust_index(index) stop = start < 0 ? [start + len, -1].min : start + len [start, stop] end def adjust_index(i) i < 0 ? i - 1 : i end end end neovim-0.8.1/lib/neovim/connection.rb0000644000004100000410000000250113506362344017600 0ustar www-datawww-datarequire "neovim/logging" require "socket" require "msgpack" module Neovim # @api private class Connection include Logging def self.tcp(host, port) socket = Socket.tcp(host, port) new(socket, socket) end def self.unix(path) socket = Socket.unix(path) new(socket, socket) end def self.child(argv) argv = argv.include?("--embed") ? argv : argv + ["--embed"] io = ::IO.popen(argv, "rb+") Process.detach(io.pid) new(io, io) end def self.stdio new(STDIN, STDOUT) end def initialize(rd, wr) @rd, @wr = [rd, wr].each { |io| io.binmode.sync = true } @unpacker = MessagePack::Unpacker.new(@rd) @packer = MessagePack::Packer.new(@wr) @running = false end def write(object) log(:debug) { {object: object} } @packer.write(object) self end def read @unpacker.read.tap do |object| log(:debug) { {object: object} } end end def flush @packer.flush self end def register_type(id) @unpacker.register_type(id) do |data| index = MessagePack.unpack(data) yield index end end def close [@rd, @wr].each do |io| begin io.close rescue ::IOError end end end end end neovim-0.8.1/lib/neovim/remote_object.rb0000644000004100000410000000241513506362344020266 0ustar www-datawww-datarequire "set" module Neovim # @abstract Superclass for all +nvim+ remote objects. # # @see Buffer # @see Window # @see Tabpage class RemoteObject attr_reader :index def initialize(index, session, api) @index = index @session = session @api = api end # Serialize object to MessagePack. # # @param packer [MessagePack::Packer] # @return [String] def to_msgpack(packer) packer.pack(@index) end # Intercept method calls and delegate to appropriate RPC methods. def method_missing(method_name, *args) if (func = @api.function_for_object_method(self, method_name)) func.call(@session, @index, *args) else super end end # Extend +respond_to_missing?+ to support RPC methods. def respond_to_missing?(method_name, *) super || rpc_methods.include?(method_name.to_sym) end # Extend +methods+ to include RPC methods def methods(*args) super | rpc_methods.to_a end # Extend +==+ to only look at class and index. def ==(other) other.class.equal?(self.class) && @index == other.index end private def rpc_methods @rpc_methods ||= @api.functions_for_object(self).map(&:method_name).to_set end end end neovim-0.8.1/lib/neovim/logging.rb0000644000004100000410000000423113506362344017071 0ustar www-datawww-datarequire "logger" module Neovim # Mixed into classes for unified logging helper methods. # # @api private module Logging TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S.%6N".freeze # Return the value of @logger, or construct it from the environment. # $NVIM_RUBY_LOG_FILE specifies a file to log to (default +STDERR+), while # $NVIM_RUBY_LOG_LEVEL specifies the level (default +WARN+) def self.logger(env=ENV) return @logger if instance_variable_defined?(:@logger) env_file, env_level = env.values_at("NVIM_RUBY_LOG_FILE", "NVIM_RUBY_LOG_LEVEL") @logger = Logger.new(env_file || STDERR) if env_level begin @logger.level = Integer(env_level) rescue ArgumentError @logger.level = Logger.const_get(env_level.upcase) end else @logger.level = Logger::WARN end @logger.formatter = json_formatter @logger end def self.logger=(logger) logger.formatter = json_formatter @logger = logger end def self.included(base) base.send(:include, Helpers) end def self.json_formatter lambda do |level, time, _, fields| require "multi_json" MultiJson.encode( { _level: level, _time: time.strftime(TIMESTAMP_FORMAT) }.merge!(fields) ) << "\n" end end private_class_method :json_formatter # @api private module Helpers private def log(level, method=nil, &block) begin Logging.logger.public_send(level) do { _class: self.class, _method: method || block.binding.eval("__method__") }.merge!(yield) end rescue => ex Logging.logger.error("failed to log: #{ex.inspect}") end rescue # Inability to log shouldn't abort process end def log_exception(level, ex, method) log(level, method) do {exception: ex.class, message: ex.message} end log(:debug, method) do {exception: ex.class, message: ex.message, backtrace: ex.backtrace} end end end end end neovim-0.8.1/lib/neovim/plugin.rb0000644000004100000410000000130513506362344016740 0ustar www-datawww-datarequire "neovim/plugin/dsl" module Neovim # @api private class Plugin attr_accessor :handlers, :setup_blocks, :script_host attr_reader :source def self.from_config_block(source) new(source).tap do |instance| yield DSL.new(instance) if block_given? end end def initialize(source) @source = source @handlers = [] @setup_blocks = [] @script_host = false end def specs @handlers.inject([]) do |acc, handler| handler.qualified? ? acc + [handler.to_spec] : acc end end def setup(client) @setup_blocks.each { |bl| bl.call(client) } end def script_host? !!@script_host end end end neovim-0.8.1/lib/neovim/executable.rb0000644000004100000410000000133413506362344017565 0ustar www-datawww-datamodule Neovim # Object representing the `nvim` executable. class Executable VERSION_PATTERN = /\ANVIM v?(.+)$/ class Error < RuntimeError; end # Load the current executable from the +NVIM_EXECUTABLE+ environment # variable. # # @param env [Hash] # @return [Executable] def self.from_env(env=ENV) new(env.fetch("NVIM_EXECUTABLE", "nvim")) end attr_reader :path def initialize(path) @path = path end # Fetch the +nvim+ version. # # @return [String] def version @version ||= IO.popen([@path, "--version"]) do |io| io.gets[VERSION_PATTERN, 1] end rescue => e raise Error, "Couldn't load #{@path}: #{e}" end end end neovim-0.8.1/lib/neovim/message.rb0000644000004100000410000000247013506362344017072 0ustar www-datawww-datarequire "neovim/logging" module Neovim # @api private class Message def self.from_array((kind, *payload)) case kind when 0 request(*payload) when 1 reqid, (_, error), value = payload response(reqid, error, value) when 2 notification(*payload) else raise "Unknown message type #{kind.inspect}" end end def self.request(id, method, args) Request.new(id, method, args) end def self.response(request_id, error, value) Response.new(request_id, error, value) end def self.notification(method, args) Notification.new(method, args) end Request = Struct.new(:id, :method_name, :arguments) do def to_a [0, id, method_name, arguments] end def received(_) yield self end def sync? true end end Response = Struct.new(:request_id, :error, :value) do def to_a [1, request_id, error, value] end def received(handlers) handlers.delete(request_id).call(self) end end Notification = Struct.new(:method_name, :arguments) do def to_a [2, method_name, arguments] end def received(_) yield self end def sync? false end end end end neovim-0.8.1/lib/neovim/client.rb0000644000004100000410000002142413506362344016724 0ustar www-datawww-datarequire "neovim/api" require "neovim/current" require "neovim/session" require "set" module Neovim # Client to a running +nvim+ instance. The interface is generated at # runtime via the +nvim_get_api_info+ RPC call. Some methods return # +RemoteObject+ subclasses (i.e. +Buffer+, +Window+, or +Tabpage+), # which similarly have dynamically generated interfaces. # # The methods documented here were generated using NVIM v0.3.7 # # @see Buffer # @see Window # @see Tabpage class Client attr_reader :session, :api def self.from_event_loop(event_loop, session=Session.new(event_loop)) api = API.new(session.request(:nvim_get_api_info)) event_loop.register_types(api, session) new(session, api) end def initialize(session, api) @session = session @api = api end def channel_id @api.channel_id end # Intercept method calls and delegate to appropriate RPC methods. def method_missing(method_name, *args) if (func = @api.function_for_object_method(self, method_name)) func.call(@session, *args) else super end end # Extend +respond_to_missing?+ to support RPC methods. def respond_to_missing?(method_name, *) super || rpc_methods.include?(method_name.to_sym) end # Extend +methods+ to include RPC methods. def methods(*args) super | rpc_methods.to_a end # Access to objects belonging to the current +nvim+ context. # # @return [Current] # @example Get the current buffer # client.current.buffer # @example Set the current line # client.current.line = "New line" # @see Current def current @current ||= Current.new(@session) end # Evaluate the VimL expression (alias for +nvim_eval+). # # @param expr [String] A VimL expression. # @return [Object] # @example Return a list from VimL # client.evaluate('[1, 2]') # => [1, 2] def evaluate(expr) @api.function_for_object_method(self, :eval).call(@session, expr) end # Display a message. # # @param string [String] The message. # @return [void] def message(string) out_write(string) end # Set an option. # # @overload set_option(key, value) # @param [String] key # @param [String] value # # @overload set_option(optstr) # @param [String] optstr # # @example Set the +timeoutlen+ option # client.set_option("timeoutlen", 0) # client.set_option("timeoutlen=0") def set_option(*args) if args.size > 1 @api.function_for_object_method(self, :set_option).call(@session, *args) else @api.function_for_object_method(self, :command).call(@session, "set #{args.first}") end end def shutdown @session.shutdown end private def rpc_methods @rpc_methods ||= @api.functions_for_object(self).map(&:method_name).to_set end public # The following methods are dynamically generated. =begin @method ui_attach(width, height, options) See +:h nvim_ui_attach()+ @param [Integer] width @param [Integer] height @param [Hash] options @return [void] @method ui_detach See +:h nvim_ui_detach()+ @return [void] @method ui_try_resize(width, height) See +:h nvim_ui_try_resize()+ @param [Integer] width @param [Integer] height @return [void] @method ui_set_option(name, value) See +:h nvim_ui_set_option()+ @param [String] name @param [Object] value @return [void] @method command(command) See +:h nvim_command()+ @param [String] command @return [void] @method get_hl_by_name(name, rgb) See +:h nvim_get_hl_by_name()+ @param [String] name @param [Boolean] rgb @return [Hash] @method get_hl_by_id(hl_id, rgb) See +:h nvim_get_hl_by_id()+ @param [Integer] hl_id @param [Boolean] rgb @return [Hash] @method feedkeys(keys, mode, escape_csi) See +:h nvim_feedkeys()+ @param [String] keys @param [String] mode @param [Boolean] escape_csi @return [void] @method input(keys) See +:h nvim_input()+ @param [String] keys @return [Integer] @method replace_termcodes(str, from_part, do_lt, special) See +:h nvim_replace_termcodes()+ @param [String] str @param [Boolean] from_part @param [Boolean] do_lt @param [Boolean] special @return [String] @method command_output(command) See +:h nvim_command_output()+ @param [String] command @return [String] @method eval(expr) See +:h nvim_eval()+ @param [String] expr @return [Object] @method execute_lua(code, args) See +:h nvim_execute_lua()+ @param [String] code @param [Array] args @return [Object] @method call_function(fn, args) See +:h nvim_call_function()+ @param [String] fn @param [Array] args @return [Object] @method call_dict_function(dict, fn, args) See +:h nvim_call_dict_function()+ @param [Object] dict @param [String] fn @param [Array] args @return [Object] @method strwidth(text) See +:h nvim_strwidth()+ @param [String] text @return [Integer] @method list_runtime_paths See +:h nvim_list_runtime_paths()+ @return [Array] @method set_current_dir(dir) See +:h nvim_set_current_dir()+ @param [String] dir @return [void] @method get_current_line See +:h nvim_get_current_line()+ @return [String] @method set_current_line(line) See +:h nvim_set_current_line()+ @param [String] line @return [void] @method del_current_line See +:h nvim_del_current_line()+ @return [void] @method get_var(name) See +:h nvim_get_var()+ @param [String] name @return [Object] @method set_var(name, value) See +:h nvim_set_var()+ @param [String] name @param [Object] value @return [void] @method del_var(name) See +:h nvim_del_var()+ @param [String] name @return [void] @method get_vvar(name) See +:h nvim_get_vvar()+ @param [String] name @return [Object] @method get_option(name) See +:h nvim_get_option()+ @param [String] name @return [Object] @method out_write(str) See +:h nvim_out_write()+ @param [String] str @return [void] @method err_write(str) See +:h nvim_err_write()+ @param [String] str @return [void] @method err_writeln(str) See +:h nvim_err_writeln()+ @param [String] str @return [void] @method list_bufs See +:h nvim_list_bufs()+ @return [Array] @method get_current_buf See +:h nvim_get_current_buf()+ @return [Buffer] @method set_current_buf(buffer) See +:h nvim_set_current_buf()+ @param [Buffer] buffer @return [void] @method list_wins See +:h nvim_list_wins()+ @return [Array] @method get_current_win See +:h nvim_get_current_win()+ @return [Window] @method set_current_win(window) See +:h nvim_set_current_win()+ @param [Window] window @return [void] @method list_tabpages See +:h nvim_list_tabpages()+ @return [Array] @method get_current_tabpage See +:h nvim_get_current_tabpage()+ @return [Tabpage] @method set_current_tabpage(tabpage) See +:h nvim_set_current_tabpage()+ @param [Tabpage] tabpage @return [void] @method create_namespace(name) See +:h nvim_create_namespace()+ @param [String] name @return [Integer] @method get_namespaces See +:h nvim_get_namespaces()+ @return [Hash] @method subscribe(event) See +:h nvim_subscribe()+ @param [String] event @return [void] @method unsubscribe(event) See +:h nvim_unsubscribe()+ @param [String] event @return [void] @method get_color_by_name(name) See +:h nvim_get_color_by_name()+ @param [String] name @return [Integer] @method get_color_map See +:h nvim_get_color_map()+ @return [Hash] @method get_mode See +:h nvim_get_mode()+ @return [Hash] @method get_keymap(mode) See +:h nvim_get_keymap()+ @param [String] mode @return [Array] @method get_commands(opts) See +:h nvim_get_commands()+ @param [Hash] opts @return [Hash] @method get_api_info See +:h nvim_get_api_info()+ @return [Array] @method set_client_info(name, version, type, methods, attributes) See +:h nvim_set_client_info()+ @param [String] name @param [Hash] version @param [String] type @param [Hash] methods @param [Hash] attributes @return [void] @method get_chan_info(chan) See +:h nvim_get_chan_info()+ @param [Integer] chan @return [Hash] @method list_chans See +:h nvim_list_chans()+ @return [Array] @method call_atomic(calls) See +:h nvim_call_atomic()+ @param [Array] calls @return [Array] @method parse_expression(expr, flags, highlight) See +:h nvim_parse_expression()+ @param [String] expr @param [String] flags @param [Boolean] highlight @return [Hash] @method list_uis See +:h nvim_list_uis()+ @return [Array] @method get_proc_children(pid) See +:h nvim_get_proc_children()+ @param [Integer] pid @return [Array] @method get_proc(pid) See +:h nvim_get_proc()+ @param [Integer] pid @return [Object] =end end end neovim-0.8.1/lib/neovim/ruby_provider/0000755000004100000410000000000013506362344020011 5ustar www-datawww-dataneovim-0.8.1/lib/neovim/ruby_provider/vim.rb0000644000004100000410000000212213506362344021126 0ustar www-datawww-datarequire "neovim/buffer" require "neovim/window" # The VIM module provides backwards compatibility for the +:ruby+, +:rubyfile+, # and +:rubydo+ +vim+ functions. module Vim Buffer = ::Neovim::Buffer Window = ::Neovim::Window @__buffer_cache = {} def self.__client=(client) @__client = client end # Delegate all method calls to the underlying +Neovim::Client+ object. def self.method_missing(method, *args, &block) if @__client @__client.public_send(method, *args, &block).tap do __refresh_globals(@__client) end else super end end def self.respond_to_missing?(method, *args) if @__client @__client.send(:respond_to_missing?, method, *args) else super end end def self.__refresh_globals(client) bufid, winid = client.evaluate("[nvim_get_current_buf(), nvim_get_current_win()]") session, api = client.session, client.api $curbuf = @__buffer_cache.fetch(bufid) do @__buffer_cache[bufid] = Buffer.new(bufid, session, api) end $curwin = Window.new(winid, session, api) end end VIM = Vim neovim-0.8.1/lib/neovim/ruby_provider/window_ext.rb0000644000004100000410000000046113506362344022526 0ustar www-datawww-datarequire "neovim/ruby_provider/vim" module Neovim # @api private class Window def self.current ::Vim.get_current_win end def self.count ::Vim.get_current_tabpage.list_wins.size end def self.[](index) ::Vim.get_current_tabpage.list_wins[index] end end end neovim-0.8.1/lib/neovim/ruby_provider/buffer_ext.rb0000644000004100000410000000041113506362344022463 0ustar www-datawww-datarequire "neovim/ruby_provider/vim" module Neovim # @api private class Buffer def self.current ::Vim.get_current_buf end def self.count ::Vim.list_bufs.size end def self.[](index) ::Vim.list_bufs[index] end end end neovim-0.8.1/lib/neovim/event_loop.rb0000644000004100000410000000425213506362344017620 0ustar www-datawww-datarequire "neovim/logging" require "neovim/connection" require "neovim/message" module Neovim # @api private class EventLoop include Logging def self.tcp(host, port) new Connection.tcp(host, port) end def self.unix(path) new Connection.unix(path) end def self.child(argv) new Connection.child(argv) end def self.stdio new Connection.stdio end def initialize(connection) @running = false @shutdown = false @connection = connection end def stop @running = false end def shutdown @running = false @shutdown = true run end def request(request_id, method, *args) log(:debug) do { request_id: request_id, method: method, arguments: args } end write(:request, request_id, method, args) end def respond(request_id, return_value, error) log(:debug) do { request_id: request_id, return_value: return_value, error: error } end write(:response, request_id, error, return_value) end def notify(method, *args) log(:debug) { {name: method, arguments: args} } write(:notification, method, args) end def run @running = true loop do break unless @running break if @shutdown begin yield read rescue EOFError => e log_exception(:debug, e, __method__) shutdown rescue => e log_exception(:error, e, __method__) end end ensure @connection.close if @shutdown end def register_types(api, session) api.types.each do |type, info| id = info.fetch("id") klass = Neovim.const_get(type) log(:debug) { {type: type, id: id} } @connection.register_type(id) do |index| klass.new(index, session, api) end end end private def read array = @connection.flush.read Message.from_array(array) end def write(type, *args) message = Message.public_send(type, *args) @connection.write(message.to_a) end end end neovim-0.8.1/lib/neovim/ruby_provider.rb0000644000004100000410000001061113506362344020335 0ustar www-datawww-datarequire "neovim/ruby_provider/vim" require "neovim/ruby_provider/buffer_ext" require "neovim/ruby_provider/window_ext" require "stringio" module Neovim # This class is used to define a +Neovim::Plugin+ to act as a backend for the # +:ruby+, +:rubyfile+, and +:rubydo+ Vim commands. It is autoloaded from # +nvim+ and not intended to be required directly. # # @api private module RubyProvider def self.__define_plugin! Thread.abort_on_exception = true Neovim.plugin do |plug| plug.__send__(:script_host!) __define_setup(plug) __define_ruby_execute(plug) __define_ruby_execute_file(plug) __define_ruby_do_range(plug) __define_ruby_chdir(plug) end end # Define the +DirChanged+ event to update the provider's pwd. def self.__define_setup(plug) plug.__send__(:setup) do |client| begin cid = client.api.channel_id client.command("au DirChanged * call rpcrequest(#{cid}, 'ruby_chdir', v:event)") rescue ArgumentError # Swallow this exception for now. This means the nvim installation is # from before DirChanged was implemented. end end end # Evaluate the provided Ruby code, exposing the +Vim+ constant for # interactions with the editor. # # This is used by the +:ruby+ command. def self.__define_ruby_execute(plug) plug.__send__(:rpc, :ruby_execute) do |nvim, ruby| __wrap_client(nvim) do eval(ruby, TOPLEVEL_BINDING, "eval") end end end private_class_method :__define_ruby_execute # Evaluate the provided Ruby file, exposing the +Vim+ constant for # interactions with the editor. # # This is used by the +:rubyfile+ command. def self.__define_ruby_execute_file(plug) plug.__send__(:rpc, :ruby_execute_file) do |nvim, path| __wrap_client(nvim) { load(path) } end end private_class_method :__define_ruby_execute_file # Evaluate the provided Ruby code over each line of a range. The contents # of the current line can be accessed and modified via the +$_+ variable. # # Since this method evaluates each line in the local binding, all local # variables and methods are available to the user. Thus the +__+ prefix # obfuscation. # # This is used by the +:rubydo+ command. def self.__define_ruby_do_range(__plug) __plug.__send__(:rpc, :ruby_do_range) do |__nvim, *__args| __wrap_client(__nvim) do __start, __stop, __ruby = __args __buffer = __nvim.get_current_buf __update_lines_in_chunks(__buffer, __start, __stop, 5000) do |__lines| __lines.map do |__line| $_ = __line eval(__ruby, binding, "eval") $_ end end end end end private_class_method :__define_ruby_do_range def self.__define_ruby_chdir(plug) plug.__send__(:rpc, :ruby_chdir) do |_, event| Dir.chdir(event.fetch("cwd")) end end private_class_method :__define_ruby_chdir def self.__wrap_client(client) Vim.__client = client Vim.__refresh_globals(client) __with_exception_handling(client) do __with_std_streams(client) do yield end end nil end private_class_method :__wrap_client def self.__with_exception_handling(client) yield rescue ScriptError, StandardError => e msg = [e.class, e.message].join(": ") client.err_writeln(msg) end private_class_method :__with_exception_handling def self.__with_std_streams(client) old_stdout = $stdout.dup old_stderr = $stderr.dup $stdout, $stderr = StringIO.new, StringIO.new begin yield client.out_write($stdout.string + $/) if $stdout.size > 0 client.err_writeln($stderr.string) if $stderr.size > 0 ensure $stdout = old_stdout $stderr = old_stderr end end private_class_method :__with_std_streams def self.__update_lines_in_chunks(buffer, start, stop, size) (start..stop).each_slice(size) do |linenos| start, stop = linenos[0] - 1, linenos[-1] lines = buffer.get_lines(start, stop, false) buffer.set_lines(start, stop, false, yield(lines)) end end private_class_method :__update_lines_in_chunks end end Neovim::RubyProvider.__define_plugin! neovim-0.8.1/lib/neovim/api.rb0000644000004100000410000000377113506362344016224 0ustar www-datawww-datamodule Neovim # @api private class API attr_reader :channel_id def initialize(payload) @channel_id, @api_info = payload end # Return all functions defined by the API. def functions @functions ||= @api_info.fetch("functions").inject({}) do |acc, func| function = Function.new(func) acc.merge(function.name => function) end end # Return information about +nvim+ types. Used for registering MessagePack # +ext+ types. def types @types ||= @api_info.fetch("types") end def function_for_object_method(obj, method_name) functions[function_name(obj, method_name)] end def functions_for_object(obj) pattern = function_pattern(obj) functions.values.select { |func| func.name =~ pattern } end # Truncate the output of inspect so console sessions are more pleasant. def inspect format("#<#{self.class}:0x%x @channel_id=#{@channel_id.inspect}>", object_id << 1) end private def function_name(obj, method_name) case obj when Client "nvim_#{method_name}" when Buffer "nvim_buf_#{method_name}" when Window "nvim_win_#{method_name}" when Tabpage "nvim_tabpage_#{method_name}" else raise "Unknown object #{obj.inspect}" end end def function_pattern(obj) case obj when Client /^nvim_(?!(buf|win|tabpage)_)/ when Buffer /^nvim_buf_/ when Window /^nvim_win_/ when Tabpage /^nvim_tabpage_/ else raise "Unknown object #{obj.inspect}" end end # @api private class Function attr_reader :name def initialize(attributes) @name = attributes.fetch("name") end def method_name @name.sub(/^nvim_(win_|buf_|tabpage_)?/, "").to_sym end # Apply this function to a running RPC session. def call(session, *args) session.request(name, *args) end end end end neovim-0.8.1/lib/neovim/tabpage.rb0000644000004100000410000000156613506362344017056 0ustar www-datawww-datarequire "neovim/remote_object" module Neovim # Class representing an +nvim+ tabpage. # # The methods documented here were generated using NVIM v0.3.7 class Tabpage < RemoteObject # The following methods are dynamically generated. =begin @method list_wins See +:h nvim_tabpage_list_wins()+ @return [Array] @method get_var(name) See +:h nvim_tabpage_get_var()+ @param [String] name @return [Object] @method set_var(name, value) See +:h nvim_tabpage_set_var()+ @param [String] name @param [Object] value @return [void] @method del_var(name) See +:h nvim_tabpage_del_var()+ @param [String] name @return [void] @method get_win See +:h nvim_tabpage_get_win()+ @return [Window] @method get_number See +:h nvim_tabpage_get_number()+ @return [Integer] @method is_valid See +:h nvim_tabpage_is_valid()+ @return [Boolean] =end end end neovim-0.8.1/lib/neovim/client_info.rb0000644000004100000410000000165613506362344017744 0ustar www-datawww-datarequire "neovim/version" module Neovim # @api private class ClientInfo HOST_METHOD_SPEC = {poll: {}, specs: {nargs: 1}}.freeze ATTRIBUTES = { website: "https://github.com/neovim/neovim-ruby", license: "MIT" }.freeze def self.for_host(host) name = host.plugins.map(&:script_host?) == [true] ? "ruby-script-host" : "ruby-rplugin-host" new(name, :host, HOST_METHOD_SPEC, ATTRIBUTES) end def self.for_client new("ruby-client", :remote, {}, ATTRIBUTES) end def initialize(name, type, method_spec, attributes) @name = name @type = type @method_spec = method_spec @attributes = attributes @version = ["major", "minor", "patch"] .zip(Neovim::VERSION.segments) .to_h end def to_args [ @name, @version, @type, @method_spec, @attributes ] end end end neovim-0.8.1/lib/neovim/window.rb0000644000004100000410000000543513506362344016761 0ustar www-datawww-datarequire "neovim/remote_object" module Neovim # Class representing an +nvim+ window. # # The methods documented here were generated using NVIM v0.3.7 class Window < RemoteObject # Get the buffer displayed in the window # # @return [Buffer] def buffer get_buf end # Get the height of the window # # @return [Integer] def height get_height end # Set the height of the window # # @param height [Integer] # @return [Integer] def height=(height) set_height(height) height end # Get the width of the window # # @return [Integer] def width get_width end # Set the width of the window # # @param width [Integer] # @return [Integer] def width=(width) set_width(width) width end # Get the cursor coordinates # # @return [Array(Integer, Integer)] def cursor get_cursor end # Set the cursor coodinates # # @param coords [Array(Integer, Integer)] # @return [Array(Integer, Integer)] def cursor=(coords) x, y = coords x = [x, 1].max y = [y, 0].max + 1 @session.request(:nvim_eval, "cursor(#{x}, #{y})") end # The following methods are dynamically generated. =begin @method get_buf See +:h nvim_win_get_buf()+ @return [Buffer] @method set_buf(buffer) See +:h nvim_win_set_buf()+ @param [Buffer] buffer @return [void] @method get_cursor See +:h nvim_win_get_cursor()+ @return [Array] @method set_cursor(pos) See +:h nvim_win_set_cursor()+ @param [Array] pos @return [void] @method get_height See +:h nvim_win_get_height()+ @return [Integer] @method set_height(height) See +:h nvim_win_set_height()+ @param [Integer] height @return [void] @method get_width See +:h nvim_win_get_width()+ @return [Integer] @method set_width(width) See +:h nvim_win_set_width()+ @param [Integer] width @return [void] @method get_var(name) See +:h nvim_win_get_var()+ @param [String] name @return [Object] @method set_var(name, value) See +:h nvim_win_set_var()+ @param [String] name @param [Object] value @return [void] @method del_var(name) See +:h nvim_win_del_var()+ @param [String] name @return [void] @method get_option(name) See +:h nvim_win_get_option()+ @param [String] name @return [Object] @method set_option(name, value) See +:h nvim_win_set_option()+ @param [String] name @param [Object] value @return [void] @method get_position See +:h nvim_win_get_position()+ @return [Array] @method get_tabpage See +:h nvim_win_get_tabpage()+ @return [Tabpage] @method get_number See +:h nvim_win_get_number()+ @return [Integer] @method is_valid See +:h nvim_win_is_valid()+ @return [Boolean] =end end end neovim-0.8.1/lib/neovim/plugin/0000755000004100000410000000000013506362344016414 5ustar www-datawww-dataneovim-0.8.1/lib/neovim/plugin/dsl.rb0000644000004100000410000001051213506362344017522 0ustar www-datawww-datarequire "neovim/plugin/handler" module Neovim class Plugin # The DSL exposed in +Neovim.plugin+ blocks. # # @api public class DSL < BasicObject def initialize(plugin) @plugin = plugin end # Register an +nvim+ command. See +:h command+. # # @param name [String] The name of the command. # @param options [Hash] Command options. # @param &block [Proc, nil] The body of the command. # # @option options [Integer] :nargs The number of arguments to accept. See # +:h command-nargs+. # @option options [String, Boolean] :range The range argument. # See +:h command-range+. # @option options [Integer] :count The default count argument. # See +:h command-count+. # @option options [Boolean] :bang Whether the command can take a +!+ # modifier. See +:h command-bang+. # @option options [Boolean] :register Whether the command can accept a # register name. See +:h command-register+. # @option options [String] :complete Set the completion attributes of the # command. See +:h command-completion+. # @option options [String] :eval An +nvim+ expression. Gets evaluated and # passed as an argument to the block. # @option options [Boolean] :sync (false) Whether +nvim+ should receive # the return value of the block. def command(name, options={}, &block) register_handler(:command, name, options, block) end # Register an +nvim+ function. See +:h function+. # # @param name [String] The name of the function. # @param options [Hash] Function options. # @param &block [Proc, nil] The body of the function. # # @option options [String, Boolean] :range The range argument. # See +:h func-range+. # @option options [String] :eval An +nvim+ expression. Gets evaluated and # passed as an argument to the block. # @option options [Boolean] :sync (false) Whether +nvim+ should receive # the return value of the block. def function(name, options={}, &block) register_handler(:function, name, options, block) end # Register an +nvim+ autocmd. See +:h autocmd+. # # @param event [String] The event type. See +:h autocmd-events+ # @param options [Hash] Autocmd options. # @param &block [Proc, nil] The body of the autocmd. # # @option options [String] :pattern The buffer name pattern. # See +:h autocmd-patterns+. # @option options [String] :eval An +nvim+ expression. Gets evaluated and # passed as an argument to the block. def autocmd(event, options={}, &block) register_handler(:autocmd, event, options, block) end private # Mark this plugin as the Ruby script host started by nvim. Should only # be used in +Neovim::RubyProvider+. def script_host! @plugin.script_host = true end # Register a setup block to run once before the host starts. The block # should expect to receive a single argument, a +Neovim::Client+. # # This is used for bootstrapping the ruby provider, and not meant to be # used publicly in plugin definitions. def setup(&block) @plugin.setup_blocks << block end # Directly define a synchronous RPC call without a namespace. # # This is used for exposing ruby provider calls, and not meant to be used # publicly in plugin definitions. def rpc(name, &block) @plugin.handlers.push(Handler.unqualified(name, block)) end def register_handler(type, name, options, block) if type == :autocmd options = options.dup else options = standardize(options.dup) end sync = !!options.delete(:sync) @plugin.handlers.push( Handler.new(@plugin.source, type, name, sync, options, block) ) end def standardize(options) if options.key?(:range) options[:range] = "" if options[:range] == true options[:range] = ::Kernel.String(options[:range]) end [:bang, :register].each do |opt| if options[opt] options[opt] = "" elsif options.key?(opt) options.delete(opt) end end options end end end end neovim-0.8.1/lib/neovim/plugin/handler.rb0000644000004100000410000000217113506362344020357 0ustar www-datawww-datamodule Neovim class Plugin # @api private class Handler attr_reader :block def self.unqualified(name, block) new(nil, nil, name, true, {qualified: false}, block) end def initialize(source, type, name, sync, options, block) @source = source @type = type.to_sym if type.respond_to?(:to_sym) @name = name.to_s @sync = sync @options = options @block = block || -> {} @qualified = options.key?(:qualified) ? options.delete(:qualified) : true end def sync? !!@sync end def qualified? @qualified end def qualified_name return @name unless qualified? if @type == :autocmd pattern = @options.fetch(:pattern, "*") "#{@source}:#{@type}:#{@name}:#{pattern}" else "#{@source}:#{@type}:#{@name}" end end def to_spec { type: @type, name: @name, sync: @sync, opts: @options } end def call(*args) @block.call(*args) end end end end neovim-0.8.1/lib/neovim/host.rb0000644000004100000410000000507713506362344016431 0ustar www-datawww-datarequire "neovim" require "neovim/client" require "neovim/client_info" require "neovim/event_loop" require "neovim/host/loader" module Neovim # @api private class Host include Logging attr_reader :plugins def self.run(rplugin_paths, event_loop=EventLoop.stdio) new(event_loop).tap do |host| Loader.new(host).load(rplugin_paths) end.run end def initialize(event_loop) @event_loop = event_loop @session = Session.new(event_loop) @handlers = {"poll" => poll_handler, "specs" => specs_handler} @plugins = [] @specs = {} end def run @session.run { |msg| handle(msg) } end def handle(message) log(:debug) { message.to_h } @handlers .fetch(message.method_name, default_handler) .call(@client, message) rescue Exception => e log_exception(:error, e, __method__) if message.sync? @session.respond(message.id, nil, e.message) else @client.err_writeln("Exception handling #{message.method_name}: (#{e.class}) #{e.message}") end raise unless e.is_a?(StandardError) end private def poll_handler @poll_handler ||= lambda do |_, req| initialize_client(req.id) initialize_plugins @session.respond(req.id, "ok") end end def specs_handler @specs_handler ||= lambda do |_, req| source = req.arguments.fetch(0) if @specs.key?(source) @session.respond(req.id, @specs.fetch(source)) else @session.respond(req.id, nil, "Unknown plugin #{source}") end end end def default_handler @default_handler ||= lambda do |_, message| next unless message.sync? @session.respond(message.id, nil, "Unknown request #{message.method_name}") end end def initialize_client(request_id) @session.request_id = request_id @session.notify(:nvim_set_client_info, *ClientInfo.for_host(self).to_args) @client = Client.from_event_loop(@event_loop, @session) end def initialize_plugins @plugins.each do |plugin| plugin.handlers.each do |handler| @handlers[handler.qualified_name] = wrap_plugin_handler(handler) end plugin.setup(@client) @specs[plugin.source] = plugin.specs end end def wrap_plugin_handler(handler) lambda do |client, message| args = message.arguments.flatten(1) result = handler.call(client, *args) @session.respond(message.id, result) if message.sync? end end end end neovim-0.8.1/lib/neovim/host/0000755000004100000410000000000013506362344016073 5ustar www-datawww-dataneovim-0.8.1/lib/neovim/host/loader.rb0000644000004100000410000000132713506362344017671 0ustar www-datawww-datarequire "neovim/plugin" module Neovim class Host # @api private class Loader def initialize(host) @host = host end def load(paths) paths.each do |path| override_plugin_method(path) do Kernel.load(path, true) end end end private def override_plugin_method(path) old_plugin_def = Neovim.method(:plugin) at_host = @host Neovim.define_singleton_method(:plugin) do |&block| plugin = Plugin.from_config_block(path, &block) at_host.plugins << plugin end yield ensure Neovim.define_singleton_method(:plugin, &old_plugin_def) end end end end neovim-0.8.1/lib/neovim.rb0000644000004100000410000000746213506362344015454 0ustar www-datawww-datarequire "neovim/client" require "neovim/client_info" require "neovim/session" require "neovim/event_loop" require "neovim/executable" require "neovim/logging" require "neovim/version" # The main entrypoint to the +Neovim+ gem. It allows you to connect to a # running +nvim+ instance programmatically or define a remote plugin to be # autoloaded by +nvim+. # # You can connect to a running +nvim+ instance by setting or inspecting the # +NVIM_LISTEN_ADDRESS+ environment variable and connecting via the appropriate # +attach_+ method. This is currently supported for both UNIX domain sockets # and TCP. You can also spawn and connect to an +nvim+ subprocess via # +Neovim.attach_child(argv)+. # # You can define a remote plugin using the +Neovim.plugin+ DSL, which allows # you to register commands, functions, and autocmds. Plugins are autoloaded by # +nvim+ from the +rplugin/ruby+ directory in your +nvim+ runtime path. # # @example Connect over a TCP socket # Neovim.attach_tcp("0.0.0.0", 3333) # => Neovim::Client # # @example Connect over a UNIX domain socket # Neovim.attach_unix("/tmp/nvim.sock") # => Neovim::Client # # @example Spawn and connect to a child +nvim+ process # Neovim.attach_child(["nvim", "--embed"]) # => Neovim::Client # # @example Define a Ruby plugin # # ~/.config/nvim/rplugin/ruby/plugin.rb # # Neovim.plugin do |plug| # # Define a command called "SetLine" which sets the contents of the # # current line. This command is executed asynchronously, so the return # # value is ignored. # plug.command(:SetLine, nargs: 1) do |nvim, str| # nvim.current.line = str # end # # # Define a function called "Sum" which adds two numbers. This function is # # executed synchronously, so the result of the block will be returned to # # nvim. # plug.function(:Sum, nargs: 2, sync: true) do |nvim, x, y| # x + y # end # # # Define an autocmd for the BufEnter event on Ruby files. # plug.autocmd(:BufEnter, pattern: "*.rb") do |nvim| # nvim.command("echom 'Ruby file, eh?'") # end # end # # @see Client # @see Plugin::DSL module Neovim # Connect to a running +nvim+ instance over TCP. # # @param host [String] The hostname or IP address # @param port [Integer] The port # @return [Client] # @see EventLoop.tcp def self.attach_tcp(host, port) attach(EventLoop.tcp(host, port)) end # Connect to a running +nvim+ instance over a UNIX domain socket. # # @param socket_path [String] The socket path # @return [Client] # @see EventLoop.unix def self.attach_unix(socket_path) attach(EventLoop.unix(socket_path)) end # Spawn and connect to a child +nvim+ process. # # @param argv [Array] The arguments to pass to the spawned process # @return [Client] # @see EventLoop.child def self.attach_child(argv=[executable.path]) attach(EventLoop.child(argv)) end # Placeholder method for exposing the remote plugin DSL. This gets # temporarily overwritten in +Host::Loader#load+. # # @see Host::Loader#load # @see Plugin::DSL def self.plugin raise "Can't call Neovim.plugin outside of a plugin host." end # Return a +Neovim::Executable+ representing the active +nvim+ executable. # # @return [Executable] # @see Executable def self.executable @executable ||= Executable.from_env end # Set the Neovim global logger. # # @param logger [Logger] The target logger # @return [Logger] # @see Logging def self.logger=(logger) Logging.logger = logger end # The Neovim global logger. # # @return [Logger] # @see Logging def self.logger Logging.logger end # @api private def self.attach(event_loop) Client.from_event_loop(event_loop).tap do |client| client.session.notify(:nvim_set_client_info, *ClientInfo.for_client.to_args) end end private_class_method :attach end neovim-0.8.1/Gemfile0000644000004100000410000000004613506362344014346 0ustar www-datawww-datasource "https://rubygems.org" gemspec neovim-0.8.1/appveyor.yml0000644000004100000410000000124713506362344015447 0ustar www-datawww-dataversion: '{build}' environment: matrix: - RUBY_VERSION: 26 - RUBY_VERSION: 25 - RUBY_VERSION: 24 - RUBY_VERSION: 23 - RUBY_VERSION: 22 cache: - vendor/bundle install: - set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem - set PATH=C:\Ruby%RUBY_VERSION%\bin;C:\tools\neovim\Neovim\bin;%PATH% - set NVIM_RUBY_LOG_LEVEL=DEBUG - set NVIM_RUBY_LOG_FILE=%cd%\ci.log - choco install neovim --pre -fy --ignore-dependencies --ignore-checksums - bundle install --path vendor/bundle build: off branches: only: - master before_test: - ruby -v - gem -v - bundle -v - nvim -v test_script: - bundle exec rake spec:functional spec:acceptance neovim-0.8.1/neovim.gemspec0000644000004100000410000000216213506362344015716 0ustar www-datawww-datalib = File.expand_path("lib", __dir__) $:.unshift(lib) unless $:.include?(lib) require "neovim/version" Gem::Specification.new do |spec| spec.name = "neovim" spec.version = Neovim::VERSION spec.authors = ["Alex Genco"] spec.email = ["alexgenco@gmail.com"] spec.summary = "A Ruby client for Neovim" spec.homepage = "https://github.com/neovim/neovim-ruby" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") spec.bindir = "exe" spec.executables = ["neovim-ruby-host"] spec.test_files = spec.files.grep(%r{^spec/}) spec.require_paths = ["lib"] spec.required_ruby_version = Gem::Requirement.new(">= 2.2.0") spec.add_dependency "msgpack", "~> 1.1" spec.add_dependency "multi_json", "~> 1.0" spec.add_development_dependency "bundler" spec.add_development_dependency "pry" spec.add_development_dependency "pry-byebug" spec.add_development_dependency "rake" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "rubocop", "0.56.0" spec.add_development_dependency "vim-flavor", "2.2.2" end neovim-0.8.1/exe/0000755000004100000410000000000013506362344013634 5ustar www-datawww-dataneovim-0.8.1/exe/neovim-ruby-host0000755000004100000410000000042313506362344017010 0ustar www-datawww-data#!/usr/bin/env ruby require "neovim/host" ARGV.each do |arg| break if arg == "--" if ["--version", "-V"].include?(arg) puts Neovim::VERSION exit(0) end end if STDIN.tty? abort("Can't run neovim-ruby-host interactively.") else Neovim::Host.run(ARGV) end neovim-0.8.1/LICENSE.txt0000644000004100000410000000205313506362344014676 0ustar www-datawww-dataCopyright (c) 2014 Alex Genco MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.