vim_client-ruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/.yardopts +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +33 -0
- data/LICENSE +19 -0
- data/README.md +50 -0
- data/Rakefile +12 -0
- data/ext/vim_client/extconf.rb +20 -0
- data/ext/vim_client/misc.c +267 -0
- data/ext/vim_client/vim_client.c +1039 -0
- data/ext/vim_client/vim_client.h +82 -0
- data/lib/vim_client/version.rb +3 -0
- data/lib/vim_client.rb +300 -0
- data/spec/vim_client_spec.rb +296 -0
- data/vim_client-ruby.gemspec +22 -0
- metadata +144 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
#ifndef VIM_CLIENT_H
|
2
|
+
#define VIM_CLIENT_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <ruby/encoding.h>
|
6
|
+
|
7
|
+
#ifdef HAVE_SYS_SELECT_H
|
8
|
+
# include <sys/select.h>
|
9
|
+
#else
|
10
|
+
# include <sys/poll.h>
|
11
|
+
struct pollfd; /* for poll __ARGS */
|
12
|
+
extern int poll __ARGS((struct pollfd *, long, int));
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#include <signal.h>
|
16
|
+
#include <X11/Intrinsic.h>
|
17
|
+
#include <X11/Xatom.h>
|
18
|
+
|
19
|
+
/* VimClient::Exception < Exception
|
20
|
+
* VimClient::NoMemoryError ex_NoMemoryError
|
21
|
+
* VimClient::XIOError ex_XIOError
|
22
|
+
* VimClient::Error < StandardError e_Error
|
23
|
+
* VimClient::TimeoutError e_TimeoutError
|
24
|
+
* VimClient::ExprError e_ExprError
|
25
|
+
*/
|
26
|
+
VALUE ex_NoMemoryError, ex_XIOError, e_Error, e_TimeoutError, e_ExprError;
|
27
|
+
|
28
|
+
/*
|
29
|
+
* Shorthand for unsigned variables. Many systems, but not all, have u_char
|
30
|
+
* already defined, so we use char_u to avoid trouble.
|
31
|
+
*/
|
32
|
+
typedef unsigned char char_u;
|
33
|
+
typedef unsigned int int_u;
|
34
|
+
typedef unsigned long long_u;
|
35
|
+
|
36
|
+
#ifndef __ARGS
|
37
|
+
/* The AIX VisualAge cc compiler defines __EXTENDED__ instead of __STDC__
|
38
|
+
* because it includes pre-ansi features. */
|
39
|
+
# if defined(__STDC__) || defined(__GNUC__) || defined(__EXTENDED__)
|
40
|
+
# define __ARGS(x) x
|
41
|
+
# else
|
42
|
+
# define __ARGS(x) ()
|
43
|
+
# endif
|
44
|
+
#endif
|
45
|
+
|
46
|
+
#define OK 1
|
47
|
+
#define FAIL 0
|
48
|
+
#define NUL '\000'
|
49
|
+
/* Used for setting the vimProperty on the commWindow */
|
50
|
+
#define VIM_VERSION_SHORT "7.3"
|
51
|
+
|
52
|
+
#define STRLEN(s) strlen((char *)(s))
|
53
|
+
#define STRCMP(d, s) strcmp((char *)(d), (char *)(s))
|
54
|
+
|
55
|
+
#ifdef HAVE_STRCASECMP
|
56
|
+
# define STRICMP(d, s) strcasecmp((char *)(d), (char *)(s))
|
57
|
+
#else
|
58
|
+
# ifdef HAVE_STRICMP
|
59
|
+
# define STRICMP(d, s) stricmp((char *)(d), (char *)(s))
|
60
|
+
# else
|
61
|
+
int vim_stricmp __ARGS((char *s1, char *s2));
|
62
|
+
# define STRICMP(d, s) vim_stricmp((char *)(d), (char *)(s))
|
63
|
+
# endif
|
64
|
+
#endif
|
65
|
+
#ifdef HAVE_STRNCASECMP
|
66
|
+
# define STRNICMP(d, s, n) strncasecmp((char *)(d), (char *)(s), (size_t)(n))
|
67
|
+
#else
|
68
|
+
# ifdef HAVE_STRNICMP
|
69
|
+
# define STRNICMP(d, s, n) strnicmp((char *)(d), (char *)(s), (size_t)(n))
|
70
|
+
# else
|
71
|
+
int vim_strnicmp __ARGS((char *s1, char *s2, size_t len));
|
72
|
+
# define STRNICMP(d, s, n) vim_strnicmp((char *)(d), (char *)(s), (size_t)(n))
|
73
|
+
# endif
|
74
|
+
#endif
|
75
|
+
|
76
|
+
char_u *vim_strsave __ARGS((char_u *string));
|
77
|
+
char_u *alloc __ARGS((long_u size));
|
78
|
+
void vim_free __ARGS((void *x));
|
79
|
+
VALUE vim2rb_enc_str __ARGS((char_u **res, char_u *server_enc));
|
80
|
+
char_u *rb2vim_enc_str __ARGS((VALUE str, char_u **cmd_enc));
|
81
|
+
|
82
|
+
#endif // VIM_CLIENT_H
|
data/lib/vim_client.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'vim_client/version'
|
2
|
+
require 'vim_client/vim_client.so'
|
3
|
+
|
4
|
+
# This module provides methods for communicating with Vim, using it's
|
5
|
+
# [+clientserver][] feature.
|
6
|
+
#
|
7
|
+
# It provides a persistent connection to the X11 server for communicating with
|
8
|
+
# a Vim server, much like another running instance of Vim.
|
9
|
+
#
|
10
|
+
# {.send\_keys} is like using [remote_send()][], and {.send\_expr} is like
|
11
|
+
# [remote_expr()][].
|
12
|
+
#
|
13
|
+
# This lets you avoid having to shell-out to Vim for each command you need to
|
14
|
+
# send (using `--remote-send` and `--remote-expr`).
|
15
|
+
#
|
16
|
+
# {.send\_keys2} would be like using [remote_send()][] ending with a call to
|
17
|
+
# [server2client()][] on the remote, then following that call with [remote_read()][].
|
18
|
+
#
|
19
|
+
# This allows you to send `keys`, then evaluate an expression on the server
|
20
|
+
# _after_ those keys are processed and receive the result of that expression.
|
21
|
+
# This may also be used to simply block until the sent keys have been processed.
|
22
|
+
#
|
23
|
+
# Usage
|
24
|
+
# -----
|
25
|
+
#
|
26
|
+
# Install the gem using:
|
27
|
+
#
|
28
|
+
# ```
|
29
|
+
# gem install vim_client-ruby
|
30
|
+
# ```
|
31
|
+
#
|
32
|
+
# Or, add it to your project's Gemfile.
|
33
|
+
#
|
34
|
+
# ```ruby
|
35
|
+
# gem 'vim_client-ruby'
|
36
|
+
# ```
|
37
|
+
#
|
38
|
+
# Require VimClient:
|
39
|
+
#
|
40
|
+
# ```ruby
|
41
|
+
# require 'vim_client'
|
42
|
+
# ```
|
43
|
+
#
|
44
|
+
# Then, be sure to set the name of your Vim server:
|
45
|
+
#
|
46
|
+
# ```ruby
|
47
|
+
# VimClient.server_name = 'vim_server1'
|
48
|
+
# VimClient.timeout_sec = 30 # and optionally change the default timeout
|
49
|
+
# ```
|
50
|
+
#
|
51
|
+
# If {.server\_name} is not set, an {Error} will be raised.
|
52
|
+
# Both {.server\_name} and {.timeout\_sec} may be updated at any time.
|
53
|
+
#
|
54
|
+
#
|
55
|
+
# Character Encodings
|
56
|
+
# -------------------
|
57
|
+
#
|
58
|
+
# Vim's default encoding is `latin1 (ISO-8859-1)`. If compiled with the
|
59
|
+
# [+multi_byte][] feature, Vim supports many other [encodings][].
|
60
|
+
#
|
61
|
+
# All returned strings from {.send\_expr} and {.send\_keys2} will be set
|
62
|
+
# to the encoding that is set on the Vim server. If the server is using
|
63
|
+
# `latin1`, then strings returned will be marked as `ISO-8859-1`. If the
|
64
|
+
# server is using `utf-8`, then strings returned will be marked as `UTF-8`.
|
65
|
+
# _No transcoding is performed on the strings returned from the server._
|
66
|
+
# The strings are simply marked using the encoding reported by the server.
|
67
|
+
#
|
68
|
+
# Vim handles all Unicode strings as UTF-8. This means if the server's encoding
|
69
|
+
# is set to any Unicode encoding, the returned string will be in UTF-8. Vim
|
70
|
+
# also expects that any command string being sent (as `keys` or an `expr`) will
|
71
|
+
# be in UTF-8. Therefore, any string you send which is in a Vim supported
|
72
|
+
# Unicode encoding will be transcoded to UTF-8 before being sent to the remote
|
73
|
+
# server.
|
74
|
+
#
|
75
|
+
# For other Vim supported encodings, Vim will be told which of it's supported
|
76
|
+
# encodings the sent string is using. If the string you send is using a
|
77
|
+
# different encoding than the 'encoding' set on the server, Vim will convert
|
78
|
+
# it and the response returned will be in the remote server's encoding.
|
79
|
+
#
|
80
|
+
# For a list of Vim's supported encodings and their corresponding Ruby
|
81
|
+
# encoding names, see the source in [ext/vim_client/misc.c][]. You can also
|
82
|
+
# view the specs to get a better idea of how this works.
|
83
|
+
#
|
84
|
+
# Basically, as long as the `keys` or `expr` strings you're sending are in
|
85
|
+
# an encoding supported by Vim, then you'll have no problems. Just remember
|
86
|
+
# that the returned string will be in the server's encoding, and that this
|
87
|
+
# will always be UTF-8 for Unicode strings.
|
88
|
+
#
|
89
|
+
#
|
90
|
+
# Signals and Errors
|
91
|
+
# ------------------
|
92
|
+
#
|
93
|
+
# If SIGINT is received while VimClient is waiting for a response from the server,
|
94
|
+
# the wait loop will be aborted, the SIGINT handler that was present when we
|
95
|
+
# entered the loop will be restored, and SIGINT will be re-raised to the calling
|
96
|
+
# process group. If this signal is ignored, the process will continue and the calling
|
97
|
+
# method `(send_expr/send_keys2)` will return `nil`.
|
98
|
+
#
|
99
|
+
# If `timeout_sec` expires while waiting for a response, {TimeoutError} will be raised,
|
100
|
+
# which you may rescue.
|
101
|
+
#
|
102
|
+
# In either case, be aware that any response received from the remote server for this
|
103
|
+
# request will be lost. Also, there is no way of knowing what state the server is in;
|
104
|
+
# especially in the case of a timeout. It could have reported an error and be waiting
|
105
|
+
# for an acknowledgement. In which case, the next sent command may simply hang as well.
|
106
|
+
# In such case, it may be best to simply stop your server and restart it before sending
|
107
|
+
# any further commands.
|
108
|
+
#
|
109
|
+
# [+clientserver]: http://vimdoc.sourceforge.net/htmldoc/remote.html#clientserver
|
110
|
+
# [remote_send()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#remote_send()
|
111
|
+
# [remote_expr()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#remote_expr()
|
112
|
+
# [server2client()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#server2client()
|
113
|
+
# [remote_read()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#remote_read()
|
114
|
+
# [+multi_byte]: http://vimdoc.sourceforge.net/htmldoc/mbyte.html
|
115
|
+
# [encodings]: http://vimdoc.sourceforge.net/htmldoc/mbyte.html#mbyte-encoding
|
116
|
+
# [ext/vim_client/misc.c]: http://github.com/burns/vim_client-ruby/tree/master/ext/vim_client/misc.c
|
117
|
+
module VimClient
|
118
|
+
class << self
|
119
|
+
# Sets the name of the remote Vim server to send messages to.
|
120
|
+
#
|
121
|
+
# This may be changed at any time, as this value is read before
|
122
|
+
# each call to {.send\_keys}, {.send\_keys2} and {.send\_expr}.
|
123
|
+
#
|
124
|
+
# **If no server name is specified, an Error will be raised.**
|
125
|
+
#
|
126
|
+
# @return [String]
|
127
|
+
# @!attribute [rw] server_name
|
128
|
+
|
129
|
+
# Sets the timeout in seconds to wait for a response.
|
130
|
+
#
|
131
|
+
# This may be changed at any time, as this value is read before
|
132
|
+
# each call to {.send\_keys2} and {.send\_expr}.
|
133
|
+
#
|
134
|
+
# **Defaults to 60 seconds if not specified.**
|
135
|
+
#
|
136
|
+
# @return [Integer]
|
137
|
+
# @!attribute [rw] timeout_sec
|
138
|
+
|
139
|
+
# Send Keys to the remote Vim server.
|
140
|
+
#
|
141
|
+
# This method is analogous to Vim's [remote_send()][] function.
|
142
|
+
#
|
143
|
+
# The `keys` string is sent to the remote server as input keys
|
144
|
+
# and the method returns immediately. If you need this call to block
|
145
|
+
# until the commands are processed, use {.send\_keys2}.
|
146
|
+
#
|
147
|
+
# Note that if the commands sent result in an error on the server,
|
148
|
+
# it will not be reported and may cause subsequent commands to fail.
|
149
|
+
#
|
150
|
+
# Use `<cr>` to insert a carriage return. If omitted, the next call
|
151
|
+
# to `send_keys` will be appended. Each new line in `keys` should
|
152
|
+
# begin with a colon `( : )`. Commands may also be separated using
|
153
|
+
# a pipe `( | )`.
|
154
|
+
#
|
155
|
+
# For example, the following two commands are equivalent:
|
156
|
+
#
|
157
|
+
# ```ruby
|
158
|
+
# VimClient.send_keys(":e ++ff=dos #{ some_file } | setlocal ff=unix | w | bd<cr>")
|
159
|
+
#
|
160
|
+
# VimClient.send_keys(":e ++ff=dos #{ some_file }<cr>:setlocal ff=unix<cr>:w | bd<cr>")
|
161
|
+
# ```
|
162
|
+
#
|
163
|
+
# These two commands would be equivalent as well:
|
164
|
+
#
|
165
|
+
# ```ruby
|
166
|
+
# VimClient.send_keys(":e ++ff=dos #{ some_file } | setlocal ff=unix") # no `<cr>`
|
167
|
+
# VimClient.send_keys(" | w | bd<cr>") # appends to the previously sent keys
|
168
|
+
# ```
|
169
|
+
#
|
170
|
+
# The basic equivalent non-VimClient call for this method would be:
|
171
|
+
#
|
172
|
+
# ```ruby
|
173
|
+
# system('gvim', '--servername', 'server_name', '--remote-send',
|
174
|
+
# ":e ++ff=dos #{ some_file } | setlocal ff=unix | w | bd<cr>")
|
175
|
+
# ```
|
176
|
+
#
|
177
|
+
# [remote_send()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#remote_send()
|
178
|
+
#
|
179
|
+
# @param keys [String] Input keys to send to the remote server.
|
180
|
+
# @return [Nil]
|
181
|
+
# @!method send_keys(keys)
|
182
|
+
|
183
|
+
# Send Keys to the remote server and wait for a reply.
|
184
|
+
#
|
185
|
+
# This method is similar to {.send\_keys}, except that it adds a callback
|
186
|
+
# to the end of the keys sent, then returns the result of `expr`.
|
187
|
+
#
|
188
|
+
# The `keys` parameter follows the same rules as {.send\_keys}.
|
189
|
+
# The `expr` parameter follows the same rules as {.send\_expr}, except
|
190
|
+
# that this expression must return a String; whereas {.send\_expr} will
|
191
|
+
# automatically convert a List (Array) into a String.
|
192
|
+
#
|
193
|
+
# For example, the following would be equivalent:
|
194
|
+
#
|
195
|
+
# ```ruby
|
196
|
+
# VimClient.send_expr('setline(1,["line one", "line two"])')
|
197
|
+
# VimClient.send_expr('getline(1,"$")') # => "line one\nline two"
|
198
|
+
#
|
199
|
+
# VimClient.send_keys2(
|
200
|
+
# ':call setline(1,["line one", "line two"])<cr>',
|
201
|
+
# 'join(getline(1,"$"), "\\n")'
|
202
|
+
# ) # => "line one\nline two"
|
203
|
+
# ```
|
204
|
+
#
|
205
|
+
# If no `expr` is given, then the `expr` will simply return an empty string.
|
206
|
+
#
|
207
|
+
# Note: While `setline()` can be used with {.send\_keys} and {.send\_keys2}
|
208
|
+
# (i.e. `--remote-send`) to set the buffer text, any [keycodes][] within
|
209
|
+
# these command strings (including within string-literals) will be interpreted.
|
210
|
+
#
|
211
|
+
# To understand this better, what this method actually does is append
|
212
|
+
# a call to Vim's [server2client()][] function.
|
213
|
+
# It then waits for the reply, like calling [remote_read()][].
|
214
|
+
#
|
215
|
+
# ```ruby
|
216
|
+
# # When you send the following:
|
217
|
+
# VimClient.send_keys2(":sleep 1<cr>")
|
218
|
+
#
|
219
|
+
# # What actually gets sent is:
|
220
|
+
# # :sleep 1<cr>server2client(expand("<client>"), "")<cr>
|
221
|
+
#
|
222
|
+
# # When sending:
|
223
|
+
# VimClient.send_keys2(":sleep 1<cr>", "localtime()")
|
224
|
+
#
|
225
|
+
# # This is sent:
|
226
|
+
# # :sleep 1<cr>server2client(expand("<client>"), localtime())<cr>
|
227
|
+
# ```
|
228
|
+
#
|
229
|
+
# The strings given for `keys` and `expr` must be in the same encoding
|
230
|
+
# or an {Error} will be raised.
|
231
|
+
#
|
232
|
+
# A call to {.send\_keys2} may be preceded by other calls to {.send\_keys},
|
233
|
+
# as keys sent asynchronously and added to the server's typeahead buffer.
|
234
|
+
#
|
235
|
+
# As with {.send\_keys}, any error reported by the server will not be
|
236
|
+
# reported. This includes any error evaluating the `expr` given to
|
237
|
+
# [server2client()][]. However, since this call is waiting for a reply,
|
238
|
+
# a {TimeoutError} would be raised.
|
239
|
+
#
|
240
|
+
# The basic equivalent non-VimClient call for this method would be:
|
241
|
+
#
|
242
|
+
# ```ruby
|
243
|
+
# response = %x{
|
244
|
+
# gvim -fes -u NONE +'call remote_send("server_name", \
|
245
|
+
# '\\'':sleep 1 | call server2client(expand("<client>"), localtime())<cr>'\\'', "sid")' \
|
246
|
+
# +'redir >> /dev/stdout | echo remote_read(sid) | redir END | q'
|
247
|
+
# }
|
248
|
+
# ```
|
249
|
+
#
|
250
|
+
# [server2client()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#server2client()
|
251
|
+
# [remote_read()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#remote_read()
|
252
|
+
# [keycodes]: http://vimdoc.sourceforge.net/htmldoc/intro.html#keycodes
|
253
|
+
#
|
254
|
+
# @param keys [String] Input keys to send to the remote server.
|
255
|
+
# @param expr [String] Expression to evaluate on the remote server after
|
256
|
+
# the given `keys` are processed.
|
257
|
+
# @return [String] Result of evaluated expression.
|
258
|
+
# @return [Nil] If aborted by SIGINT.
|
259
|
+
# @raise [TimeoutError] if no response is returned before {.timeout\_sec} expires.
|
260
|
+
# @!method send_keys2(keys, expr = '""')
|
261
|
+
|
262
|
+
# Evaluate an expression on the remote Vim server.
|
263
|
+
#
|
264
|
+
# This method is analogous to Vim's [remote_expr()][] function.
|
265
|
+
#
|
266
|
+
# If the expression results in a List (Array), Vim will return the items
|
267
|
+
# separated by newlines using: `join(list, "\n")`
|
268
|
+
#
|
269
|
+
# For example, to evaluate a simple expression:
|
270
|
+
#
|
271
|
+
# ```ruby
|
272
|
+
# ret = VimClient.send_expr('2+2')
|
273
|
+
# # ret == '4'
|
274
|
+
# ```
|
275
|
+
#
|
276
|
+
# To retrieve all lines in the current buffer:
|
277
|
+
#
|
278
|
+
# ```ruby
|
279
|
+
# ret = VimClient.send_expr('getline(1,"$")')
|
280
|
+
# # ret == "line one\nline two\nline three"
|
281
|
+
# ```
|
282
|
+
#
|
283
|
+
# The basic equivalent non-VimClient call for this method would be:
|
284
|
+
#
|
285
|
+
# ```ruby
|
286
|
+
# %x{ gvim --servername server_name --remote-expr 'getline(1,"$")' }
|
287
|
+
# ```
|
288
|
+
#
|
289
|
+
# [remote_expr()]: http://vimdoc.sourceforge.net/htmldoc/eval.html#remote_expr()
|
290
|
+
#
|
291
|
+
# @param expr [String] Expression to evaluate on the remote server.
|
292
|
+
# @return [String] Result of evaluated expression.
|
293
|
+
# @return [Nil] If aborted by SIGINT.
|
294
|
+
# @raise [ExprError] if the server reports an error. The error message
|
295
|
+
# will contain the error message from the server.
|
296
|
+
# @raise [TimeoutError] if no response is returned before
|
297
|
+
# {.timeout\_sec} expires.
|
298
|
+
# @!method send_expr(expr)
|
299
|
+
end
|
300
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rspec/autorun'
|
4
|
+
require 'vim_client'
|
5
|
+
|
6
|
+
module VimServer
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# This will run the server in the foreground and leave it open.
|
10
|
+
# Only good for checking a specific test, as it will only allow the
|
11
|
+
# server to be started once. This is a good thing :)
|
12
|
+
# e.g. change test to: it 'debug' do
|
13
|
+
# then use: rspec spec/ --example debug
|
14
|
+
DEBUG = false
|
15
|
+
|
16
|
+
# Place the server in 'visual' mode.
|
17
|
+
# Note that this does not bring the server to the foreground.
|
18
|
+
# If you do use visual mode, also note that each new line of keys
|
19
|
+
# must be prefixed with a colon (:). Using Ex mode (-e or -E),
|
20
|
+
# this will cause lines to begin with two colons (::). However,
|
21
|
+
# this has no ill effects. So, it's best to use them so that
|
22
|
+
# switching between modes is possible.
|
23
|
+
VISUAL = false
|
24
|
+
|
25
|
+
def running
|
26
|
+
!!(/#{ server_name }/im =~ `gvim --serverlist`)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Each test starts and stops a separate server.
|
30
|
+
# Cycling this quickly with the same server name does not work well,
|
31
|
+
# so each will be assigned it's own serial number.
|
32
|
+
def server_name(update = false)
|
33
|
+
@serial = @serial.to_i + 1 if update
|
34
|
+
"vim_server#{ @serial }"
|
35
|
+
end
|
36
|
+
|
37
|
+
def start
|
38
|
+
return if @pid
|
39
|
+
|
40
|
+
opts = DEBUG ? '-nfE' : '-nfEs'
|
41
|
+
@pid = spawn(
|
42
|
+
*%W{ gvim #{ opts } -u NONE --servername #{ server_name(true) } },
|
43
|
+
[:out, :err] => '/dev/null'
|
44
|
+
)
|
45
|
+
@thr = Process.detach(@pid)
|
46
|
+
|
47
|
+
t = 0; until running; t += 1; sleep 0.1
|
48
|
+
abort "VimServer #{ server_name } failed to start" if t > 30; end
|
49
|
+
|
50
|
+
%x{ gvim --servername #{ server_name } \
|
51
|
+
--remote-expr 'feedkeys(":visual\\<cr>")' } if VISUAL
|
52
|
+
|
53
|
+
VimClient.server_name = server_name
|
54
|
+
VimClient.timeout_sec = 10
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
return if (DEBUG || !@pid)
|
59
|
+
|
60
|
+
if running
|
61
|
+
system(*%W{ gvim --servername #{ server_name }
|
62
|
+
--remote-send :q!<cr> })
|
63
|
+
t = 0; until !running; t += 1; sleep 0.1; break if t > 30; end
|
64
|
+
end
|
65
|
+
|
66
|
+
if @thr.alive?
|
67
|
+
warn 'Killing Server!'
|
68
|
+
Process.kill(:TERM, @pid, Process::WNOHANG) rescue nil
|
69
|
+
end
|
70
|
+
|
71
|
+
@pid = nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'VimClient' do
|
77
|
+
before(:each) { VimServer.start }
|
78
|
+
after(:each) { VimServer.stop }
|
79
|
+
|
80
|
+
describe '.send_keys' do
|
81
|
+
|
82
|
+
it 'should send keys' do
|
83
|
+
VimClient.send_keys(':q<cr>')
|
84
|
+
|
85
|
+
t = 0; until !VimServer.running
|
86
|
+
t += 1; sleep 0.1; break if t > 30; end
|
87
|
+
VimServer.running.should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should raise an error if server_name is not set' do
|
91
|
+
VimClient.server_name = nil
|
92
|
+
expect do
|
93
|
+
VimClient.send_keys(':foo<cr>')
|
94
|
+
end.to raise_error(VimClient::Error, 'server_name must be set')
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should raise an error if server not found' do
|
98
|
+
VimClient.server_name = 'unknown_server'
|
99
|
+
expect do
|
100
|
+
VimClient.send_keys(':foo<cr>')
|
101
|
+
end.to raise_error(
|
102
|
+
VimClient::Error,
|
103
|
+
"No registered Vim server found for 'unknown_server'"
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'will always return nil' do
|
108
|
+
VimClient.send_keys(':echo "foo"<cr>').should be_nil
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'will not report invalid commands' do
|
112
|
+
VimClient.send_keys(':foo<cr>').should be_nil
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'will not block' do
|
116
|
+
start = Time.now
|
117
|
+
VimClient.send_keys(':sleep 1<cr>')
|
118
|
+
(Time.now - start).should be < 1
|
119
|
+
end
|
120
|
+
|
121
|
+
end # describe '.send_keys'
|
122
|
+
|
123
|
+
describe '.send_expr' do
|
124
|
+
|
125
|
+
it 'should send an expression' do
|
126
|
+
VimClient.send_expr('2+2').should == '4'
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should raise an error if server_name is not set' do
|
130
|
+
VimClient.server_name = nil
|
131
|
+
expect do
|
132
|
+
VimClient.send_expr('2+2')
|
133
|
+
end.to raise_error(VimClient::Error, 'server_name must be set')
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should raise an error if server not found' do
|
137
|
+
VimClient.server_name = 'unknown_server'
|
138
|
+
expect do
|
139
|
+
VimClient.send_expr('2+2')
|
140
|
+
end.to raise_error(
|
141
|
+
VimClient::Error,
|
142
|
+
"No registered Vim server found for 'unknown_server'"
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should raise an error for invalid expressions' do
|
147
|
+
expect do
|
148
|
+
VimClient.send_expr('foo')
|
149
|
+
end.to raise_error(VimClient::ExprError)
|
150
|
+
end
|
151
|
+
|
152
|
+
end # describe '.send_expr'
|
153
|
+
|
154
|
+
describe '.send_keys2' do
|
155
|
+
|
156
|
+
it 'should wait for a response, using default callback' do
|
157
|
+
start = Time.now
|
158
|
+
ret = VimClient.send_keys2(':sleep 1<cr>')
|
159
|
+
(Time.now - start).should be > 1
|
160
|
+
# default callback returns an empty string
|
161
|
+
ret.should == ''
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should wait for the callback response' do
|
165
|
+
VimClient.send_keys2(
|
166
|
+
':call setline(1,"foo")<cr>', 'getline(1)'
|
167
|
+
).should == 'foo'
|
168
|
+
end
|
169
|
+
|
170
|
+
# The timeout here is due to an error running server2client()
|
171
|
+
# on the server (E730: Using List as a String)
|
172
|
+
# Just like send_keys(), errors on the server are not returned.
|
173
|
+
it 'requires callback expression to return a string' do
|
174
|
+
expect do
|
175
|
+
VimClient.send_keys2(
|
176
|
+
':call setline(1,["foo","bar"])<cr>', 'getline(1,"$")')
|
177
|
+
end.to raise_error(VimClient::TimeoutError)
|
178
|
+
|
179
|
+
VimClient.send_keys2(
|
180
|
+
':call setline(1,["foo","bar"])<cr>', 'join(getline(1,"$"),"\n")'
|
181
|
+
).should == "foo\nbar"
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should raise an error if keys and expr use different encodings' do
|
185
|
+
expect do
|
186
|
+
VimClient.send_keys2(
|
187
|
+
':echo "foo"', 'localtime()'.encode('ISO-8859-1'))
|
188
|
+
end.to raise_error(
|
189
|
+
VimClient::Error, "'keys' and 'expr' must use the same encoding"
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
end # describe '.send_keys2'
|
194
|
+
|
195
|
+
describe 'character encoding',
|
196
|
+
:unless => `gvim --version | grep '+multi_byte'`.empty? do
|
197
|
+
|
198
|
+
it 'should mark all Vim Unicode responses as UTF-8' do
|
199
|
+
%w{ ucs-2 ucs-2le utf-16 utf-16le ucs-4 ucs-4le }.each do |enc_name|
|
200
|
+
VimClient.send_keys2(":set enc=#{ enc_name }<cr>")
|
201
|
+
ret = VimClient.send_expr('&encoding')
|
202
|
+
# the server will report the encoding that was set
|
203
|
+
ret.should == enc_name
|
204
|
+
# however the response is actually in UTF-8, and we mark it as such.
|
205
|
+
ret.encoding.name.should == 'UTF-8'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# The following tests will fail due to the returned text including
|
210
|
+
# invalid UTF-8 if the server is running in standard Ex mode.
|
211
|
+
# When using send_keys/send_keys2 (i.e. --remote-send) along with
|
212
|
+
# setline(), the ü gets converted to <fc>. This is not the case if
|
213
|
+
# send_expr()/--remote-expr is used.
|
214
|
+
#
|
215
|
+
# There are two ways this can be solved:
|
216
|
+
# 1. Run the server in Improved Ex mode (-E).
|
217
|
+
# 2. Run the server in Ex mode (-e), then switch to 'visual' mode.
|
218
|
+
|
219
|
+
# Vim treats all Unicode encodings as UTF-8, so any commands sent in these
|
220
|
+
# encodings are converted to UTF-8 before being sent to the server.
|
221
|
+
it 'should convert non-UTF-8 Unicode commands to UTF-8' do
|
222
|
+
utf8 = "\u00FC" # ü
|
223
|
+
utf8_expr = "setline(1,\"#{ utf8 }\")"
|
224
|
+
utf8_keys = ":call setline(1,\"#{ utf8 }\")<cr>"
|
225
|
+
utf8_callback = 'getline(1)'
|
226
|
+
|
227
|
+
VimClient.send_keys2(":set enc=utf-8<cr>")
|
228
|
+
|
229
|
+
%w{ UTF-16BE UTF-16LE UTF-32LE UTF-32BE }.each do |enc|
|
230
|
+
VimClient.send_expr(utf8_expr.encode(enc))
|
231
|
+
VimClient.send_expr(utf8_callback).should == utf8
|
232
|
+
|
233
|
+
VimClient.send_keys2(
|
234
|
+
utf8_keys.encode(enc), utf8_callback.encode(enc)
|
235
|
+
).should == utf8
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should handle encoding conversions' do
|
240
|
+
[ # Ruby Encoding Vim Encoding UTF-8 Character Encoded
|
241
|
+
['ISO-8859-1', 'latin1', "\u00FC"], # ü "\xFC"
|
242
|
+
['ISO-8859-15', 'iso-8859-15', "\u20AC"], # € "\xA4"
|
243
|
+
['IBM437', 'cp437', "\u03B4"], # δ "\xEB"
|
244
|
+
['Shift_JIS', 'sjis', "\u3042"], # あ "\x{82A0}"
|
245
|
+
['EUC-JP', 'euc-jp', "\u3042"] # あ "\x{A4A2}"
|
246
|
+
].each do |enc, vim_enc, utf8|
|
247
|
+
utf8_expr = "setline(1,\"#{ utf8 }\")"
|
248
|
+
utf8_keys = ":call setline(1,\"#{ utf8 }\")<cr>"
|
249
|
+
utf8_callback = 'getline(1)'
|
250
|
+
|
251
|
+
##
|
252
|
+
# Set Vim server encoding to UTF-8
|
253
|
+
#
|
254
|
+
VimClient.send_keys2(':set enc=utf-8<cr>')
|
255
|
+
ret = VimClient.send_expr('&encoding')
|
256
|
+
# response is in UTF-8
|
257
|
+
ret.encoding.name.should == 'UTF-8'
|
258
|
+
# server's encoding was set
|
259
|
+
ret.should == 'utf-8'
|
260
|
+
|
261
|
+
##
|
262
|
+
# Commands are sent in the tested encoding.
|
263
|
+
# Results received as UTF-8
|
264
|
+
#
|
265
|
+
VimClient.send_expr(utf8_expr.encode(enc))
|
266
|
+
VimClient.send_expr(utf8_callback).should == utf8
|
267
|
+
|
268
|
+
VimClient.send_keys2(
|
269
|
+
utf8_keys.encode(enc), utf8_callback.encode(enc)
|
270
|
+
).should == utf8
|
271
|
+
|
272
|
+
##
|
273
|
+
# Set Vim server encoding to the tested encoding.
|
274
|
+
#
|
275
|
+
VimClient.send_keys2(":set enc=#{ vim_enc }<cr>")
|
276
|
+
ret = VimClient.send_expr('&encoding')
|
277
|
+
# response is in the tested encoding
|
278
|
+
ret.encoding.name.should == enc
|
279
|
+
# server's encoding was set
|
280
|
+
ret.should == vim_enc
|
281
|
+
|
282
|
+
##
|
283
|
+
# Commands are sent as UTF-8
|
284
|
+
# Results received in the tested encoding
|
285
|
+
VimClient.send_expr(utf8_expr)
|
286
|
+
VimClient.send_expr(utf8_callback).should == utf8.encode(enc)
|
287
|
+
|
288
|
+
VimClient.send_keys2(
|
289
|
+
utf8_keys, utf8_callback
|
290
|
+
).should == utf8.encode(enc)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
end # describe 'character encoding'
|
295
|
+
|
296
|
+
end
|