Compare commits
22 Commits
3dfb198aa5
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| fac9ee028b | |||
| 7ab919f925 | |||
| 383db1270e | |||
| 2d4c8e4f31 | |||
| a8ad015e05 | |||
| 0dc21cbb82 | |||
| 890c6cdfcc | |||
| b13d9c2397 | |||
| e367e31890 | |||
| bd277c0214 | |||
| 12a5e4935e | |||
| 01ede1ac49 | |||
| 2758d6b62b | |||
| 9f1c3cc452 | |||
| 7c4c9ecf88 | |||
| 0b231ac749 | |||
| 0fc440e7ce | |||
| 080ba41cf1 | |||
| d1643091c3 | |||
| a4ef662da7 | |||
| 6cb66985aa | |||
| 52db0b8a31 |
BIN
home/abaddon/.config/newton/lsps/ropeproject/autoimport.db
Normal file
BIN
home/abaddon/.config/newton/lsps/ropeproject/autoimport.db
Normal file
Binary file not shown.
@@ -39,6 +39,24 @@ class Plugin(PluginCode):
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event("unregister_command",
|
||||
command_name = "autopairs",
|
||||
command = Handler,
|
||||
binding_mode = "held",
|
||||
binding = [
|
||||
"'", "`", "[", "]",
|
||||
'<Shift>"',
|
||||
'<Shift>(',
|
||||
'<Shift>)',
|
||||
'<Shift>{',
|
||||
'<Shift>}'
|
||||
]
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
autopairs = None
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
97
plugins/code/commands/file_history/autopairs.py
Normal file
97
plugins/code/commands/file_history/autopairs.py
Normal file
@@ -0,0 +1,97 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class Autopairs:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
def handle_word_wrap(self, buffer, char_str: str):
|
||||
wrap_block = self.get_wrap_block(char_str)
|
||||
if not wrap_block: return
|
||||
|
||||
selection = buffer.get_selection_bounds()
|
||||
if not selection:
|
||||
self.insert_pair(buffer, char_str, wrap_block)
|
||||
return True
|
||||
|
||||
self.wrap_selection(buffer, char_str, wrap_block, selection)
|
||||
|
||||
return True
|
||||
|
||||
def insert_pair(
|
||||
self, buffer, char_str: str, wrap_block: tuple
|
||||
):
|
||||
buffer.begin_user_action()
|
||||
|
||||
left_block, right_block = wrap_block
|
||||
insert_mark = buffer.get_insert()
|
||||
|
||||
insert_itr = buffer.get_iter_at_mark(insert_mark)
|
||||
buffer.insert(insert_itr, f"{left_block}{right_block}")
|
||||
insert_itr = buffer.get_iter_at_mark( insert_mark )
|
||||
insert_itr.backward_char()
|
||||
|
||||
buffer.place_cursor(insert_itr)
|
||||
|
||||
buffer.end_user_action()
|
||||
|
||||
def wrap_selection(
|
||||
self, buffer, char_str: str, wrap_block: tuple, selection
|
||||
):
|
||||
left_block, \
|
||||
right_block = wrap_block
|
||||
start_itr, \
|
||||
end_itr = selection
|
||||
data = buffer.get_text(
|
||||
start_itr, end_itr, include_hidden_chars = False
|
||||
)
|
||||
start_mark = buffer.create_mark("startclose", start_itr, False)
|
||||
end_mark = buffer.create_mark("endclose", end_itr, True)
|
||||
|
||||
buffer.begin_user_action()
|
||||
|
||||
buffer.insert(start_itr, left_block)
|
||||
end_itr = buffer.get_iter_at_mark(end_mark)
|
||||
buffer.insert(end_itr, right_block)
|
||||
|
||||
start = buffer.get_iter_at_mark(start_mark)
|
||||
end = buffer.get_iter_at_mark(end_mark)
|
||||
|
||||
buffer.select_range(start, end)
|
||||
buffer.delete_mark_by_name("startclose")
|
||||
buffer.delete_mark_by_name("endclose")
|
||||
|
||||
buffer.end_user_action()
|
||||
|
||||
def get_wrap_block(self, char_str) -> tuple:
|
||||
left_block = ""
|
||||
right_block = ""
|
||||
|
||||
match char_str:
|
||||
case "(" | ")":
|
||||
left_block = "("
|
||||
right_block = ")"
|
||||
case "[" | "]":
|
||||
left_block = "["
|
||||
right_block = "]"
|
||||
case "{" | "}":
|
||||
left_block = "{"
|
||||
right_block = "}"
|
||||
case '"':
|
||||
left_block = '"'
|
||||
right_block = '"'
|
||||
case "'":
|
||||
left_block = "'"
|
||||
right_block = "'"
|
||||
case "`":
|
||||
left_block = "`"
|
||||
right_block = "`"
|
||||
case _:
|
||||
return ()
|
||||
|
||||
return left_block, right_block
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "Toggle Source View",
|
||||
"name": "File History",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
65
plugins/code/commands/file_history/plugin.py
Normal file
65
plugins/code/commands/file_history/plugin.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
|
||||
|
||||
history: list = []
|
||||
history_size: int = 30
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.RemovedFileEvent):
|
||||
if event.file.ftype == "buffer": return
|
||||
|
||||
if len(history) == history_size:
|
||||
history.pop(0)
|
||||
|
||||
history.append(event.file.fpath)
|
||||
|
||||
def load(self):
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "file_history_pop",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = "<Shift><Control>t"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: any,
|
||||
char_str: str,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: File History")
|
||||
if len(history) == 0: return
|
||||
|
||||
view._on_uri_data_received(
|
||||
[
|
||||
f"file://{history.pop()}"
|
||||
]
|
||||
)
|
||||
@@ -14,31 +14,44 @@ from .helpers import clear_temp_cut_buffer_delayed, set_temp_cut_buffer_delayed
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: GtkSource.View,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
def execute(view: GtkSource.View, *args, **kwargs):
|
||||
logger.debug("Command: Cut to Temp Buffer")
|
||||
|
||||
clear_temp_cut_buffer_delayed(view)
|
||||
|
||||
buffer = view.get_buffer()
|
||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
|
||||
start_itr = itr.copy()
|
||||
start_itr.set_line_offset(0)
|
||||
if buffer.get_has_selection():
|
||||
start_itr, end_itr = buffer.get_selection_bounds()
|
||||
|
||||
end_itr = start_itr.copy()
|
||||
if not end_itr.forward_line():
|
||||
end_itr = buffer.get_end_iter()
|
||||
start_itr.set_line_offset(0)
|
||||
|
||||
if not end_itr.ends_line():
|
||||
end_itr.forward_to_line_end()
|
||||
|
||||
if not end_itr.is_end():
|
||||
end_itr.forward_char()
|
||||
else:
|
||||
itr = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
|
||||
start_itr = itr.copy()
|
||||
start_itr.set_line_offset(0)
|
||||
|
||||
end_itr = start_itr.copy()
|
||||
if not end_itr.forward_line():
|
||||
end_itr = buffer.get_end_iter()
|
||||
|
||||
if not hasattr(view, "_cut_buffer"):
|
||||
view._cut_buffer = ""
|
||||
|
||||
line_str = buffer.get_text(start_itr, end_itr, True)
|
||||
view._cut_buffer += line_str
|
||||
text = buffer.get_text(start_itr, end_itr, True)
|
||||
|
||||
if not text.endswith("\n"):
|
||||
text += "\n"
|
||||
|
||||
view._cut_buffer += text
|
||||
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.place_cursor(start_itr)
|
||||
|
||||
set_temp_cut_buffer_delayed(view)
|
||||
set_temp_cut_buffer_delayed(view)
|
||||
@@ -21,7 +21,13 @@ class Plugin(PluginCode):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
event = Event_Factory.create_event("register_command",
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "cut_to_temp_buffer",
|
||||
command = Handler,
|
||||
binding_mode = "held",
|
||||
@@ -30,7 +36,7 @@ class Plugin(PluginCode):
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event("register_command",
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "paste_temp_buffer",
|
||||
command = Handler2,
|
||||
binding_mode = "held",
|
||||
|
||||
77
plugins/code/commands/split_pane/close_split_view.py
Normal file
77
plugins/code/commands/split_pane/close_split_view.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
|
||||
|
||||
emit_to: callable = None
|
||||
|
||||
def get_source_view(widget):
|
||||
if isinstance(widget, GtkSource.View):
|
||||
return widget
|
||||
|
||||
if isinstance(widget, Gtk.ScrolledWindow):
|
||||
return widget.get_child()
|
||||
|
||||
if isinstance(widget, Gtk.Paned):
|
||||
return get_source_view(widget.get_child1())
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def execute(
|
||||
source_view,
|
||||
char_str,
|
||||
modkeys_states
|
||||
):
|
||||
logger.debug("Command: Close Split Pane")
|
||||
|
||||
scrolled_win = source_view.get_parent()
|
||||
pane = scrolled_win.get_parent()
|
||||
|
||||
if not isinstance(pane, Gtk.Paned): return
|
||||
|
||||
container = pane.get_parent()
|
||||
source_view1 = pane.get_child1()
|
||||
source_view2 = pane.get_child2()
|
||||
|
||||
if scrolled_win == source_view1:
|
||||
remaining = source_view2
|
||||
closing_view = source_view
|
||||
else:
|
||||
remaining = source_view1
|
||||
closing_view = source_view
|
||||
|
||||
remaining_view = get_source_view(remaining)
|
||||
left = closing_view.sibling_left
|
||||
right = closing_view.sibling_right
|
||||
|
||||
if left:
|
||||
left.sibling_right = right
|
||||
|
||||
if right:
|
||||
right.sibling_left = left
|
||||
|
||||
pane.remove(source_view1)
|
||||
pane.remove(source_view2)
|
||||
|
||||
container.remove(pane)
|
||||
container.add(remaining)
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"remove_source_view",
|
||||
view = closing_view
|
||||
)
|
||||
emit_to("source_views", event)
|
||||
|
||||
remaining_view.grab_focus()
|
||||
67
plugins/code/commands/split_pane/create_split_view.py
Normal file
67
plugins/code/commands/split_pane/create_split_view.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
from libs.dto.states import SourceViewStates
|
||||
|
||||
|
||||
|
||||
emit_to: callable = None
|
||||
|
||||
def execute(
|
||||
source_view1,
|
||||
char_str,
|
||||
modkeys_states
|
||||
):
|
||||
logger.debug("Command: Split Pane")
|
||||
|
||||
scrolled_win1 = source_view1.get_parent()
|
||||
container = scrolled_win1.get_parent()
|
||||
pane = Gtk.Paned()
|
||||
event = Event_Factory.create_event(
|
||||
"create_source_view",
|
||||
state = SourceViewStates.INSERT
|
||||
)
|
||||
emit_to("source_views", event)
|
||||
|
||||
scrolled_win2, \
|
||||
source_view2 = event.response
|
||||
old_sibling_right = None
|
||||
|
||||
if source_view1.sibling_right:
|
||||
old_sibling_right = source_view1.sibling_right
|
||||
|
||||
source_view1.sibling_right = source_view2
|
||||
if old_sibling_right:
|
||||
old_sibling_right.sibling_left = source_view2
|
||||
source_view2.sibling_right = old_sibling_right
|
||||
|
||||
source_view2.sibling_left = source_view1
|
||||
|
||||
pane.set_hexpand(True)
|
||||
pane.set_vexpand(True)
|
||||
scrolled_win1.set_hexpand(True)
|
||||
scrolled_win2.set_vexpand(True)
|
||||
pane.set_wide_handle(True)
|
||||
|
||||
container.remove(scrolled_win1)
|
||||
pane.pack1( scrolled_win1, True, True )
|
||||
pane.pack2( scrolled_win2, True, True )
|
||||
container.add(pane)
|
||||
|
||||
if char_str == "|":
|
||||
pane.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
elif char_str == "\\":
|
||||
pane.set_orientation(Gtk.Orientation.HORIZONTAL)
|
||||
|
||||
pane.show_all()
|
||||
|
||||
source_view2.command.exec("new_file")
|
||||
source_view2.grab_focus()
|
||||
@@ -7,6 +7,7 @@ gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
@@ -18,5 +19,4 @@ def execute(
|
||||
):
|
||||
logger.debug("Command: Focus Left Sibling")
|
||||
if not view.sibling_left: return
|
||||
view.sibling_left.get_parent().show()
|
||||
view.sibling_left.grab_focus()
|
||||
@@ -18,5 +18,4 @@ def execute(
|
||||
):
|
||||
logger.debug("Command: Focus Right Sibling")
|
||||
if not view.sibling_right: return
|
||||
view.sibling_right.get_parent().show()
|
||||
view.sibling_right.grab_focus()
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "LSP Manager",
|
||||
"name": "Split Pane",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
100
plugins/code/commands/split_pane/plugin.py
Normal file
100
plugins/code/commands/split_pane/plugin.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from . import create_split_view, \
|
||||
close_split_view, \
|
||||
focus_left_sibling, \
|
||||
focus_right_sibling, \
|
||||
move_to_left_sibling, \
|
||||
move_to_right_sibling
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
gemit_to = self.emit_to
|
||||
self._manage_signals("register_command")
|
||||
|
||||
def unload(self):
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
_create_split_view = create_split_view
|
||||
_close_split_view = close_split_view
|
||||
_create_split_view.emit_to = self.emit_to
|
||||
_close_split_view.emit_to = self.emit_to
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "create_split_view",
|
||||
command = _create_split_view,
|
||||
binding_mode = "released",
|
||||
binding = ["<Control>\\", "<Shift><Control>|"]
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "close_split_view",
|
||||
command = _close_split_view,
|
||||
binding_mode = "released",
|
||||
binding = "<Alt>\\"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "focus_left_sibling",
|
||||
command = focus_left_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>Page_Up"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "focus_right_sibling",
|
||||
command = focus_right_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>Page_Down"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "move_to_left_sibling",
|
||||
command = move_to_left_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control><Shift>Up"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "move_to_right_sibling",
|
||||
command = move_to_right_sibling,
|
||||
binding_mode = "released",
|
||||
binding = "<Control><Shift>Down"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -1,54 +0,0 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
event = Event_Factory.create_event("register_command",
|
||||
command_name = "toggle_source_view",
|
||||
command = Handler,
|
||||
binding_mode = "released",
|
||||
binding = "<Shift><Control>h"
|
||||
)
|
||||
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
|
||||
class Handler:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: any,
|
||||
char_str: str,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
logger.debug("Command: Toggle Source View")
|
||||
target = view.get_parent()
|
||||
target.hide() if target.is_visible() else target.show()
|
||||
|
||||
if view.sibling_left:
|
||||
target = view.sibling_left.get_parent()
|
||||
target.show()
|
||||
view.sibling_left.grab_focus()
|
||||
|
||||
if view.sibling_right:
|
||||
target = view.sibling_right.get_parent()
|
||||
target.show()
|
||||
view.sibling_right.grab_focus()
|
||||
|
||||
@@ -35,5 +35,15 @@ class Plugin(PluginCode):
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "Example Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
@@ -51,7 +51,10 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
...
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
|
||||
@@ -35,5 +35,15 @@ class Plugin(PluginCode):
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "Snippets Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
@@ -49,7 +49,10 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
...
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
|
||||
@@ -35,5 +35,15 @@ class Plugin(PluginCode):
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event(
|
||||
"unregister_provider",
|
||||
provider_name = "Words Completer"
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
self.provider = None
|
||||
del self.provider
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
@@ -35,11 +35,14 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
...
|
||||
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
buffer = event.file.buffer
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
buffer = event.buffer
|
||||
self._clear_temp_delay()
|
||||
self._set_temp_delay(buffer)
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
...
|
||||
|
||||
def _clear_temp_delay(self):
|
||||
if self._temp_timeout_id:
|
||||
GLib.source_remove(self._temp_timeout_id)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Plugin Package
|
||||
"""
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Extend Source View Menu",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .source_view_menu import extend_source_view_menu
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.PopulateSourceViewPopupEvent):
|
||||
extend_source_view_menu(event.buffer, event.menu)
|
||||
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def unload(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1,68 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def on_case_handle(menuitem, buffer, action):
|
||||
start_itr, \
|
||||
end_itr = buffer.get_selection_bounds()
|
||||
data = buffer.get_text(start_itr, end_itr, False)
|
||||
text = data
|
||||
|
||||
if action == "on_all_upper":
|
||||
text = data.upper()
|
||||
elif action == "on_all_lower":
|
||||
text = data.lower()
|
||||
elif action == "on_invert":
|
||||
text = data.swapcase()
|
||||
elif action == "on_title":
|
||||
text = data.title()
|
||||
elif action == "on_title_strip":
|
||||
text = data.title().replace("-", "").replace("_", "").replace(" ", "")
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text)
|
||||
buffer.end_user_action()
|
||||
|
||||
|
||||
|
||||
def extend_source_view_menu(buffer, menu):
|
||||
if not buffer.get_selection_bounds(): return
|
||||
|
||||
for child in menu.get_children():
|
||||
if not child.get_label() == "C_hange Case": continue
|
||||
menu.remove(child)
|
||||
|
||||
change_case_item = Gtk.MenuItem(label = "Change Case")
|
||||
|
||||
case_menu = Gtk.Menu()
|
||||
au_case_item = Gtk.MenuItem(label = "All Upper Case")
|
||||
al_case_item = Gtk.MenuItem(label = "All Lower Case")
|
||||
inver_case_item = Gtk.MenuItem(label = "Invert Case")
|
||||
title_case_item = Gtk.MenuItem(label = "Title Case")
|
||||
title_strip_case_item = Gtk.MenuItem(label = "Title Strip Case")
|
||||
|
||||
au_case_item.connect("activate", on_case_handle, buffer, "on_all_upper")
|
||||
al_case_item.connect("activate", on_case_handle, buffer, "on_all_lower")
|
||||
inver_case_item.connect("activate", on_case_handle, buffer, "on_invert")
|
||||
title_case_item.connect("activate", on_case_handle, buffer, "on_title")
|
||||
title_strip_case_item.connect("activate", on_case_handle, buffer, "on_title_strip")
|
||||
|
||||
case_menu.append(au_case_item)
|
||||
case_menu.append(al_case_item)
|
||||
case_menu.append(inver_case_item)
|
||||
case_menu.append(title_case_item)
|
||||
case_menu.append(title_strip_case_item)
|
||||
change_case_item.set_submenu(case_menu)
|
||||
|
||||
menu.append(change_case_item)
|
||||
@@ -17,16 +17,28 @@ class Plugin(PluginCode):
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.TextChangedEvent):
|
||||
event.file.check_file_on_disk()
|
||||
if not isinstance(event, Code_Event_Types.FocusedViewEvent): return
|
||||
event = Event_Factory.create_event(
|
||||
"get_file", buffer = event.view.get_buffer()
|
||||
)
|
||||
self.emit_to("files", event)
|
||||
|
||||
if event.file.is_deleted():
|
||||
file_is_deleted(event)
|
||||
elif event.file.is_externally_modified():
|
||||
file_is_externally_modified(event)
|
||||
file = event.response
|
||||
if not file: return
|
||||
if file.ftype == "buffer": return
|
||||
|
||||
file.check_file_on_disk()
|
||||
|
||||
if file.is_deleted():
|
||||
file_is_deleted(file, self.emit)
|
||||
elif file.is_externally_modified():
|
||||
file_is_externally_modified(file, self.emit)
|
||||
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def unload(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
@@ -10,23 +10,45 @@ from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
|
||||
|
||||
def file_is_deleted(event):
|
||||
event.file.was_deleted = True
|
||||
def ask_yes_no(message):
|
||||
dialog = Gtk.MessageDialog(
|
||||
parent = None,
|
||||
flags = 0,
|
||||
message_type = Gtk.MessageType.QUESTION,
|
||||
buttons = Gtk.ButtonsType.YES_NO,
|
||||
text = message,
|
||||
)
|
||||
dialog.set_title("Confirm")
|
||||
|
||||
response = dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
return response == Gtk.ResponseType.YES
|
||||
|
||||
|
||||
def file_is_deleted(file, emit):
|
||||
file.was_deleted = True
|
||||
event = Event_Factory.create_event(
|
||||
"file_externally_deleted",
|
||||
file = event.file,
|
||||
buffer = event.buffer
|
||||
file = file,
|
||||
buffer = file.buffer
|
||||
)
|
||||
self.emit(event)
|
||||
emit(event)
|
||||
|
||||
|
||||
def file_is_externally_modified(event):
|
||||
# event = Event_Factory.create_event(
|
||||
# "file_externally_modified",
|
||||
# file = event.file,
|
||||
# buffer = event.buffer
|
||||
# )
|
||||
# self.emit(event)
|
||||
def file_is_externally_modified(file, emit):
|
||||
event = Event_Factory.create_event(
|
||||
"file_externally_modified",
|
||||
file = file,
|
||||
buffer = file.buffer
|
||||
)
|
||||
emit(event)
|
||||
|
||||
...
|
||||
if not file.buffer.get_modified():
|
||||
file.reload()
|
||||
return
|
||||
|
||||
result = ask_yes_no("File has been externally modified. Reload?")
|
||||
if not result: return
|
||||
|
||||
file.reload()
|
||||
|
||||
@@ -32,5 +32,8 @@ class Plugin(PluginCode):
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def unload(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
@@ -13,8 +13,6 @@ from gi.repository import Gtk
|
||||
|
||||
|
||||
def add_prettify_json(buffer, menu):
|
||||
menu.append( Gtk.SeparatorMenuItem() )
|
||||
|
||||
def on_prettify_json(menuitem, buffer):
|
||||
start_itr, \
|
||||
end_itr = buffer.get_start_iter(), buffer.get_end_iter()
|
||||
@@ -28,4 +26,4 @@ def add_prettify_json(buffer, menu):
|
||||
|
||||
item = Gtk.MenuItem(label = "Prettify JSON")
|
||||
item.connect("activate", on_prettify_json, buffer)
|
||||
menu.append(item)
|
||||
menu.append(item)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,214 @@
|
||||
{
|
||||
"info": "https://download.eclipse.org/jdtls/",
|
||||
"info-init-options": "https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line",
|
||||
"info-import-build": "https://www.javahotchocolate.com/tutorials/build-path.html",
|
||||
"info-external-class-paths": "https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3291",
|
||||
"link": "https://download.eclipse.org/jdtls/milestones/?d",
|
||||
"command": "lsp-ws-proxy --listen 4114 -- jdtls",
|
||||
"alt-command": "lsp-ws-proxy -- jdtls",
|
||||
"alt-command2": "java-language-server",
|
||||
"socket": "ws://127.0.0.1:9999/java",
|
||||
"socket-two": "ws://127.0.0.1:9999/?name=jdtls",
|
||||
"alt-socket": "ws://127.0.0.1:9999/?name=java-language-server",
|
||||
"initialization-options": {
|
||||
"bundles": [
|
||||
"intellicode-core.jar"
|
||||
],
|
||||
"workspaceFolders": [
|
||||
"file://{workspace.folder}"
|
||||
],
|
||||
"extendedClientCapabilities": {
|
||||
"classFileContentsSupport": true,
|
||||
"executeClientCommandSupport": false
|
||||
},
|
||||
"settings": {
|
||||
"java": {
|
||||
"autobuild": {
|
||||
"enabled": true
|
||||
},
|
||||
"jdt": {
|
||||
"ls": {
|
||||
"javac": {
|
||||
"enabled": true
|
||||
},
|
||||
"java": {
|
||||
"home": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2"
|
||||
},
|
||||
"lombokSupport": {
|
||||
"enabled": true
|
||||
},
|
||||
"protobufSupport":{
|
||||
"enabled": true
|
||||
},
|
||||
"androidSupport": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"configuration": {
|
||||
"updateBuildConfiguration": "automatic",
|
||||
"maven": {
|
||||
"userSettings": "{user.home}/.config/jdtls/settings.xml",
|
||||
"globalSettings": "{user.home}/.config/jdtls/settings.xml"
|
||||
},
|
||||
"runtimes": [
|
||||
{
|
||||
"name": "JavaSE-17",
|
||||
"path": "/usr/lib/jvm/java-17-openjdk",
|
||||
"javadoc": "https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "JavaSE-22",
|
||||
"path": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2",
|
||||
"javadoc": "https://docs.oracle.com/en/java/javase/22/docs/api/",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"classPath": [
|
||||
"{user.home}/.config/jdtls/m2/repository/**/*-sources.jar",
|
||||
"lib/**/*-sources.jar"
|
||||
],
|
||||
"docPath": [
|
||||
"{user.home}/.config/jdtls/m2/repository/**/*-javadoc.jar",
|
||||
"lib/**/*-javadoc.jar"
|
||||
],
|
||||
"project": {
|
||||
"encoding": "ignore",
|
||||
"outputPath": "bin",
|
||||
"referencedLibraries": [
|
||||
"{user.home}/.config/jdtls/m2/repository/**/*.jar",
|
||||
"lib/**/*.jar"
|
||||
],
|
||||
"importOnFirstTimeStartup": "automatic",
|
||||
"importHint": true,
|
||||
"resourceFilters": [
|
||||
"node_modules",
|
||||
"\\.git"
|
||||
],
|
||||
"sourcePaths": [
|
||||
"src",
|
||||
"{user.home}/.config/jdtls/m2/repository/**/*.jar"
|
||||
]
|
||||
},
|
||||
"sources": {
|
||||
"organizeImports": {
|
||||
"starThreshold": 99,
|
||||
"staticStarThreshold": 99
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"gradle": {
|
||||
"wrapper": {
|
||||
"checksums": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"import": {
|
||||
"maven": {
|
||||
"enabled": true,
|
||||
"offline": {
|
||||
"enabled": false
|
||||
},
|
||||
"disableTestClasspathFlag": false
|
||||
},
|
||||
"gradle": {
|
||||
"enabled": false,
|
||||
"wrapper": {
|
||||
"enabled": true
|
||||
},
|
||||
"version": "",
|
||||
"home": "abs(static/gradle-7.3.3)",
|
||||
"java": {
|
||||
"home": "abs(static/launch_jres/17.0.6-linux-x86_64)"
|
||||
},
|
||||
"offline": {
|
||||
"enabled": false
|
||||
},
|
||||
"arguments": [],
|
||||
"jvmArguments": [],
|
||||
"user": {
|
||||
"home": ""
|
||||
},
|
||||
"annotationProcessing": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"exclusions": [
|
||||
"**/node_modules/**",
|
||||
"**/.metadata/**",
|
||||
"**/archetype-resources/**",
|
||||
"**/META-INF/maven/**"
|
||||
],
|
||||
"generatesMetadataFilesAtProjectRoot": false
|
||||
},
|
||||
"maven": {
|
||||
"downloadSources": true,
|
||||
"updateSnapshots": true
|
||||
},
|
||||
"silentNotification": true,
|
||||
"contentProvider": {
|
||||
"preferred": "fernflower"
|
||||
},
|
||||
"signatureHelp": {
|
||||
"enabled": true,
|
||||
"description": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"completion": {
|
||||
"enabled": true,
|
||||
"engine": "ecj",
|
||||
"matchCase": "firstletter",
|
||||
"maxResults": 25,
|
||||
"guessMethodArguments": true,
|
||||
"lazyResolveTextEdit": {
|
||||
"enabled": true
|
||||
},
|
||||
"postfix": {
|
||||
"enabled": true
|
||||
},
|
||||
"favoriteStaticMembers": [
|
||||
"org.junit.Assert.*",
|
||||
"org.junit.Assume.*",
|
||||
"org.junit.jupiter.api.Assertions.*",
|
||||
"org.junit.jupiter.api.Assumptions.*",
|
||||
"org.junit.jupiter.api.DynamicContainer.*",
|
||||
"org.junit.jupiter.api.DynamicTest.*"
|
||||
],
|
||||
"importOrder": [
|
||||
"#",
|
||||
"java",
|
||||
"javax",
|
||||
"org",
|
||||
"com"
|
||||
]
|
||||
},
|
||||
"references": {
|
||||
"includeAccessors": true,
|
||||
"includeDecompiledSources": true
|
||||
},
|
||||
"codeGeneration": {
|
||||
"toString": {
|
||||
"template": "${object.className}{${member.name()}=${member.value}, ${otherMembers}}"
|
||||
},
|
||||
"insertionLocation": "afterCursor",
|
||||
"useBlocks": true
|
||||
},
|
||||
"implementationsCodeLens": {
|
||||
"enabled": true
|
||||
},
|
||||
"referencesCodeLens": {
|
||||
"enabled": true
|
||||
},
|
||||
"progressReports": {
|
||||
"enabled": false
|
||||
},
|
||||
"saveActions": {
|
||||
"organizeImports": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Java LSP Client",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"autoload": false,
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Python imports
|
||||
from os import path
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .response_handler import JavaHandler
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
dirPth = path.dirname( path.realpath(__file__) )
|
||||
with open(f"{dirPth}/config/lsp-server-config.json", "r") as f:
|
||||
config = f.read()
|
||||
event = Event_Factory.create_event("register_lsp_client",
|
||||
lang_id = "java",
|
||||
lang_config = config,
|
||||
handler = JavaHandler
|
||||
)
|
||||
self.emit_to("lsp_manager", event)
|
||||
|
||||
def unload(self):
|
||||
event = Event_Factory.create_event("unregister_lsp_client",
|
||||
lang_id = "java"
|
||||
)
|
||||
self.emit_to("lsp_manager", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -0,0 +1 @@
|
||||
from .java import JavaHandler
|
||||
@@ -0,0 +1,51 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from lsp_manager.response_handlers.default import DefaultHandler
|
||||
|
||||
|
||||
|
||||
class JavaHandler(DefaultHandler):
|
||||
"""Java-specific: overrides definition, handles classFileContents."""
|
||||
|
||||
def handle(self, method: str, response, controller):
|
||||
match method:
|
||||
case "textDocument/definition":
|
||||
self._handle_definition(response, controller)
|
||||
case "java/classFileContents":
|
||||
self._handle_class_file_contents(response)
|
||||
case _:
|
||||
super().handle(method, response, controller)
|
||||
|
||||
def _handle_definition(self, response, controller):
|
||||
if not response: return
|
||||
|
||||
uri = response[0]["uri"]
|
||||
if "jdt://" in uri:
|
||||
controller._lsp_java_class_file_contents(uri)
|
||||
return
|
||||
|
||||
self._prompt_goto_request(uri, response[0]["range"])
|
||||
|
||||
def _handle_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)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
LSP Clients Module
|
||||
"""
|
||||
@@ -0,0 +1,56 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ..config import get_lsp_init_config
|
||||
from ..dto.code.lsp.lsp_messages import get_message_str
|
||||
from ..dto.code.lsp.lsp_message_structs import \
|
||||
LSPResponseTypes, ClientRequest, ClientNotification
|
||||
from .lsp_client_websocket import LSPClientWebsocket
|
||||
|
||||
|
||||
|
||||
class LSPClient(LSPClientWebsocket):
|
||||
def __init__(self):
|
||||
super(LSPClient, self).__init__()
|
||||
|
||||
self._socket: str = ""
|
||||
self._language: str = ""
|
||||
self._workspace_path: str = ""
|
||||
self._message_id: int = -1
|
||||
self._event_history: dict[int, str] = {}
|
||||
self._init_params: dict = get_lsp_init_config()
|
||||
self._init_opts: dict[str, str] = {}
|
||||
self.doc_vers: dict[str, int] = {}
|
||||
|
||||
|
||||
def set_language(self, language: str):
|
||||
self._language = language
|
||||
|
||||
def set_workspace_path(self, workspace_path: str):
|
||||
self._workspace_path = workspace_path
|
||||
|
||||
def set_init_opts(self, init_opts: dict[str, str]):
|
||||
self._init_opts = init_opts
|
||||
|
||||
def set_socket(self, socket: str):
|
||||
self._socket = socket
|
||||
|
||||
def unset_socket(self):
|
||||
self._socket = ""
|
||||
|
||||
def send_notification(self, method: str, params: dict = {}):
|
||||
self._send_message( ClientNotification(method, params) )
|
||||
|
||||
def send_request(self, method: str, params: dict = {}):
|
||||
self._message_id += 1
|
||||
self._event_history[self._message_id] = method
|
||||
self._send_message( ClientRequest(self._message_id, method, params) )
|
||||
|
||||
def get_event_by_id(self, message_id: int) -> str:
|
||||
if not message_id in self._event_history: return
|
||||
return self._event_history[message_id]
|
||||
|
||||
def handle_lsp_response(self, lsp_response: LSPResponseTypes | dict):
|
||||
raise NotImplementedError
|
||||
@@ -3,12 +3,13 @@
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .lsp_controller_events import LSPControllerEvents
|
||||
from libs.dto.code.lsp.lsp_message_structs import ClientRequest, ClientNotification
|
||||
from ..dto.code.lsp.lsp_message_structs import ClientRequest, ClientNotification
|
||||
|
||||
from .lsp_client_events import LSPClientEvents
|
||||
|
||||
|
||||
|
||||
class LSPControllerBase(LSPControllerEvents):
|
||||
class LSPClientBase(LSPClientEvents):
|
||||
def _send_message(self, data: ClientRequest or ClientNotification):
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# Python imports
|
||||
import os
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ..dto.code.lsp.lsp_messages import get_message_obj
|
||||
from ..dto.code.lsp.lsp_messages import didopen_notification
|
||||
from ..dto.code.lsp.lsp_messages import didsave_notification
|
||||
from ..dto.code.lsp.lsp_messages import didclose_notification
|
||||
from ..dto.code.lsp.lsp_messages import didchange_notification
|
||||
from ..dto.code.lsp.lsp_messages import didchange_notification_range
|
||||
from ..dto.code.lsp.lsp_messages import completion_request
|
||||
from ..dto.code.lsp.lsp_messages import definition_request
|
||||
from ..dto.code.lsp.lsp_messages import implementation_request
|
||||
from ..dto.code.lsp.lsp_messages import references_request
|
||||
from ..dto.code.lsp.lsp_messages import symbols_request
|
||||
from ..dto.code.lsp.lsp_messages import shutdown_request
|
||||
from ..dto.code.lsp.lsp_messages import exit_request
|
||||
|
||||
|
||||
|
||||
class LSPClientEvents:
|
||||
def send_initialize_message(self):
|
||||
folder_name = os.path.basename(self._workspace_path)
|
||||
workspace_uri = f"file://{self._workspace_path}"
|
||||
|
||||
self._init_params["processId"] = None
|
||||
self._init_params["rootPath"] = self._workspace_path
|
||||
self._init_params["rootUri"] = workspace_uri
|
||||
self._init_params["workspaceFolders"] = [
|
||||
{
|
||||
"name": folder_name,
|
||||
"uri": workspace_uri
|
||||
}
|
||||
]
|
||||
|
||||
self._init_params["initializationOptions"] = self._init_opts
|
||||
self.send_request("initialize", self._init_params)
|
||||
|
||||
def send_initialized_notification(self):
|
||||
self.send_notification("initialized")
|
||||
|
||||
def send_shutdown_request(self):
|
||||
self.send_request("shutdown")
|
||||
|
||||
def send_exit_notification(self):
|
||||
self.send_notification("exit")
|
||||
|
||||
def _lsp_did_open(self, data: dict):
|
||||
method = "textDocument/didOpen"
|
||||
params = didopen_notification["params"]
|
||||
self.doc_vers[ data["uri"] ] = -1
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["textDocument"]["languageId"] = data["language_id"]
|
||||
params["textDocument"]["text"] = data["text"]
|
||||
|
||||
self.send_notification( method, params )
|
||||
|
||||
def _lsp_did_save(self, data: dict):
|
||||
method = "textDocument/didSave"
|
||||
params = didsave_notification["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["text"] = data["text"]
|
||||
|
||||
self.send_notification( method, params )
|
||||
|
||||
def _lsp_did_close(self, data: dict):
|
||||
method = "textDocument/didClose"
|
||||
params = didclose_notification["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
|
||||
self.send_notification( method, params )
|
||||
|
||||
def _lsp_did_change(self, data: dict):
|
||||
method = "textDocument/didChange"
|
||||
params = didchange_notification["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["textDocument"]["languageId"] = data["language_id"]
|
||||
params["textDocument"]["version"] = data["version"]
|
||||
|
||||
contentChanges = params["contentChanges"][0]
|
||||
contentChanges["text"] = data["text"]
|
||||
|
||||
self.send_notification( method, params )
|
||||
|
||||
def _lsp_did_change_range(self, data: dict):
|
||||
method = "textDocument/didChange"
|
||||
params = didchange_notification_range["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["textDocument"]["languageId"] = data["language_id"]
|
||||
params["textDocument"]["version"] = data["version"]
|
||||
|
||||
contentChanges = params["contentChanges"][0]
|
||||
start = contentChanges["range"]["start"]
|
||||
end = contentChanges["range"]["end"]
|
||||
contentChanges["text"] = data["text"]
|
||||
start["line"] = data["line"]
|
||||
start["character"] = data["column"]
|
||||
end["line"] = data["end_line"]
|
||||
end["character"] = data["end_column"]
|
||||
|
||||
self.send_notification( method, params )
|
||||
|
||||
def _lsp_definition(self, data: dict):
|
||||
method = "textDocument/definition"
|
||||
params = definition_request["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["textDocument"]["languageId"] = data["language_id"]
|
||||
params["textDocument"]["version"] = data["version"]
|
||||
params["position"]["line"] = data["line"]
|
||||
params["position"]["character"] = data["column"]
|
||||
|
||||
self.send_request( method, params )
|
||||
|
||||
def _lsp_implementation(self, data: dict):
|
||||
method = "textDocument/implementation"
|
||||
params = implementation_request["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["position"]["line"] = data["line"]
|
||||
params["position"]["character"] = data["column"]
|
||||
|
||||
self.send_request( method, params )
|
||||
|
||||
def _lsp_references(self, data: dict):
|
||||
method = "textDocument/references"
|
||||
params = references_request["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["textDocument"]["languageId"] = data["language_id"]
|
||||
params["textDocument"]["version"] = data["version"]
|
||||
params["position"]["line"] = data["line"]
|
||||
params["position"]["character"] = data["column"]
|
||||
|
||||
self.send_request( method, params )
|
||||
|
||||
def _lsp_completion(self, data: dict):
|
||||
method = "textDocument/completion"
|
||||
params = completion_request["params"]
|
||||
|
||||
params["textDocument"]["uri"] = data["uri"]
|
||||
params["position"]["line"] = data["line"]
|
||||
params["position"]["character"] = data["column"]
|
||||
|
||||
self.send_request( method, params )
|
||||
|
||||
def _lsp_java_class_file_contents(self, uri: str):
|
||||
method = "java/classFileContents"
|
||||
params = {
|
||||
"uri": uri
|
||||
}
|
||||
|
||||
self.send_request( method, params )
|
||||
@@ -0,0 +1,57 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
# from libs import websockets
|
||||
from ..dto.code.lsp.lsp_messages import get_message_str, get_message_obj
|
||||
from ..dto.code.lsp.lsp_message_structs import \
|
||||
LSPResponseTypes, ClientRequest, ClientNotification, \
|
||||
LSPResponseRequest, LSPResponseNotification, LSPIDResponseNotification
|
||||
|
||||
from .lsp_client_base import LSPClientBase
|
||||
from .websocket import Websocket
|
||||
|
||||
|
||||
|
||||
class LSPClientWebsocket(LSPClientBase):
|
||||
def _send_message(self, data: ClientRequest | ClientNotification):
|
||||
if not data: return
|
||||
|
||||
message_str = get_message_str(data)
|
||||
message_size = len(message_str)
|
||||
message = f"Content-Length: {message_size}\r\n\r\n{message_str}"
|
||||
|
||||
logger.debug(f"Client: {message_str}")
|
||||
self.websocket.send(message_str)
|
||||
|
||||
def start_client(self):
|
||||
self.websocket = Websocket()
|
||||
self.websocket.set_socket(self._socket)
|
||||
self.websocket.set_callback(self._monitor_lsp_response)
|
||||
self.websocket.start_client()
|
||||
|
||||
return self.websocket
|
||||
|
||||
def stop_client(self):
|
||||
if not hasattr(self, "websocket"): return
|
||||
self.websocket.close_client()
|
||||
|
||||
def _monitor_lsp_response(self, data: dict | None):
|
||||
if not data: return {}
|
||||
|
||||
message = get_message_obj(data)
|
||||
keys = message.keys()
|
||||
lsp_response = data
|
||||
|
||||
if "result" in keys:
|
||||
lsp_response = LSPResponseRequest(**get_message_obj(data))
|
||||
|
||||
if "method" in keys:
|
||||
lsp_response = LSPResponseNotification(**get_message_obj(data)) if not "id" in keys else LSPIDResponseNotification( **get_message_obj(data) )
|
||||
|
||||
if isinstance(lsp_response, str):
|
||||
lsp_response = get_message_obj(lsp_response)
|
||||
|
||||
GLib.idle_add(self.handle_lsp_response, lsp_response)
|
||||
@@ -9,7 +9,7 @@ from ..libs import websocket
|
||||
|
||||
|
||||
|
||||
class WebsocketClient:
|
||||
class Websocket:
|
||||
def __init__(self):
|
||||
self.ws = None
|
||||
self._socket = None
|
||||
@@ -59,4 +59,4 @@ class WebsocketClient:
|
||||
on_error = self.on_error,
|
||||
on_close = self.on_close)
|
||||
|
||||
self.ws.run_forever(reconnect = 0.5)
|
||||
self.ws.run_forever(reconnect = 0.5)
|
||||
@@ -0,0 +1,60 @@
|
||||
# Python imports
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .config import get_lsp_connect_timout
|
||||
from .mixins.client_manager_events_mixin import ClientManagerEventsMixin
|
||||
from .client.lsp_client import LSPClient
|
||||
|
||||
|
||||
|
||||
class ClientManager(ClientManagerEventsMixin):
|
||||
def __init__(self):
|
||||
super(ClientManager, self).__init__()
|
||||
|
||||
self._cache_refresh_timeout_id: int = None
|
||||
|
||||
self.executor: ThreadPoolExecutor = ThreadPoolExecutor(max_workers = 1)
|
||||
self.active_language_id: str = ""
|
||||
self.clients: dict = {}
|
||||
|
||||
|
||||
def create_client(
|
||||
self,
|
||||
lang_id: str,
|
||||
workspace_path: str,
|
||||
init_opts: dict[str, str],
|
||||
address: str = "127.0.0.1",
|
||||
port: str = "9999"
|
||||
) -> LSPClient:
|
||||
if lang_id in self.clients: return None
|
||||
|
||||
uri = f"ws://{address}:{port}/{lang_id}?workspace={workspace_path}"
|
||||
client = LSPClient()
|
||||
|
||||
client.set_socket(uri)
|
||||
client.set_language(lang_id)
|
||||
client.set_workspace_path(workspace_path)
|
||||
client.set_init_opts(init_opts)
|
||||
client.start_client()
|
||||
|
||||
if not client.websocket.wait_for_connection(timeout = get_lsp_connect_timout()):
|
||||
logger.error(f"Failed to connect to LSP server for {lang_id}")
|
||||
return None
|
||||
|
||||
self.clients[lang_id] = client
|
||||
|
||||
return client
|
||||
|
||||
def close_client(self, lang_id: str) -> bool:
|
||||
if lang_id not in self.clients: return False
|
||||
|
||||
controller = self.clients.pop(lang_id)
|
||||
controller.stop_client()
|
||||
|
||||
return True
|
||||
|
||||
def get_active_client(self) -> LSPClient:
|
||||
return self.clients[self.active_language_id]
|
||||
87
plugins/code/language_server_clients/lsp_manager/commands.py
Normal file
87
plugins/code/language_server_clients/lsp_manager/commands.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class Commands:
|
||||
lsp_manager: callable = None
|
||||
|
||||
class lsp_manager_toggle:
|
||||
@staticmethod
|
||||
def execute(
|
||||
source_view: GtkSource,
|
||||
char_str: str,
|
||||
modkeys_states: tuple
|
||||
):
|
||||
logger.debug("Command: LSP Manager Toggle")
|
||||
if Commands.lsp_manager.ui_manager.is_visible():
|
||||
Commands.lsp_manager.ui_manager.hide()
|
||||
else:
|
||||
Commands.lsp_manager.ui_manager.show()
|
||||
|
||||
class lsp_references:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: GtkSource,
|
||||
char_str: str,
|
||||
modkeys_states: tuple
|
||||
):
|
||||
logger.debug("Command: LSP References")
|
||||
|
||||
file = view.command.exec("get_current_file")
|
||||
buffer = view.get_buffer()
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = iter.get_line()
|
||||
column = iter.get_line_offset()
|
||||
|
||||
Commands.lsp_manager.client_manager.process_references_definition(
|
||||
file.ftype, file.fpath, line, column
|
||||
)
|
||||
|
||||
class lsp_implementation:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: GtkSource,
|
||||
char_str: str,
|
||||
modkeys_states: tuple
|
||||
):
|
||||
logger.debug("Command: LSP Implements")
|
||||
|
||||
file = view.command.exec("get_current_file")
|
||||
buffer = view.get_buffer()
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = iter.get_line()
|
||||
column = iter.get_line_offset()
|
||||
|
||||
Commands.lsp_manager.client_manager.process_implementation_definition(
|
||||
file.ftype, file.fpath, line, column
|
||||
)
|
||||
|
||||
class lsp_definition:
|
||||
@staticmethod
|
||||
def execute(
|
||||
view: GtkSource,
|
||||
char_str: str,
|
||||
modkeys_states: tuple
|
||||
):
|
||||
logger.debug("Command: LSP Definition (Go-To)")
|
||||
|
||||
file = view.command.exec("get_current_file")
|
||||
buffer = view.get_buffer()
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = iter.get_line()
|
||||
column = iter.get_line_offset()
|
||||
|
||||
Commands.lsp_manager.client_manager.process_definition(
|
||||
file.ftype, file.fpath, line, column
|
||||
)
|
||||
38
plugins/code/language_server_clients/lsp_manager/config.py
Normal file
38
plugins/code/language_server_clients/lsp_manager/config.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# Python imports
|
||||
from os import path
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
LSP_HOST: str = "127.0.0.1"
|
||||
LSP_PORT: int = 9999
|
||||
LSP_CONNECT_TIMOUT: float = 5.0
|
||||
|
||||
|
||||
|
||||
def get_lsp_host_addr() -> str:
|
||||
return LSP_HOST
|
||||
|
||||
def get_lsp_host_port() -> int:
|
||||
return LSP_PORT
|
||||
|
||||
def get_lsp_connect_timout() -> float:
|
||||
return LSP_CONNECT_TIMOUT
|
||||
|
||||
def get_lsp_init_config() -> dict:
|
||||
try:
|
||||
_USER_HOME = path.expanduser('~')
|
||||
_SCRIPT_PTH = path.dirname( path.realpath(__file__) )
|
||||
_LSP_INIT_CONFIG = f"{_SCRIPT_PTH}/configs/initialize-params-slim.json"
|
||||
|
||||
with open(_LSP_INIT_CONFIG) as file:
|
||||
data = file.read()
|
||||
return json.loads(data)
|
||||
except Exception as e:
|
||||
logger.error( f"LSP Controller: {_LSP_INIT_CONFIG}\n\t\t{repr(e)}" )
|
||||
|
||||
return {}
|
||||
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
Libs Code DTO(s) Events Package
|
||||
"""
|
||||
|
||||
from .lsp_event import LspEvent
|
||||
|
||||
from .register_lsp_client_event import RegisterLspClientEvent
|
||||
from .unregister_lsp_client_event import UnregisterLspClientEvent
|
||||
@@ -0,0 +1,13 @@
|
||||
# Python imports
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.dto.code.events import CodeEvent
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class LspEvent(CodeEvent):
|
||||
...
|
||||
@@ -0,0 +1,17 @@
|
||||
# Python imports
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ....response_handlers.base_handler import BaseHandler
|
||||
|
||||
from .lsp_event import LspEvent
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class RegisterLspClientEvent(LspEvent):
|
||||
lang_id: str = ""
|
||||
lang_config: str = "{}"
|
||||
handler: BaseHandler = None
|
||||
@@ -0,0 +1,13 @@
|
||||
# Python imports
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .lsp_event import LspEvent
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class UnregisterLspClientEvent(LspEvent):
|
||||
lang_id: str = ""
|
||||
@@ -96,10 +96,10 @@ didchange_notification_range = {
|
||||
"uri": "file://",
|
||||
"languageId": "python",
|
||||
"version": 1,
|
||||
"text": ""
|
||||
},
|
||||
"contentChanges": [
|
||||
{
|
||||
"text": "",
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
@@ -108,9 +108,8 @@ didchange_notification_range = {
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 1,
|
||||
},
|
||||
"rangeLength": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -125,19 +124,11 @@ completion_request = {
|
||||
"method": "textDocument/completion",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": "file://",
|
||||
"languageId": "python",
|
||||
"version": 1,
|
||||
"text": ""
|
||||
"uri": "file://"
|
||||
},
|
||||
"position": {
|
||||
"line": 5,
|
||||
"character": 12,
|
||||
"offset": 0
|
||||
},
|
||||
"contet": {
|
||||
"triggerKind": 3,
|
||||
"triggerCharacter": ""
|
||||
"character": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +150,19 @@ definition_request = {
|
||||
}
|
||||
}
|
||||
|
||||
implementation_request = {
|
||||
"method": "textDocument/implementation",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": "file://"
|
||||
},
|
||||
"position": {
|
||||
"line": 5,
|
||||
"character": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
references_request = {
|
||||
"method": "textDocument/references",
|
||||
"params": {
|
||||
@@ -179,7 +183,6 @@ references_request = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
symbols_request = {
|
||||
"method": "textDocument/documentSymbol",
|
||||
"params": {
|
||||
@@ -190,4 +193,15 @@ symbols_request = {
|
||||
"text": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shutdown_request = {
|
||||
"method": "shutdown",
|
||||
"params": None
|
||||
}
|
||||
|
||||
exit_request = {
|
||||
"method": "exit",
|
||||
"params": None
|
||||
}
|
||||
|
||||
162
plugins/code/language_server_clients/lsp_manager/lsp_manager.py
Normal file
162
plugins/code/language_server_clients/lsp_manager/lsp_manager.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from libs.controllers.controller_base import ControllerBase
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from .dto.code.events import \
|
||||
RegisterLspClientEvent, UnregisterLspClientEvent
|
||||
from .dto.code.lsp.lsp_message_structs import \
|
||||
LSPResponseTypes, LSPResponseRequest, LSPResponseNotification
|
||||
|
||||
from .ui_manager import UIManager
|
||||
|
||||
from .provider import Provider
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
from .client_manager import ClientManager
|
||||
from .response_handlers.response_registry import ResponseRegistry
|
||||
|
||||
|
||||
|
||||
class LSPManager(ControllerBase):
|
||||
def __init__(self):
|
||||
super(LSPManager, self).__init__()
|
||||
|
||||
self._init()
|
||||
self._load_widgets()
|
||||
self._do_bind_mapping()
|
||||
|
||||
|
||||
def _init(self):
|
||||
self.provider: Provider = Provider()
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
self.client_manager: ClientManager = ClientManager()
|
||||
self.response_registry: ResponseRegistry = ResponseRegistry()
|
||||
|
||||
def _load_widgets(self):
|
||||
self.ui_manager: LSPManagerUI = UIManager()
|
||||
self.ui_manager.connect('create-client', self._on_create_client)
|
||||
self.ui_manager.connect('close-client', self._on_close_client)
|
||||
|
||||
def _do_bind_mapping(self):
|
||||
self.response_cache.set_lsp_manager_client(self.client_manager)
|
||||
self.provider.response_cache = self.response_cache
|
||||
self.response_registry.set_event_hub(
|
||||
self.emit, self.emit_to, self.provider
|
||||
)
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.RegisterLspClientEvent):
|
||||
self.response_registry.register_handler(event.lang_id, event.handler)
|
||||
self.ui_manager.add_client_listing(event.lang_id, event.lang_config)
|
||||
elif isinstance(event, Code_Event_Types.UnregisterLspClientEvent):
|
||||
self.response_registry.unregister_handler(event.lang_id)
|
||||
self.ui_manager.remove_client_listing(event.lang_id)
|
||||
|
||||
def _on_create_client(self, ui, lang_id: str, workspace_path: str) -> bool:
|
||||
init_opts = ui.get_init_opts(lang_id)
|
||||
result = self.create_client(
|
||||
lang_id,
|
||||
workspace_path,
|
||||
init_opts,
|
||||
ui.adddress_entry.get_text(),
|
||||
f"{ int( ui.adddress_port.get_value() ) }"
|
||||
)
|
||||
|
||||
if result:
|
||||
ui.toggle_client_buttons(show_close = True)
|
||||
|
||||
return result
|
||||
|
||||
def _on_close_client(self, ui, lang_id: str) -> bool:
|
||||
result = self.close_client(lang_id)
|
||||
|
||||
if result:
|
||||
ui.toggle_client_buttons(show_close = False)
|
||||
|
||||
return result
|
||||
|
||||
def handle_destroy(self):
|
||||
self.ui_manager.disconnect_by_func(self._on_create_client)
|
||||
self.ui_manager.disconnect_by_func(self._on_close_client)
|
||||
|
||||
def create_client(
|
||||
self,
|
||||
lang_id: str,
|
||||
workspace_path: str,
|
||||
init_opts: dict[str, str],
|
||||
address: str,
|
||||
port: str
|
||||
) -> bool:
|
||||
client = self.client_manager.create_client(
|
||||
lang_id, workspace_path, init_opts, address, port
|
||||
)
|
||||
handler = self.response_registry.get_handler(lang_id)
|
||||
self.client_manager.active_language_id = lang_id
|
||||
|
||||
if not client or not handler:
|
||||
logger.error(f"LSP Manager: Either 'client' or 'handler' didn't get created...'")
|
||||
self.close_client(lang_id)
|
||||
return False
|
||||
|
||||
handler.set_context(self.response_registry)
|
||||
handler.set_response_cache(self.response_cache)
|
||||
|
||||
client.handle_lsp_response = self.server_response
|
||||
|
||||
return True
|
||||
|
||||
def close_client(self, lang_id: str) -> bool:
|
||||
controller = self.client_manager.get_active_client()
|
||||
controller.send_shutdown_request()
|
||||
|
||||
def _close():
|
||||
self.client_manager.close_client(lang_id)
|
||||
self.response_registry.close_handler(lang_id)
|
||||
|
||||
return False
|
||||
|
||||
GLib.timeout_add(5000, _close)
|
||||
|
||||
return True
|
||||
|
||||
def server_response(self, lsp_response: LSPResponseTypes | dict):
|
||||
logger.debug(f"LSP Response: { lsp_response }")
|
||||
|
||||
if isinstance(lsp_response, dict):
|
||||
if not self.client_manager.active_language_id in self.client_manager.clients:
|
||||
logger.debug(f"No LSP client for '{self.client_manager.active_language_id}', skipping 'server_response'")
|
||||
return
|
||||
|
||||
controller = self.client_manager.get_active_client()
|
||||
if "type" in lsp_response and lsp_response["type"] == "connected":
|
||||
controller.send_initialize_message()
|
||||
|
||||
return
|
||||
|
||||
if isinstance(lsp_response, LSPResponseRequest):
|
||||
if not self.client_manager.active_language_id in self.client_manager.clients:
|
||||
logger.debug(f"No LSP client for '{self.client_manager.active_language_id}', skipping 'server_response'")
|
||||
return
|
||||
|
||||
controller = self.client_manager.get_active_client()
|
||||
event = controller.get_event_by_id(lsp_response.id)
|
||||
handler = self.response_registry.get_handler(
|
||||
self.client_manager.active_language_id, event
|
||||
)
|
||||
|
||||
if not handler: return
|
||||
handler.handle(event, lsp_response.result, controller)
|
||||
elif isinstance(lsp_response, LSPResponseNotification):
|
||||
handler = self.response_registry.get_handler("default", lsp_response.method)
|
||||
|
||||
if not handler: return
|
||||
|
||||
handler.set_context(self.response_registry)
|
||||
handler.set_response_cache(self.response_cache)
|
||||
handler.handle(lsp_response.method, lsp_response.params, None)
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "LSP Manager",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"pre_launch": true,
|
||||
"autoload": false,
|
||||
"requests": {}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Code_Event_Types
|
||||
|
||||
|
||||
|
||||
class ClientManagerEventsMixin:
|
||||
def _get_controller(self, lang_id, action: str):
|
||||
controller = self.clients.get(lang_id)
|
||||
if not controller:
|
||||
logger.debug(f"No LSP client for '{lang_id}', skipping {action}...")
|
||||
|
||||
return controller
|
||||
|
||||
def _uri(self, fpath: str) -> str:
|
||||
return fpath if fpath.startswith("file://") else f"file://{fpath}"
|
||||
|
||||
def _text(self, buffer, *, hidden = False):
|
||||
return buffer.get_text(
|
||||
*buffer.get_bounds(),
|
||||
include_hidden_chars=hidden
|
||||
)
|
||||
|
||||
def _version(self, controller, uri, bump = False):
|
||||
if bump:
|
||||
controller.doc_vers[uri] = controller.doc_vers.get(uri, -1) + 1
|
||||
|
||||
return controller.doc_vers.get(uri, 0)
|
||||
|
||||
def _activate(self, lang_id):
|
||||
self.active_language_id = lang_id
|
||||
|
||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||
f = event.file
|
||||
if not (c := self._get_controller(f.ftype, "didOpen")): return
|
||||
|
||||
uri = self._uri(f.fpath)
|
||||
self._activate(f.ftype)
|
||||
|
||||
c._lsp_did_open({
|
||||
"uri": uri,
|
||||
"language_id": f.ftype,
|
||||
"text": self._text(f.buffer),
|
||||
})
|
||||
|
||||
def process_file_close(self, event: Code_Event_Types.RemovedFileEvent):
|
||||
f = event.file
|
||||
if not (c := self._get_controller(f.ftype, "didClose")): return
|
||||
|
||||
uri = self._uri(f.fpath)
|
||||
c.doc_vers.pop(uri, None)
|
||||
c._lsp_did_close({"uri": uri})
|
||||
|
||||
def process_file_save(self, event: Code_Event_Types.SavedFileEvent):
|
||||
f = event.file
|
||||
if not (c := self._get_controller(f.ftype, "didSave")): return
|
||||
|
||||
uri = self._uri(f.fpath)
|
||||
self._activate(f.ftype)
|
||||
|
||||
c._lsp_did_save({
|
||||
"uri": uri,
|
||||
"text": self._text(f.buffer),
|
||||
})
|
||||
|
||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||
f = event.file
|
||||
if not (c := self._get_controller(f.ftype, "didChange")): return
|
||||
|
||||
uri = self._uri(f.fpath)
|
||||
self._activate(f.ftype)
|
||||
|
||||
version = self._version(c, uri, bump = True)
|
||||
|
||||
c._lsp_did_change({
|
||||
"uri": uri,
|
||||
"language_id": f.ftype,
|
||||
"version": version,
|
||||
"text": self._text(f.buffer, hidden = True),
|
||||
})
|
||||
|
||||
it = f.buffer.get_iter_at_mark(f.buffer.get_insert())
|
||||
self._set_cache_refresh_trigger(
|
||||
f.ftype, f.fpath,
|
||||
it.get_line(),
|
||||
it.get_line_offset() + 1
|
||||
)
|
||||
|
||||
def _iter_pos(self, it):
|
||||
return it.get_line(), it.get_line_offset()
|
||||
|
||||
def process_file_text_inserted(self, event: Code_Event_Types.TextInsertedEvent):
|
||||
f = event.file
|
||||
if not (c := self._get_controller(f.ftype, "didChange")): return
|
||||
|
||||
uri = self._uri(f.fpath)
|
||||
self._activate(f.ftype)
|
||||
|
||||
start_it = event.location.copy()
|
||||
end_it = event.location.copy()
|
||||
|
||||
if event.length > 1:
|
||||
start_it.backward_chars(event.length)
|
||||
|
||||
sl, sc = self._iter_pos(start_it)
|
||||
el, ec = self._iter_pos(end_it)
|
||||
sc -= 0 if event.length > 1 else 1
|
||||
ec -= 1
|
||||
|
||||
version = self._version(c, uri, bump = True)
|
||||
|
||||
c._lsp_did_change_range({
|
||||
"uri": uri,
|
||||
"language_id": f.ftype,
|
||||
"version": version,
|
||||
"text": event.text,
|
||||
"line": sl,
|
||||
"column": sc,
|
||||
"end_line": el,
|
||||
"end_column": ec,
|
||||
})
|
||||
|
||||
it = event.buffer.get_iter_at_mark(event.buffer.get_insert())
|
||||
self._set_cache_refresh_trigger(
|
||||
f.ftype, f.fpath, *self._iter_pos(it)
|
||||
)
|
||||
|
||||
def process_file_delete_range(self, event: Code_Event_Types.DeleteRangeEvent):
|
||||
f = event.file
|
||||
if not (c := self._get_controller(f.ftype, "didChange")): return
|
||||
|
||||
uri = self._uri(f.fpath)
|
||||
self._activate(f.ftype)
|
||||
|
||||
start_it, end_it = event.start.copy(), event.end.copy()
|
||||
if start_it.compare(end_it) > 0:
|
||||
start_it, end_it = end_it, start_it
|
||||
|
||||
sl, sc = self._iter_pos(start_it)
|
||||
el, ec = self._iter_pos(end_it)
|
||||
|
||||
version = self._version(c, uri, bump = True)
|
||||
|
||||
c._lsp_did_change_range({
|
||||
"uri": uri,
|
||||
"language_id": f.ftype,
|
||||
"version": version,
|
||||
"text": "",
|
||||
"line": sl,
|
||||
"column": sc,
|
||||
"end_line": el,
|
||||
"end_column": ec,
|
||||
})
|
||||
|
||||
it = event.buffer.get_iter_at_mark(event.buffer.get_insert())
|
||||
self._set_cache_refresh_trigger(
|
||||
f.ftype, f.fpath, *self._iter_pos(it)
|
||||
)
|
||||
|
||||
def _request(self, method, lang_id, fpath, **extra):
|
||||
if not (c := self._get_controller(lang_id, method)): return
|
||||
|
||||
uri = self._uri(fpath)
|
||||
self._activate(lang_id)
|
||||
|
||||
payload = {
|
||||
"uri": uri,
|
||||
"language_id": lang_id,
|
||||
"version": self._version(c, uri),
|
||||
**extra
|
||||
}
|
||||
|
||||
getattr(c, method)(payload)
|
||||
|
||||
def process_definition(self, lang_id, fpath, line, column):
|
||||
self._request("_lsp_definition", lang_id, fpath, line = line, column = column)
|
||||
|
||||
def process_implementation_definition(self, lang_id, fpath, line, column):
|
||||
self._request("_lsp_implementation", lang_id, fpath, line = line, column = column)
|
||||
|
||||
def process_references_definition(self, lang_id, fpath, line, column):
|
||||
self._request("_lsp_references", lang_id, fpath, line = line, column = column)
|
||||
|
||||
def process_completion_request(self, lang_id, fpath, line, column):
|
||||
self._request("_lsp_completion", lang_id, fpath, line = line, column = column)
|
||||
|
||||
def _set_cache_refresh_trigger(self, lang_id, fpath, line, column):
|
||||
self.process_completion_request(lang_id, fpath, line, column)
|
||||
@@ -0,0 +1,72 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GObject
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class UIManagerClientsMixin:
|
||||
def _create_client(self, widget, sibling):
|
||||
if not self.source_view: return
|
||||
|
||||
buffer = self.source_view.get_buffer()
|
||||
lang_id = self.combo_box.get_active_text()
|
||||
|
||||
if not lang_id: return
|
||||
|
||||
workspace_dir = self.path_entry.get_text()
|
||||
self.emit('create-client', lang_id, workspace_dir)
|
||||
|
||||
def _close_client(self, widget, sibling):
|
||||
lang_id = self.combo_box.get_active_text()
|
||||
|
||||
if not lang_id: return
|
||||
|
||||
self.emit('close-client', lang_id)
|
||||
|
||||
def set_source_view_text(self, workspace_dir: str):
|
||||
lang_id = self.combo_box.get_active_text()
|
||||
|
||||
if not lang_id: return
|
||||
|
||||
json_str = self.client_configs[lang_id]\
|
||||
.replace("{workspace.folder}", workspace_dir)\
|
||||
.replace("{user.home}", self._USER_HOME)
|
||||
|
||||
self.source_view.get_buffer().set_text(json_str, -1)
|
||||
|
||||
def add_client_listing(self, lang_id: str, lang_config: str):
|
||||
self.combo_box.append_text(lang_id)
|
||||
self.client_configs[lang_id] = lang_config
|
||||
|
||||
def remove_client_listing(self, lang_id: str):
|
||||
model = self.combo_box.get_model()
|
||||
|
||||
for i, row in enumerate(model):
|
||||
if row[0] == lang_id:
|
||||
self.combo_box.remove(i)
|
||||
break
|
||||
|
||||
self.client_configs.pop(lang_id, None)
|
||||
|
||||
def toggle_client_buttons(self, show_close: bool):
|
||||
self.create_client_bttn.set_visible(not show_close)
|
||||
self.close_client_bttn.set_visible(show_close)
|
||||
|
||||
def get_init_opts(self, lang_id: str) -> dict:
|
||||
if not lang_id or lang_id not in self.client_configs: return {}
|
||||
|
||||
try:
|
||||
buffer = self.source_view.get_buffer()
|
||||
json_str = buffer.get_text(*buffer.get_bounds(), -1)
|
||||
lang_config = json.loads(json_str)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Invalid JSON for {lang_id}: {e}")
|
||||
return {}
|
||||
|
||||
return lang_config.get("initialization-options", {})
|
||||
@@ -0,0 +1,77 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
from gi.repository import GLib
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class UIManagerEventsMixin:
|
||||
def _setup_signals(self):
|
||||
self.connect("show", self._handle_show)
|
||||
self.connect("destroy", self._handle_destroy)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _handle_show(self, widget):
|
||||
GLib.idle_add(self.path_entry.grab_focus)
|
||||
|
||||
def _handle_destroy(self, widget):
|
||||
self.disconnect_by_func(self._handle_show)
|
||||
self.disconnect_by_func(self._handle_destroy)
|
||||
self.path_bttn.disconnect_by_func(self._file_set)
|
||||
self.combo_box.disconnect_by_func(self._on_combo_changed)
|
||||
self.hide_bttn.disconnect(self.hide_bttn_id)
|
||||
self.create_client_bttn.disconnect_by_func(self._create_client)
|
||||
self.close_client_bttn.disconnect_by_func(self._close_client)
|
||||
|
||||
def _map_resize(self, widget, parent):
|
||||
parent_x, \
|
||||
parent_y = parent.get_position()
|
||||
parent_width, \
|
||||
parent_height = parent.get_size()
|
||||
|
||||
if parent_width == 0 or parent_height == 0: return
|
||||
|
||||
width = int(parent_width * 0.75)
|
||||
height = int(parent_height * 0.75)
|
||||
|
||||
widget.resize(width, height)
|
||||
|
||||
x = parent_x + (parent_width - width) // 2
|
||||
y = parent_y + (parent_height - height) // 2
|
||||
widget.move(x, y)
|
||||
|
||||
def _path_changed(self, widget, buttons_widget):
|
||||
if not widget.get_text():
|
||||
self.path_bttn.unselect_all()
|
||||
self.path_bttn.emit("file-set")
|
||||
buttons_widget.hide()
|
||||
return
|
||||
|
||||
self.set_source_view_text( self.path_entry.get_text() )
|
||||
buttons_widget.show()
|
||||
|
||||
def _file_set(self, widget):
|
||||
fname = widget.get_filename()
|
||||
fname = "" if not fname else fname
|
||||
self.path_entry.set_text(fname)
|
||||
|
||||
lang_id = self.combo_box.get_active_text()
|
||||
if not lang_id or lang_id not in self.client_configs: return
|
||||
|
||||
self.set_source_view_text(
|
||||
"{workspace.folder}" if not fname else fname
|
||||
)
|
||||
|
||||
def _on_combo_changed(self, combo: Gtk.ComboBoxText):
|
||||
lang_id = combo.get_active_text()
|
||||
self.set_source_view_text( self.path_entry.get_text() )
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from ..config import get_lsp_host_addr, get_lsp_host_port
|
||||
|
||||
|
||||
|
||||
class UIManagerSetupMixin:
|
||||
def _setup_styling(self):
|
||||
self.set_modal(True)
|
||||
self.set_decorated(False)
|
||||
self.set_vexpand(True)
|
||||
self.set_hexpand(True)
|
||||
|
||||
def _load_widgets(self):
|
||||
content_area = self.get_content_area()
|
||||
self.main_box = Gtk.Grid()
|
||||
self.path_entry = Gtk.SearchEntry()
|
||||
self.path_bttn = Gtk.FileChooserButton.new(
|
||||
title = "Workspace Folder",
|
||||
action = Gtk.FileChooserAction.SELECT_FOLDER
|
||||
)
|
||||
self.combo_box = Gtk.ComboBoxText()
|
||||
self.hide_bttn = Gtk.Button(label = "X")
|
||||
|
||||
self.adddress_entry = Gtk.Entry()
|
||||
adjustment = Gtk.Adjustment(
|
||||
value = get_lsp_host_port(),
|
||||
lower = 1,
|
||||
upper = 65535,
|
||||
step_increment = 1,
|
||||
page_increment = 10,
|
||||
page_size = 0
|
||||
)
|
||||
|
||||
self.adddress_port = Gtk.SpinButton()
|
||||
self.adddress_port.set_adjustment(adjustment)
|
||||
self.adddress_port.set_digits(0) # integers only
|
||||
|
||||
bttn_box = Gtk.Box()
|
||||
self.create_client_bttn = Gtk.Button(label = "Create Language Client")
|
||||
self.close_client_bttn = Gtk.Button(label = "Close Language Client")
|
||||
|
||||
self.path_entry.set_can_focus(False)
|
||||
self.path_entry.set_placeholder_text("Workspace Folder...")
|
||||
self.path_entry.connect("changed", self._path_changed, bttn_box)
|
||||
self.path_bttn.set_halign(Gtk.Align.FILL)
|
||||
|
||||
self.adddress_entry.set_placeholder_text("Address...")
|
||||
self.adddress_entry.set_text( get_lsp_host_addr() )
|
||||
|
||||
self.path_bttn.connect("file-set", self._file_set)
|
||||
self.combo_box.connect("changed", self._on_combo_changed)
|
||||
self.hide_bttn_id = self.hide_bttn.connect("clicked", lambda widget: self.hide())
|
||||
self.create_client_bttn.connect("clicked", self._create_client, self.close_client_bttn)
|
||||
self.close_client_bttn.connect("clicked", self._close_client, self.create_client_bttn)
|
||||
|
||||
self.main_box.set_column_spacing(15)
|
||||
self.main_box.set_row_spacing(15)
|
||||
|
||||
bttn_box.pack_start(self.create_client_bttn, False, False, 0)
|
||||
bttn_box.pack_start(self.close_client_bttn, False, False, 0)
|
||||
|
||||
self.main_box.attach(child = self.path_entry, left = 0, top = 0, width = 4, height = 1)
|
||||
self.main_box.attach(child = self.path_bttn, left = 4, top = 0, width = 1, height = 1)
|
||||
self.main_box.attach(child = self.combo_box, left = 5, top = 0, width = 1, height = 1)
|
||||
self.main_box.attach(child = self.hide_bttn, left = 6, top = 0, width = 1, height = 1)
|
||||
|
||||
self.main_box.attach(child = self.adddress_entry, left = 0, top = 1, width = 2, height = 1)
|
||||
self.main_box.attach(child = self.adddress_port, left = 2, top = 1, width = 2, height = 1)
|
||||
self.main_box.attach(child = bttn_box, left = 4, top = 1, width = 3, height = 1)
|
||||
|
||||
content_area.set_vexpand(True)
|
||||
content_area.set_hexpand(True)
|
||||
|
||||
content_area.add(self.main_box)
|
||||
content_area.show_all()
|
||||
self.close_client_bttn.hide()
|
||||
bttn_box.hide()
|
||||
|
||||
def set_source_view(self, scrolled_win, source_view):
|
||||
lang_manager = GtkSource.LanguageManager()
|
||||
buffer = source_view.get_buffer()
|
||||
language = lang_manager.get_language("json")
|
||||
self.source_view = source_view
|
||||
|
||||
buffer.set_language(language)
|
||||
buffer.set_style_scheme(self.source_view.syntax_theme)
|
||||
|
||||
self.main_box.attach(scrolled_win, 0, 2, 7, 1)
|
||||
118
plugins/code/language_server_clients/lsp_manager/plugin.py
Normal file
118
plugins/code/language_server_clients/lsp_manager/plugin.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
from libs.dto.states import SourceViewStates
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
from .dto.code import events as lsp_events
|
||||
from .commands import Commands
|
||||
from .lsp_manager import LSPManager
|
||||
|
||||
|
||||
|
||||
lsp_manager = LSPManager()
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
...
|
||||
|
||||
def load(self):
|
||||
Event_Factory.register_events( lsp_events.__dict__.items() )
|
||||
|
||||
self.register_controller("lsp_manager", lsp_manager)
|
||||
|
||||
window = self.request_ui_element("main-window")
|
||||
|
||||
lsp_manager.ui_manager.map_parent_resize_event(window)
|
||||
|
||||
self._manage_signals("register_command")
|
||||
|
||||
self._manage_provider("register_provider")
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"create_source_view",
|
||||
state = SourceViewStates.INDEPENDENT
|
||||
)
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
scrolled_win, source_view = event.response
|
||||
lsp_manager.ui_manager.set_source_view(scrolled_win, source_view)
|
||||
|
||||
def unload(self):
|
||||
Event_Factory.unregister_events( lsp_events.__dict__.items() )
|
||||
|
||||
self.unregister_controller("lsp_manager")
|
||||
|
||||
window = self.request_ui_element("main-window")
|
||||
|
||||
lsp_manager.ui_manager.unmap_parent_resize_event(window)
|
||||
|
||||
self._manage_signals("unregister_command")
|
||||
|
||||
self._manage_provider("unregister_provider")
|
||||
|
||||
lsp_manager.handle_destroy()
|
||||
|
||||
def _manage_signals(self, action: str):
|
||||
_commands = Commands
|
||||
_commands.lsp_manager = lsp_manager
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "lsp_manager_toggle",
|
||||
command = _commands.lsp_manager_toggle,
|
||||
binding_mode = "released",
|
||||
binding = "<Shift><Control>l"
|
||||
)
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "lsp_references",
|
||||
command = _commands.lsp_references,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>i"
|
||||
)
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "lsp_implementation",
|
||||
command = _commands.lsp_implementation,
|
||||
binding_mode = "released",
|
||||
binding = "<Shift><Control>i"
|
||||
)
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
event = Event_Factory.create_event(action,
|
||||
command_name = "lsp_definition",
|
||||
command = _commands.lsp_definition,
|
||||
binding_mode = "released",
|
||||
binding = "<Control>g"
|
||||
)
|
||||
self.emit_to("source_views", event)
|
||||
|
||||
def _manage_provider(self, action: str):
|
||||
event = Event_Factory.create_event(
|
||||
action,
|
||||
provider_name = "LSP Completer",
|
||||
provider = lsp_manager.provider,
|
||||
language_ids = []
|
||||
)
|
||||
self.emit_to("completion", event)
|
||||
|
||||
def run(self):
|
||||
...
|
||||
|
||||
def generate_plugin_element(self):
|
||||
...
|
||||
@@ -0,0 +1,2 @@
|
||||
from .provider import Provider
|
||||
from .provider_response_cache import ProviderResponseCache
|
||||
@@ -22,7 +22,7 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
def __init__(self):
|
||||
super(Provider, self).__init__()
|
||||
|
||||
self.response_cache: ProviderResponseCache = ProviderResponseCache()
|
||||
self.response_cache: ProviderResponseCache = None
|
||||
|
||||
|
||||
def pre_populate(self, context):
|
||||
@@ -33,13 +33,6 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
|
||||
def do_match(self, context):
|
||||
iter = self.response_cache.get_iter_correctly(context)
|
||||
iter.backward_char()
|
||||
ch = iter.get_char()
|
||||
|
||||
# NOTE: Look to re-add or apply supprting logic to use spaces
|
||||
# As is it slows down the editor in certain contexts...
|
||||
if not (ch in ('_', '.', ' ') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
buffer = iter.get_buffer()
|
||||
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
|
||||
@@ -62,6 +55,7 @@ class Provider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
# return GtkSource.CompletionActivation.NONE
|
||||
return GtkSource.CompletionActivation.USER_REQUESTED
|
||||
# return GtkSource.CompletionActivation.INTERACTIVE
|
||||
# return GtkSource.CompletionActivation.USER_REQUESTED | GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
results = self.response_cache.filter_with_context(context)
|
||||
@@ -0,0 +1,48 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from core.widgets.code.completion_providers.provider_response_cache_base import ProviderResponseCacheBase
|
||||
|
||||
|
||||
|
||||
class ProviderResponseCache(ProviderResponseCacheBase):
|
||||
def __init__(self):
|
||||
super(ProviderResponseCache, self).__init__()
|
||||
|
||||
self.matchers: dict = {}
|
||||
self.lsp_manager_client = None
|
||||
|
||||
def set_lsp_manager_client(self, lsp_client):
|
||||
self.lsp_manager_client = lsp_client
|
||||
|
||||
def process_file_load(self, event):
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_load(event)
|
||||
|
||||
def process_file_close(self, event):
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_close(event)
|
||||
|
||||
def process_file_save(self, event):
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_save(event)
|
||||
|
||||
def process_file_text_inserted(self, event):
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_text_inserted(event)
|
||||
|
||||
def process_file_delete_range(self, event):
|
||||
if self.lsp_manager_client:
|
||||
self.lsp_manager_client.process_file_delete_range(event)
|
||||
|
||||
def filter(self, word: str) -> list[dict]:
|
||||
return []
|
||||
|
||||
def filter_with_context(self, context: GtkSource.CompletionContext) -> list[dict]:
|
||||
return list( self.matchers.values() )
|
||||
@@ -0,0 +1,3 @@
|
||||
from .base_handler import BaseHandler
|
||||
from .default import DefaultHandler
|
||||
from .response_registry import ResponseRegistry
|
||||
@@ -0,0 +1,30 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class BaseHandler:
|
||||
def __init__(self):
|
||||
self.context = None
|
||||
self.response_cache = None
|
||||
|
||||
|
||||
def set_context(self, context):
|
||||
self.context = context
|
||||
|
||||
def set_response_cache(self, response_cache):
|
||||
self.response_cache = response_cache
|
||||
|
||||
@property
|
||||
def emit(self):
|
||||
return self.context.emit
|
||||
|
||||
@property
|
||||
def emit_to(self):
|
||||
return self.context.emit_to
|
||||
|
||||
def handle(self, method: str, response, controller):
|
||||
pass
|
||||
@@ -0,0 +1,142 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from .base_handler import BaseHandler
|
||||
|
||||
|
||||
|
||||
class DefaultHandler(BaseHandler):
|
||||
"""Fallback handler for unknown languages - uses generic LSP handling."""
|
||||
|
||||
def handle(self, method: str, response, controller):
|
||||
match method:
|
||||
case "initialize":
|
||||
controller.send_initialized_notification()
|
||||
case "shutdown":
|
||||
controller.send_exit_notification()
|
||||
case "textDocument/completion":
|
||||
self._handle_completion(response)
|
||||
case "textDocument/definition":
|
||||
self._handle_definition(response, controller)
|
||||
case "textDocument/references":
|
||||
...
|
||||
case "textDocument/implementation":
|
||||
...
|
||||
case "textDocument/publishDiagnostics":
|
||||
self._handle_diagnostics(response)
|
||||
|
||||
def _handle_completion(self, result):
|
||||
if not result: return
|
||||
|
||||
items = result.get("items", []) if isinstance(result, dict) else result
|
||||
|
||||
self.response_cache.matchers.clear()
|
||||
for item in items:
|
||||
label = item.get("label")
|
||||
if not label:
|
||||
continue
|
||||
|
||||
text = (
|
||||
item.get("insertText")
|
||||
or item.get("textEdit", {}).get("newText")
|
||||
or item.get("textEditText", "")
|
||||
or label
|
||||
)
|
||||
|
||||
detail = item.get("detail")
|
||||
doc = item.get("documentation")
|
||||
|
||||
if detail:
|
||||
info = detail
|
||||
elif isinstance(doc, dict):
|
||||
info = doc.get("value", "")
|
||||
else:
|
||||
info = str(doc) if doc else ""
|
||||
|
||||
self.response_cache.matchers[label] = {
|
||||
"label": label,
|
||||
"text": text,
|
||||
"info": info,
|
||||
}
|
||||
|
||||
self._prompt_completion_request()
|
||||
|
||||
def _handle_definition(self, response, controller):
|
||||
if not response: return
|
||||
|
||||
uri = response[0]["uri"]
|
||||
self._prompt_goto_request(uri, response[0]["range"])
|
||||
|
||||
def _handle_diagnostics(self, params):
|
||||
if not params: return
|
||||
|
||||
uri = params.get("uri", "")
|
||||
diagnostics = params.get("diagnostics", [])
|
||||
|
||||
errors = []
|
||||
warnings = []
|
||||
hints = []
|
||||
|
||||
for diag in diagnostics:
|
||||
severity = diag.get("severity", 1)
|
||||
message = diag.get("message", "")
|
||||
range = diag.get("range", {})
|
||||
|
||||
diag_info = {
|
||||
"message": message,
|
||||
"range": range
|
||||
}
|
||||
|
||||
if severity == 1:
|
||||
errors.append(diag_info)
|
||||
elif severity == 2:
|
||||
warnings.append(diag_info)
|
||||
elif severity == 3:
|
||||
hints.append(diag_info)
|
||||
|
||||
self.response_cache.lsp_diagnostics = {
|
||||
"uri": uri,
|
||||
"errors": errors,
|
||||
"warnings": warnings,
|
||||
"hints": hints
|
||||
}
|
||||
|
||||
logger.debug(f"LSP Diagnostics for {uri}: {len(errors)} errors, {len(warnings)} warnings, {len(hints)} hints")
|
||||
|
||||
def _prompt_goto_request(self, uri: str, pointer_pos: dict):
|
||||
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 _prompt_completion_request(self):
|
||||
event = Event_Factory.create_event("get_active_view")
|
||||
self.emit_to("source_views", event)
|
||||
view = event.response
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"request_completion",
|
||||
view = view,
|
||||
provider = self.context._provider
|
||||
)
|
||||
|
||||
self.emit_to("completion", event)
|
||||
@@ -0,0 +1,52 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .base_handler import BaseHandler
|
||||
from .default import DefaultHandler
|
||||
|
||||
|
||||
|
||||
class ResponseRegistry:
|
||||
def __init__(self):
|
||||
|
||||
self._instances: dict = {}
|
||||
self._lang_handlers: dict = {
|
||||
"default": DefaultHandler
|
||||
}
|
||||
|
||||
|
||||
def set_event_hub(self, emit, emit_to, provider = None):
|
||||
self.emit = emit
|
||||
self.emit_to = emit_to
|
||||
self._provider = provider
|
||||
|
||||
|
||||
def _get_instance(self, handler_cls: type[BaseHandler]) -> BaseHandler:
|
||||
if handler_cls in self._instances: return self._instances[handler_cls]
|
||||
|
||||
self._instances[handler_cls] = handler_cls()
|
||||
|
||||
return self._instances[handler_cls]
|
||||
|
||||
def register_handler(self, lang_id: str, handler_cls: type[BaseHandler]):
|
||||
self._lang_handlers[lang_id] = handler_cls
|
||||
|
||||
def unregister_handler(self, lang_id: str):
|
||||
del self._lang_handlers[lang_id]
|
||||
|
||||
def get_handler(self, lang_id: str = "", method: str = ""):
|
||||
handler_cls = self._lang_handlers.get(
|
||||
lang_id, self._lang_handlers.get("default", DefaultHandler)
|
||||
)
|
||||
|
||||
if not handler_cls: return None
|
||||
|
||||
return self._get_instance(handler_cls)
|
||||
|
||||
def close_handler(self, lang_id: str):
|
||||
if not lang_id in self._lang_handlers: return
|
||||
|
||||
handler_cls = self._lang_handlers[lang_id]
|
||||
self._instances.pop(handler_cls, None)
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# set -o xtrace ## To debug scripts
|
||||
# set -o errexit ## To exit on error
|
||||
# set -o errunset ## To exit if a variable is referenced but not set
|
||||
|
||||
|
||||
CONTAINER="newton-lsp"
|
||||
38
plugins/code/language_server_clients/lsp_manager/scripts/start.sh
Executable file
38
plugins/code/language_server_clients/lsp_manager/scripts/start.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
. CONFIG.sh
|
||||
|
||||
# set -o xtrace ## To debug scripts
|
||||
# set -o errexit ## To exit on error
|
||||
# set -o errunset ## To exit if a variable is referenced but not set
|
||||
|
||||
|
||||
function main() {
|
||||
SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
cd "${SCRIPTPATH}"
|
||||
echo "Working Dir: " $(pwd)
|
||||
|
||||
ID=$(podman ps --filter "ancestor=localhost/${CONTAINER}:latest" --format "{{.ID}}")
|
||||
if [ "${ID}" != "" ]; then
|
||||
echo "Is up..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CODE_HOST="${HOME}/Coding"
|
||||
CODE_CONTAINER="${HOME}/Coding"
|
||||
CONFIG_HOST="${HOME}/.config/lsps"
|
||||
CONFIG_CONTAINER="${HOME}/.config/lsps"
|
||||
|
||||
# podman run -d -m 4G \
|
||||
podman run -m 4G \
|
||||
-p 9999:9999 \
|
||||
-e HOME="${HOME}" \
|
||||
-e MAVEN_OPTS="-Duser.home=${HOME}" \
|
||||
-e JAVA_TOOL_OPTIONS="-Duser.home=${HOME}" \
|
||||
-e JDTLS_CONFIG_PATH="${CONFIG_CONTAINER}/jdtls" \
|
||||
-e JDTLS_DATA_PATH="${JDTLS_CONFIG_PATH}/data" \
|
||||
-v "${CODE_HOST}":"${CODE_CONTAINER}" \
|
||||
-v "${CONFIG_HOST}":"${CONFIG_CONTAINER}" \
|
||||
"${CONTAINER}:latest"
|
||||
}
|
||||
main $@;
|
||||
23
plugins/code/language_server_clients/lsp_manager/scripts/stop.sh
Executable file
23
plugins/code/language_server_clients/lsp_manager/scripts/stop.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
. CONFIG.sh
|
||||
|
||||
# set -o xtrace ## To debug scripts
|
||||
# set -o errexit ## To exit on error
|
||||
# set -o errunset ## To exit if a variable is referenced but not set
|
||||
|
||||
|
||||
function main() {
|
||||
SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
cd "${SCRIPTPATH}"
|
||||
echo "Working Dir: " $(pwd)
|
||||
|
||||
ID=$(podman ps --filter "ancestor=localhost/${CONTAINER}:latest" --format "{{.ID}}")
|
||||
if [ "${ID}" == "" ]; then
|
||||
echo "Is not up..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
podman container stop "${ID}"
|
||||
}
|
||||
main $@;
|
||||
@@ -0,0 +1,44 @@
|
||||
# Python imports
|
||||
from os import path
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from .mixins.ui_manager_setup_mixin import UIManagerSetupMixin
|
||||
from .mixins.ui_manager_events_mixin import UIManagerEventsMixin
|
||||
from .mixins.ui_manager_clients_mixin import UIManagerClientsMixin
|
||||
|
||||
|
||||
|
||||
class UIManager(
|
||||
Gtk.Dialog,
|
||||
UIManagerSetupMixin,
|
||||
UIManagerEventsMixin,
|
||||
UIManagerClientsMixin
|
||||
):
|
||||
__gsignals__ = {
|
||||
'create-client': (GObject.SignalFlags.RUN_LAST, None, (str, str)),
|
||||
'close-client': (GObject.SignalFlags.RUN_LAST, None, (str,)),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super(UIManager, self).__init__()
|
||||
self._USER_HOME = path.expanduser("~")
|
||||
self.client_configs: dict[str, str] = {}
|
||||
self.source_view = None
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def map_parent_resize_event(self, parent):
|
||||
self.size_allocate_id = parent.connect("size-allocate", lambda w, r: self._map_resize(self, parent))
|
||||
|
||||
def unmap_parent_resize_event(self, parent):
|
||||
parent.disconnect(self.size_allocate_id)
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"info": "https://github.com/python-lsp/python-lsp-server",
|
||||
"command": "lsp-ws-proxy -- pylsp",
|
||||
"alt-command": "pylsp",
|
||||
"alt-command2": "lsp-ws-proxy --listen 4114 -- pylsp",
|
||||
"alt-command3": "pylsp --ws --port 4114",
|
||||
"socket": "ws://127.0.0.1:9999/python",
|
||||
"socket-two": "ws://127.0.0.1:9999/?name=pylsp",
|
||||
"initialization-options": {
|
||||
"pylsp": {
|
||||
"rope": {
|
||||
"ropeFolder": "{user.home}/.config/newton/lsps/ropeproject"
|
||||
},
|
||||
"plugins": {
|
||||
"ruff": {
|
||||
"enabled": true,
|
||||
"extendSelect": ["I"],
|
||||
"lineLength": 80
|
||||
},
|
||||
"pycodestyle": {
|
||||
"enabled": false
|
||||
},
|
||||
"pyflakes": {
|
||||
"enabled": false
|
||||
},
|
||||
"pylint": {
|
||||
"enabled": true
|
||||
},
|
||||
"mccabe": {
|
||||
"enabled": false
|
||||
},
|
||||
"pylsp_rope": {
|
||||
"rename": false
|
||||
},
|
||||
"rope_rename": {
|
||||
"enabled": false
|
||||
},
|
||||
"rope_autoimport": {
|
||||
"enabled": true
|
||||
},
|
||||
"rope_completion": {
|
||||
"enabled": false,
|
||||
"eager": false
|
||||
},
|
||||
"jedi_rename": {
|
||||
"enabled": true
|
||||
},
|
||||
"jedi_completion": {
|
||||
"enabled": true,
|
||||
"include_class_objects": true,
|
||||
"include_function_objects": true,
|
||||
"fuzzy": false
|
||||
},
|
||||
"jedi": {
|
||||
"root_dir": "file://{workspace.folder}",
|
||||
"extra_paths": [
|
||||
"{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"python - jedi-language-server": {
|
||||
"hidden": true,
|
||||
"info": "https://pypi.org/project/jedi-language-server/",
|
||||
"command": "jedi-language-server",
|
||||
"alt-command": "lsp-ws-proxy --listen 3030 -- jedi-language-server",
|
||||
"socket": "ws://127.0.0.1:9999/python",
|
||||
"socket-two": "ws://127.0.0.1:9999/?name=jedi-language-server",
|
||||
"initialization-options": {
|
||||
"jediSettings": {
|
||||
"autoImportModules": [],
|
||||
"caseInsensitiveCompletion": true,
|
||||
"debug": false
|
||||
},
|
||||
"completion": {
|
||||
"disableSnippets": false,
|
||||
"resolveEagerly": false,
|
||||
"ignorePatterns": []
|
||||
},
|
||||
"markupKindPreferred": "markdown",
|
||||
"workspace": {
|
||||
"extraPaths": [
|
||||
"{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages"
|
||||
],
|
||||
"environmentPath": "{user.home}/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/python",
|
||||
"symbols": {
|
||||
"ignoreFolders": [
|
||||
".nox",
|
||||
".tox",
|
||||
".venv",
|
||||
"__pycache__",
|
||||
"venv"
|
||||
],
|
||||
"maxSymbols": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user