This repository has been archived by the owner on May 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the Java DAP implementation (+LSP uprev)
This change bundles a pre-built binary of the Java DAP with microsoft/java-debug#379 applied, so that we can specify what port it should bind to. In order for this to function correctly, it also needs to be running a more recent version of the Java LSP, so we will upgrade that too.
- Loading branch information
Showing
8 changed files
with
140 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#!/usr/bin/env python3 | ||
"""Small wrapper to correctly initialize the Java DAP.""" | ||
|
||
import json | ||
import logging | ||
import os | ||
import signal | ||
import subprocess | ||
import sys | ||
import time | ||
|
||
from typing import Any, IO, Dict, List, Optional | ||
|
||
_JAVA_DAP_BUNDLE = '/run_dir/com.microsoft.java.debug.plugin-0.32.0.jar' | ||
|
||
|
||
def _send_lsp_message(msg: Dict[str, Any], lsp: IO[bytes]) -> None: | ||
serialized_msg = json.dumps({ | ||
'jsonrpc': '2.0', | ||
**msg, | ||
}) | ||
payload = len(serialized_msg) | ||
lsp.write((f'Content-Length: {len(serialized_msg)}\r\n\r\n' + | ||
serialized_msg).encode('utf-8')) | ||
lsp.flush() | ||
|
||
|
||
def _receive_lsp_message(lsp: IO[bytes]) -> Optional[Dict[str, Any]]: | ||
headers = b'' | ||
while not headers.endswith(b'\r\n\r\n'): | ||
byte = lsp.read(1) | ||
if len(byte) == 0: | ||
return None | ||
headers += byte | ||
content_length = 0 | ||
for header in headers.strip().split(b'\r\n'): | ||
name, value = header.split(b':', maxsplit=2) | ||
if name.strip().lower() == b'content-length': | ||
content_length = int(value.strip()) | ||
serialized = b'' | ||
while content_length: | ||
chunk = lsp.read(content_length) | ||
if not chunk: | ||
raise Exception(f'short read: {serialized!r}') | ||
content_length -= len(chunk) | ||
serialized += chunk | ||
return json.loads(serialized) | ||
|
||
|
||
def _main() -> None: | ||
with subprocess.Popen(['/usr/bin/run-language-server', '-l', 'java'], | ||
stdout=subprocess.PIPE, | ||
stdin=subprocess.PIPE) as dap: | ||
try: | ||
_send_lsp_message( | ||
{ | ||
'id': 1, | ||
'method': 'initialize', | ||
'params': { | ||
'processId': None, | ||
'initializationOptions': { | ||
'bundles': [ | ||
_JAVA_DAP_BUNDLE, | ||
], | ||
}, | ||
'trace': 'verbose', | ||
'capabilities': {}, | ||
}, | ||
}, dap.stdin) | ||
# Wait for the initialize message has been acknowledged. | ||
# This maximizes the probability of success. | ||
while True: | ||
message = _receive_lsp_message(dap.stdout) | ||
assert message | ||
if message.get('method') == 'window/logMessage': | ||
print(message.get('params', {}).get('message'), | ||
file=sys.stderr) | ||
if message.get('id') == 1: | ||
break | ||
_send_lsp_message( | ||
{ | ||
'id': 2, | ||
'method': 'workspace/executeCommand', | ||
'params': { | ||
'command': 'vscode.java.startDebugSession', | ||
}, | ||
}, dap.stdin) | ||
# Wait for the reply. If the request errored out, exit early to | ||
# send a clear signal to the caller. | ||
while True: | ||
message = _receive_lsp_message(dap.stdout) | ||
assert message | ||
if message.get('method') == 'window/logMessage': | ||
print(message.get('params', {}).get('message'), | ||
file=sys.stderr) | ||
if message.get('id') == 2: | ||
if 'error' in message: | ||
print(message['error'].get('message'), file=sys.stderr) | ||
return | ||
break | ||
# Keep reading to drain the queue. | ||
while True: | ||
message = _receive_lsp_message(dap.stdout) | ||
if not message: | ||
break | ||
if message.get('method') == 'window/logMessage': | ||
print(message.get('params', {}).get('message'), | ||
file=sys.stderr) | ||
except Exception: | ||
logging.exception('failed') | ||
finally: | ||
pgrp = os.getpgid(dap.pid) | ||
os.killpg(pgrp, signal.SIGINT) | ||
|
||
|
||
if __name__ == '__main__': | ||
_main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.