feat(lsp): support Java class file contents and improve definition navigation handling
- Add `_lsp_java_class_file_contents` request to fetch contents of compiled Java classes via LSP (`java/classFileContents`). - Handle `java/classFileContents` responses by opening a new buffer with Java syntax highlighting and inserting the returned source. - Update definition handling to pass URI and range, enabling precise cursor placement after navigation. - Detect `jdt://` URIs in `textDocument/definition` responses and request class file contents instead of direct navigation. - Move goto navigation logic into `LSPServerEventsMixin`, using event system to access the active view and position the cursor. - Expose `emit` and `emit_to` to the response cache for event dispatching. - Restrict completion activation to `USER_REQUESTED`. - Add TODO note about mapping language IDs to dedicated response handlers.
This commit is contained in:
@@ -119,3 +119,11 @@ class LSPControllerEvents:
|
|||||||
params["position"]["character"] = data["column"]
|
params["position"]["character"] = data["column"]
|
||||||
|
|
||||||
GLib.idle_add( self.send_request, method, params )
|
GLib.idle_add( self.send_request, method, params )
|
||||||
|
|
||||||
|
def _lsp_java_class_file_contents(self, uri: str):
|
||||||
|
method = "java/classFileContents"
|
||||||
|
params = {
|
||||||
|
"uri": uri
|
||||||
|
}
|
||||||
|
|
||||||
|
GLib.idle_add( self.send_request, method, params )
|
||||||
|
|||||||
@@ -3,18 +3,20 @@
|
|||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
|
gi.require_version('GtkSource', '4')
|
||||||
|
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
from gi.repository import GtkSource
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from libs.event_factory import Code_Event_Types
|
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LSPServerEventsMixin:
|
class LSPServerEventsMixin:
|
||||||
|
|
||||||
def _handle_definition_response(self, result: dict or list):
|
def _handle_definition_response(self, uri: str, pointer_pos: dict):
|
||||||
if not result: return
|
self._prompt_goto_request(uri, pointer_pos)
|
||||||
self._prompt_goto_request(result[0]["uri"])
|
|
||||||
|
|
||||||
def _handle_completion_response(self, result: dict or list):
|
def _handle_completion_response(self, result: dict or list):
|
||||||
if not result: return
|
if not result: return
|
||||||
@@ -52,8 +54,38 @@ class LSPServerEventsMixin:
|
|||||||
|
|
||||||
self._prompt_completion_request()
|
self._prompt_completion_request()
|
||||||
|
|
||||||
def _prompt_completion_request(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def _prompt_goto_request(self, uri: str):
|
def _prompt_goto_request(self, uri: str, pointer_pos: dict):
|
||||||
raise NotImplementedError
|
event = Event_Factory.create_event(
|
||||||
|
"get_active_view",
|
||||||
|
)
|
||||||
|
self.emit_to("source_views", event)
|
||||||
|
view = event.response
|
||||||
|
view._on_uri_data_received( [uri] )
|
||||||
|
|
||||||
|
buffer = view.get_buffer()
|
||||||
|
|
||||||
|
def move_cursor(buffer, pointer_pos):
|
||||||
|
itr = buffer.get_iter_at_line( pointer_pos["end"]["line"] )
|
||||||
|
itr.forward_chars( pointer_pos["end"]["character"] )
|
||||||
|
buffer.place_cursor(itr)
|
||||||
|
view.scroll_to_iter(itr, 0.2, False, 0, 0)
|
||||||
|
|
||||||
|
GLib.idle_add( move_cursor, buffer, pointer_pos )
|
||||||
|
|
||||||
|
def _handle_java_class_file_contents(self, text: str):
|
||||||
|
event = Event_Factory.create_event(
|
||||||
|
"get_active_view",
|
||||||
|
)
|
||||||
|
self.emit_to("source_views", event)
|
||||||
|
|
||||||
|
view = event.response
|
||||||
|
file = view.command.exec("new_file")
|
||||||
|
buffer = view.get_buffer()
|
||||||
|
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||||
|
lm = GtkSource.LanguageManager.get_default()
|
||||||
|
language = lm.get_language("java")
|
||||||
|
file.ftype = "java"
|
||||||
|
|
||||||
|
buffer.set_language(language)
|
||||||
|
buffer.insert(itr, text, -1)
|
||||||
|
|||||||
@@ -55,11 +55,9 @@ class Plugin(PluginCode):
|
|||||||
lsp_manager.load_lsp_servers_config()
|
lsp_manager.load_lsp_servers_config()
|
||||||
lsp_manager.set_source_view(source_view)
|
lsp_manager.set_source_view(source_view)
|
||||||
lsp_manager.load_lsp_servers_config_placeholders()
|
lsp_manager.load_lsp_servers_config_placeholders()
|
||||||
lsp_manager.provider.response_cache._prompt_completion_request = \
|
lsp_manager.provider.response_cache.emit = self.emit
|
||||||
self._prompt_completion_request
|
lsp_manager.provider.response_cache.emit_to = self.emit_to
|
||||||
|
lsp_manager.provider.response_cache._prompt_completion_request = self._prompt_completion_request
|
||||||
lsp_manager.provider.response_cache._prompt_goto_request = \
|
|
||||||
self._prompt_goto_request
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
...
|
...
|
||||||
@@ -82,15 +80,6 @@ class Plugin(PluginCode):
|
|||||||
self.emit_to("completion", event)
|
self.emit_to("completion", event)
|
||||||
|
|
||||||
|
|
||||||
def _prompt_goto_request(self, uri: str):
|
|
||||||
event = Event_Factory.create_event(
|
|
||||||
"get_active_view",
|
|
||||||
)
|
|
||||||
self.emit_to("source_views", event)
|
|
||||||
view = event.response
|
|
||||||
view._on_uri_data_received( [uri] )
|
|
||||||
|
|
||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def execute(
|
def execute(
|
||||||
|
|||||||
@@ -60,9 +60,8 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
|||||||
def do_get_activation(self):
|
def do_get_activation(self):
|
||||||
""" The context for when a provider will show results """
|
""" The context for when a provider will show results """
|
||||||
# return GtkSource.CompletionActivation.NONE
|
# return GtkSource.CompletionActivation.NONE
|
||||||
# return GtkSource.CompletionActivation.USER_REQUESTED
|
return GtkSource.CompletionActivation.USER_REQUESTED
|
||||||
# return GtkSource.CompletionActivation.INTERACTIVE
|
# return GtkSource.CompletionActivation.INTERACTIVE
|
||||||
return GtkSource.CompletionActivation.INTERACTIVE | GtkSource.CompletionActivation.USER_REQUESTED
|
|
||||||
|
|
||||||
def do_populate(self, context):
|
def do_populate(self, context):
|
||||||
results = self.response_cache.filter_with_context(context)
|
results = self.response_cache.filter_with_context(context)
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ class ProviderResponseCache(LSPClientEventsMixin, LSPServerEventsMixin, Provider
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# TODO: Need to map 'lang_id' to a given language response class and
|
||||||
|
# pass the controller to a 'server_response' method there.
|
||||||
|
# It would allow clean separation of each language's idiosyncracies
|
||||||
def server_response(self, lsp_response: LSPResponseTypes):
|
def server_response(self, lsp_response: LSPResponseTypes):
|
||||||
logger.debug(f"LSP Response: { lsp_response }")
|
logger.debug(f"LSP Response: { lsp_response }")
|
||||||
|
|
||||||
@@ -81,7 +84,16 @@ class ProviderResponseCache(LSPClientEventsMixin, LSPServerEventsMixin, Provider
|
|||||||
case "textDocument/completion":
|
case "textDocument/completion":
|
||||||
self._handle_completion_response(lsp_response.result)
|
self._handle_completion_response(lsp_response.result)
|
||||||
case "textDocument/definition":
|
case "textDocument/definition":
|
||||||
self._handle_definition_response(lsp_response.result)
|
result = lsp_response.result
|
||||||
|
if not result: return
|
||||||
|
uri = result[0]["uri"]
|
||||||
|
if "jdt://" in uri:
|
||||||
|
controller._lsp_java_class_file_contents(uri)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._handle_definition_response(uri, result[0]["range"])
|
||||||
|
case "java/classFileContents":
|
||||||
|
self._handle_java_class_file_contents(lsp_response.result)
|
||||||
case _:
|
case _:
|
||||||
...
|
...
|
||||||
elif isinstance(lsp_response, LSPResponseNotification):
|
elif isinstance(lsp_response, LSPResponseNotification):
|
||||||
|
|||||||
Reference in New Issue
Block a user