vim_client-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,3 @@
1
+ module VimClient
2
+ VERSION = '0.1.0'
3
+ end
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