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.
@@ -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