Skip to content

Commit b80ae6c

Browse files
yegappanchrisbra
authored andcommitted
patch 9.0.1939: still a problem when processing LSP RPC requests
Problem: still a problem when processing LSP RPC requests Solution: When processing async LSP RPC requests, compare sequence numbers only in response messages A LSP request message can be sent to the language server either synchronously (ch_evalexpr) or asynchronously (ch_sendexpr). In both cases, when looking for response messages by using the sequence number, LSP requests messages from the language server with the same sequence number should not be used. Patch 9.0.1927 fixed this issue for synchronous requests. This PR fixes the issue for asynchronous requests and adds additional tests. closes: #13158 Signed-off-by: Christian Brabandt <[email protected]> Co-authored-by: Yegappan Lakshmanan <[email protected]>
1 parent ceffca6 commit b80ae6c

3 files changed

Lines changed: 76 additions & 13 deletions

File tree

src/channel.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3052,13 +3052,27 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
30523052
{
30533053
// JSON or JS or LSP mode: invoke the one-time callback with the
30543054
// matching nr
3055-
for (cbitem = cbhead->cq_next; cbitem != NULL; cbitem = cbitem->cq_next)
3056-
if (cbitem->cq_seq_nr == seq_nr)
3055+
int lsp_req_msg = FALSE;
3056+
3057+
// Don't use a LSP server request message with the same sequence number
3058+
// as the client request message as the response message.
3059+
if (ch_mode == CH_MODE_LSP && argv[1].v_type == VAR_DICT
3060+
&& dict_has_key(argv[1].vval.v_dict, "method"))
3061+
lsp_req_msg = TRUE;
3062+
3063+
if (!lsp_req_msg)
3064+
{
3065+
for (cbitem = cbhead->cq_next; cbitem != NULL;
3066+
cbitem = cbitem->cq_next)
30573067
{
3058-
invoke_one_time_callback(channel, cbhead, cbitem, argv);
3059-
called_otc = TRUE;
3060-
break;
3068+
if (cbitem->cq_seq_nr == seq_nr)
3069+
{
3070+
invoke_one_time_callback(channel, cbhead, cbitem, argv);
3071+
called_otc = TRUE;
3072+
break;
3073+
}
30613074
}
3075+
}
30623076
}
30633077

30643078
if (seq_nr > 0 && (ch_mode != CH_MODE_LSP || called_otc))

src/testdir/test_channel.vim

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,23 +2478,32 @@ func Test_job_start_with_invalid_argument()
24782478
call assert_fails('call job_start([0zff])', 'E976:')
24792479
endfunc
24802480

2481-
" Test for the 'lsp' channel mode
2481+
" Process requests received from the LSP server
2482+
func LspProcessServerRequests(chan, msg)
2483+
if a:msg['method'] == 'server-req-in-middle'
2484+
\ && a:msg['params']['text'] == 'server-req'
2485+
call ch_sendexpr(a:chan, #{method: 'server-req-in-middle-resp',
2486+
\ id: a:msg['id'], params: #{text: 'client-resp'}})
2487+
endif
2488+
endfunc
2489+
2490+
" LSP channel message callback function
24822491
func LspCb(chan, msg)
24832492
call add(g:lspNotif, a:msg)
24842493
if a:msg->has_key('method')
2485-
" Requests received from the LSP server
2486-
if a:msg['method'] == 'server-req-in-middle'
2487-
\ && a:msg['params']['text'] == 'server-req'
2488-
call ch_sendexpr(a:chan, #{method: 'server-req-in-middle-resp',
2489-
\ id: a:msg['id'], params: #{text: 'client-resp'}})
2490-
endif
2494+
call LspProcessServerRequests(a:chan, a:msg)
24912495
endif
24922496
endfunc
24932497

2498+
" LSP one-time message callback function (used for ch_sendexpr())
24942499
func LspOtCb(chan, msg)
24952500
call add(g:lspOtMsgs, a:msg)
2501+
if a:msg->has_key('method')
2502+
call LspProcessServerRequests(a:chan, a:msg)
2503+
endif
24962504
endfunc
24972505

2506+
" Test for the 'lsp' channel mode
24982507
func LspTests(port)
24992508
" call ch_logfile('Xlspclient.log', 'w')
25002509
let ch = ch_open(s:localhost .. a:port, #{mode: 'lsp', callback: 'LspCb'})
@@ -2661,7 +2670,7 @@ func LspTests(port)
26612670
call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
26622671

26632672
" Test for processing a request message from the server while the client
2664-
" is waiting for a response with the same identifier.
2673+
" is waiting for a response with the same identifier (sync-rpc)
26652674
let g:lspNotif = []
26662675
let resp = ch_evalexpr(ch, #{method: 'server-req-in-middle',
26672676
\ params: #{text: 'client-req'}})
@@ -2673,6 +2682,44 @@ func LspTests(port)
26732682
\ #{id: 28, jsonrpc: '2.0', method: 'server-req-in-middle',
26742683
\ params: #{text: 'server-req'}}], g:lspNotif)
26752684

2685+
" Test for processing a request message from the server while the client
2686+
" is waiting for a response with the same identifier (async-rpc using the
2687+
" channel callback function)
2688+
let g:lspNotif = []
2689+
call ch_sendexpr(ch, #{method: 'server-req-in-middle', id: 500,
2690+
\ params: #{text: 'client-req'}})
2691+
" Send three pings to wait for all the notification messages to arrive
2692+
for i in range(3)
2693+
call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
2694+
endfor
2695+
call assert_equal([
2696+
\ #{id: -1, jsonrpc: '2.0', method: 'server-req-in-middle',
2697+
\ params: #{text: 'server-notif'}},
2698+
\ #{id: 500, jsonrpc: '2.0', method: 'server-req-in-middle',
2699+
\ params: #{text: 'server-req'}},
2700+
\ #{id: 500, jsonrpc: '2.0', result: #{text: 'server-resp'}}
2701+
\ ], g:lspNotif)
2702+
2703+
" Test for processing a request message from the server while the client
2704+
" is waiting for a response with the same identifier (async-rpc using a
2705+
" one-time callback function)
2706+
let g:lspNotif = []
2707+
let g:lspOtMsgs = []
2708+
call ch_sendexpr(ch, #{method: 'server-req-in-middle',
2709+
\ params: #{text: 'client-req'}}, #{callback: 'LspOtCb'})
2710+
" Send a ping to wait for all the notification messages to arrive
2711+
for i in range(3)
2712+
call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
2713+
endfor
2714+
call assert_equal([
2715+
\ #{id: 32, jsonrpc: '2.0', result: #{text: 'server-resp'}}],
2716+
\ g:lspOtMsgs)
2717+
call assert_equal([
2718+
\ #{id: -1, jsonrpc: '2.0', method: 'server-req-in-middle',
2719+
\ params: #{text: 'server-notif'}},
2720+
\ #{id: 32, jsonrpc: '2.0', method: 'server-req-in-middle',
2721+
\ params: {'text': 'server-req'}}], g:lspNotif)
2722+
26762723
" Test for invoking an unsupported method
26772724
let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200})
26782725
call assert_equal({}, resp)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,8 @@ static char *(features[]) =
699699

700700
static int included_patches[] =
701701
{ /* Add new patch number below this line */
702+
/**/
703+
1939,
702704
/**/
703705
1938,
704706
/**/

0 commit comments

Comments
 (0)