patch 9.2.0060: No support for the DAP channel mode

Problem:  No support for the DAP channel mode
Solution: Add native channel support for the debug-adapter-protocol
          (Foxe Chen)

closes: #19432

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Foxe Chen
2026-02-25 20:53:21 +00:00
committed by Christian Brabandt
parent e8432bc5d5
commit b7eb0c2d38
9 changed files with 362 additions and 71 deletions
+49 -15
View File
@@ -1,4 +1,4 @@
*channel.txt* For Vim version 9.2. Last change: 2026 Feb 18
*channel.txt* For Vim version 9.2. Last change: 2026 Feb 25
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -26,6 +26,7 @@ The Netbeans interface also uses a channel. |netbeans|
13. Controlling a job |job-control|
14. Using a prompt buffer |prompt-buffer|
15. Language Server Protocol |language-server-protocol|
16. Debug Adapter Protocol |debug-adapter-protocol|
*E1277*
{only when compiled with the |+channel| feature for channel stuff}
@@ -56,6 +57,7 @@ NL every message ends in a NL (newline) character
JSON JSON encoding |json_encode()|
JS JavaScript style JSON-like encoding |js_encode()|
LSP Language Server Protocol encoding |language-server-protocol|
DAP Debug Adapter Protocol encoding |debug-adapter-protocol|
Common combination are:
- Using a job connected through pipes in NL mode. E.g., to run a style
@@ -143,6 +145,7 @@ unreachable on the network.
"nl" - Use messages that end in a NL character
"raw" - Use raw messages
"lsp" - Use language server protocol encoding
"dap" - Use debug adapter protocol encoding
*channel-callback* *E921*
"callback" A function that is called when a message is received that is
not handled otherwise (e.g. a JSON message with ID zero). It
@@ -153,8 +156,9 @@ unreachable on the network.
endfunc
let channel = ch_open("localhost:8765", {"callback": "Handle"})
<
When "mode" is "json" or "js" or "lsp" the "msg" argument is
the body of the received message, converted to Vim types.
When "mode" is any of "json", "js", "lsp" or "dap" the "msg"
argument is the body of the received message, converted to Vim
types.
When "mode" is "nl" the "msg" argument is one message,
excluding the NL.
When "mode" is "raw" the "msg" argument is the whole message
@@ -537,7 +541,8 @@ ch_evalexpr({handle}, {expr} [, {options}]) *ch_evalexpr()*
according to the type of channel. The function cannot be used
with a raw channel. See |channel-use|.
{handle} can be a Channel or a Job that has a Channel.
When using the "lsp" channel mode, {expr} must be a |Dict|.
When using the "lsp" or "dap" channel mode, {expr} must be a
|Dict|.
*E917*
{options} must be a Dictionary. It must not have a "callback"
entry. It can have a "timeout" entry to specify the timeout
@@ -545,8 +550,8 @@ ch_evalexpr({handle}, {expr} [, {options}]) *ch_evalexpr()*
ch_evalexpr() waits for a response and returns the decoded
expression. When there is an error or timeout it returns an
empty |String| or, when using the "lsp" channel mode, returns an
empty |Dict|.
empty |String| or, when using the "lsp" or "dap" channel mode,
returns an empty |Dict|.
Note that while waiting for the response, Vim handles other
messages. You need to make sure this doesn't cause trouble.
@@ -627,7 +632,7 @@ ch_info({handle}) *ch_info()*
"err_io" "out", "null", "pipe", "file" or "buffer"
"err_timeout" timeout in msec
"in_status" "open" or "closed"
"in_mode" "NL", "RAW", "JSON", "JS" or "LSP"
"in_mode" "NL", "RAW", "JSON", "JS" or "LSP" or "DAP"
"in_io" "null", "pipe", "file" or "buffer"
"in_timeout" timeout in msec
@@ -733,14 +738,15 @@ ch_sendexpr({handle}, {expr} [, {options}]) *ch_sendexpr()*
with a raw channel.
See |channel-use|. *E912*
{handle} can be a Channel or a Job that has a Channel.
When using the "lsp" channel mode, {expr} must be a |Dict|.
When using the "lsp" or "dap" channel mode, {expr} must be a
|Dict|.
If the channel mode is "lsp", then returns a Dict. Otherwise
returns an empty String. If the "callback" item is present in
{options}, then the returned Dict contains the ID of the
request message. The ID can be used to send a cancellation
request to the LSP server (if needed). Returns an empty Dict
on error.
If the channel mode is "lsp" or "dap", then returns a Dict.
Otherwise returns an empty String. If the "callback" item is
present in {options}, then the returned Dict contains the ID
of the request message. The ID can be used to send a
cancellation request to the LSP server or debug adapter (if
needed). Returns an empty Dict on error.
If a response message is not expected for {expr}, then don't
specify the "callback" item in {options}.
@@ -1607,5 +1613,33 @@ The "params" field is optional: >
"params": <list|dict>
}
<
==============================================================================
16. Debug Adapter Protocol *debug-adapter-protocol*
The debug adapter protocol is very similar to the language server protocol,
with the main difference being that it does not use the JSON-RPC format. The
specification can be found here:
https://microsoft.github.io/debug-adapter-protocol/specification
The protocol uses the same header format as the LSP protocol.
To encode and send a DAP request/notification message in a Vim |Dict| into a
JSON message and to receive and decode a DAP JSON response/notification
message into a Vim |Dict|, connect to the debug adapter with the
|channel-mode| set to "dap".
For messages received on a channel with |channel-mode| set to "dap", Vim will
process the HTTP header and decode the JSON payload into a Vim |Dict| type.
When sending messages on a channel using the |ch_evalexpr()| or
|ch_sendexpr()| functions, Vim will add the HTTP header and encode the Vim
expression into JSON.
Vim will automatically add the "seq" field to the JSON DAP message, and manage
the "request_seq" field as well for responses. However it will not add the
"type" field, it should be manually specified in the |Dict|.
Otherwise the behaviour is the same as how Vim handles the "lsp" channel mode
|language-server-protocol|.
vim:tw=78:ts=8:noet:ft=help:norl:
+1
View File
@@ -6989,6 +6989,7 @@ dav pi_netrw.txt /*dav*
davs pi_netrw.txt /*davs*
daw motion.txt /*daw*
dd change.txt /*dd*
debug-adapter-protocol channel.txt /*debug-adapter-protocol*
debug-gcc debug.txt /*debug-gcc*
debug-highlight debugger.txt /*debug-highlight*
debug-leaks debug.txt /*debug-leaks*
+2 -1
View File
@@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.2. Last change: 2026 Feb 24
*version9.txt* For Vim version 9.2. Last change: 2026 Feb 25
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -52592,6 +52592,7 @@ Other ~
-----
- The new |xdg.vim| script for full XDG compatibility is included.
- |ConPTY| support is considered stable as of Windows 11.
- Support for "dap" channel mode for the |debug-adapter-protocol|.
*changed-9.3*
Changed~
+110 -54
View File
@@ -1981,9 +1981,9 @@ channel_collapse(channel_T *channel, ch_part_T part, int want_nl)
last_node = node->rq_next;
len = node->rq_buflen + last_node->rq_buflen;
if (want_nl || mode == CH_MODE_LSP)
if (want_nl || mode == CH_MODE_LSP || mode == CH_MODE_DAP)
while (last_node->rq_next != NULL
&& (mode == CH_MODE_LSP
&& (mode == CH_MODE_LSP || mode == CH_MODE_DAP
|| channel_first_nl(last_node) == NULL))
{
last_node = last_node->rq_next;
@@ -2134,16 +2134,22 @@ channel_fill(js_read_T *reader)
}
/*
* Process the HTTP header in a Language Server Protocol (LSP) message.
* Process the HTTP header in a Language Server Protocol (LSP) message or
* Debug Adapter Protocol (DAP) message.
*
* The message format is described in the LSP specification:
* https://microsoft.github.io/language-server-protocol/specification
*
* For DAP:
* https://microsoft.github.io/debug-adapter-protocol/specification
*
* It has the following two fields:
*
* Content-Length: ...
* Content-Type: application/vscode-jsonrpc; charset=utf-8
*
* For DAP, there is no "Content-Type" field (as of now).
*
* Each field ends with "\r\n". The header ends with an additional "\r\n".
*
* Returns OK if a valid header is received and FAIL if some fields in the
@@ -2151,7 +2157,7 @@ channel_fill(js_read_T *reader)
* need to wait for more data to arrive.
*/
static int
channel_process_lsp_http_hdr(js_read_T *reader)
channel_process_lspdap_http_hdr(js_read_T *reader)
{
char_u *line_start;
char_u *p;
@@ -2235,8 +2241,9 @@ channel_parse_json(channel_T *channel, ch_part_T part)
reader.js_cookie = channel;
reader.js_cookie_arg = part;
if (chanpart->ch_mode == CH_MODE_LSP)
status = channel_process_lsp_http_hdr(&reader);
if (chanpart->ch_mode == CH_MODE_LSP
|| chanpart->ch_mode == CH_MODE_DAP)
status = channel_process_lspdap_http_hdr(&reader);
// When a message is incomplete we wait for a short while for more to
// arrive. After the delay drop the input, otherwise a truncated string
@@ -2253,12 +2260,13 @@ channel_parse_json(channel_T *channel, ch_part_T part)
{
// Only accept the response when it is a list with at least two
// items.
if (chanpart->ch_mode == CH_MODE_LSP && listtv.v_type != VAR_DICT)
if ((chanpart->ch_mode == CH_MODE_LSP || chanpart->ch_mode == CH_MODE_DAP)
&& listtv.v_type != VAR_DICT)
{
ch_error(channel, "Did not receive a LSP dict, discarding");
clear_tv(&listtv);
}
else if (chanpart->ch_mode != CH_MODE_LSP
else if (chanpart->ch_mode != CH_MODE_LSP && chanpart->ch_mode != CH_MODE_DAP
&& (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2))
{
if (listtv.v_type != VAR_LIST)
@@ -2467,7 +2475,7 @@ channel_has_block_id(chanpart_T *chanpart, int id)
/*
* Get a message from the JSON queue for channel "channel".
* When "id" is positive it must match the first number in the list.
* When "id" is zero or negative jut get the first message. But not one
* When "id" is zero or negative just get the first message. But not one
* in the ch_block_ids list.
* When "without_callback" is TRUE also get messages that were pushed back.
* Return OK when found and return the value in "rettv".
@@ -2489,7 +2497,8 @@ channel_get_json(
list_T *l;
typval_T *tv;
if (channel->ch_part[part].ch_mode != CH_MODE_LSP)
if (channel->ch_part[part].ch_mode != CH_MODE_LSP
&& channel->ch_part[part].ch_mode != CH_MODE_DAP)
{
l = item->jq_value->vval.v_list;
CHECK_LIST_MATERIALIZE(l);
@@ -2500,29 +2509,50 @@ channel_get_json(
dict_T *d;
dictitem_T *di;
// LSP message payload is a JSON-RPC dict.
// For RPC requests and responses, the 'id' item will be present.
// For notifications, it will not be present.
if (id > 0)
if (channel->ch_part[part].ch_mode == CH_MODE_LSP)
{
if (item->jq_value->v_type != VAR_DICT)
goto nextitem;
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;
tv = &di->di_tv;
// LSP message payload is a JSON-RPC dict. For RPC requests and
// responses, the 'id' item will be present. For notifications,
// it will not be present.
if (id > 0)
{
if (item->jq_value->v_type != VAR_DICT)
goto nextitem;
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;
tv = &di->di_tv;
}
else
tv = item->jq_value;
}
else
tv = item->jq_value;
{
if (id > 0)
{
if (item->jq_value->v_type != VAR_DICT)
goto nextitem;
d = item->jq_value->vval.v_dict;
if (d == NULL)
goto nextitem;
di = dict_find(d, (char_u *)"request_seq", -1);
if (di == NULL)
goto nextitem;
tv = &di->di_tv;
}
else
tv = item->jq_value;
}
}
if ((without_callback || !item->jq_no_callback)
@@ -2904,7 +2934,8 @@ channel_use_json_head(channel_T *channel, ch_part_T part)
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
return ch_mode == CH_MODE_JSON || ch_mode == CH_MODE_JS
|| ch_mode == CH_MODE_LSP;
|| ch_mode == CH_MODE_LSP
|| ch_mode == CH_MODE_DAP;
}
/*
@@ -2961,10 +2992,10 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
// Get any json message in the queue.
if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL)
{
if (ch_mode == CH_MODE_LSP)
// In the "lsp" mode, the http header and the json payload may
// be received in multiple messages. So concatenate all the
// received messages.
if (ch_mode == CH_MODE_LSP || ch_mode == CH_MODE_DAP)
// In the "lsp" or "dap" mode, the http header and the json
// payload may be received in multiple messages. So concatenate
// all the received messages.
(void)channel_collapse(channel, part, FALSE);
// Parse readahead, return when there is still no message.
@@ -2973,7 +3004,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
return FALSE;
}
if (ch_mode == CH_MODE_LSP)
if (ch_mode == CH_MODE_LSP || ch_mode == CH_MODE_DAP)
{
dict_T *d = listtv->vval.v_dict;
dictitem_T *di;
@@ -2981,7 +3012,10 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
seq_nr = 0;
if (d != NULL)
{
di = dict_find(d, (char_u *)"id", -1);
if (ch_mode == CH_MODE_LSP)
di = dict_find(d, (char_u *)"id", -1);
else
di = dict_find(d, (char_u *)"seq", -1);
if (di != NULL && di->di_tv.v_type == VAR_NUMBER)
seq_nr = di->di_tv.vval.v_number;
}
@@ -3098,13 +3132,14 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
called_otc = FALSE;
if (seq_nr > 0)
{
// JSON or JS or LSP mode: invoke the one-time callback with the
// JSON or JS or LSP or DAP mode: invoke the one-time callback with the
// matching 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
// Don't use a LSP/DAP server request message with the same sequence
// number as the client request message as the response message.
if ((ch_mode == CH_MODE_LSP || ch_mode == CH_MODE_DAP)
&& argv[1].v_type == VAR_DICT
&& dict_has_key(argv[1].vval.v_dict, "method"))
lsp_req_msg = TRUE;
@@ -3123,7 +3158,8 @@ may_invoke_callback(channel_T *channel, ch_part_T part)
}
}
if (seq_nr > 0 && (ch_mode != CH_MODE_LSP || called_otc))
if (seq_nr > 0 && ((ch_mode != CH_MODE_LSP && ch_mode != CH_MODE_DAP)
|| called_otc))
{
if (!called_otc)
{
@@ -3315,6 +3351,7 @@ channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part)
case CH_MODE_JSON: s = "JSON"; break;
case CH_MODE_JS: s = "JS"; break;
case CH_MODE_LSP: s = "LSP"; break;
case CH_MODE_DAP: s = "DAP"; break;
}
dict_add_string(dict, namebuf, (char_u *)s);
@@ -3982,10 +4019,10 @@ channel_read_json_block(
for (;;)
{
if (mode == CH_MODE_LSP)
// In the "lsp" mode, the http header and the json payload may be
// received in multiple messages. So concatenate all the received
// messages.
if (mode == CH_MODE_LSP || mode == CH_MODE_DAP)
// In the "lsp" or "dap" mode, the http header and the json payload
// may be received in multiple messages. So concatenate all the
// received messages.
(void)channel_collapse(channel, part, FALSE);
more = channel_parse_json(channel, part);
@@ -4558,7 +4595,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
return;
}
if (ch_mode == CH_MODE_LSP)
if (ch_mode == CH_MODE_LSP || ch_mode == CH_MODE_DAP)
{
dict_T *d;
dictitem_T *di;
@@ -4571,11 +4608,15 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
return;
d = argvars[1].vval.v_dict;
di = dict_find(d, (char_u *)"id", -1);
if (ch_mode == CH_MODE_LSP)
di = dict_find(d, (char_u *)"id", -1);
else
di = dict_find(d, (char_u *)"seq", -1);
if (di != NULL && di->di_tv.v_type != VAR_NUMBER)
{
// only number type is supported for the 'id' item
semsg(_(e_invalid_value_for_argument_str), "id");
// only number type is supported for the 'id' or 'seq' item
semsg(_(e_invalid_value_for_argument_str),
ch_mode == CH_MODE_LSP ? "id" : "seq");
return;
}
@@ -4583,7 +4624,16 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
if (dict_has_key(argvars[2].vval.v_dict, "callback"))
callback_present = TRUE;
if (eval || callback_present)
if (ch_mode == CH_MODE_DAP)
{
// DAP message always has a sequence number (id)
id = ++channel->ch_last_msg_id;
if (di == NULL)
dict_add_number(d, "seq", id);
else
di->di_tv.vval.v_number = id;
}
else if (eval || callback_present)
{
// When evaluating an expression or sending an expression with a
// callback, always assign a generated ID
@@ -4601,7 +4651,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
if (di != NULL)
id = di->di_tv.vval.v_number;
}
if (!dict_has_key(d, "jsonrpc"))
if (ch_mode == CH_MODE_LSP && !dict_has_key(d, "jsonrpc"))
dict_add_string(d, "jsonrpc", (char_u *)"2.0");
text = json_encode_lsp_msg(&argvars[1]);
}
@@ -4626,7 +4676,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
if (channel_read_json_block(channel, part_read, timeout, id, &listtv)
== OK)
{
if (ch_mode == CH_MODE_LSP)
if (ch_mode == CH_MODE_LSP || ch_mode == CH_MODE_DAP)
{
*rettv = *listtv;
// Change the type to avoid the value being freed.
@@ -4646,7 +4696,13 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
}
}
free_job_options(&opt);
if (ch_mode == CH_MODE_LSP && !eval && callback_present)
if (ch_mode == CH_MODE_DAP && !eval)
{
// A DAP message always has a sequence number.
if (rettv->vval.v_dict != NULL)
dict_add_number(rettv->vval.v_dict, "seq", id);
}
else if (ch_mode == CH_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
+2
View File
@@ -33,6 +33,8 @@ handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo)
*modep = CH_MODE_JSON;
else if (STRCMP(val, "lsp") == 0)
*modep = CH_MODE_LSP;
else if (STRCMP(val, "dap") == 0)
*modep = CH_MODE_DAP;
else
{
semsg(_(e_invalid_argument_str), val);
+3 -1
View File
@@ -2599,7 +2599,9 @@ typedef enum
CH_MODE_RAW,
CH_MODE_JSON,
CH_MODE_JS,
CH_MODE_LSP // Language Server Protocol (http + json)
CH_MODE_LSP, // Language Server Protocol (http + json)
CH_MODE_DAP // Debug Adapter Protocol (like LSP, but does not
// strictly follow JSON-RPC standard)
} ch_mode_T;
typedef enum {
+58
View File
@@ -2764,6 +2764,64 @@ func Test_channel_lsp_mode()
call RunServer('test_channel_lsp.py', 'LspTests', [])
endfunc
" Test for the 'dap' channel mode. Don't need to test much since most of the
" logic is same as 'lsp' mode.
func DapTests(port)
let ch = ch_open(s:localhost .. a:port, #{
\ mode: 'dap',
\ })
if ch_status(ch) == "fail"
call assert_report("Can't open the dap channel")
return
endif
" check for channel information
let info = ch_info(ch)
call assert_equal('DAP', info.sock_mode)
let resp = ch_evalexpr(ch, #{
\ type: 'request',
\ command: 'initialize'
\ })
call assert_equal({
\ 'seq': 1,
\ 'request_seq': 1,
\ 'type': 'response',
\ 'success': v:true,
\ 'body': {'supportsConfigurationDoneRequest': v:true},
\ 'command': 'initialize'
\ }, resp)
let resp = ch_read(ch)
call assert_equal({
\ 'seq': 2,
\ 'type': 'event',
\ 'event': 'initialized',
\ 'body': {}
\ }, resp)
let resp = ch_evalexpr(ch, #{
\ type: 'request',
\ command: 'test'
\ })
call assert_equal({
\ 'seq': 3,
\ 'request_seq': 2,
\ 'type': 'response',
\ 'success': v:true,
\ 'body': {},
\ 'command': 'test'
\ }, resp)
endfunc
func Test_channel_dap_mode()
let g:giveup_same_error = 0
call RunServer('test_channel_dap.py', 'DapTests', [])
endfunc
func Test_error_callback_terminal()
CheckUnix
CheckFeature terminal
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
# Used by Test_channel_dap_mode in test_channel.vim to test DAP functionality.
import json
import socket
import threading
import time
try:
import socketserver
except ImportError:
import SocketServer as socketserver
def make_dap_message(obj):
payload = json.dumps(obj).encode("utf-8")
header = f"Content-Length: {len(payload)}\r\n\r\n".encode("ascii")
return header + payload
def parse_messages(buffer):
messages = []
while True:
hdr_end = buffer.find(b"\r\n\r\n")
if hdr_end == -1:
break
header = buffer[:hdr_end].decode("ascii", errors="ignore")
content_length = None
for line in header.split("\r\n"):
if line.lower().startswith("content-length:"):
content_length = int(line.split(":")[1].strip())
if content_length is None:
break
total_len = hdr_end + 4 + content_length
if len(buffer) < total_len:
break # partial
body = buffer[hdr_end + 4:total_len]
messages.append(json.loads(body.decode("utf-8")))
buffer = buffer[total_len:]
return messages, buffer
class DAPHandler(socketserver.BaseRequestHandler):
def setup(self):
self.request.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.seq = 1 # server sequence counter
def send(self, obj):
obj["seq"] = self.seq
self.seq += 1
self.request.sendall(make_dap_message(obj))
def send_response(self, request, body=None, success=True):
self.send({
"type": "response",
"request_seq": request["seq"],
"success": success,
"command": request["command"],
"body": body or {}
})
def send_event(self, event, body=None):
self.send({
"type": "event",
"event": event,
"body": body or {}
})
def handle_request(self, msg):
cmd = msg.get("command")
if cmd == "initialize":
self.send_response(msg, {
"supportsConfigurationDoneRequest": True
})
self.send_event("initialized")
else:
self.send_response(msg)
return True
def handle(self):
buffer = b""
while True:
data = self.request.recv(4096)
if not data:
break
buffer += data
messages, buffer = parse_messages(buffer)
for msg in messages:
if msg.get("type") == "request":
if not self.handle_request(msg):
return
class ThreadedDAPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True
def write_port_to_file(port, filename="Xportnr"):
with open(filename, "w") as f:
f.write(str(port))
def main():
server = ThreadedDAPServer(("localhost", 0), DAPHandler)
# Get the actual assigned port
ip, assigned_port = server.server_address
# Write port so client/test can read it
write_port_to_file(assigned_port)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
try:
while thread.is_alive():
thread.join(1)
except KeyboardInterrupt:
server.shutdown()
if __name__ == "__main__":
main()
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
60,
/**/
59,
/**/