Module: VimClient

Defined in:
lib/vim_client.rb,
lib/vim_client/version.rb,
ext/vim_client/vim_client.c

Overview

This module provides methods for communicating with Vim, using it's +clientserver feature.

It provides a persistent connection to the X11 server for communicating with a Vim server, much like another running instance of Vim.

VimClient.send_keys is like using remote_send(), and VimClient.send_expr is like remote_expr().

This lets you avoid having to shell-out to Vim for each command you need to send (using --remote-send and --remote-expr).

VimClient.send_keys2 would be like using remote_send() ending with a call to server2client() on the remote, then following that call with remote_read().

This allows you to send keys, then evaluate an expression on the server after those keys are processed and receive the result of that expression. This may also be used to simply block until the sent keys have been processed.

Usage

Install the gem using:

gem install vim_client-ruby

Or, add it to your project's Gemfile.

gem 'vim_client-ruby'

Require VimClient:

require 'vim_client'

Then, be sure to set the name of your Vim server:

VimClient.server_name = 'vim_server1'
VimClient.timeout_sec = 30 # and optionally change the default timeout

If #server_name is not set, an Error will be raised.
Both #server_name and #timeout_sec may be updated at any time.

Character Encodings

Vim's default encoding is latin1 (ISO-8859-1). If compiled with the +multi_byte feature, Vim supports many other encodings.

All returned strings from VimClient.send_expr and VimClient.send_keys2 will be set to the encoding that is set on the Vim server. If the server is using latin1, then strings returned will be marked as ISO-8859-1. If the server is using utf-8, then strings returned will be marked as UTF-8.
No transcoding is performed on the strings returned from the server. The strings are simply marked using the encoding reported by the server.

Vim handles all Unicode strings as UTF-8. This means if the server's encoding is set to any Unicode encoding, the returned string will be in UTF-8. Vim also expects that any command string being sent (as keys or an expr) will be in UTF-8. Therefore, any string you send which is in a Vim supported Unicode encoding will be transcoded to UTF-8 before being sent to the remote server.

For other Vim supported encodings, Vim will be told which of it's supported encodings the sent string is using. If the string you send is using a different encoding than the 'encoding' set on the server, Vim will convert it and the response returned will be in the remote server's encoding.

For a list of Vim's supported encodings and their corresponding Ruby encoding names, see the source in ext/vim_client/misc.c. You can also view the specs to get a better idea of how this works.

Basically, as long as the keys or expr strings you're sending are in an encoding supported by Vim, then you'll have no problems. Just remember that the returned string will be in the server's encoding, and that this will always be UTF-8 for Unicode strings.

Signals and Errors

If SIGINT is received while VimClient is waiting for a response from the server, the wait loop will be aborted, the SIGINT handler that was present when we entered the loop will be restored, and SIGINT will be re-raised to the calling process group. If this signal is ignored, the process will continue and the calling method (send_expr/send_keys2) will return nil.

If timeout_sec expires while waiting for a response, TimeoutError will be raised, which you may rescue.

In either case, be aware that any response received from the remote server for this request will be lost. Also, there is no way of knowing what state the server is in; especially in the case of a timeout. It could have reported an error and be waiting for an acknowledgement. In which case, the next sent command may simply hang as well. In such case, it may be best to simply stop your server and restart it before sending any further commands.

Defined Under Namespace

Classes: Error, Exception, ExprError, NoMemoryError, TimeoutError, XIOError

Constant Summary collapse

VERSION =
'0.1.1'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#server_nameString

Sets the name of the remote Vim server to send messages to.

This may be changed at any time, as this value is read before each call to send_keys, send_keys2 and send_expr.

If no server name is specified, an Error will be raised.

Returns:

  • (String)


# File 'lib/vim_client.rb', line 119

#timeout_secInteger

Sets the timeout in seconds to wait for a response.

This may be changed at any time, as this value is read before each call to send_keys2 and send_expr.

Defaults to 60 seconds if not specified.

Returns:

  • (Integer)


# File 'lib/vim_client.rb', line 129

Class Method Details

.send_expr(expr) ⇒ Object



974
975
976
977
# File 'ext/vim_client/vim_client.c', line 974

static VALUE
send_expr(VALUE self, VALUE expr) {
    return send2vim(expr, asExpr);
}

.send_keys(keys) ⇒ Object



969
970
971
972
# File 'ext/vim_client/vim_client.c', line 969

static VALUE
send_keys(VALUE self, VALUE keys) {
    return send2vim(keys, asKeys);
}

.send_keys2(*args) ⇒ Object



979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
# File 'ext/vim_client/vim_client.c', line 979

static VALUE
send_keys2(int argc, VALUE *argv, VALUE self) {
    VALUE keys, expr;
    rb_scan_args(argc, argv, "11", &keys, &expr);

    /* Create a callback string using the same encoding as keys.
     * Note that concatination must be used, as opposed to printf (%)
     * since we could be dealing with 16/32 bit encodings at this point.
     */
    VALUE callback, close;
    rb_encoding *enc = rb_enc_get(keys);
    callback = rb_str_export_to_enc(rb_str_new2(
            ":call server2client(expand(\"<client>\"), "), enc);
    close = rb_str_export_to_enc(rb_str_new2(")<cr>"), enc);

    if (NIL_P(expr)) /* Return an empty string from the callback */
        expr = rb_str_export_to_enc(rb_str_new2("\"\""), enc);
    else if (enc != rb_enc_get(expr))
        rb_raise(e_Error, "'keys' and 'expr' must use the same encoding");

    /* Insert expr into the callback */
    rb_str_append(callback, expr);
    rb_str_append(callback, close);

    /* Append the callback to the keys.
     * rb_str_plus is used so we don't modify the user's 'keys' string */
    keys = rb_str_plus(keys, callback);

    return send2vim(keys, asKeys2);
}

