Merge remote-tracking branch 'vim/master'

This commit is contained in:
Kazuki Sakamoto
2016-02-03 19:16:19 -08:00
6 changed files with 207 additions and 65 deletions
+1
View File
@@ -91,6 +91,7 @@ SRC_ALL = \
src/testdir/README.txt \
src/testdir/Make_all.mak \
src/testdir/*.in \
src/testdir/*.py \
src/testdir/sautest/autoload/*.vim \
src/testdir/runtest.vim \
src/testdir/test[0-9]*.ok \
+2
View File
@@ -2029,6 +2029,7 @@ test_arglist \
test_assert \
test_backspace_opt \
test_cdo \
test_channel \
test_cursor_func \
test_delete \
test_expand \
@@ -2569,6 +2570,7 @@ shadow: runtime pixmaps
../../testdir/Make_all.mak \
../../testdir/*.in \
../../testdir/*.vim \
../../testdir/*.py \
../../testdir/python* \
../../testdir/sautest \
../../testdir/test83-tags? \
+23 -14
View File
@@ -136,22 +136,25 @@ FILE *debugfd = NULL;
add_channel(void)
{
int idx;
channel_T *new_channels;
channel_T *ch;
if (channels != NULL)
{
for (idx = 0; idx < channel_count; ++idx)
if (channels[idx].ch_fd < 0)
/* re-use a closed channel slot */
return idx;
if (channel_count == MAX_OPEN_CHANNELS)
return -1;
new_channels = (channel_T *)alloc(sizeof(channel_T) * (channel_count + 1));
if (new_channels == NULL)
return -1;
if (channels != NULL)
mch_memmove(new_channels, channels, sizeof(channel_T) * channel_count);
channels = new_channels;
if (channel_count == MAX_OPEN_CHANNELS)
return -1;
}
else
{
channels = (channel_T *)alloc((int)sizeof(channel_T)
* MAX_OPEN_CHANNELS);
if (channels == NULL)
return -1;
}
ch = &channels[channel_count];
(void)vim_memset(ch, 0, sizeof(channel_T));
@@ -716,17 +719,21 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
{
int is_eval = cmd[1] == 'v';
if (is_eval && arg3->v_type != VAR_NUMBER)
if (is_eval && (arg3 == NULL || arg3->v_type != VAR_NUMBER))
{
if (p_verbose > 2)
EMSG("E904: third argument for eval must be a number");
}
else
{
typval_T *tv = eval_expr(arg, NULL);
typval_T *tv;
typval_T err_tv;
char_u *json;
/* Don't pollute the display with errors. */
++emsg_skip;
tv = eval_expr(arg, NULL);
--emsg_skip;
if (is_eval)
{
if (tv == NULL)
@@ -739,7 +746,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
channel_send(idx, json, "eval");
vim_free(json);
}
free_tv(tv);
if (tv != &err_tv)
free_tv(tv);
}
}
else if (p_verbose > 2)
@@ -791,7 +799,7 @@ may_invoke_callback(int idx)
typval_T *arg3 = NULL;
char_u *cmd = typetv->vval.v_string;
/* ["cmd", arg] */
/* ["cmd", arg] or ["cmd", arg, arg] */
if (list->lv_len == 3)
arg3 = &list->lv_last->li_tv;
channel_exe_cmd(idx, cmd, &argv[1], arg3);
@@ -1144,7 +1152,8 @@ channel_read_json_block(int ch_idx, int id, typval_T **rettv)
/* Wait for up to 2 seconds.
* TODO: use timeout set on the channel. */
if (channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL)
if (channels[ch_idx].ch_fd < 0
|| channel_wait(channels[ch_idx].ch_fd, 2000) == FAIL)
break;
channel_read(ch_idx);
}
Executable → Regular
+74 -43
View File
@@ -7,12 +7,6 @@
# Then Vim can send requests to the server:
# :let response = ch_sendexpr(handle, 'hello!')
#
# And you can control Vim by typing a JSON message here, e.g.:
# ["ex","echo 'hi there'"]
#
# There is no prompt, just type a line and press Enter.
# To exit cleanly type "quit<Enter>".
#
# See ":help channel-demo" in Vim.
#
# This requires Python 2.6 or later.
@@ -30,58 +24,95 @@ except ImportError:
# Python 2
import SocketServer as socketserver
thesocket = None
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
print("=== socket opened ===")
global thesocket
thesocket = self.request
while True:
try:
data = self.request.recv(4096).decode('utf-8')
received = self.request.recv(4096).decode('utf-8')
except socket.error:
print("=== socket error ===")
break
except IOError:
print("=== socket closed ===")
break
if data == '':
if received == '':
print("=== socket closed ===")
break
print("received: {}".format(data))
try:
decoded = json.loads(data)
except ValueError:
print("json decoding failed")
decoded = [-1, '']
print("received: {}".format(received))
# Send a response if the sequence number is positive.
# Negative numbers are used for "eval" responses.
if decoded[0] >= 0:
if decoded[1] == 'hello!':
# simply send back a string
response = "got it"
elif decoded[1] == 'make change':
# Send two ex commands at the same time, before replying to
# the request.
cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
print("sending: {}".format(cmd))
thesocket.sendall(cmd.encode('utf-8'))
response = "ok"
elif decoded[1] == '!quit!':
# we're done
sys.exit(0)
# We may receive two messages at once. Take the part up to the
# matching "]" (recognized by finding "][").
todo = received
while todo != '':
splitidx = todo.find('][')
if splitidx < 0:
used = todo
todo = ''
else:
response = "what?"
used = todo[:splitidx + 1]
todo = todo[splitidx + 1:]
if used != received:
print("using: {}".format(used))
encoded = json.dumps([decoded[0], response])
print("sending: {}".format(encoded))
thesocket.sendall(encoded.encode('utf-8'))
try:
decoded = json.loads(used)
except ValueError:
print("json decoding failed")
decoded = [-1, '']
thesocket = None
# Send a response if the sequence number is positive.
if decoded[0] >= 0:
if decoded[1] == 'hello!':
# simply send back a string
response = "got it"
elif decoded[1] == 'make change':
# Send two ex commands at the same time, before
# replying to the request.
cmd = '["ex","call append(\\"$\\",\\"added1\\")"]'
cmd += '["ex","call append(\\"$\\",\\"added2\\")"]'
print("sending: {}".format(cmd))
self.request.sendall(cmd.encode('utf-8'))
response = "ok"
elif decoded[1] == 'eval-works':
# Send an eval request. We ignore the response.
cmd = '["eval","\\"foo\\" . 123", -1]'
print("sending: {}".format(cmd))
self.request.sendall(cmd.encode('utf-8'))
response = "ok"
elif decoded[1] == 'eval-fails':
# Send an eval request that will fail.
cmd = '["eval","xxx", -2]'
print("sending: {}".format(cmd))
self.request.sendall(cmd.encode('utf-8'))
response = "ok"
elif decoded[1] == 'eval-bad':
# Send an eval request missing the third argument.
cmd = '["eval","xxx"]'
print("sending: {}".format(cmd))
self.request.sendall(cmd.encode('utf-8'))
response = "ok"
elif decoded[1] == 'eval-result':
# Send back the last received eval result.
response = last_eval
elif decoded[1] == '!quit!':
# we're done
self.server.shutdown()
break
elif decoded[1] == '!crash!':
# Crash!
42 / 0
else:
response = "what?"
encoded = json.dumps([decoded[0], response])
print("sending: {}".format(encoded))
self.request.sendall(encoded.encode('utf-8'))
# Negative numbers are used for "eval" responses.
elif decoded[0] < 0:
last_eval = decoded
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
@@ -97,7 +128,6 @@ if __name__ == "__main__":
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
# Write the port number in Xportnr, so that the test knows it.
@@ -105,6 +135,7 @@ if __name__ == "__main__":
f.write("{}".format(port))
f.close()
# Block here
print("Listening on port {}".format(port))
server.serve_forever()
# Main thread terminates, but the server continues running
# until server.shutdown() is called.
+87 -8
View File
@@ -2,14 +2,32 @@
scriptencoding utf-8
" This requires the Python command to run the test server.
" This most likely only works on Unix.
if !has('unix') || !executable('python')
" This most likely only works on Unix and Windows console.
if has('unix')
" We also need the pkill command to make sure the server can be stopped.
if !executable('python') || !executable('pkill')
finish
endif
elseif has('win32') && !has('gui_win32')
" Use Python Launcher for Windows (py.exe).
if !executable('py')
finish
endif
else
finish
endif
func Test_communicate()
let s:port = -1
func s:start_server()
" The Python program writes the port number in Xportnr.
silent !./test_channel.py&
call delete("Xportnr")
if has('win32')
silent !start cmd /c start "test_channel" py test_channel.py
else
silent !python test_channel.py&
endif
" Wait for up to 2 seconds for the port number to be there.
let cnt = 20
@@ -29,11 +47,29 @@ func Test_communicate()
if len(l) == 0
" Can't make the connection, give up.
call system("killall test_channel.py")
call s:kill_server()
call assert_false(1, "Can't start test_channel.py")
return -1
endif
let s:port = l[0]
let handle = ch_open('localhost:' . s:port, 'json')
return handle
endfunc
func s:kill_server()
if has('win32')
call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
else
call system("pkill -f test_channel.py")
endif
endfunc
func Test_communicate()
let handle = s:start_server()
if handle < 0
return
endif
let port = l[0]
let handle = ch_open('localhost:' . port, 'json')
" Simple string request and reply.
call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
@@ -46,8 +82,51 @@ func Test_communicate()
call assert_equal('added1', getline(line('$') - 1))
call assert_equal('added2', getline('$'))
" Send an eval request that works.
call assert_equal('ok', ch_sendexpr(handle, 'eval-works'))
call assert_equal([-1, 'foo123'], ch_sendexpr(handle, 'eval-result'))
" Send an eval request that fails.
call assert_equal('ok', ch_sendexpr(handle, 'eval-fails'))
call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result'))
" Send a bad eval request. There will be no response.
call assert_equal('ok', ch_sendexpr(handle, 'eval-bad'))
call assert_equal([-2, 'ERROR'], ch_sendexpr(handle, 'eval-result'))
" make the server quit, can't check if this works, should not hang.
call ch_sendexpr(handle, '!quit!', 0)
call system("killall test_channel.py")
call s:kill_server()
endfunc
" Test that we can open two channels.
func Test_two_channels()
let handle = s:start_server()
if handle < 0
return
endif
call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
let newhandle = ch_open('localhost:' . s:port, 'json')
call assert_equal('got it', ch_sendexpr(newhandle, 'hello!'))
call assert_equal('got it', ch_sendexpr(handle, 'hello!'))
call ch_close(handle)
call assert_equal('got it', ch_sendexpr(newhandle, 'hello!'))
call s:kill_server()
endfunc
" Test that a server crash is handled gracefully.
func Test_server_crash()
let handle = s:start_server()
if handle < 0
return
endif
call ch_sendexpr(handle, '!crash!')
" kill the server in case if failed to crash
sleep 10m
call s:kill_server()
endfunc
+20
View File
@@ -757,6 +757,26 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1256,
/**/
1255,
/**/
1254,
/**/
1253,
/**/
1252,
/**/
1251,
/**/
1250,
/**/
1249,
/**/
1248,
/**/
1247,
/**/
1246,
/**/