Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java adapter #106

Merged
merged 22 commits into from
Sep 18, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/adapters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .python import Python
from .go import Go
from .php import PHP
from .java import Java
from .ruby import Ruby
from .elixir import Elixir
from .lua import Lua
Expand Down
105 changes: 105 additions & 0 deletions modules/adapters/java.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Basic idea here is...
# When installing the adapter it would add the com.microsoft.java.debug.plugin-0.30.0.jar as a plugin to lsp-jdts
# When starting the adapter it will ask lsp-jdts to start the adapter by calling the command lsp_jdts_start_debugging exposed by lsp-jdts
# lsp-jdts will then call debugger_lsp_jdts_start_debugging_response after it has started the adapter
# Debugger will then connect to the given port and start debugging

# see https://github.com/Microsoft/java-debug for how the lsp side needs to be setup
# command to start the debug session
# {
# "command": "vscode.java.startDebugSession"
# }

from ..typecheck import Optional, Dict
from ..import core
from ..import dap
from .import util

import sublime
import sublime_plugin
import os


class DebuggerLspJdtlsStartDebuggingResponseCommand(sublime_plugin.WindowCommand):
# window.run_command('debugger_lsp_jdtls_start_debugging_response', {'id': 1, 'port': 12345, 'error': None})

def run(self, **args):
future = Java.pending_adapters.get(args["id"])
if not future:
print("Hmm... unable to find a future port for this id")
return

future.set_result(args)


class Java(dap.AdapterConfiguration):
pending_adapters: Dict[int, core.Future] = {}
pending_adapters_current_id = 0

type = "java"
docs = "https://github.com/redhat-developer/vscode-java/blob/master/README.md"

async def start(self, log, configuration):
pc_settings = sublime.load_settings("Package Control.sublime-settings")
installed_packages = pc_settings.get("installed_packages", [])

if "LSP-jdtls" not in installed_packages or "LSP" not in installed_packages:
raise core.Error("LSP and LSP-jdtls required to debug Java!")

# probably need to add some sort of timeout
future = core.Future()

id = Java.pending_adapters_current_id
Java.pending_adapters_current_id += 1
Java.pending_adapters[id] = future

# ask lsp_jdts to start the debug adapter
# lsp_jdts will call debugger_lsp_jdts_start_debugging_response with the id it was given and a port to connect to the adapter with or an error
# note: the active window might not match the debugger window but generally will... probably need a way to get the actual window.
sublime.active_window().run_command("lsp_jdtls_start_debug_session", {"id": id})

args = await future
if "cwd" not in configuration:
configuration["cwd"], _ = os.path.split(
sublime.active_window().project_file_name()
)
if "mainClass" not in configuration or not configuration["mainClass"]:
if "mainClass" not in args:
raise core.Error(args["error"])
configuration["mainClass"] = args["mainClass"]
if "classPaths" not in configuration:
if "classPaths" not in args:
raise core.Error(args["error"])
configuration["classPaths"] = args["classPaths"]
if "modulePaths" not in configuration:
configuration["modulePaths"] = args["modulePaths"]
if "console" not in configuration:
configuration["console"] = "internalConsole"
if args["enablePreview"]:
if "vmArgs" in configuration:
configuration["vmArgs"] += " --enable-preview"
else:
configuration["vmArgs"] = "--enable-preview"

return dap.SocketTransport(log, "localhost", args["port"])

async def install(self, log):
url = await util.openvsx.latest_release_vsix("vscjava", "vscode-java-debug")
await util.vscode.install(self.type, url, log)

async def installed_status(self, log):
return await util.openvsx.installed_status(
"vscjava", "vscode-java-debug", self.installed_version
)

@property
def installed_version(self) -> Optional[str]:
return util.vscode.installed_version(self.type)

@property
def configuration_snippets(self) -> Optional[list]:
return util.vscode.configuration_snippets(self.type)

