Project

nvim

0.0
A long-lived project that still receives updates
Yet another Ruby client for Neovim
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

Ruby-Nvim

Ruby support for Neovim.

Clean code, minimal dependecies, no frills, no wokeness.

I would have written a shorter letter, but I did not have the time. -- Blaise Pascal

Installation

sudo gem uninstall neovim || true
sudo gem install nvim

You may prefer to also install the dependencies. Yet, this is not neccessary, as they are small and Ruby-Nvim includes a copy of them.

sudo gem install supplement mplight

Usage

Command, Function and Autoload Plugins

Put this into a new buffer:

Neovim.plugin do |dsl|
  dsl.command :SetLine, nargs: 1 do |client,(str)|
    client.set_current_line str
  end
  dsl.function :Sum, nargs: 2, sync: true do |client,(x,y)|
    x + y
  end
  dsl.autocmd :BufEnter, pattern: "*.rb" do |client|
    client.command "echom 'Hello, Ruby!'"
  end
end

Then run these Vim commands:

w ++p ~/.config/nvim/rplugin/ruby/demo.rb
UpdateRemotePlugins

" Check the generated manifest file
split ~/.local/share/nvim/rplugin.vim
help remote-plugin-manifest

Open a new Neovim and see what happens:

e dummy.rb
SetLine some text
echo Sum(13,7)

Calling the :ruby... Interface

The :ruby... commands and the rubyeval() function behave as descibed in :h ruby.

Additionally you can directly execute the buffers contents (here, I did :set number):

 1 class C
 2   def f
 3     :F
 4   end
 5 end
 6 c = C.new
~
~
~
:1,6ruby |

The last value, if it is not nil, will be added through #inspect as a comment.

 1 class C
 2   def f
 3     :F
 4   end
 5 end
 6 c = C.new
 7 #=> #<C:0x000001b347456478>
~
~
:

The classes and variables will be preserved and are available during the next call.

 5 end
 6 c = C.new
 7 #=> #<C:0x00001fd2fd1f89d0>
 8 [ c.f, C]
~
~
~
:8ruby |

This results in:

 7 #=> #<C:0x00001fd2fd1f89d0>
 8 [ c.f, C]
 9 #=> [:F, C]
~
~
:

To inhibit the output of the last value, set the $result variable to false.

$result Output
nil every result except nil
false nothing
true every result, even nil
 1 $result = true
 2 puts "hi"
 3 #=> nil
~
~
:1,2ruby |

Last return value

The anonymous variable _ will hold the last non-nil result.

 1 7*11*13
~
~
:%ruby |

Then this will work:

 1 7*11*13
 2 #=> 1001
 3 _ - 1
~
~
:3ruby |

Standard output

Output will be added to the buffer, too.

 1 puts "ba" + "na"*2
 2 print "hell"
 3 puts "o"
~
~
:1,3ruby |

Error output will be displayed on the command line, highlighted by ErrorMsg.

 1 $stderr.puts "Oh, no!"
~
~
:%ruby |

This even applies to subprocesses. They won't mess up the RPC communication.

 1 system *%w(ls -l)
 2 system *%w(ls nonexistent)
~
~
:%ruby |

Yet, I suggest not to use fork and exec, except when you're absolutely sure what you're doing.

Output to a specific buffer

You may specify an output buffer by its number.

 1 a = 0
 2 begin
 3   puts a
 4   a = (a * 61 + 637) % 1000
 5 end while a.nonzero?
~
~
:%ruby @3

Use :execute if you determine the buffer number by a function.

:exe "%ruby @".bufnr("#")

Exception handling

If you prefer, you may return an error as a value, too.

 1 $rescue = true
 2 z = 0
 3 q = 1 / z
~
~
:set ft=ruby|1,3ruby |

Then:

 1 $rescue = true
 2 z = 0
 3 q = 1 / z
 4 #!= ZeroDivisionError
 5 #!  divided by 0
 6 puts "=begin", _.backtrace, "=end"
~
~
:5ruby |

If $result is false, $rescue will be ignored.

Even non-standard errors wil be caught.

 1 def fn ; fn ; end
 2 fn
~
~
:1,2ruby |
 1 $$
 2 #=> 49042
 3 sleep 1000
~
~
:3ruby |

Then say 'kill 49042' somewhere else.

Global variables

The global variable $vim will be set to the client.

~
~
:ruby $vim.command "split"

Further, the variables $range and $lines will be set.

 1 foo
 2 bar
 3 baz
~
:%ruby puts $range, $lines

The legacy variables $curbuf and $curwin are supported.

~
~
:ruby puts $curbuf.get_name, $curwin.get_height

Visit and change lines by :rubydo

The $_ and $. variables contain the line itself and the line number. $_ may be changed or assigned to.

:rubydo $_.strip!                                    # unindent all
:rubydo $_.insert 0, "%4d " % $.                     # number lines
:rubydo i = i.to_i.succ ; $_ = "%4d %s" % [ i, $_]   # dto.

