neovim-0.10.0/0000755000004100000410000000000014661722423013124 5ustar www-datawww-dataneovim-0.10.0/.gitignore0000644000004100000410000000044514661722423015117 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/pack spec/acceptance/runtime/rplugin_manifest.vim vendor/bundle bin .vim-flavor neovim-0.10.0/exe/0000755000004100000410000000000014661722423013705 5ustar www-datawww-dataneovim-0.10.0/exe/neovim-ruby-host0000755000004100000410000000015514661722423017063 0ustar www-datawww-data#!/usr/bin/env ruby require "neovim/host/cli" Neovim::Host::CLI.run(__FILE__, ARGV, STDIN, STDOUT, STDERR) neovim-0.10.0/Flavorfile.lock0000644000004100000410000000010714661722423016065 0ustar www-datawww-datathinca/vim-themis (c1f4d465ce7dd23735513551b5c4c918d9c1bab1 at master) neovim-0.10.0/.github/0000755000004100000410000000000014661722423014464 5ustar www-datawww-dataneovim-0.10.0/.github/workflows/0000755000004100000410000000000014661722423016521 5ustar www-datawww-dataneovim-0.10.0/.github/workflows/docs.yml0000644000004100000410000000215514661722423020177 0ustar www-datawww-dataname: Docs on: push: branches: [main] schedule: - cron: '0 0 * * 0' jobs: docs: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '[skip ci]')" permissions: contents: write pull-requests: write steps: - uses: actions/checkout@v2 with: repository: neovim/neovim-ruby - uses: ruby/setup-ruby@v1 with: ruby-version: head - name: Install libfuse run: sudo apt install libfuse2 - name: Bundle install run: | bundle config set path vendor/bundle bundle install --jobs 3 --retry 3 - name: Install Neovim run: bundle exec rake ci:download_nvim - name: Generate docs env: NVIM_EXECUTABLE: "_nvim/bin/nvim" run: bundle exec rake docs:generate - name: Open pull request uses: peter-evans/create-pull-request@v3 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "[skip ci] Update generated docs" author: Docs Workflow title: "Update generated docs" neovim-0.10.0/.github/workflows/tests.yml0000644000004100000410000000351214661722423020407 0ustar www-datawww-dataname: Tests on: push: branches: [main] pull_request: branches: [main] schedule: - cron: '0 0 * * 0' jobs: linux_osx: strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] ruby: [ruby, head] runs-on: ${{ matrix.os }} if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - name: Fix up git URLs run: echo -e '[url "https://github.com/"]\n insteadOf = "git://github.com/"' >> ~/.gitconfig - uses: actions/checkout@v2 with: repository: neovim/neovim-ruby - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Install libfuse run: sudo apt install libfuse2 if: matrix.os == 'ubuntu-latest' - name: Install Neovim run: bundle exec rake ci:download_nvim - name: Run tests env: NVIM_EXECUTABLE: "_nvim/bin/nvim" run: bundle exec rake spec windows: strategy: fail-fast: false runs-on: windows-latest if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - name: Fix up git URLs run: | echo '[url "https://github.com/"]' >> ~/.gitconfig echo ' insteadOf = "git://github.com/"' >> ~/.gitconfig - uses: actions/checkout@v2 with: repository: neovim/neovim-ruby - uses: ruby/setup-ruby@v1 with: ruby-version: ruby bundler-cache: true - name: Install Neovim uses: crazy-max/ghaction-chocolatey@v1 with: args: install neovim -fy --ignore-dependencies --ignore-checksums - name: Run tests env: VIM_FLAVOR_HOME: 'D:\' NVIM_EXECUTABLE: 'C:\tools\neovim\nvim-win64\bin\nvim' run: bundle exec rake spec neovim-0.10.0/lib/0000755000004100000410000000000014661722423013672 5ustar www-datawww-dataneovim-0.10.0/lib/neovim/0000755000004100000410000000000014661722423015167 5ustar www-datawww-dataneovim-0.10.0/lib/neovim/remote_module/0000755000004100000410000000000014661722423020027 5ustar www-datawww-dataneovim-0.10.0/lib/neovim/remote_module/dsl.rb0000644000004100000410000000137714661722423021146 0ustar www-datawww-datamodule Neovim class RemoteModule # The DSL exposed in +Neovim.start_remote+ blocks. # # @api public class DSL < BasicObject attr_reader :handlers def initialize(&block) @handlers = ::Hash.new do |h, name| h[name] = ::Proc.new do |_, *| raise NotImplementedError, "undefined handler #{name.inspect}" end end block&.call(self) end # Define an RPC handler for use in remote modules. # # @param name [String] The handler name. # @param block [Proc] The body of the handler. def register_handler(name, &block) @handlers[name.to_s] = ::Proc.new do |client, *args| block.call(client, *args) end end end end end neovim-0.10.0/lib/neovim/ruby_provider.rb0000644000004100000410000001171114661722423020410 0ustar www-datawww-datarequire "neovim/ruby_provider/vim" require "neovim/ruby_provider/object_ext" 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_eval(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, "ruby_execute") end nil end end private_class_method :__define_ruby_execute # Evaluate the provided Ruby code, exposing the +Vim+ constant for # interactions with the editor and returning the value. # # This is used by the +:rubyeval+ command. def self.__define_ruby_eval(plug) plug.__send__(:rpc, :ruby_eval) do |nvim, ruby| __wrap_client(nvim) do eval(ruby, TOPLEVEL_BINDING, "ruby_eval") end end end private_class_method :__define_ruby_eval # 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) } nil 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, 1_000) do |__lines| __lines.map do |__line| $_ = __line eval(__ruby, binding, "ruby_do_range") $_ end end end nil 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 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.tap do client.out_write($stdout.string + $/) if $stdout.size > 0 client.err_writeln($stderr.string) if $stderr.size > 0 end 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.10.0/lib/neovim/logging.rb0000644000004100000410000000424714661722423017151 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 /\S+/.match?(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.10.0/lib/neovim/window.rb0000644000004100000410000000657514661722423017040 0ustar www-datawww-datarequire "neovim/remote_object" module Neovim # Class representing an +nvim+ window. # # The methods documented here were generated using NVIM v0.10.0 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_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 set_config(config) See +:h nvim_win_set_config()+ @param [Hash] config @return [void] @method get_config See +:h nvim_win_get_config()+ @return [Hash] @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_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] @method hide See +:h nvim_win_hide()+ @return [void] @method close(force) See +:h nvim_win_close()+ @param [Boolean] force @return [void] @method call(fun) See +:h nvim_win_call()+ @param [LuaRef] fun @return [Object] @method set_hl_ns(ns_id) See +:h nvim_win_set_hl_ns()+ @param [Integer] ns_id @return [void] @method text_height(opts) See +:h nvim_win_text_height()+ @param [Hash] opts @return [Hash] =end end end neovim-0.10.0/lib/neovim/host/0000755000004100000410000000000014661722423016144 5ustar www-datawww-dataneovim-0.10.0/lib/neovim/host/loader.rb0000644000004100000410000000132714661722423017742 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.10.0/lib/neovim/host/cli.rb0000644000004100000410000000163614661722423017246 0ustar www-datawww-datarequire "neovim/connection" require "neovim/event_loop" require "neovim/host" require "neovim/version" require "optparse" module Neovim class Host # @api private class CLI def self.run(path, argv, inn, out, err) cmd = File.basename(path) OptionParser.new do |opts| opts.on("-V", "--version") do out.puts Neovim::VERSION exit(0) end opts.on("-h", "--help") do out.puts "Usage: #{cmd} [-hV] rplugin_path ..." exit(0) end end.order!(argv) if inn.tty? err.puts("Can't run #{cmd} interactively.") exit(1) else conn = Connection.new(inn, out) event_loop = EventLoop.new(conn) Host.run(argv, event_loop) end rescue OptionParser::InvalidOption => e err.puts(e.message) exit(1) end end end end neovim-0.10.0/lib/neovim/buffer.rb0000644000004100000410000001777114661722423017002 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.10.0 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, 1) @lines[index - 1] end # Set the given line (1-indexed). # # @param index [Integer] # @param str [String] # @return [String] def []=(index, str) check_index(index, 1) @lines[index - 1] = str end # Delete the given line (1-indexed). # # @param index [Integer] # @return [void] def delete(index) check_index(index, 1) @lines.delete(index - 1) nil end # Append a line after the given line (1-indexed). # # Unlike the other methods, `0` is a valid index argument here, and inserts # into the first line of the buffer. # # 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, 0) 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, min) raise ArgumentError, "Index out of bounds" if index < min 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 set_text(start_row, start_col, end_row, end_col, replacement) See +:h nvim_buf_set_text()+ @param [Integer] start_row @param [Integer] start_col @param [Integer] end_row @param [Integer] end_col @param [Array] replacement @return [void] @method get_text(start_row, start_col, end_row, end_col, opts) See +:h nvim_buf_get_text()+ @param [Integer] start_row @param [Integer] start_col @param [Integer] end_row @param [Integer] end_col @param [Hash] opts @return [Array] @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 set_keymap(mode, lhs, rhs, opts) See +:h nvim_buf_set_keymap()+ @param [String] mode @param [String] lhs @param [String] rhs @param [Hash] opts @return [void] @method del_keymap(mode, lhs) See +:h nvim_buf_del_keymap()+ @param [String] mode @param [String] lhs @return [void] @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_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 del_mark(name) See +:h nvim_buf_del_mark()+ @param [String] name @return [Boolean] @method set_mark(name, line, col, opts) See +:h nvim_buf_set_mark()+ @param [String] name @param [Integer] line @param [Integer] col @param [Hash] opts @return [Boolean] @method get_mark(name) See +:h nvim_buf_get_mark()+ @param [String] name @return [Array] @method call(fun) See +:h nvim_buf_call()+ @param [LuaRef] fun @return [Object] @method create_user_command(name, command, opts) See +:h nvim_buf_create_user_command()+ @param [String] name @param [Object] command @param [Hash] opts @return [void] @method del_user_command(name) See +:h nvim_buf_del_user_command()+ @param [String] name @return [void] @method get_commands(opts) See +:h nvim_buf_get_commands()+ @param [Hash] opts @return [Hash] @method get_number See +:h nvim_buf_get_number()+ @return [Integer] @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(src_id, line, chunks, opts) See +:h nvim_buf_set_virtual_text()+ @param [Integer] src_id @param [Integer] line @param [Array] chunks @param [Hash] opts @return [Integer] @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_extmark_by_id(ns_id, id, opts) See +:h nvim_buf_get_extmark_by_id()+ @param [Integer] ns_id @param [Integer] id @param [Hash] opts @return [Array] @method get_extmarks(ns_id, start, end, opts) See +:h nvim_buf_get_extmarks()+ @param [Integer] ns_id @param [Object] start @param [Object] end @param [Hash] opts @return [Array] @method set_extmark(ns_id, line, col, opts) See +:h nvim_buf_set_extmark()+ @param [Integer] ns_id @param [Integer] line @param [Integer] col @param [Hash] opts @return [Integer] @method del_extmark(ns_id, id) See +:h nvim_buf_del_extmark()+ @param [Integer] ns_id @param [Integer] id @return [Boolean] @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] =end end end neovim-0.10.0/lib/neovim/session.rb0000644000004100000410000000557614661722423017214 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 Disconnected < RuntimeError def initialize super("Disconnected from nvim process") 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) block ||= ->(msg) { @pending_messages << msg } @running = true @event_loop.run do |message| Fiber.new { message.received(@response_handlers, &block) }.resume end end def next return @pending_messages.shift if @pending_messages.any? run { |msg| stop; msg } 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(Disconnected) 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_handlers[@request_id] = ->(res) { stop; res } run 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.10.0/lib/neovim/client_info.rb0000644000004100000410000000165614661722423020015 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.10.0/lib/neovim/remote_object.rb0000644000004100000410000000241514661722423020337 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.10.0/lib/neovim/host.rb0000644000004100000410000000507714661722423016502 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.10.0/lib/neovim/plugin.rb0000644000004100000410000000130514661722423017011 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.10.0/lib/neovim/ruby_provider/0000755000004100000410000000000014661722423020062 5ustar www-datawww-dataneovim-0.10.0/lib/neovim/ruby_provider/vim.rb0000644000004100000410000000212214661722423021177 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.10.0/lib/neovim/ruby_provider/window_ext.rb0000644000004100000410000000046114661722423022577 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.10.0/lib/neovim/ruby_provider/object_ext.rb0000644000004100000410000000010614661722423022532 0ustar www-datawww-dataclass Object def to_msgpack(packer) packer.pack(to_s) end end neovim-0.10.0/lib/neovim/ruby_provider/buffer_ext.rb0000644000004100000410000000041114661722423022534 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.10.0/lib/neovim/version.rb0000644000004100000410000000007114661722423017177 0ustar www-datawww-datamodule Neovim VERSION = Gem::Version.new("0.10.0") end neovim-0.10.0/lib/neovim/executable.rb0000644000004100000410000000133414661722423017636 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.10.0/lib/neovim/plugin/0000755000004100000410000000000014661722423016465 5ustar www-datawww-dataneovim-0.10.0/lib/neovim/plugin/handler.rb0000644000004100000410000000217114661722423020430 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.10.0/lib/neovim/plugin/dsl.rb0000644000004100000410000001051214661722423017573 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.10.0/lib/neovim/current.rb0000644000004100000410000000315514661722423017202 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.10.0/lib/neovim/api.rb0000644000004100000410000000377114661722423016275 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.10.0/lib/neovim/tabpage.rb0000644000004100000410000000172614661722423017125 0ustar www-datawww-datarequire "neovim/remote_object" module Neovim # Class representing an +nvim+ tabpage. # # The methods documented here were generated using NVIM v0.10.0 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 set_win(win) See +:h nvim_tabpage_set_win()+ @param [Window] win @return [void] @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.10.0/lib/neovim/client.rb0000644000004100000410000003646614661722423017011 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.10.0 # # @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 get_autocmds(opts) See +:h nvim_get_autocmds()+ @param [Hash] opts @return [Array] @method create_autocmd(event, opts) See +:h nvim_create_autocmd()+ @param [Object] event @param [Hash] opts @return [Integer] @method del_autocmd(id) See +:h nvim_del_autocmd()+ @param [Integer] id @return [void] @method clear_autocmds(opts) See +:h nvim_clear_autocmds()+ @param [Hash] opts @return [void] @method create_augroup(name, opts) See +:h nvim_create_augroup()+ @param [String] name @param [Hash] opts @return [Integer] @method del_augroup_by_id(id) See +:h nvim_del_augroup_by_id()+ @param [Integer] id @return [void] @method del_augroup_by_name(name) See +:h nvim_del_augroup_by_name()+ @param [String] name @return [void] @method exec_autocmds(event, opts) See +:h nvim_exec_autocmds()+ @param [Object] event @param [Hash] opts @return [void] @method parse_cmd(str, opts) See +:h nvim_parse_cmd()+ @param [String] str @param [Hash] opts @return [Hash] @method cmd(cmd, opts) See +:h nvim_cmd()+ @param [Hash] cmd @param [Hash] opts @return [String] @method create_user_command(name, command, opts) See +:h nvim_create_user_command()+ @param [String] name @param [Object] command @param [Hash] opts @return [void] @method del_user_command(name) See +:h nvim_del_user_command()+ @param [String] name @return [void] @method get_commands(opts) See +:h nvim_get_commands()+ @param [Hash] opts @return [Hash] @method exec(src, output) See +:h nvim_exec()+ @param [String] src @param [Boolean] output @return [String] @method command_output(command) See +:h nvim_command_output()+ @param [String] command @return [String] @method execute_lua(code, args) See +:h nvim_execute_lua()+ @param [String] code @param [Array] args @return [Object] @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 get_hl_by_name(name, rgb) See +:h nvim_get_hl_by_name()+ @param [String] name @param [Boolean] rgb @return [Hash] @method get_option_info(name) See +:h nvim_get_option_info()+ @param [String] name @return [Hash] @method get_option(name) See +:h nvim_get_option()+ @param [String] name @return [Object] @method call_atomic(calls) See +:h nvim_call_atomic()+ @param [Array] calls @return [Array] @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 set_decoration_provider(ns_id, opts) See +:h nvim_set_decoration_provider()+ @param [Integer] ns_id @param [Hash] opts @return [void] @method get_option_value(name, opts) See +:h nvim_get_option_value()+ @param [String] name @param [Hash] opts @return [Object] @method set_option_value(name, value, opts) See +:h nvim_set_option_value()+ @param [String] name @param [Object] value @param [Hash] opts @return [void] @method get_all_options_info See +:h nvim_get_all_options_info()+ @return [Hash] @method get_option_info2(name, opts) See +:h nvim_get_option_info2()+ @param [String] name @param [Hash] opts @return [Hash] @method ui_attach(width, height, options) See +:h nvim_ui_attach()+ @param [Integer] width @param [Integer] height @param [Hash] options @return [void] @method ui_set_focus(gained) See +:h nvim_ui_set_focus()+ @param [Boolean] gained @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 ui_try_resize_grid(grid, width, height) See +:h nvim_ui_try_resize_grid()+ @param [Integer] grid @param [Integer] width @param [Integer] height @return [void] @method ui_pum_set_height(height) See +:h nvim_ui_pum_set_height()+ @param [Integer] height @return [void] @method ui_pum_set_bounds(width, height, row, col) See +:h nvim_ui_pum_set_bounds()+ @param [Float] width @param [Float] height @param [Float] row @param [Float] col @return [void] @method ui_term_event(event, value) See +:h nvim_ui_term_event()+ @param [String] event @param [Object] value @return [void] @method get_hl_id_by_name(name) See +:h nvim_get_hl_id_by_name()+ @param [String] name @return [Integer] @method get_hl(ns_id, opts) See +:h nvim_get_hl()+ @param [Integer] ns_id @param [Hash] opts @return [Hash] @method set_hl(ns_id, name, val) See +:h nvim_set_hl()+ @param [Integer] ns_id @param [String] name @param [Hash] val @return [void] @method get_hl_ns(opts) See +:h nvim_get_hl_ns()+ @param [Hash] opts @return [Integer] @method set_hl_ns(ns_id) See +:h nvim_set_hl_ns()+ @param [Integer] ns_id @return [void] @method set_hl_ns_fast(ns_id) See +:h nvim_set_hl_ns_fast()+ @param [Integer] ns_id @return [void] @method feedkeys(keys, mode, escape_ks) See +:h nvim_feedkeys()+ @param [String] keys @param [String] mode @param [Boolean] escape_ks @return [void] @method input(keys) See +:h nvim_input()+ @param [String] keys @return [Integer] @method input_mouse(button, action, modifier, grid, row, col) See +:h nvim_input_mouse()+ @param [String] button @param [String] action @param [String] modifier @param [Integer] grid @param [Integer] row @param [Integer] col @return [void] @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 exec_lua(code, args) See +:h nvim_exec_lua()+ @param [String] code @param [Array] args @return [Object] @method notify(msg, log_level, opts) See +:h nvim_notify()+ @param [String] msg @param [Integer] log_level @param [Hash] opts @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 get_runtime_file(name, all) See +:h nvim_get_runtime_file()+ @param [String] name @param [Boolean] all @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 set_vvar(name, value) See +:h nvim_set_vvar()+ @param [String] name @param [Object] value @return [void] @method echo(chunks, history, opts) See +:h nvim_echo()+ @param [Array] chunks @param [Boolean] history @param [Hash] opts @return [void] @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 create_buf(listed, scratch) See +:h nvim_create_buf()+ @param [Boolean] listed @param [Boolean] scratch @return [Buffer] @method open_term(buffer, opts) See +:h nvim_open_term()+ @param [Buffer] buffer @param [Hash] opts @return [Integer] @method chan_send(chan, data) See +:h nvim_chan_send()+ @param [Integer] chan @param [String] data @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 paste(data, crlf, phase) See +:h nvim_paste()+ @param [String] data @param [Boolean] crlf @param [Integer] phase @return [Boolean] @method put(lines, type, after, follow) See +:h nvim_put()+ @param [Array] lines @param [String] type @param [Boolean] after @param [Boolean] follow @return [void] @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_context(opts) See +:h nvim_get_context()+ @param [Hash] opts @return [Hash] @method load_context(dict) See +:h nvim_load_context()+ @param [Hash] dict @return [Object] @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 set_keymap(mode, lhs, rhs, opts) See +:h nvim_set_keymap()+ @param [String] mode @param [String] lhs @param [String] rhs @param [Hash] opts @return [void] @method del_keymap(mode, lhs) See +:h nvim_del_keymap()+ @param [String] mode @param [String] lhs @return [void] @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 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] @method select_popupmenu_item(item, insert, finish, opts) See +:h nvim_select_popupmenu_item()+ @param [Integer] item @param [Boolean] insert @param [Boolean] finish @param [Hash] opts @return [void] @method del_mark(name) See +:h nvim_del_mark()+ @param [String] name @return [Boolean] @method get_mark(name, opts) See +:h nvim_get_mark()+ @param [String] name @param [Hash] opts @return [Array] @method eval_statusline(str, opts) See +:h nvim_eval_statusline()+ @param [String] str @param [Hash] opts @return [Hash] @method exec2(src, opts) See +:h nvim_exec2()+ @param [String] src @param [Hash] opts @return [Hash] @method command(command) See +:h nvim_command()+ @param [String] command @return [void] @method eval(expr) See +:h nvim_eval()+ @param [String] expr @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 parse_expression(expr, flags, highlight) See +:h nvim_parse_expression()+ @param [String] expr @param [String] flags @param [Boolean] highlight @return [Hash] @method open_win(buffer, enter, config) See +:h nvim_open_win()+ @param [Buffer] buffer @param [Boolean] enter @param [Hash] config @return [Window] =end end end neovim-0.10.0/lib/neovim/line_range.rb0000644000004100000410000000653214661722423017625 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.10.0/lib/neovim/message.rb0000644000004100000410000000247014661722423017143 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.10.0/lib/neovim/connection.rb0000644000004100000410000000245214661722423017656 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) 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.10.0/lib/neovim/event_loop.rb0000644000004100000410000000437514661722423017677 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 @connection.close 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 last_value = nil loop do break unless @running break if @shutdown begin last_value = yield(read) rescue EOFError, Errno::EPIPE => e log_exception(:debug, e, __method__) shutdown rescue => e log_exception(:error, e, __method__) end end last_value 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.10.0/lib/neovim/remote_module.rb0000644000004100000410000000200014661722423020344 0ustar www-datawww-datarequire "neovim/client" require "neovim/event_loop" require "neovim/logging" require "neovim/remote_module/dsl" require "neovim/session" module Neovim class RemoteModule include Logging def self.from_config_block(&block) new(DSL::new(&block).handlers) end def initialize(handlers) @handlers = handlers end def start event_loop = EventLoop.stdio session = Session.new(event_loop) client = nil session.run do |message| case message when Message::Request begin client ||= Client.from_event_loop(event_loop, session) args = message.arguments.flatten(1) @handlers[message.method_name].call(client, *args).tap do |rv| session.respond(message.id, rv, nil) if message.sync? end rescue => e log_exception(:error, e, __method__) session.respond(message.id, nil, e.message) if message.sync? end end end end end end neovim-0.10.0/lib/neovim.rb0000644000004100000410000000774514661722423015531 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/remote_module" 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+ process using 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 using # +Neovim.attach_child+. # # 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 # Start a remote module process with handlers defined in the config block. # Blocks indefinitely to handle messages. # # @see RemoteModule::DSL def self.start_remote(&block) RemoteModule.from_config_block(&block).start 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.10.0/LICENSE.txt0000644000004100000410000000205314661722423014747 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. neovim-0.10.0/spec/0000755000004100000410000000000014661722423014056 5ustar www-datawww-dataneovim-0.10.0/spec/neovim/0000755000004100000410000000000014661722423015353 5ustar www-datawww-dataneovim-0.10.0/spec/neovim/client_spec.rb0000644000004100000410000000432014661722423020167 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Client do let(:client) { Support.persistent_client } 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 = Neovim.attach_child(Support.child_argv) client.shutdown expect { client.strwidth("hi") }.to raise_error(Neovim::Session::Disconnected) 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.10.0/spec/neovim/logging_spec.rb0000644000004100000410000000761314661722423020347 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.10.0/spec/neovim/window_spec.rb0000644000004100000410000000202214661722423020215 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Window do let(:client) { Support.persistent_client } 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.10.0/spec/neovim/host/0000755000004100000410000000000014661722423016330 5ustar www-datawww-dataneovim-0.10.0/spec/neovim/host/loader_spec.rb0000644000004100000410000000304014661722423021132 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.10.0/spec/neovim/host/cli_spec.rb0000644000004100000410000000533314661722423020442 0ustar www-datawww-datarequire "helper" require "neovim/host/cli" begin require "pty" rescue LoadError # Not available on Windows end module Neovim class Host RSpec.describe CLI do let(:stdin) { StringIO.new } let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } specify "-V" do expect do CLI.run("/exe/nv-rb-host", ["-V"], stdin, stdout, stderr) end.to raise_error(SystemExit) { |e| expect(e.status).to eq(0) } expect(stderr.string).to be_empty expect(stdout.string).to eq(Neovim::VERSION.to_s + "\n") end specify "-h" do expect do CLI.run("/exe/nv-rb-host", ["-h"], stdin, stdout, stderr) end.to raise_error(SystemExit) { |e| expect(e.status).to eq(0) } expect(stderr.string).to be_empty expect(stdout.string).to eq("Usage: nv-rb-host [-hV] rplugin_path ...\n") end it "fails with invalid arguments" do expect do CLI.run("/exe/nv-rb-host", ["-x"], stdin, stdout, stderr) end.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) } expect(stdout.string).to be_empty expect(stderr.string).to eq("invalid option: -x\n") end it "fails when run interactively" do if !defined?(PTY) skip "Skipping without `pty` library." end PTY.open do |tty,| expect do CLI.run("/exe/nv-rb-host", ["plugin.rb"], tty, stdout, stderr) end.to raise_error(SystemExit) { |e| expect(e.status).to eq(1) } expect(stdout.string).to be_empty expect(stderr.string).to eq("Can't run nv-rb-host interactively.\n") end end it "starts a stdio host" do nvim_r, host_w = IO.pipe host_r, nvim_w = IO.pipe nvim_u = MessagePack::Unpacker.new(nvim_r) nvim_p = MessagePack::Packer.new(nvim_w) Support.file_path("plugin").tap do |path| File.write(path, "Neovim.plugin { |p| p.function('Foo') }") thr = Thread.new do CLI.run("/exe/nv-rb-host", [path], host_r, host_w, stderr) end begin nvim_p.write([0, 1, :poll, []]).flush expect(nvim_u.read[0..1]).to eq([2, "nvim_set_client_info"]) expect(nvim_u.read[0..2]).to eq([0, 2, "nvim_get_api_info"]) nvim_p.write([1, 2, nil, [10, {"functions" => {}, "types" => {}}]]).flush expect(nvim_u.read).to eq([1, 1, nil, "ok"]) nvim_p.write([0, 3, :specs, [path]]).flush *prefix, (payload, *) = nvim_u.read expect(prefix).to eq([1, 3, nil]) expect(payload["name"]).to eq("Foo") ensure thr.kill.join end end end end end end neovim-0.10.0/spec/neovim/executable_spec.rb0000644000004100000410000000152114661722423021032 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.10.0/spec/neovim/host_spec.rb0000644000004100000410000000764414661722423017702 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 { host_thread.kill.join } 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") api_info = Support.persistent_client.get_api_info 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.10.0/spec/neovim/current_spec.rb0000644000004100000410000000673214661722423020404 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Current do let(:client) { Support.persistent_client } let(:current) { client.current } 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.10.0/spec/neovim/remote_object_spec.rb0000644000004100000410000000540014661722423021532 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe RemoteObject do let(:client) { Support.persistent_client } 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.10.0/spec/neovim/ruby_provider/0000755000004100000410000000000014661722423020246 5ustar www-datawww-dataneovim-0.10.0/spec/neovim/ruby_provider/object_ext_spec.rb0000644000004100000410000000036414661722423023736 0ustar www-datawww-datarequire "helper" require "neovim/ruby_provider/object_ext" RSpec.describe Object do describe "#to_msgpack" do it "converts classes to strings" do expect(MessagePack.pack(String)).to eq(MessagePack.pack("String")) end end end neovim-0.10.0/spec/neovim/ruby_provider/window_ext_spec.rb0000644000004100000410000000216514661722423024000 0ustar www-datawww-datarequire "helper" require "neovim/ruby_provider/window_ext" module Neovim RSpec.describe Window do let!(:nvim) do Support.persistent_client.tap do |client| stub_const("::Vim", client) end end 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 window = Window[0] expect(window).to be_a(Window) expect(window).to eq(nvim.list_wins[0]) end it "only includes windows within a tabpage" do expect do nvim.command("tabnew") end.to change { Window[0] } end end end end neovim-0.10.0/spec/neovim/ruby_provider/vim_spec.rb0000644000004100000410000000304714661722423022404 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 = Support.persistent_client 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.10.0/spec/neovim/ruby_provider/buffer_ext_spec.rb0000644000004100000410000000152314661722423023737 0ustar www-datawww-datarequire "helper" require "neovim/ruby_provider/buffer_ext" module Neovim RSpec.describe Buffer do let!(:nvim) do Support.persistent_client.tap do |client| stub_const("::Vim", client) end end 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 buffer = Buffer[0] expect(buffer).to be_a(Buffer) expect(buffer).to eq(nvim.list_bufs[0]) end end end end neovim-0.10.0/spec/neovim/client_info_spec.rb0000644000004100000410000000366314661722423021213 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.10.0/spec/neovim/plugin_spec.rb0000644000004100000410000000746414661722423020223 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.10.0/spec/neovim/buffer_spec.rb0000644000004100000410000001063614661722423020171 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Buffer do let(:client) { Support.persistent_client } let(:buffer) { client.current.buffer } before do client.command("normal ione") client.command("normal otwo") client.command("normal gg") end 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 do client.command("new") end.to change { client.get_current_buf.number } 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.10.0/spec/neovim/api_spec.rb0000644000004100000410000000345414661722423017471 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe API do let(:client) { Support.persistent_client } 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.10.0/spec/neovim/message_spec.rb0000644000004100000410000000647714661722423020354 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.10.0/spec/neovim/session_spec.rb0000644000004100000410000000336414661722423020403 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe Session do let(:client) { Support.persistent_client } let(:session) { client.session } 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 "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 "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 "#next" do it "returns the next message from the event loop" do cid, = session.request(:nvim_get_api_info) session.request(:nvim_command, "call rpcnotify(#{cid}, 'my_event', 'foo')") message = session.next expect(message.sync?).to eq(false) expect(message.method_name).to eq("my_event") expect(message.arguments).to eq(["foo"]) end it "returns asynchronous notification errors", nvim_version: ">= 0.4.pre.dev" do session.notify(:nvim_set_current_line, "too", "many", "args") message = session.next expect(message.sync?).to eq(false) expect(message.method_name).to eq("nvim_error_event") expect(message.arguments).to eq([0, "Wrong number of arguments: expecting 1 but got 3"]) end end end end neovim-0.10.0/spec/neovim/line_range_spec.rb0000644000004100000410000001117114661722423021016 0ustar www-datawww-datarequire "helper" module Neovim RSpec.describe LineRange do let(:client) { Support.persistent_client } let(:buffer) { client.current.buffer } let(:line_range) { LineRange.new(buffer) } before do buffer.set_lines(0, -1, true, ["1", "2", "3", "4"]) end 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.10.0/spec/neovim/connection_spec.rb0000644000004100000410000000520714661722423021055 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.10.0/spec/neovim/event_loop_spec.rb0000644000004100000410000000423514661722423021070 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 "returns the last message received" do server_wr.write(MessagePack.pack([0, 1, :foo_method, []])) server_wr.flush message = event_loop.run { |req| event_loop.stop; req } 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.10.0/spec/helper.rb0000644000004100000410000000306314661722423015664 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__) ENV["NVIM_RUBY_LOG_LEVEL"] ||= "FATAL" 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) do Support.clean_persistent_client spec.run end ensure Support.teardown_workspace end end config.before(:example, :nvim_version) do |spec| comparator = spec.metadata.fetch(:nvim_version) requirement = Gem::Requirement.create(comparator) nvim_version = Support .nvim_version .split("+") .first if !requirement.satisfied_by?(Gem::Version.new(nvim_version)) skip "Skipping on nvim #{nvim_version} (requires #{comparator})" end end config.after(:suite) do Support.persistent_client.shutdown end Kernel.srand config.seed end Thread.abort_on_exception = true neovim-0.10.0/spec/neovim_spec.rb0000644000004100000410000000174214661722423016716 0ustar www-datawww-datarequire "helper" RSpec.describe Neovim do let(:client) { Support.persistent_client } let(:stream) do case Support.backend_strategy when /^tcp/, /^unix/ "socket" else "stdio" end end describe ".attach" do 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 ".executable" do it "returns the current executable" do expect(Neovim.executable).to be_a(Neovim::Executable) end end end neovim-0.10.0/spec/acceptance/0000755000004100000410000000000014661722423016144 5ustar www-datawww-dataneovim-0.10.0/spec/acceptance/remote_module_spec.vim0000644000004100000410000000054114661722423022533 0ustar www-datawww-datalet s:suite = themis#suite("Remote module") let s:expect = themis#helper("expect") call themis#helper('command').with(s:) function! s:suite.defines_commands() abort RbSetVar set_from_rb_mod foobar call s:expect(g:set_from_rb_mod).to_equal('foobar') endfunction function! s:suite.propagates_errors() abort Throws /oops/ :RbWillRaise endfunction neovim-0.10.0/spec/acceptance/rubydo_spec.vim0000644000004100000410000000326114661722423021201 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.10.0/spec/acceptance/rubyfile/0000755000004100000410000000000014661722423017765 5ustar www-datawww-dataneovim-0.10.0/spec/acceptance/rubyfile/call_foo.rb0000644000004100000410000000000414661722423022062 0ustar www-datawww-datafoo neovim-0.10.0/spec/acceptance/rubyfile/set_pwd_before.rb0000644000004100000410000000007514661722423023303 0ustar www-datawww-dataVim.command("let s:var = ['#{Dir.pwd.sub(/^[A-Z]:/, '')}']") neovim-0.10.0/spec/acceptance/rubyfile/ruby_interface.rb0000644000004100000410000000034314661722423023313 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.10.0/spec/acceptance/rubyfile/nested_inner.rb0000644000004100000410000000003714661722423022767 0ustar www-datawww-dataVim.command("let s:var = 123") neovim-0.10.0/spec/acceptance/rubyfile/raise_standard_error.rb0000644000004100000410000000001514661722423024502 0ustar www-datawww-dataraise "BOOM" neovim-0.10.0/spec/acceptance/rubyfile/curbuf_ivar_get.rb0000644000004100000410000000010314661722423023452 0ustar www-datawww-dataVim.command("let s:var = #{$curbuf.instance_variable_get(:@var)}") neovim-0.10.0/spec/acceptance/rubyfile/curbuf_ivar_set.rb0000644000004100000410000000005214661722423023471 0ustar www-datawww-data$curbuf.instance_variable_set(:@var, 123) neovim-0.10.0/spec/acceptance/rubyfile/raise_syntax_error.rb0000644000004100000410000000000714661722423024231 0ustar www-datawww-dataputs [ neovim-0.10.0/spec/acceptance/rubyfile/define_foo.rb0000644000004100000410000000005314661722423022405 0ustar www-datawww-datadef foo Vim.command("let s:var = 1") end neovim-0.10.0/spec/acceptance/rubyfile/set_pwd_after.rb0000644000004100000410000000005514661722423023140 0ustar www-datawww-dataVim.command("call add(s:var, '#{Dir.pwd}')") neovim-0.10.0/spec/acceptance/rubyfile/nested.rb0000644000004100000410000000005214661722423021571 0ustar www-datawww-dataVim.command("rubyfile ./nested_inner.rb") neovim-0.10.0/spec/acceptance/rubyfile_spec.vim0000644000004100000410000000405414661722423021517 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 1,$delete call append(0, ["one", "two"]) endfunction function! s:suite.after() abort execute("cd " . s:pwd) 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.10.0/spec/acceptance/runtime/0000755000004100000410000000000014661722423017627 5ustar www-datawww-dataneovim-0.10.0/spec/acceptance/runtime/init.vim0000644000004100000410000000066214661722423021313 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" if has("win32") || has("win64") let g:ruby_host_prog = getcwd() . "/script/host_wrapper.bat" else let g:ruby_host_prog = getcwd() . "/script/host_wrapper.sh" endif ruby require "rspec/expectations" ruby include ::RSpec::Matchers.dup set rtp=./spec/acceptance/runtime,$VIMRUNTIME neovim-0.10.0/spec/acceptance/runtime/example_remote_module.rb0000644000004100000410000000033614661722423024531 0ustar www-datawww-datarequire "neovim" Neovim.start_remote do |mod| mod.register_handler("rb_set_var") do |nvim, name, val| nvim.set_var(name, val.to_s) end mod.register_handler("rb_will_raise") do |nvim| raise "oops" end end neovim-0.10.0/spec/acceptance/runtime/rplugin/0000755000004100000410000000000014661722423021307 5ustar www-datawww-dataneovim-0.10.0/spec/acceptance/runtime/rplugin/ruby/0000755000004100000410000000000014661722423022270 5ustar www-datawww-dataneovim-0.10.0/spec/acceptance/runtime/rplugin/ruby/functions.rb0000644000004100000410000000123614661722423024627 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.10.0/spec/acceptance/runtime/rplugin/ruby/autocmds.rb0000644000004100000410000000072214661722423024435 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.10.0/spec/acceptance/runtime/rplugin/ruby/commands.rb0000644000004100000410000000436614661722423024427 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", nargs: 1, sync: true) do |nvim, arg| attrs = nvim.command_output("silent command RPluginCommandCompletion") compl = attrs.split($/).last.split[2] nvim.set_var("rplugin_command_completion", [compl, arg]) 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.10.0/spec/acceptance/runtime/plugin/0000755000004100000410000000000014661722423021125 5ustar www-datawww-dataneovim-0.10.0/spec/acceptance/runtime/plugin/example_remote_module.lua0000644000004100000410000000101014661722423026173 0ustar www-datawww-datalocal chan local function ensure_job() if chan then return chan end chan = vim.fn.jobstart({ 'ruby', '-I', 'lib', 'spec/acceptance/runtime/example_remote_module.rb', }, { rpc = true }) return chan end vim.api.nvim_create_user_command('RbSetVar', function(args) vim.fn.rpcrequest(ensure_job(), 'rb_set_var', args.fargs) end, { nargs = '*' }) vim.api.nvim_create_user_command('RbWillRaise', function(args) vim.fn.rpcrequest(ensure_job(), 'rb_will_raise', args.fargs) end, { nargs = 0 }) neovim-0.10.0/spec/acceptance/ruby_spec.vim0000644000004100000410000000421314661722423020654 0ustar www-datawww-datalet s:suite = themis#suite(":ruby") let s:expect = themis#helper("expect") function! s:suite.before_each() abort let s:pwd = getcwd() 1,$delete call append(0, ["one", "two"]) unlet! s:var endfunction function! s:suite.after_each() abort execute("cd " . s:pwd) 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(/^[A-Z]:/, "")}'") 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.10.0/spec/acceptance/rplugin_command_spec.vim0000644000004100000410000000536714661722423023064 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 foo call s:expect(g:rplugin_command_completion).to_equal(["buffer", "foo"]) 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.10.0/spec/acceptance/rubyeval_spec.vim0000644000004100000410000000136414661722423021530 0ustar www-datawww-datalet s:suite = themis#suite(":rubyeval") let s:expect = themis#helper("expect") function! s:suite.evaluates_ruby_objects() abort call s:expect(rubyeval("123")).to_equal(123) call s:expect(rubyeval("1.2")).to_equal(1.2) call s:expect(rubyeval("'test'")).to_equal("test") call s:expect(rubyeval("nil")).to_equal(v:null) call s:expect(rubyeval("true")).to_equal(v:true) call s:expect(rubyeval("false")).to_equal(v:false) call s:expect(rubyeval("{x: 1}")).to_equal({"x": 1}) call s:expect(rubyeval(":test")).to_equal("test") call s:expect(rubyeval(":test.class")).to_equal("Symbol") endfunction function! s:suite.propagates_exceptions() abort try rubyeval("raise 'BOOM'") throw "Nothing raised" catch /BOOM/ endtry endfunction neovim-0.10.0/spec/acceptance/rplugin_autocmd_spec.vim0000644000004100000410000000150514661722423023070 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.10.0/spec/acceptance/client_info_spec.vim0000644000004100000410000000170014661722423022162 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_client_info() abort for client_info in s:client_chans 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_match("ruby-\\(script\\|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() endfor endfunction neovim-0.10.0/spec/acceptance/rplugin_function_spec.vim0000644000004100000410000000174014661722423023262 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.10.0/spec/support.rb0000644000004100000410000000317614661722423016126 0ustar www-datawww-datarequire "shellwords" module Support class << self attr_accessor :nvim_version end def self.clean_persistent_client persistent_client.command("%bdelete! | tabonly | only | set all&") end def self.backend_strategy @backend_strategy ||= ENV.fetch("NVIM_RUBY_SPEC_BACKEND", "child") end def self.persistent_client return @persistent_client if defined?(@persistent_client) case backend_strategy when /^child:?(.*)$/ @persistent_client = Neovim.attach_child(child_argv + Shellwords.split($1)) when /^tcp:(.+)$/ @persistent_client = Neovim.attach_tcp(*$1.split(":", 2)) when /^unix:(.+)$/ @persistent_client = Neovim.attach_unix($1) else raise "Unrecognized $NVIM_RUBY_SPEC_BACKEND #{backend_strategy.inspect}" end 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.10.0/neovim.gemspec0000644000004100000410000000206414661722423015770 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", "~> 0.14" spec.add_development_dependency "pry-byebug" spec.add_development_dependency "rake" spec.add_development_dependency "rspec" spec.add_development_dependency "vim-flavor" end neovim-0.10.0/Rakefile0000644000004100000410000000207114661722423014571 0ustar www-datawww-datarequire "bundler/gem_tasks" require "rspec/core/rake_task" Bundler.setup 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.rb", "--reporter", "dot", "spec/acceptance") end namespace :acceptance do desc "Install acceptance spec dependencies" task :deps do sh "vim-flavor update --vimfiles-path=spec/acceptance/runtime" end end end namespace :docs do desc "Generate Neovim remote API docs" task :generate do run_script("generate_docs.rb") end end namespace :ci do task :download_nvim do run_script("ci/download_nvim.sh") end end desc "Run specs" task spec: [:"spec:functional", :"spec:acceptance"] task default: :spec def run_script(relpath, *args) path = File.expand_path("script/#{relpath}", __dir__) cmd_handler = ->(ok, status) { ok || exit(status.exitstatus) } if File.extname(path) == ".rb" ruby(path, *args, &cmd_handler) else sh(path, *args, &cmd_handler) end end neovim-0.10.0/CODE_OF_CONDUCT.md0000644000004100000410000000622314661722423015726 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 [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ neovim-0.10.0/Flavorfile0000644000004100000410000000005514661722423015140 0ustar www-datawww-dataflavor "thinca/vim-themis", branch: "master" neovim-0.10.0/Gemfile0000644000004100000410000000004614661722423014417 0ustar www-datawww-datasource "https://rubygems.org" gemspec neovim-0.10.0/README.md0000644000004100000410000000525214661722423014407 0ustar www-datawww-data# Neovim Ruby [![Build Status](https://github.com/neovim/neovim-ruby/workflows/Tests/badge.svg)](https://github.com/neovim/neovim-ruby/actions) [![Gem Version](https://badge.fury.io/rb/neovim.svg)](https://badge.fury.io/rb/neovim) Ruby support for [Neovim](https://github.com/neovim/neovim). ## Installation Add this line to your application's Gemfile: ```ruby gem "neovim" ``` And then execute: ```shell bundle ``` Or install it yourself as: ```shell gem install neovim ``` ## Usage Neovim supports the `--listen` option for specifying an address to serve its RPC API. To connect to Neovim over a Unix socket, start it up like this: ```shell $ nvim --listen /tmp/nvim.sock ``` 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](https://www.rubydoc.info/github/neovim/neovim-ruby/main/Neovim) for other ways to connect to `nvim`, and the [`Neovim::Client` docs](https://www.rubydoc.info/github/neovim/neovim-ruby/main/Neovim/Client) for a summary of the client interface. ### Remote Modules Remote modules allow users to define custom handlers in Ruby. To implement a remote module: - Define your handlers in a plain Ruby script that imports `neovim` - Spawn the script from lua using `jobstart` - Define commands in lua using `nvim_create_user_command` that route to the job's channel ID For usage examples, see: - [`example_remote_module.rb`](spec/acceptance/runtime/example_remote_module.rb) - [`example_remote_module.lua`](spec/acceptance/runtime/plugin/example_remote_module.lua) - [`remote_module_spec.vim`](spec/acceptance/remote_module_spec.vim) *Note*: Remote modules are a replacement for the deprecated "remote plugin" architecture. See https://github.com/neovim/neovim/issues/27949 for details. ### 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: * Main: ## Contributing 1. Fork it () 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.10.0/CHANGELOG.md0000644000004100000410000001212714661722423014740 0ustar www-datawww-data# 0.10.0 - Add `Neovim.start_remote` helper for remote module support (https://github.com/neovim/neovim-ruby/pull/107) # 0.9.1 - Fix bug where `Buffer#[]` with `0` returned the last line of the buffer (https://github.com/neovim/neovim-ruby/issues/97) # 0.9.0 - Add RPC support for `:rubyeval`. - Add `Neovim::Session#next`. - Rename `Neovim::Session::Exited` -> `Neovim::Session::Disconnected`. # 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.10.0/script/0000755000004100000410000000000014661722423014430 5ustar www-datawww-dataneovim-0.10.0/script/mp2j.rb0000755000004100000410000000026414661722423015632 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.10.0/script/generate_docs.rb0000755000004100000410000000505014661722423017562 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.10.0/script/host_wrapper.bat0000755000004100000410000000011714661722423017637 0ustar www-datawww-data@echo off pushd "%~dp0\.." 2>NUL ruby -I %CD%\lib %CD%\exe\neovim-ruby-host %* neovim-0.10.0/script/host_wrapper.sh0000755000004100000410000000013514661722423017503 0ustar www-datawww-data#!/usr/bin/env bash cd "$(dirname "$0")/.." exec ruby -I ./lib ./exe/neovim-ruby-host "$@" neovim-0.10.0/script/ci/0000755000004100000410000000000014661722423015023 5ustar www-datawww-dataneovim-0.10.0/script/ci/download_nvim.sh0000755000004100000410000000112714661722423020223 0ustar www-datawww-data#!/usr/bin/env bash set -eu : ${RUNNER_OS:?} case "$(echo "$RUNNER_OS" | tr "[:upper:]" "[:lower:]")" in macos) wget -nv -P /tmp \ "https://github.com/neovim/neovim/releases/download/stable/nvim-macos-x86_64.tar.gz" tar -C /tmp -xzf /tmp/nvim-macos-x86_64.tar.gz mv /tmp/nvim-macos-x86_64 ./_nvim ;; linux) mkdir -p _nvim/bin wget -nv -O _nvim/bin/nvim \ "https://github.com/neovim/neovim/releases/download/stable/nvim.appimage" ;; *) echo "Unrecognized \$RUNNER_OS" >&2 exit 1 ;; esac chmod u+x _nvim/bin/nvim _nvim/bin/nvim --version neovim-0.10.0/script/dump_api.rb0000755000004100000410000000052114661722423016554 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.10.0/script/j2mp.rb0000755000004100000410000000024314661722423015627 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.10.0/script/run_acceptance.rb0000755000004100000410000000161614661722423017736 0ustar www-datawww-data#!/usr/bin/env ruby require "fileutils" ENV.delete("VIM") ENV.delete("VIMRUNTIME") root = File.expand_path("..", __dir__) acceptance_root = File.join(root, "spec/acceptance") themis_rtp = File.join(acceptance_root, "runtime") themis_home = File.join(themis_rtp, "pack/flavors/start/thinca_vim-themis") manifest = File.join(themis_rtp, "rplugin_manifest.vim") vimrc = File.join(themis_rtp, "init.vim") nvim = ENV.fetch("NVIM_EXECUTABLE", "nvim") 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 --headless -u #{vimrc}" } FileUtils.rm_f(manifest) Dir.chdir(root) do system( env, nvim, "-e", "--headless", "-u", vimrc, "+UpdateRemotePlugins", "+qa!" ) exec(env, themis_exe, *ARGV) end