Instance Method Details

#send_expr(expr) ⇒ String, Nil

Evaluate an expression on the remote Vim server.

This method is analogous to Vim's remote_expr() function.

If the expression results in a List (Array), Vim will return the items separated by newlines using: join(list, "\n")

For example, to evaluate a simple expression:

  ret = VimClient.send_expr('2+2')
  # ret == '4'

To retrieve all lines in the current buffer:

  ret = VimClient.send_expr('getline(1,"$")')
  # ret == "line one\nline two\nline three"

The basic equivalent non-VimClient call for this method would be:

  %x{ gvim --servername server_name --remote-expr 'getline(1,"$")' }

Parameters:

  • expr (String)

    Expression to evaluate on the remote server.

Returns:

  • (String)

    Result of evaluated expression.

  • (Nil)

    If aborted by SIGINT.

Raises:

  • (ExprError)

    if the server reports an error. The error message will contain the error message from the server.

  • (TimeoutError)

    if no response is returned before #timeout_sec expires.



# File 'lib/vim_client.rb', line 262

#send_keys(keys) ⇒ Nil

Send Keys to the remote Vim server.

This method is analogous to Vim's remote_send() function.

The keys string is sent to the remote server as input keys and the method returns immediately. If you need this call to block until the commands are processed, use send_keys2.

Note that if the commands sent result in an error on the server, it will not be reported and may cause subsequent commands to fail.

Use <cr> to insert a carriage return. If omitted, the next call to send_keys will be appended. Each new line in keys should begin with a colon ( : ). Commands may also be separated using a pipe ( | ).

For example, the following two commands are equivalent:

  VimClient.send_keys(":e ++ff=dos #{ some_file } | setlocal ff=unix | w | bd<cr>")

  VimClient.send_keys(":e ++ff=dos #{ some_file }<cr>:setlocal ff=unix<cr>:w | bd<cr>")

These two commands would be equivalent as well:

  VimClient.send_keys(":e ++ff=dos #{ some_file } | setlocal ff=unix") # no `<cr>`
  VimClient.send_keys(" | w | bd<cr>") # appends to the previously sent keys

The basic equivalent non-VimClient call for this method would be:

  system('gvim', '--servername', 'server_name', '--remote-send',
    ":e ++ff=dos #{ some_file } | setlocal ff=unix | w | bd<cr>")

Parameters:

  • keys (String)

    Input keys to send to the remote server.

Returns:

  • (Nil)


# File 'lib/vim_client.rb', line 139

#send_keys2(keys, expr = '""') ⇒ String, Nil

Send Keys to the remote server and wait for a reply.

This method is similar to send_keys, except that it adds a callback to the end of the keys sent, then returns the result of expr.

The keys parameter follows the same rules as send_keys. The expr parameter follows the same rules as send_expr, except that this expression must return a String; whereas send_expr will automatically convert a List (Array) into a String.

For example, the following would be equivalent:

  VimClient.send_expr('setline(1,["line one", "line two"])')
  VimClient.send_expr('getline(1,"$")') # => "line one\nline two"

  VimClient.send_keys2(
    ':call setline(1,["line one", "line two"])<cr>',
    'join(getline(1,"$"), "\\n")'
  ) # => "line one\nline two"

If no expr is given, then the expr will simply return an empty string.

Note: While setline() can be used with send_keys and send_keys2 (i.e. --remote-send) to set the buffer text, any keycodes within these command strings (including within string-literals) will be interpreted.

To understand this better, what this method actually does is append a call to Vim's server2client() function.
It then waits for the reply, like calling remote_read().

  # When you send the following:
  VimClient.send_keys2(":sleep 1<cr>")

  # What actually gets sent is:
  # :sleep 1<cr>server2client(expand("<client>"), "")<cr>

  # When sending:
  VimClient.send_keys2(":sleep 1<cr>", "localtime()")

  # This is sent:
  # :sleep 1<cr>server2client(expand("<client>"), localtime())<cr>

The strings given for keys and expr must be in the same encoding or an Error will be raised.

A call to send_keys2 may be preceded by other calls to send_keys, as keys sent asynchronously and added to the server's typeahead buffer.

As with send_keys, any error reported by the server will not be reported. This includes any error evaluating the expr given to server2client(). However, since this call is waiting for a reply, a TimeoutError would be raised.

The basic equivalent non-VimClient call for this method would be:

  response = %x{
    gvim -fes -u NONE +'call remote_send("server_name",  \
    '\\'':sleep 1 | call server2client(expand("<client>"), localtime())<cr>'\\'', "sid")'  \
    +'redir >> /dev/stdout | echo remote_read(sid) | redir END | q'
  }

Parameters:

  • keys (String)

    Input keys to send to the remote server.

  • expr (String) (defaults to: '""')

    Expression to evaluate on the remote server after the given keys are processed.

Returns:

  • (String)

    Result of evaluated expression.

  • (Nil)

    If aborted by SIGINT.

Raises:



# File 'lib/vim_client.rb', line 183