mirror of
https://github.com/vim/vim.git
synced 2025-09-27 04:14:06 -04:00
patch 9.0.1924: LSP server message still wrongly handled (after 9.0.1922)
Problem: LSP server message still wrongly handled (after 9.0.1922) Solution: Handle 'method' messages properly, don't discard them, add tests. closes: #13141 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
committed by
Christian Brabandt
parent
db54e989b5
commit
1926ae4184
@@ -2466,6 +2466,12 @@ channel_get_json(
|
||||
d = item->jq_value->vval.v_dict;
|
||||
if (d == NULL)
|
||||
goto nextitem;
|
||||
// When looking for a response message from the LSP server,
|
||||
// ignore new LSP request and notification messages. LSP
|
||||
// request and notification messages have the "method" field in
|
||||
// the header and the response messages do not have this field.
|
||||
if (dict_has_key(d, "method"))
|
||||
goto nextitem;
|
||||
di = dict_find(d, (char_u *)"id", -1);
|
||||
if (di == NULL)
|
||||
goto nextitem;
|
||||
@@ -2927,16 +2933,9 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
|
||||
seq_nr = 0;
|
||||
if (d != NULL)
|
||||
{
|
||||
// When looking for a response message from the LSP server,
|
||||
// ignore new LSP request and notification messages. LSP
|
||||
// request and notification messages have the "method" field in
|
||||
// the header and the response messages do not have this field.
|
||||
if (!dict_has_key(d, "method"))
|
||||
{
|
||||
di = dict_find(d, (char_u *)"id", -1);
|
||||
if (di != NULL && di->di_tv.v_type == VAR_NUMBER)
|
||||
seq_nr = di->di_tv.vval.v_number;
|
||||
}
|
||||
di = dict_find(d, (char_u *)"id", -1);
|
||||
if (di != NULL && di->di_tv.v_type == VAR_NUMBER)
|
||||
seq_nr = di->di_tv.vval.v_number;
|
||||
}
|
||||
|
||||
argv[1] = *listtv;
|
||||
|
@@ -1858,7 +1858,7 @@ did_set_isopt(optset_T *args)
|
||||
* The 'jumpoptions' option is changed.
|
||||
*/
|
||||
char *
|
||||
did_set_jumpoptions(optset_T *args)
|
||||
did_set_jumpoptions(optset_T *args UNUSED)
|
||||
{
|
||||
if (opt_strings_flags(p_jop, p_jop_values, &jop_flags, TRUE) != OK)
|
||||
return e_invalid_argument;
|
||||
|
@@ -2481,6 +2481,14 @@ endfunc
|
||||
" Test for the 'lsp' channel mode
|
||||
func LspCb(chan, msg)
|
||||
call add(g:lspNotif, a:msg)
|
||||
if a:msg->has_key('method')
|
||||
" Requests received from the LSP server
|
||||
if a:msg['method'] == 'server-req-in-middle'
|
||||
\ && a:msg['params']['text'] == 'server-req'
|
||||
call ch_sendexpr(a:chan, #{method: 'server-req-in-middle-resp',
|
||||
\ id: a:msg['id'], params: #{text: 'client-resp'}})
|
||||
endif
|
||||
endif
|
||||
endfunc
|
||||
|
||||
func LspOtCb(chan, msg)
|
||||
@@ -2652,6 +2660,19 @@ func LspTests(port)
|
||||
" send a ping to make sure communication still works
|
||||
call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
|
||||
|
||||
" Test for processing a request message from the server while the client
|
||||
" is waiting for a response with the same identifier.
|
||||
let g:lspNotif = []
|
||||
let resp = ch_evalexpr(ch, #{method: 'server-req-in-middle',
|
||||
\ params: #{text: 'client-req'}})
|
||||
call assert_equal(#{jsonrpc: '2.0', id: 28,
|
||||
\ result: #{text: 'server-resp'}}, resp)
|
||||
call assert_equal([
|
||||
\ #{id: -1, jsonrpc: '2.0', method: 'server-req-in-middle',
|
||||
\ params: #{text: 'server-notif'}},
|
||||
\ #{id: 28, jsonrpc: '2.0', method: 'server-req-in-middle',
|
||||
\ params: #{text: 'server-req'}}], g:lspNotif)
|
||||
|
||||
" Test for invoking an unsupported method
|
||||
let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200})
|
||||
call assert_equal({}, resp)
|
||||
|
@@ -29,7 +29,20 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
with open("Xlspserver.log", "a") as myfile:
|
||||
myfile.write(msg)
|
||||
|
||||
def send_lsp_msg(self, msgid, resp_dict):
|
||||
def send_lsp_req(self, msgid, method, params):
|
||||
v = {'jsonrpc': '2.0', 'id': msgid, 'method': method}
|
||||
if len(params) != 0:
|
||||
v['params'] = params
|
||||
s = json.dumps(v)
|
||||
req = "Content-Length: " + str(len(s)) + "\r\n"
|
||||
req += "Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n"
|
||||
req += "\r\n"
|
||||
req += s
|
||||
if self.debug:
|
||||
self.debuglog("SEND: ({0} bytes) '{1}'\n".format(len(req), req))
|
||||
self.request.sendall(req.encode('utf-8'))
|
||||
|
||||
def send_lsp_resp(self, msgid, resp_dict):
|
||||
v = {'jsonrpc': '2.0', 'result': resp_dict}
|
||||
if msgid != -1:
|
||||
v['id'] = msgid
|
||||
@@ -118,56 +131,56 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
def do_ping(self, payload):
|
||||
time.sleep(0.2)
|
||||
self.send_lsp_msg(payload['id'], 'alive')
|
||||
self.send_lsp_resp(payload['id'], 'alive')
|
||||
|
||||
def do_echo(self, payload):
|
||||
self.send_lsp_msg(-1, payload)
|
||||
self.send_lsp_resp(-1, payload)
|
||||
|
||||
def do_simple_rpc(self, payload):
|
||||
# test for a simple RPC request
|
||||
self.send_lsp_msg(payload['id'], 'simple-rpc')
|
||||
self.send_lsp_resp(payload['id'], 'simple-rpc')
|
||||
|
||||
def do_rpc_with_notif(self, payload):
|
||||
# test for sending a notification before replying to a request message
|
||||
self.send_lsp_msg(-1, 'rpc-with-notif-notif')
|
||||
self.send_lsp_resp(-1, 'rpc-with-notif-notif')
|
||||
# sleep for some time to make sure the notification is delivered
|
||||
time.sleep(0.2)
|
||||
self.send_lsp_msg(payload['id'], 'rpc-with-notif-resp')
|
||||
self.send_lsp_resp(payload['id'], 'rpc-with-notif-resp')
|
||||
|
||||
def do_wrong_payload(self, payload):
|
||||
# test for sending a non dict payload
|
||||
self.send_wrong_payload()
|
||||
time.sleep(0.2)
|
||||
self.send_lsp_msg(-1, 'wrong-payload')
|
||||
self.send_lsp_resp(-1, 'wrong-payload')
|
||||
|
||||
def do_large_payload(self, payload):
|
||||
# test for sending a large (> 64K) payload
|
||||
self.send_lsp_msg(payload['id'], payload)
|
||||
self.send_lsp_resp(payload['id'], payload)
|
||||
|
||||
def do_rpc_resp_incorrect_id(self, payload):
|
||||
self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-1')
|
||||
self.send_lsp_msg(-1, 'rpc-resp-incorrect-id-2')
|
||||
self.send_lsp_msg(1, 'rpc-resp-incorrect-id-3')
|
||||
self.send_lsp_resp(-1, 'rpc-resp-incorrect-id-1')
|
||||
self.send_lsp_resp(-1, 'rpc-resp-incorrect-id-2')
|
||||
self.send_lsp_resp(1, 'rpc-resp-incorrect-id-3')
|
||||
time.sleep(0.2)
|
||||
self.send_lsp_msg(payload['id'], 'rpc-resp-incorrect-id-4')
|
||||
self.send_lsp_resp(payload['id'], 'rpc-resp-incorrect-id-4')
|
||||
|
||||
def do_simple_notif(self, payload):
|
||||
# notification message test
|
||||
self.send_lsp_msg(-1, 'simple-notif')
|
||||
self.send_lsp_resp(-1, 'simple-notif')
|
||||
|
||||
def do_multi_notif(self, payload):
|
||||
# send multiple notifications
|
||||
self.send_lsp_msg(-1, 'multi-notif1')
|
||||
self.send_lsp_msg(-1, 'multi-notif2')
|
||||
self.send_lsp_resp(-1, 'multi-notif1')
|
||||
self.send_lsp_resp(-1, 'multi-notif2')
|
||||
|
||||
def do_msg_with_id(self, payload):
|
||||
self.send_lsp_msg(payload['id'], 'msg-with-id')
|
||||
self.send_lsp_resp(payload['id'], 'msg-with-id')
|
||||
|
||||
def do_msg_specific_cb(self, payload):
|
||||
self.send_lsp_msg(payload['id'], 'msg-specific-cb')
|
||||
self.send_lsp_resp(payload['id'], 'msg-specific-cb')
|
||||
|
||||
def do_server_req(self, payload):
|
||||
self.send_lsp_msg(201, {'method': 'checkhealth', 'params': {'a': 20}})
|
||||
self.send_lsp_resp(201, {'method': 'checkhealth', 'params': {'a': 20}})
|
||||
|
||||
def do_extra_hdr_fields(self, payload):
|
||||
self.send_extra_hdr_fields(payload['id'], 'extra-hdr-fields')
|
||||
@@ -190,6 +203,19 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
def do_empty_payload(self, payload):
|
||||
self.send_empty_payload()
|
||||
|
||||
def do_server_req_in_middle(self, payload):
|
||||
# Send a notification message to the client in the middle of processing
|
||||
# a request message from the client
|
||||
self.send_lsp_req(-1, 'server-req-in-middle', {'text': 'server-notif'})
|
||||
# Send a request message to the client in the middle of processing a
|
||||
# request message from the client.
|
||||
self.send_lsp_req(payload['id'], 'server-req-in-middle', {'text': 'server-req'})
|
||||
|
||||
def do_server_req_in_middle_resp(self, payload):
|
||||
# After receiving a response from the client send the response to the
|
||||
# client request.
|
||||
self.send_lsp_resp(payload['id'], {'text': 'server-resp'})
|
||||
|
||||
def process_msg(self, msg):
|
||||
try:
|
||||
decoded = json.loads(msg)
|
||||
@@ -213,7 +239,9 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
'hdr-with-wrong-len': self.do_hdr_with_wrong_len,
|
||||
'hdr-with-negative-len': self.do_hdr_with_negative_len,
|
||||
'empty-header': self.do_empty_header,
|
||||
'empty-payload': self.do_empty_payload
|
||||
'empty-payload': self.do_empty_payload,
|
||||
'server-req-in-middle': self.do_server_req_in_middle,
|
||||
'server-req-in-middle-resp': self.do_server_req_in_middle_resp,
|
||||
}
|
||||
if decoded['method'] in test_map:
|
||||
test_map[decoded['method']](decoded)
|
||||
|
@@ -699,6 +699,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1924,
|
||||
/**/
|
||||
1923,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user