generated from itdominator/Python-With-Gtk-Template
Merge pull request 'feature/lsp-integration-work' (#2) from feature/lsp-integration-work into develop
Reviewed-on: #2
This commit is contained in:
commit
d886ef37d4
|
@ -13,7 +13,10 @@ from .capabilities import Capabilities
|
|||
class ReadPipe(threading.Thread):
|
||||
def __init__(self, pipe):
|
||||
threading.Thread.__init__(self)
|
||||
self.pipe = pipe
|
||||
|
||||
self.daemon = True
|
||||
self.pipe = pipe
|
||||
|
||||
|
||||
def run(self):
|
||||
line = self.pipe.readline().decode('utf-8')
|
||||
|
@ -21,7 +24,6 @@ class ReadPipe(threading.Thread):
|
|||
line = self.pipe.readline().decode('utf-8')
|
||||
|
||||
|
||||
|
||||
class LSPController:
|
||||
def __init__(self, lsp_servers_config = {}):
|
||||
super().__init__()
|
||||
|
@ -29,6 +31,7 @@ class LSPController:
|
|||
self.lsp_servers_config = lsp_servers_config
|
||||
self.lsp_clients = {}
|
||||
|
||||
|
||||
def _blame(self, response):
|
||||
for d in response['diagnostics']:
|
||||
if d['severity'] == 1:
|
||||
|
@ -63,8 +66,10 @@ class LSPController:
|
|||
if not language or not server_proc: return False
|
||||
|
||||
root_path = None
|
||||
root_uri = 'file:///home/abaddon/Coding/Projects/Active/C_n_CPP_Projects/gtk/Newton/src/'
|
||||
workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}]
|
||||
# root_uri = 'file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/Newton_Editor/src/'
|
||||
# workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}]
|
||||
root_uri = ''
|
||||
workspace_folders = [{'name': '', 'uri': root_uri}]
|
||||
|
||||
lsp_client = self._generate_client(language, server_proc)
|
||||
lsp_client.initialize(
|
||||
|
@ -74,6 +79,7 @@ class LSPController:
|
|||
initializationOptions = initialization_options, \
|
||||
capabilities = Capabilities.data, \
|
||||
trace = "off", \
|
||||
# trace = "on", \
|
||||
workspaceFolders = workspace_folders
|
||||
)
|
||||
|
||||
|
@ -121,16 +127,17 @@ class LSPController:
|
|||
|
||||
return []
|
||||
|
||||
def do_change(self, language_id, line, start, end, text):
|
||||
def do_change(self, uri, language_id, line, start, end, text):
|
||||
if language_id in self.lsp_clients.keys():
|
||||
|
||||
start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset())
|
||||
end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset())
|
||||
range_info = pylspclient.lsp_structs.Range(start_pos, end_pos)
|
||||
text_length = len(text)
|
||||
change_event = pylspclient.lsp_structs.TextDocumentContentChangeEvent(range_info, text_length, text)
|
||||
start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset())
|
||||
end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset())
|
||||
range_info = pylspclient.lsp_structs.Range(start_pos, end_pos)
|
||||
text_length = len(text)
|
||||
text_document = pylspclient.lsp_structs.TextDocumentItem(uri, language_id, 1, text)
|
||||
change_event = pylspclient.lsp_structs.TextDocumentContentChangeEvent(range_info, text_length, text)
|
||||
|
||||
return self.lsp_clients[language_id].didChange( None, change_event )
|
||||
return self.lsp_clients[language_id].didChange( text_document, change_event )
|
||||
|
||||
return []
|
||||
|
||||
|
@ -148,11 +155,12 @@ class LSPController:
|
|||
return self.lsp_clients[language_id].completion(
|
||||
pylspclient.lsp_structs.TextDocumentIdentifier(uri),
|
||||
pylspclient.lsp_structs.Position(line, offset),
|
||||
pylspclient.lsp_structs.CompletionContext(trigger, _char)
|
||||
None
|
||||
# pylspclient.lsp_structs.CompletionContext(trigger, _char)
|
||||
)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
|
||||
def load_lsp_server(self, language_id):
|
||||
if not language_id in self.lsp_servers_config.keys():
|
||||
|
|
|
@ -24,7 +24,7 @@ class Plugin(PluginBase):
|
|||
super().__init__()
|
||||
|
||||
self.name = "LSP Client" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
|
||||
# where self.name should not be needed for message comms
|
||||
# where self.name should not be needed for message comms
|
||||
self.lsp_config_path: str = os.path.dirname(os.path.realpath(__file__)) + "/../../lsp_servers_config.json"
|
||||
self.lsp_servers_config: dict = {}
|
||||
self.lsp_controller = None
|
||||
|
@ -53,30 +53,18 @@ class Plugin(PluginBase):
|
|||
def inner_subscribe_to_events(self):
|
||||
self._event_system.subscribe("shutting_down", self._shutting_down)
|
||||
|
||||
self._event_system.subscribe("textDocument/didChange", self._buffer_changed)
|
||||
self._event_system.subscribe("textDocument/didOpen", self.lsp_controller.do_open)
|
||||
self._event_system.subscribe("textDocument/didSave", self.lsp_controller.do_save)
|
||||
self._event_system.subscribe("textDocument/didClose", self.lsp_controller.do_close)
|
||||
# self._event_system.subscribe("buffer_changed", self._buffer_changed)
|
||||
self._event_system.subscribe("textDocument/didChange", self._buffer_changed)
|
||||
self._event_system.subscribe("textDocument/didOpen", self.lsp_controller.do_open)
|
||||
self._event_system.subscribe("textDocument/didSave", self.lsp_controller.do_save)
|
||||
self._event_system.subscribe("textDocument/didClose", self.lsp_controller.do_close)
|
||||
self._event_system.subscribe("textDocument/definition", self._do_goto)
|
||||
self._event_system.subscribe("textDocument/completion", self.completion)
|
||||
self._event_system.subscribe("textDocument/completion", self._do_completion)
|
||||
|
||||
def _shutting_down(self):
|
||||
self.lsp_controller._shutting_down()
|
||||
|
||||
def cancel_timer(self):
|
||||
if self.timer:
|
||||
self.timer.cancel()
|
||||
GLib.idle_remove_by_data(None)
|
||||
|
||||
def delay_completion_glib(self, source_view, context, callback):
|
||||
GLib.idle_add(self._do_completion, source_view, context, callback)
|
||||
|
||||
def delay_completion(self, source_view, context, callback):
|
||||
self.timer = threading.Timer(0.8, self.delay_completion_glib, (source_view, context, callback,))
|
||||
self.timer.daemon = True
|
||||
self.timer.start()
|
||||
|
||||
def _buffer_changed(self, language_id, buffer):
|
||||
def _buffer_changed(self, file_type, buffer):
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = iter.get_line()
|
||||
start = iter.copy()
|
||||
|
@ -86,32 +74,34 @@ class Plugin(PluginBase):
|
|||
start.forward_line()
|
||||
end.forward_to_line_end()
|
||||
|
||||
text = buffer.get_text(start, end, include_hidden_chars = False)
|
||||
result = self.lsp_controller.do_change(language_id, line, start, end, text)
|
||||
# print(result)
|
||||
text = buffer.get_text(start, end, include_hidden_chars = False)
|
||||
result = self.lsp_controller.do_change(buffer.uri, buffer.language_id, line, start, end, text)
|
||||
|
||||
|
||||
def completion(self, source_view, context, callback):
|
||||
self.cancel_timer()
|
||||
self.delay_completion(source_view, context, callback)
|
||||
|
||||
def _do_completion(self, source_view, context, callback):
|
||||
filepath = source_view.get_current_file()
|
||||
def _do_completion(self, source_view):
|
||||
filepath = source_view.get_current_file()
|
||||
|
||||
if not filepath: return
|
||||
|
||||
uri = filepath.get_uri()
|
||||
buffer = source_view.get_buffer()
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = iter.get_line() + 1
|
||||
line = iter.get_line()
|
||||
|
||||
_char = iter.get_char()
|
||||
if iter.backward_char():
|
||||
_char = iter.get_char()
|
||||
|
||||
offset = iter.get_line_offset()
|
||||
result = self.lsp_controller.do_completion(source_view.get_filetype(), uri, line, offset, _char)
|
||||
callback(context, result)
|
||||
result = self.lsp_controller.do_completion(
|
||||
source_view.get_filetype(),
|
||||
uri,
|
||||
line,
|
||||
offset,
|
||||
_char
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def _do_goto(self, language_id, uri, line, offset):
|
||||
results = self.lsp_controller.do_goto(language_id, uri, line, offset)
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from . import lsp_structs
|
||||
|
||||
|
||||
|
@ -40,13 +45,13 @@ class LspClient(object):
|
|||
"""
|
||||
|
||||
self.lsp_endpoint.start()
|
||||
return self.lsp_endpoint.call_method("initialize", \
|
||||
processId = processId, \
|
||||
rootPath = rootPath, \
|
||||
rootUri = rootUri, \
|
||||
initializationOptions = initializationOptions, \
|
||||
capabilities = capabilities, \
|
||||
trace = trace, \
|
||||
return self.lsp_endpoint.call_method("initialize",
|
||||
processId = processId,
|
||||
rootPath = rootPath,
|
||||
rootUri = rootUri,
|
||||
initializationOptions = initializationOptions,
|
||||
capabilities = capabilities,
|
||||
trace = trace,
|
||||
workspaceFolders = workspaceFolders
|
||||
)
|
||||
|
||||
|
@ -61,18 +66,12 @@ class LspClient(object):
|
|||
|
||||
def shutdown(self):
|
||||
"""
|
||||
The initialized notification is sent from the client to the server after the client received the result of the initialize request
|
||||
but before the client is sending any other request or notification to the server. The server can use the initialized notification
|
||||
for example to dynamically register capabilities. The initialized notification may only be sent once.
|
||||
"""
|
||||
|
||||
return self.lsp_endpoint.call_method("shutdown")
|
||||
|
||||
def exit(self):
|
||||
"""
|
||||
The initialized notification is sent from the client to the server after the client received the result of the initialize request
|
||||
but before the client is sending any other request or notification to the server. The server can use the initialized notification
|
||||
for example to dynamically register capabilities. The initialized notification may only be sent once.
|
||||
"""
|
||||
|
||||
self.lsp_endpoint.send_notification("exit")
|
||||
|
@ -115,7 +114,7 @@ class LspClient(object):
|
|||
The document change notification is sent from the client to the server to signal changes to a text document.
|
||||
In 2.0 the shape of the params has changed to include proper version numbers and language ids.
|
||||
|
||||
:param VersionedTextDocumentIdentifier textDocument: The initial trace setting. If omitted trace is disabled ('off').
|
||||
:param TextDocumentItem textDocument: The text document.
|
||||
:param TextDocumentContentChangeEvent[] contentChanges: The actual content changes. The content changes describe single state changes
|
||||
to the document. So if there are two content changes c1 and c2 for a document in state S then c1 move the document
|
||||
to S' and c2 to S''.
|
||||
|
|
|
@ -10,9 +10,6 @@ import tracemalloc
|
|||
tracemalloc.start()
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from __builtins__ import *
|
||||
|
@ -20,34 +17,38 @@ from app import Application
|
|||
|
||||
|
||||
|
||||
def main(args, unknownargs):
|
||||
setproctitle(f'{app_name}')
|
||||
|
||||
if args.debug == "true":
|
||||
settings_manager.set_debug(True)
|
||||
|
||||
if args.trace_debug == "true":
|
||||
settings_manager.set_trace_debug(True)
|
||||
|
||||
settings_manager.do_dirty_start_check()
|
||||
Application(args, unknownargs)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
''' Set process title, get arguments, and create GTK main thread. '''
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
# Add long and short arguments
|
||||
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
|
||||
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
|
||||
parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
|
||||
|
||||
parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.")
|
||||
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
|
||||
|
||||
# Read arguments (If any...)
|
||||
args, unknownargs = parser.parse_known_args()
|
||||
|
||||
try:
|
||||
setproctitle(f'{app_name}')
|
||||
faulthandler.enable() # For better debug info
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
# Add long and short arguments
|
||||
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
|
||||
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
|
||||
parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
|
||||
parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
|
||||
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
|
||||
|
||||
# Read arguments (If any...)
|
||||
args, unknownargs = parser.parse_known_args()
|
||||
|
||||
if args.debug == "true":
|
||||
settings_manager.set_debug(True)
|
||||
|
||||
if args.trace_debug == "true":
|
||||
settings_manager.set_trace_debug(True)
|
||||
|
||||
settings_manager.do_dirty_start_check()
|
||||
Application(args, unknownargs)
|
||||
Gtk.main()
|
||||
main(args, unknownargs)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
quit()
|
34
src/app.py
34
src/app.py
|
@ -16,35 +16,39 @@ class AppLaunchException(Exception):
|
|||
|
||||
|
||||
|
||||
class Application(IPCServer):
|
||||
class Application:
|
||||
""" docstring for Application. """
|
||||
|
||||
def __init__(self, args, unknownargs):
|
||||
super(Application, self).__init__()
|
||||
|
||||
if not settings_manager.is_trace_debug():
|
||||
self.socket_realization_check()
|
||||
|
||||
if not self.is_ipc_alive:
|
||||
for arg in unknownargs + [args.new_tab,]:
|
||||
if os.path.isfile(arg):
|
||||
message = f"FILE|{arg}"
|
||||
self.send_ipc_message(message)
|
||||
|
||||
raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
|
||||
self.load_ipc(args, unknownargs)
|
||||
|
||||
self.setup_debug_hook()
|
||||
Window(args, unknownargs)
|
||||
Window(args, unknownargs).main()
|
||||
|
||||
|
||||
def socket_realization_check(self):
|
||||
def load_ipc(self, args, unknownargs):
|
||||
ipc_server = IPCServer()
|
||||
self.ipc_realization_check(ipc_server)
|
||||
|
||||
if not ipc_server.is_ipc_alive:
|
||||
for arg in unknownargs + [args.new_tab,]:
|
||||
if os.path.isfile(arg):
|
||||
message = f"FILE|{arg}"
|
||||
ipc_server.send_ipc_message(message)
|
||||
|
||||
raise AppLaunchException(f"{app_name} IPC Server Exists: Have sent path(s) to it and closing...")
|
||||
|
||||
def ipc_realization_check(self, ipc_server):
|
||||
try:
|
||||
self.create_ipc_listener()
|
||||
ipc_server.create_ipc_listener()
|
||||
except Exception:
|
||||
self.send_test_ipc_message()
|
||||
ipc_server.send_test_ipc_message()
|
||||
|
||||
try:
|
||||
self.create_ipc_listener()
|
||||
ipc_server.create_ipc_listener()
|
||||
except Exception as e:
|
||||
...
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Gtk Bound Signal Module
|
||||
"""
|
|
@ -14,9 +14,9 @@ from .editors_container import EditorsContainer
|
|||
|
||||
|
||||
|
||||
class CoreWidget(Gtk.Box):
|
||||
class BaseContainer(Gtk.Box):
|
||||
def __init__(self):
|
||||
super(CoreWidget, self).__init__()
|
||||
super(BaseContainer, self).__init__()
|
||||
|
||||
builder = settings_manager.get_builder()
|
||||
self.ctx = self.get_style_context()
|
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Controllers Module
|
||||
"""
|
|
@ -10,13 +10,14 @@ from gi.repository import Gdk
|
|||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from .controller_data import ControllerData
|
||||
from .containers.core_widget import CoreWidget
|
||||
from .mixins.signals_mixins import SignalsMixins
|
||||
from ..mixins.signals_mixins import SignalsMixins
|
||||
from ..containers.base_container import BaseContainer
|
||||
|
||||
from .base_controller_data import BaseControllerData
|
||||
|
||||
|
||||
|
||||
class Controller(SignalsMixins, ControllerData):
|
||||
class BaseController(SignalsMixins, BaseControllerData):
|
||||
def __init__(self, args, unknownargs):
|
||||
messages = []
|
||||
for arg in unknownargs + [args.new_tab,]:
|
||||
|
@ -60,7 +61,7 @@ class Controller(SignalsMixins, ControllerData):
|
|||
self.builder.expose_object("main_window", self.window)
|
||||
|
||||
settings_manager.set_builder(self.builder)
|
||||
self.core_widget = CoreWidget()
|
||||
self.core_widget = BaseContainer()
|
||||
|
||||
settings_manager.register_signals_to_builder([self, self.core_widget])
|
||||
|
|
@ -9,9 +9,8 @@ from plugins.plugins_controller import PluginsController
|
|||
|
||||
|
||||
|
||||
|
||||
class ControllerData:
|
||||
''' ControllerData contains most of the state of the app at ay given time. It also has some support methods. '''
|
||||
class BaseControllerData:
|
||||
''' BaseControllerData contains most of the state of the app at ay given time. It also has some support methods. '''
|
||||
|
||||
def setup_controller_data(self) -> None:
|
||||
self.window = settings_manager.get_main_window()
|
|
@ -39,7 +39,13 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
|
|||
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
|
||||
return False
|
||||
|
||||
event_system.emit("textDocument/completion", (self._source_view, context, self.do_populate))
|
||||
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()):
|
||||
# if not (ch in ('_', '.') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
|
@ -49,7 +55,9 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
|
|||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context, result = None):
|
||||
result = event_system.emit_and_await("textDocument/completion", (self._source_view,))
|
||||
proposals = []
|
||||
|
||||
if result:
|
||||
if not result.items is None:
|
||||
for item in result.items:
|
||||
|
@ -86,6 +94,5 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
|
|||
|
||||
comp_item.set_icon( self.get_icon_for_type(item.kind) )
|
||||
comp_item.set_info(item.documentation)
|
||||
|
||||
|
||||
return comp_item
|
||||
|
||||
return comp_item
|
|
@ -81,7 +81,7 @@ class KeyInputController:
|
|||
|
||||
return True
|
||||
|
||||
if keyname in ["z", "y", "m", "s", "h", "g", "equal", "minus", "Up", "Down"]:
|
||||
if keyname in ["z", "y", "m", "s", "h", "g", "d", "k", "u", "equal", "minus", "Up", "Down"]:
|
||||
if keyname == "z":
|
||||
self.keyboard_undo()
|
||||
if keyname == "y":
|
||||
|
@ -94,6 +94,12 @@ class KeyInputController:
|
|||
self.toggle_highlight_line()
|
||||
if keyname == "g":
|
||||
self.go_to_call()
|
||||
if keyname == "d":
|
||||
self.duplicate_line()
|
||||
if keyname == "k":
|
||||
self.cut_to_buffer()
|
||||
if keyname == "u":
|
||||
self.paste_cut_buffer()
|
||||
|
||||
if keyname == "equal":
|
||||
self.scale_up_text()
|
||||
|
|
|
@ -6,12 +6,13 @@ import gi
|
|||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GLib
|
||||
from gi.repository import Gio
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from ..custom_completion_providers.lsp_completion_provider import LSPCompletionProvider
|
||||
|
||||
# from ..custom_completion_providers.lsp_completion_provider import LSPCompletionProvider
|
||||
from ..custom_completion_providers.python_completion_provider import PythonCompletionProvider
|
||||
|
||||
|
||||
class FileEventsMixin:
|
||||
|
@ -79,7 +80,7 @@ class FileEventsMixin:
|
|||
self.update_labels(gfile)
|
||||
self._loading_file = False
|
||||
|
||||
self._file_loader.load_async(io_priority = 80,
|
||||
self._file_loader.load_async(io_priority = GLib.PRIORITY_HIGH,
|
||||
cancellable = None,
|
||||
progress_callback = None,
|
||||
progress_callback_data = None,
|
||||
|
@ -139,19 +140,24 @@ class FileEventsMixin:
|
|||
def _document_loaded(self, line: int = 0):
|
||||
for provider in self._completion.get_providers():
|
||||
self._completion.remove_provider(provider)
|
||||
|
||||
uri = self._current_file.get_uri()
|
||||
buffer = self.get_buffer()
|
||||
|
||||
uri = self._current_file.get_uri()
|
||||
buffer = self.get_buffer()
|
||||
buffer.uri = uri
|
||||
buffer.language_id = self._current_filetype
|
||||
|
||||
event_system.emit("textDocument/didOpen", (self._current_filetype, uri,))
|
||||
|
||||
word_completion = GtkSource.CompletionWords.new("word_completion")
|
||||
word_completion.register(buffer)
|
||||
self._completion.add_provider(word_completion)
|
||||
|
||||
lsp_completion_provider = LSPCompletionProvider(self)
|
||||
self._completion.add_provider(lsp_completion_provider)
|
||||
|
||||
# lsp_completion_provider = LSPCompletionProvider(self)
|
||||
# self._completion.add_provider(lsp_completion_provider)
|
||||
|
||||
# if self._current_filetype in ("python", "python3"):
|
||||
# py_lsp_completion_provider = PythonCompletionProvider(uri)
|
||||
# self._completion.add_provider(py_lsp_completion_provider)
|
||||
|
||||
self.got_to_line(buffer, line)
|
||||
|
||||
|
||||
event_system.emit("buffer_changed_first_load", (buffer, ))
|
|
@ -1,4 +1,5 @@
|
|||
# Python imports
|
||||
import threading
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
@ -33,6 +34,9 @@ class SourceView(SourceViewControllerMixin, GtkSource.View):
|
|||
self._current_filename: str = ""
|
||||
self._current_filepath: str = None
|
||||
self._current_filetype: str = "buffer"
|
||||
self._cut_buffer: str = ""
|
||||
self._timer: threading.Timer = None
|
||||
self._idle_id: int = None
|
||||
|
||||
self._skip_file_load = False
|
||||
self._ignore_internal_change = False
|
||||
|
@ -95,4 +99,20 @@ class SourceView(SourceViewControllerMixin, GtkSource.View):
|
|||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
self._set_up_dnd()
|
||||
self._set_up_dnd()
|
||||
|
||||
def cancel_timer(self):
|
||||
if self._timer:
|
||||
self._timer.cancel()
|
||||
GLib.idle_remove_by_data(self._idle_id)
|
||||
|
||||
def delay_cut_buffer_clear_glib(self):
|
||||
self._idle_id = GLib.idle_add(self._clear_cut_buffer)
|
||||
|
||||
def clear_cut_buffer_delayed(self):
|
||||
self._timer = threading.Timer(15, self.delay_cut_buffer_clear_glib, ())
|
||||
self._timer.daemon = True
|
||||
self._timer.start()
|
||||
|
||||
def _clear_cut_buffer(self):
|
||||
self._cut_buffer = ""
|
||||
|
|
|
@ -34,6 +34,57 @@ class SourceViewControllerMixin(KeyInputController, SourceViewEvents):
|
|||
|
||||
event_system.emit("textDocument/definition", (self.get_filetype(), uri, line, offset,))
|
||||
|
||||
def duplicate_line(self, buffer = None):
|
||||
buffer = self.get_buffer() if not buffer else buffer
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
start_itr = itr.copy()
|
||||
end_itr = itr.copy()
|
||||
start_line = itr.get_line() + 1
|
||||
start_char = itr.get_line_offset()
|
||||
|
||||
start_itr.backward_visible_line()
|
||||
start_itr.forward_line()
|
||||
end_itr.forward_line()
|
||||
end_itr.backward_char()
|
||||
|
||||
line_str = buffer.get_slice(start_itr, end_itr, True)
|
||||
|
||||
end_itr.forward_char()
|
||||
buffer.insert(end_itr, f"{line_str}\n", -1)
|
||||
|
||||
new_itr = buffer.get_iter_at_line_offset(start_line, start_char)
|
||||
buffer.place_cursor(new_itr)
|
||||
|
||||
def cut_to_buffer(self, buffer = None):
|
||||
self.cancel_timer()
|
||||
|
||||
buffer = self.get_buffer() if not buffer else buffer
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
start_itr = itr.copy()
|
||||
end_itr = itr.copy()
|
||||
start_line = itr.get_line() + 1
|
||||
start_char = itr.get_line_offset()
|
||||
|
||||
start_itr.backward_visible_line()
|
||||
start_itr.forward_line()
|
||||
end_itr.forward_line()
|
||||
|
||||
line_str = buffer.get_slice(start_itr, end_itr, True)
|
||||
self._cut_buffer += f"{line_str}"
|
||||
buffer.delete(start_itr, end_itr)
|
||||
|
||||
self.clear_cut_buffer_delayed()
|
||||
|
||||
def paste_cut_buffer(self, buffer = None):
|
||||
self.cancel_timer()
|
||||
|
||||
buffer = self.get_buffer() if not buffer else buffer
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
insert_itr = itr.copy()
|
||||
|
||||
buffer.insert(insert_itr, self._cut_buffer, -1)
|
||||
|
||||
self.clear_cut_buffer_delayed()
|
||||
|
||||
def update_cursor_position(self, buffer = None):
|
||||
buffer = self.get_buffer() if not buffer else buffer
|
||||
|
@ -117,22 +168,67 @@ class SourceViewControllerMixin(KeyInputController, SourceViewEvents):
|
|||
buffer.redo()
|
||||
|
||||
def keyboard_move_lines_up(self):
|
||||
buffer = self.get_buffer()
|
||||
buffer = self.get_buffer()
|
||||
|
||||
self.begin_user_action(buffer)
|
||||
|
||||
had_selection = buffer.get_has_selection()
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = itr.get_line() - 1
|
||||
line_index = itr.get_line_index()
|
||||
selection_bounds = None
|
||||
|
||||
if had_selection:
|
||||
selection_bounds = buffer.get_selection_bounds()
|
||||
sbounds_start = selection_bounds[0].get_line_offset()
|
||||
sbounds_end = selection_bounds[1].get_line_offset()
|
||||
|
||||
self.emit("move-lines", *(False,))
|
||||
# unindent_lines
|
||||
# self.emit("move-words", *(self, 4,))
|
||||
if not had_selection:
|
||||
self.emit("select-all", *(False,))
|
||||
line_itr = buffer.get_iter_at_line_offset(line, line_index)
|
||||
self.get_buffer().place_cursor(line_itr)
|
||||
else:
|
||||
buffer = self.get_buffer()
|
||||
sbounds = buffer.get_selection_bounds()
|
||||
start_itr = buffer.get_iter_at_line_offset( sbounds[0].get_line(), sbounds_start)
|
||||
end_itr = buffer.get_iter_at_line_offset( sbounds[1].get_line() - 1, sbounds_end)
|
||||
|
||||
self.emit("select-all", *(False,))
|
||||
buffer.select_range(start_itr, end_itr)
|
||||
|
||||
self.end_user_action(buffer)
|
||||
|
||||
def keyboard_move_lines_down(self):
|
||||
buffer = self.get_buffer()
|
||||
buffer = self.get_buffer()
|
||||
|
||||
self.begin_user_action(buffer)
|
||||
|
||||
had_selection = buffer.get_has_selection()
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = itr.get_line() + 1
|
||||
line_index = itr.get_line_index()
|
||||
selection_bounds = None
|
||||
sbounds_start = None
|
||||
sbounds_end = None
|
||||
|
||||
if had_selection:
|
||||
selection_bounds = buffer.get_selection_bounds()
|
||||
sbounds_start = selection_bounds[0].get_line_offset()
|
||||
sbounds_end = selection_bounds[1].get_line_offset()
|
||||
|
||||
self.emit("move-lines", *(True,))
|
||||
# self.emit("move-words", *(self, -4,))
|
||||
if not had_selection:
|
||||
self.emit("select-all", *(False,))
|
||||
line_itr = buffer.get_iter_at_line_offset(line, line_index)
|
||||
self.get_buffer().place_cursor(line_itr)
|
||||
else:
|
||||
buffer = self.get_buffer()
|
||||
sbounds = buffer.get_selection_bounds()
|
||||
start_itr = buffer.get_iter_at_line_offset( sbounds[0].get_line(), sbounds_start)
|
||||
end_itr = buffer.get_iter_at_line_offset( sbounds[1].get_line() - 1, sbounds_end)
|
||||
|
||||
self.emit("select-all", *(False,))
|
||||
buffer.select_range(start_itr, end_itr)
|
||||
|
||||
self.end_user_action(buffer)
|
|
@ -26,9 +26,8 @@ class SourceViewEvents(SourceViewDnDMixin, MarkEventsMixin, FileEventsMixin):
|
|||
|
||||
if not self._loading_file:
|
||||
event_system.emit("buffer_changed", (buffer, ))
|
||||
event_system.emit("textDocument/didChange", (file_type, buffer, ))
|
||||
else:
|
||||
event_system.emit("buffer_changed_first_load", (buffer, ))
|
||||
# event_system.emit("textDocument/didChange", (file_type, buffer, ))
|
||||
# event_system.emit("textDocument/completion", (self, ))
|
||||
|
||||
self.update_cursor_position(buffer)
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Python imports
|
||||
import time
|
||||
import signal
|
||||
|
||||
# Lib imports
|
||||
|
@ -12,7 +11,8 @@ from gi.repository import Gdk
|
|||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from core.controller import Controller
|
||||
from core.controllers.base_controller import BaseController
|
||||
|
||||
|
||||
|
||||
class ControllerStartExceptiom(Exception):
|
||||
|
@ -20,7 +20,6 @@ class ControllerStartExceptiom(Exception):
|
|||
|
||||
|
||||
|
||||
|
||||
class Window(Gtk.ApplicationWindow):
|
||||
"""docstring for Window."""
|
||||
|
||||
|
@ -30,12 +29,12 @@ class Window(Gtk.ApplicationWindow):
|
|||
|
||||
self._controller = None
|
||||
|
||||
self._set_window_data()
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
|
||||
self._load_widgets(args, unknownargs)
|
||||
|
||||
self._set_window_data()
|
||||
self._set_size_constraints()
|
||||
|
||||
self.show()
|
||||
|
@ -49,21 +48,26 @@ class Window(Gtk.ApplicationWindow):
|
|||
|
||||
ctx = self.get_style_context()
|
||||
ctx.add_class("main-window")
|
||||
ctx.add_class(f"mw_transparency_{settings.theming.transparency}")
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("focus-in-event", self._on_focus_in_event)
|
||||
self.connect("focus-out-event", self._on_focus_out_event)
|
||||
|
||||
self.connect("delete-event", self._tear_down)
|
||||
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("tear_down", self._tear_down)
|
||||
event_system.subscribe("load_interactive_debug", self._load_interactive_debug)
|
||||
|
||||
def _load_widgets(self, args, unknownargs):
|
||||
if settings_manager.is_debug():
|
||||
self.set_interactive_debugging(True)
|
||||
|
||||
self._controller = Controller(args, unknownargs)
|
||||
self._controller = BaseController(args, unknownargs)
|
||||
if not self._controller:
|
||||
raise ControllerStartException("Controller exited and doesn't exist...")
|
||||
raise ControllerStartException("BaseController exited and doesn't exist...")
|
||||
|
||||
self.add( self._controller.get_core_widget() )
|
||||
|
||||
|
@ -83,7 +87,7 @@ class Window(Gtk.ApplicationWindow):
|
|||
screen = self.get_screen()
|
||||
visual = screen.get_rgba_visual()
|
||||
|
||||
if visual != None and screen.is_composited():
|
||||
if visual != None and screen.is_composited() and settings.config.make_transparent == 0:
|
||||
self.set_visual(visual)
|
||||
self.set_app_paintable(True)
|
||||
self.connect("draw", self._area_draw)
|
||||
|
@ -102,6 +106,15 @@ class Window(Gtk.ApplicationWindow):
|
|||
cr.set_operator(cairo.OPERATOR_OVER)
|
||||
|
||||
|
||||
def _on_focus_in_event(self, widget, event):
|
||||
event_system.emit("pause_dnd_signals")
|
||||
|
||||
def _on_focus_out_event(self, widget, event):
|
||||
event_system.emit("listen_dnd_signals")
|
||||
|
||||
def _load_interactive_debug(self):
|
||||
self.set_interactive_debugging(True)
|
||||
|
||||
def _tear_down(self, widget = None, eve = None):
|
||||
event_system.emit("shutting_down")
|
||||
|
||||
|
@ -116,3 +129,6 @@ class Window(Gtk.ApplicationWindow):
|
|||
|
||||
settings_manager.clear_pid()
|
||||
Gtk.main_quit()
|
||||
|
||||
def main(self):
|
||||
Gtk.main()
|
|
@ -70,4 +70,4 @@ class EventSystem(Singleton):
|
|||
if not response in (None, ''):
|
||||
break
|
||||
|
||||
return response
|
||||
return response
|
|
@ -12,13 +12,27 @@ function main() {
|
|||
|
||||
files=()
|
||||
for f in "$@"; do
|
||||
# ff=${f%:*}
|
||||
# ii=${f#*:}
|
||||
# target=$(readlink -f "${ff}")
|
||||
|
||||
target=$(readlink -f "${f}")
|
||||
i="${#files[@]}"
|
||||
size=$(($i + 1))
|
||||
|
||||
files[$size]="${target}"
|
||||
|
||||
# if [ -n "$ii" ]; then
|
||||
# files[$size]="${target}:${ii}"
|
||||
# else
|
||||
# files[$size]="${target}${ii}"
|
||||
# fi
|
||||
done
|
||||
|
||||
echo "${files[@]}"
|
||||
|
||||
|
||||
cd "/opt/"
|
||||
python /opt/newton.zip "${files[@]}"
|
||||
}
|
||||
main "$@";
|
||||
main "$@";
|
|
@ -81,6 +81,11 @@ notebook > stack > scrolledwindow > textview {
|
|||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
textview {
|
||||
padding-bottom: 50em;
|
||||
}
|
||||
|
||||
|
||||
/* any popover */
|
||||
popover {
|
||||
background: rgba(39, 43, 52, 0.86);
|
||||
|
|
Loading…
Reference in New Issue