patch 8.2.4758: when using an LSP channel want to get the message ID

Problem:    When using an LSP channel want to get the message ID.
Solution:   Have ch_sendexpr() return the ID. (Yegappan Lakshmanan,
            closes #10202)
diff --git a/src/channel.c b/src/channel.c
index 85089cb..ef747d0 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -4520,6 +4520,7 @@
     ch_part_T	part_read;
     jobopt_T    opt;
     int		timeout;
+    int		callback_present = FALSE;
 
     // return an empty string by default
     rettv->v_type = VAR_STRING;
@@ -4546,7 +4547,9 @@
     {
 	dict_T		*d;
 	dictitem_T	*di;
-	int		callback_present = FALSE;
+
+	// return an empty dict by default
+	rettv_dict_alloc(rettv);
 
 	if (argvars[1].v_type != VAR_DICT)
 	{
@@ -4629,6 +4632,14 @@
 	}
     }
     free_job_options(&opt);
+    if (ch_mode == MODE_LSP && !eval && callback_present)
+    {
+	// if ch_sendexpr() is used to send a LSP message and a callback
+	// function is specified, then return the generated identifier for the
+	// message.  The user can use this to cancel the request (if needed).
+	if (rettv->vval.v_dict != NULL)
+	    dict_add_number(rettv->vval.v_dict, "id", id);
+    }
 }
 
 /*
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 690b921..5736463 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1660,7 +1660,7 @@
     {"ch_readraw",	1, 2, FEARG_1,	    arg2_chan_or_job_dict,
 			ret_string,	    JOB_FUNC(f_ch_readraw)},
     {"ch_sendexpr",	2, 3, FEARG_1,	    arg23_chanexpr,
-			ret_void,	    JOB_FUNC(f_ch_sendexpr)},
+			ret_any,	    JOB_FUNC(f_ch_sendexpr)},
     {"ch_sendraw",	2, 3, FEARG_1,	    arg23_chanraw,
 			ret_void,	    JOB_FUNC(f_ch_sendraw)},
     {"ch_setoptions",	2, 2, FEARG_1,	    arg2_chan_or_job_dict,
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
index 6ad9dc0..e270a4b 100644
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -2494,9 +2494,10 @@
 
   " Wrong payload notification test
   let g:lspNotif = []
-  call ch_sendexpr(ch, #{method: 'wrong-payload', params: {}})
+  let r = ch_sendexpr(ch, #{method: 'wrong-payload', params: {}})
+  call assert_equal({}, r)
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', result: 'wrong-payload'}], g:lspNotif)
 
   " Test for receiving a response with incorrect 'id' and additional
@@ -2516,14 +2517,14 @@
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'simple-notif', params: [#{a: 10, b: []}]})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', result: 'simple-notif'}], g:lspNotif)
 
   " multiple notifications test
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'multi-notif', params: [#{a: {}, b: {}}]})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', result: 'multi-notif1'},
         \ #{jsonrpc: '2.0', result: 'multi-notif2'}], g:lspNotif)
 
@@ -2531,7 +2532,7 @@
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'msg-with-id', id: 93, params: #{s: 'str'}})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', id: 93, result: 'msg-with-id'}],
         \ g:lspNotif)
 
@@ -2541,16 +2542,17 @@
 
   " Test for using a one time callback function to process a response
   let g:lspOtMsgs = []
-  call ch_sendexpr(ch, #{method: 'msg-specifc-cb', params: {}},
+  let r = ch_sendexpr(ch, #{method: 'msg-specifc-cb', params: {}},
         \ #{callback: 'LspOtCb'})
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal(9, r.id)
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{id: 9, jsonrpc: '2.0', result: 'msg-specifc-cb'}],
         \ g:lspOtMsgs)
 
   " Test for generating a request message from the other end (server)
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'server-req', params: #{}})
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([{'id': 201, 'jsonrpc': '2.0',
         \ 'result': {'method': 'checkhealth', 'params': {'a': 20}}}],
         \ g:lspNotif)
@@ -2559,7 +2561,7 @@
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'echo', params: #{s: 'msg-without-id'}})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', result:
         \ #{method: 'echo', jsonrpc: '2.0', params: #{s: 'msg-without-id'}}}],
         \ g:lspNotif)
@@ -2568,7 +2570,7 @@
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'echo', id: 110, params: #{s: 'msg-with-id'}})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', result:
         \ #{method: 'echo', jsonrpc: '2.0', id: 110,
         \ params: #{s: 'msg-with-id'}}}], g:lspNotif)
@@ -2581,46 +2583,41 @@
   " Test for processing a HTTP header without the Content-Length field
   let resp = ch_evalexpr(ch, #{method: 'hdr-without-len', params: {}},
         \ #{timeout: 200})
-  call assert_equal('', resp)
+  call assert_equal({}, resp)
   " send a ping to make sure communication still works
-  let resp = ch_evalexpr(ch, #{method: 'ping'})
-  call assert_equal({'id': 16, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
 
   " Test for processing a HTTP header with wrong length
   let resp = ch_evalexpr(ch, #{method: 'hdr-with-wrong-len', params: {}},
         \ #{timeout: 200})
-  call assert_equal('', resp)
+  call assert_equal({}, resp)
   " send a ping to make sure communication still works
-  let resp = ch_evalexpr(ch, #{method: 'ping'})
-  call assert_equal({'id': 18, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
 
   " Test for processing a HTTP header with negative length
   let resp = ch_evalexpr(ch, #{method: 'hdr-with-negative-len', params: {}},
         \ #{timeout: 200})
-  call assert_equal('', resp)
+  call assert_equal({}, resp)
   " send a ping to make sure communication still works
-  let resp = ch_evalexpr(ch, #{method: 'ping'})
-  call assert_equal({'id': 20, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
 
   " Test for an empty header
   let resp = ch_evalexpr(ch, #{method: 'empty-header', params: {}},
         \ #{timeout: 200})
-  call assert_equal('', resp)
+  call assert_equal({}, resp)
   " send a ping to make sure communication still works
-  let resp = ch_evalexpr(ch, #{method: 'ping'})
-  call assert_equal({'id': 22, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
 
   " Test for an empty payload
   let resp = ch_evalexpr(ch, #{method: 'empty-payload', params: {}},
         \ #{timeout: 200})
-  call assert_equal('', resp)
+  call assert_equal({}, resp)
   " send a ping to make sure communication still works
-  let resp = ch_evalexpr(ch, #{method: 'ping'})
-  call assert_equal({'id': 24, 'jsonrpc': '2.0', 'result': 'alive'}, resp)
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
 
   " Test for invoking an unsupported method
   let resp = ch_evalexpr(ch, #{method: 'xyz', params: {}}, #{timeout: 200})
-  call assert_equal('', resp)
+  call assert_equal({}, resp)
 
   " Test for sending a message without a callback function. Notification
   " message should be dropped but RPC response should not be dropped.
@@ -2628,14 +2625,14 @@
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'echo', params: #{s: 'no-callback'}})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([], g:lspNotif)
   " Restore the callback function
   call ch_setoptions(ch, #{callback: 'LspCb'})
   let g:lspNotif = []
   call ch_sendexpr(ch, #{method: 'echo', params: #{s: 'no-callback'}})
   " Send a ping to wait for all the notification messages to arrive
-  call ch_evalexpr(ch, #{method: 'ping'})
+  call assert_equal('alive', ch_evalexpr(ch, #{method: 'ping'}).result)
   call assert_equal([#{jsonrpc: '2.0', result:
         \ #{method: 'echo', jsonrpc: '2.0', params: #{s: 'no-callback'}}}],
         \ g:lspNotif)
diff --git a/src/version.c b/src/version.c
index 9a50e09..2a3e8d6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4758,
+/**/
     4757,
 /**/
     4756,