mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-07 15:37:14 +02:00
Merge remote-tracking branch 'vim/master'
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user