@property
def configuration_schema(self) -> Optional[dict]:
return util.vscode.configuration_schema(self.type)
9 changes: 6 additions & 3 deletions modules/dap/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ async def on_initialized_event(self):

if self.capabilities.supportsConfigurationDoneRequest:
try:
await self.request('configurationDone', None)
await self.request('configurationDone', {})
except core.Error as e:
self.log.error('there was an error in configuration done {}'.format(e))

Expand Down Expand Up @@ -773,8 +773,11 @@ def set_selected(self, thread: Thread, frame: Optional[dap.StackFrame]):
# @NOTE threads_for_id will retain all threads for the entire session even if they are removed
@core.schedule
async def refresh_threads(self):
response = await self.request('threads', None)
threads: list[dap.Thread] = response['threads']
# the Java debugger requires an empty object instead of `None`
# See https://github.com/daveleroy/sublime_debugger/pull/106#issuecomment-793802989
response = await self.request('threads', {})
# See https://github.com/daveleroy/sublime_debugger/pull/106#issuecomment-795949070
threads: list[dap.Thread] = response.get('threads', [])

self.threads.clear()
for thread in threads:
Expand Down
38 changes: 33 additions & 5 deletions modules/source_navigation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations

from .typecheck import *

from .import core
Expand Down Expand Up @@ -97,7 +98,6 @@ def clear_generated_view(self):
self.generated_view = None

async def navigate_to_source(self, source: dap.SourceLocation, move_cursor: bool = False) -> sublime.View:

# if we aren't going to reuse the previous generated view throw away any generated view
if not source.source.sourceReference:
self.clear_generated_view()
Expand All @@ -121,7 +121,7 @@ async def navigate_to_source(self, source: dap.SourceLocation, move_cursor: bool
self.generated_view = view
view.set_name(source.source.name or "")
view.set_read_only(False)


syntax = syntax_name_for_mime_type.get(mime_type, 'text.plain')
view.assign_syntax(sublime.find_syntax_by_scope(syntax)[0])
Expand All @@ -131,11 +131,39 @@ async def navigate_to_source(self, source: dap.SourceLocation, move_cursor: bool
view.set_read_only(True)
view.set_scratch(True)
elif source.source.path:
view = await core.sublime_open_file_async(self.project.window, source.source.path, group=0)
if source.source.path.startswith("jdt:"):
LDAP marked this conversation as resolved.
Show resolved Hide resolved
lsp_args = {
"location": {
"uri": source.source.path,
"range": {
"start": {
"line": line,
"character": column
},
"end": {
"line": line,
"character": column
}
}
},
"session_name": "jdtls"
}
view = self.project.window.active_view()
assert view is not None
view.run_command("lsp_open_location", lsp_args)
# very dirty hack
# todo: wait for the view to appear without sleep
await core.sleep(1)
for v in self.project.window.views():
if v.name() == source.source.path:
view = v
view.set_name(source.source.name or "")
view.set_read_only(True)
break
else:
view = await core.sublime_open_file_async(self.project.window, source.source.path, group=0)
else:
raise core.Error('source has no reference or path')


show_line(view, line, column, move_cursor)

return view
3 changes: 2 additions & 1 deletion start.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# import all the commands so that sublime sees them
from .modules.command import CommandsRegistry, DebuggerExecCommand, DebuggerCommand, DebuggerInputCommand
from .modules.adapters.java import DebuggerLspJdtlsStartDebuggingResponseCommand

from .modules.core.sublime import DebuggerAsyncTextCommand, DebuggerEventsListener
from .modules.debugger_output_panel import DebuggerConsoleListener
Expand Down Expand Up @@ -216,4 +217,4 @@ def on_view_gutter_clicked(self, view: sublime.View, line: int, button: int) ->
if source_breakpoints:
debugger.breakpoints.source.edit_breakpoints(source_breakpoints)

return True
return True