If $_ is nil, the line will be deleted. If it is an Enumerable, each element will be inserted as its own line. All other non-string values will be converted by #to_s.

:rubydo $_ = nil if $_ =~ /^#/       # delete comment lines
:rubydo $_ = $_.split                # put each word on its own line
:rubydo $_ = [$_]*3 if ~/knock/i     # triple lines containing /knock/
:rubydo $_ = [$_, ""]                # spread by empty lines

Requiring Ruby files

In addition to the :rubyfile command as documented, that command can also be used to just require a Ruby file. Put the name into angle brackets, and the file will be searched in $:. Sorry, file name completion will not work.

~
~
:rubyfile <yaml>

In this case, the global variables $range and $lines will not be set. Yet, $vim still will be available. See the Nxxd gem for a nice example.

List all API functions

To show a list of the API functions call something like this:

pp $vim.functions.sort
pp $vim.obj_classes.map { |c| [ c.type, ($vim.obj_functions c).sort] }.to_h
~
~
:%ruby |-

Deprecated functions will be hidden unless you set the environment variable NVIM_RUBY_STRICT=off. Old functions not starting with nvim_ will be hidden. The full list of API functions can be obtained by a call to get_api_info.

pp $vim.get_api_info
~
~
:.ruby |-

See the script examples/dump_api for a more elaborated and colorized output.

Calculator

Further, a simple number/cash summing tool is included.

Apples    :     3.99
Bananas   : 5 * 0.40          # multiplication
Oranges   :     3.59 - 10%    # percentage added (here subtracted)
Kiwi      :     0,40          # comma is allowed
Coconut   :     5,-           # empty decimal places
# !dot                        # dot forced now
Tangerines:     4.44
# !comma                      # result with comma
~
~
:%ruby +

Modern rpcrequest() Calls

Put this into a new buffer:

require "neovim"
counter = 0
Neovim.start_remote do |dsl|
  dsl.register_handler "rb_add" do |client,n|
    counter += n
    client.command "echo 'Counter value now is: '..#{counter}..'.'"
  end
  dsl.register_handler "rb_raise" do |client|
    raise "Ouch!"
  end
end

Then enter these Vim commands:

w demo_remote.rb
let chan = jobstart(['ruby','demo_remote.rb'], { 'rpc': v:true })
call rpcrequest(chan, 'rb_add', 7)
call rpcrequest(chan, 'rb_raise')
call jobstop(chan)

If you prefer, you can also use a shebang line.

#!/usr/bin/env ruby
require "neovim"
Neovim.start_remote do |dsl|
  # ... (as above)
end

Then enter these Vim commands:

w demo_remote.rb
!chmod +x %
let chan = jobstart('./demo_remote.rb', { 'rpc': v:true })
" proceed as above

Logging and Debugging

Logging is a easy as this:

export NVIM_RUBY_LOG_LEVEL=all NVIM_RUBY_LOG_FILE=ruby.log
nvim +'ruby puts "hi"*10'

If the logfile isn't an absoulte path and doesn't start with "./", it will be relative to Neovim's stdpath("log").

To show the log levels, simply run in Neovim:

ruby puts Neovim::Logging::LEVELS.keys

If you are inside a Tmux, you might prefer to trace the colored log in a split window.

tmux split-window -fhd 'echo -e "\e[33m==== $$ ==== `tty` ====\e[m" ; ln -sf `tty` /tmp/tmux-`id -u`/debug ; exec cat >/dev/null 2>&1'
export NVIM_RUBY_LOG_LEVEL=all NVIM_RUBY_LOG_FILE=/tmp/tmux-`id -u`/debug

examples/demo_attach

nvim +'ruby puts "hi"*10'

You may start an interactive session and control a running Neovim through it. Open Neovim specifying the --listen option

nvim --listen /path/to/some.sock

or ask the running Neovim for its server name.

echo v:servername

Then connect to it. This requires the Intar gem.

$ intar -r neovim/remote
main:0:001> include Neovim
=> Object
main:0:002> Remote.start_client ConnectionUnix, "/path/to/some.sock" do |c|&
main:1:001> c.command "e /etc/passwd"
main:1:002> b = c.get_current_buf
=> #<Neovim::Buffer:400 1>
main:1:003> b[1]
=> ["root:*:0:0:Charlie &:/root:/bin/sh"]
main:1:004> \q!!

Miscellaneous Tools

Put text into an X selection or a TMux register.

rubyfile <neovim/tools/copy>
'<,'>ruby xsel $lines
'<,'>ruby xsel! $lines
'<,'>ruby tmuxbuf $lines

Maybe you like to install the Nxxd gem.

rubyfile <nxxd>
ruby r = (0...0x80).to_a.map { |c| c.chr }.join
ruby puts Nxxd::Dump.new.run r

Or even:

rubyfile <nxxd>
HexDump /etc/localtime

Copyright