vim_client-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|