diff --git a/src/channel.c b/src/channel.c index 1de376884a..38f610a9fb 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3052,13 +3052,27 @@ may_invoke_callback(channel_T *channel, ch_part_T part) { // JSON or JS or LSP mode: invoke the one-time callback with the // matching nr - for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next) - if (cbitem->cq_seq_nr == seq_nr) + int lsp_req_msg = FALSE; + + // Don't use a LSP server request message with the same sequence number + // as the client request message as the response message. + if (ch_mode == CH_MODE_LSP && argv[1].v_type == VAR_DICT + && dict_has_key(argv[1].vval.v_dict, "method")) + lsp_req_msg = TRUE; + + if (!lsp_req_msg) + { + for (cbitem = cbhead->cq_next; cbitem != NULL; + cbitem = cbitem->cq_next) { - invoke_one_time_callback(channel, cbhead, cbitem, argv); - called_otc = TRUE; - break; + if (cbitem->cq_seq_nr == seq_nr) + { + invoke_one_time_callback(channel, cbhead, cbitem, argv); + called_otc = TRUE; + break; + } } + } } if (seq_nr > 0 && (ch_mode != CH_MODE_LSP || called_otc)) diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index edfd56e0ba..8f1291c22c 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2478,23 +2478,32 @@ func Test_job_start_with_invalid_argument() call assert_fails('call job_start([0zff])', 'E976:') 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 +" Process requests received from the LSP server +func LspProcessServerRequests(chan, msg) + 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 endfunc -func LspOtCb(chan, msg) - call add(g:lspOtMsgs, a:msg) +" LSP channel message callback function +func LspCb(chan, msg) + call add(g:lspNotif, a:msg) + if a:msg->has_key('method') + call LspProcessServerRequests(a:chan, a:msg) + endif endfunc +" LSP one-time message callback function (used for ch_sendexpr()) +func LspOtCb(chan, msg) + call add(g:lspOtMsgs, a:msg) + if a:msg->has_key('method') + call LspProcessServerRequests(a:chan, a:msg) + endif +endfunc + +" Test for the 'lsp' channel mode func LspTests(port) " call ch_logfile('Xlspclient.log', 'w') let ch = ch_open(s:localhost .. a:port, #{mode: 'lsp', callback: 'LspCb'}) @@ -2661,7 +2670,7 @@ func LspTests(port) 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. + " is waiting for a response with the same identifier (sync-rpc) let g:lspNotif = [] let resp = ch_evalexpr(ch, #{method: 'server-req-in-middle', \ params: #{text: 'client-req'}}) @@ -2673,6 +2682,44 @@ func LspTests(port) \ #{id: 28, jsonrpc: '2.0', method: 'server-req-in-middle', \ params: #{text: 'server-req'}}], g:lspNotif) + " Test for processing a request message from the server while the client + " is waiting for a response with the same identifier (async-rpc using the + " channel callback function) + let g:lspNotif = [] + call ch_sendexpr(ch, #{method: 'server-req-in-middle', id: 500, + \ params: #{text: 'client-req'}}) + " Send three pings to wait for all the notification messages to arrive + for i in range(3) + call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result) + endfor + call assert_equal([ + \ #{id: -1, jsonrpc: '2.0', method: 'server-req-in-middle', + \ params: #{text: 'server-notif'}}, + \ #{id: 500, jsonrpc: '2.0', method: 'server-req-in-middle', + \ params: #{text: 'server-req'}}, + \ #{id: 500, jsonrpc: '2.0', result: #{text: 'server-resp'}} + \ ], g:lspNotif) + + " Test for processing a request message from the server while the client + " is waiting for a response with the same identifier (async-rpc using a + " one-time callback function) + let g:lspNotif = [] + let g:lspOtMsgs = [] + call ch_sendexpr(ch, #{method: 'server-req-in-middle', + \ params: #{text: 'client-req'}}, #{callback: 'LspOtCb'}) + " Send a ping to wait for all the notification messages to arrive + for i in range(3) + call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result) + endfor + call assert_equal([ + \ #{id: 32, jsonrpc: '2.0', result: #{text: 'server-resp'}}], + \ g:lspOtMsgs) + call assert_equal([ + \ #{id: -1, jsonrpc: '2.0', method: 'server-req-in-middle', + \ params: #{text: 'server-notif'}}, + \ #{id: 32, 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) diff --git a/src/version.c b/src/version.c index 16e3e8a359..417bbc15d8 100644 --- a/src/version.c +++ b/src/version.c @@ -699,6 +699,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1939, /**/ 1938, /**/