Merge pull request 'feature/lsp-integration-work' (#2) from feature/lsp-integration-work into develop

Reviewed-on: #2
This commit is contained in:
itdominator 2024-03-02 00:57:47 +00:00
commit d886ef37d4
20 changed files with 318 additions and 147 deletions

View File

@ -13,7 +13,10 @@ from .capabilities import Capabilities
class ReadPipe(threading.Thread): class ReadPipe(threading.Thread):
def __init__(self, pipe): def __init__(self, pipe):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.pipe = pipe
self.daemon = True
self.pipe = pipe
def run(self): def run(self):
line = self.pipe.readline().decode('utf-8') line = self.pipe.readline().decode('utf-8')
@ -21,7 +24,6 @@ class ReadPipe(threading.Thread):
line = self.pipe.readline().decode('utf-8') line = self.pipe.readline().decode('utf-8')
class LSPController: class LSPController:
def __init__(self, lsp_servers_config = {}): def __init__(self, lsp_servers_config = {}):
super().__init__() super().__init__()
@ -29,6 +31,7 @@ class LSPController:
self.lsp_servers_config = lsp_servers_config self.lsp_servers_config = lsp_servers_config
self.lsp_clients = {} self.lsp_clients = {}
def _blame(self, response): def _blame(self, response):
for d in response['diagnostics']: for d in response['diagnostics']:
if d['severity'] == 1: if d['severity'] == 1:
@ -63,8 +66,10 @@ class LSPController:
if not language or not server_proc: return False if not language or not server_proc: return False
root_path = None root_path = None
root_uri = 'file:///home/abaddon/Coding/Projects/Active/C_n_CPP_Projects/gtk/Newton/src/' # root_uri = 'file:///home/abaddon/Coding/Projects/Active/Python_Projects/000_Usable/gtk/Newton_Editor/src/'
workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}] # 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 = self._generate_client(language, server_proc)
lsp_client.initialize( lsp_client.initialize(
@ -74,6 +79,7 @@ class LSPController:
initializationOptions = initialization_options, \ initializationOptions = initialization_options, \
capabilities = Capabilities.data, \ capabilities = Capabilities.data, \
trace = "off", \ trace = "off", \
# trace = "on", \
workspaceFolders = workspace_folders workspaceFolders = workspace_folders
) )
@ -121,16 +127,17 @@ class LSPController:
return [] 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(): if language_id in self.lsp_clients.keys():
start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset()) start_pos = pylspclient.lsp_structs.Position(line, start.get_line_offset())
end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset()) end_pos = pylspclient.lsp_structs.Position(line, end.get_line_offset())
range_info = pylspclient.lsp_structs.Range(start_pos, end_pos) range_info = pylspclient.lsp_structs.Range(start_pos, end_pos)
text_length = len(text) text_length = len(text)
change_event = pylspclient.lsp_structs.TextDocumentContentChangeEvent(range_info, text_length, 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 [] return []
@ -148,7 +155,8 @@ class LSPController:
return self.lsp_clients[language_id].completion( return self.lsp_clients[language_id].completion(
pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.TextDocumentIdentifier(uri),
pylspclient.lsp_structs.Position(line, offset), pylspclient.lsp_structs.Position(line, offset),
pylspclient.lsp_structs.CompletionContext(trigger, _char) None
# pylspclient.lsp_structs.CompletionContext(trigger, _char)
) )
return [] return []

View File

@ -24,7 +24,7 @@ class Plugin(PluginBase):
super().__init__() super().__init__()
self.name = "LSP Client" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus 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_config_path: str = os.path.dirname(os.path.realpath(__file__)) + "/../../lsp_servers_config.json"
self.lsp_servers_config: dict = {} self.lsp_servers_config: dict = {}
self.lsp_controller = None self.lsp_controller = None
@ -53,30 +53,18 @@ class Plugin(PluginBase):
def inner_subscribe_to_events(self): def inner_subscribe_to_events(self):
self._event_system.subscribe("shutting_down", self._shutting_down) self._event_system.subscribe("shutting_down", self._shutting_down)
self._event_system.subscribe("textDocument/didChange", self._buffer_changed) # self._event_system.subscribe("buffer_changed", self._buffer_changed)
self._event_system.subscribe("textDocument/didOpen", self.lsp_controller.do_open) self._event_system.subscribe("textDocument/didChange", self._buffer_changed)
self._event_system.subscribe("textDocument/didSave", self.lsp_controller.do_save) self._event_system.subscribe("textDocument/didOpen", self.lsp_controller.do_open)
self._event_system.subscribe("textDocument/didClose", self.lsp_controller.do_close) 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/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): def _shutting_down(self):
self.lsp_controller._shutting_down() self.lsp_controller._shutting_down()
def cancel_timer(self): def _buffer_changed(self, file_type, buffer):
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):
iter = buffer.get_iter_at_mark( buffer.get_insert() ) iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line() line = iter.get_line()
start = iter.copy() start = iter.copy()
@ -87,31 +75,33 @@ class Plugin(PluginBase):
end.forward_to_line_end() end.forward_to_line_end()
text = buffer.get_text(start, end, include_hidden_chars = False) text = buffer.get_text(start, end, include_hidden_chars = False)
result = self.lsp_controller.do_change(language_id, line, start, end, text) result = self.lsp_controller.do_change(buffer.uri, buffer.language_id, line, start, end, text)
# print(result)
def completion(self, source_view, context, callback): def _do_completion(self, source_view):
self.cancel_timer() filepath = source_view.get_current_file()
self.delay_completion(source_view, context, callback)
def _do_completion(self, source_view, context, callback):
filepath = source_view.get_current_file()
if not filepath: return if not filepath: return
uri = filepath.get_uri() uri = filepath.get_uri()
buffer = source_view.get_buffer() buffer = source_view.get_buffer()
iter = buffer.get_iter_at_mark( buffer.get_insert() ) iter = buffer.get_iter_at_mark( buffer.get_insert() )
line = iter.get_line() + 1 line = iter.get_line()
_char = iter.get_char() _char = iter.get_char()
if iter.backward_char(): if iter.backward_char():
_char = iter.get_char() _char = iter.get_char()
offset = iter.get_line_offset() offset = iter.get_line_offset()
result = self.lsp_controller.do_completion(source_view.get_filetype(), uri, line, offset, _char) result = self.lsp_controller.do_completion(
callback(context, result) source_view.get_filetype(),
uri,
line,
offset,
_char
)
return result
def _do_goto(self, language_id, uri, line, offset): def _do_goto(self, language_id, uri, line, offset):
results = self.lsp_controller.do_goto(language_id, uri, line, offset) results = self.lsp_controller.do_goto(language_id, uri, line, offset)

View File

@ -1,3 +1,8 @@
# Python imports
# Lib imports
# Application imports
from . import lsp_structs from . import lsp_structs
@ -40,13 +45,13 @@ class LspClient(object):
""" """
self.lsp_endpoint.start() self.lsp_endpoint.start()
return self.lsp_endpoint.call_method("initialize", \ return self.lsp_endpoint.call_method("initialize",
processId = processId, \ processId = processId,
rootPath = rootPath, \ rootPath = rootPath,
rootUri = rootUri, \ rootUri = rootUri,
initializationOptions = initializationOptions, \ initializationOptions = initializationOptions,
capabilities = capabilities, \ capabilities = capabilities,
trace = trace, \ trace = trace,
workspaceFolders = workspaceFolders workspaceFolders = workspaceFolders
) )
@ -61,18 +66,12 @@ class LspClient(object):
def shutdown(self): 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") return self.lsp_endpoint.call_method("shutdown")
def exit(self): 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") 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. 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. 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 :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 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''. to S' and c2 to S''.

View File

@ -10,9 +10,6 @@ import tracemalloc
tracemalloc.start() tracemalloc.start()
# Lib imports # Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports # Application imports
from __builtins__ import * 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__": if __name__ == "__main__":
''' Set process title, get arguments, and create GTK main thread. ''' ''' 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: try:
setproctitle(f'{app_name}')
faulthandler.enable() # For better debug info faulthandler.enable() # For better debug info
main(args, unknownargs)
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()
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
quit() quit()

View File

@ -16,35 +16,39 @@ class AppLaunchException(Exception):
class Application(IPCServer): class Application:
""" docstring for Application. """ """ docstring for Application. """
def __init__(self, args, unknownargs): def __init__(self, args, unknownargs):
super(Application, self).__init__() super(Application, self).__init__()
if not settings_manager.is_trace_debug(): if not settings_manager.is_trace_debug():
self.socket_realization_check() self.load_ipc(args, unknownargs)
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.setup_debug_hook() 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: try:
self.create_ipc_listener() ipc_server.create_ipc_listener()
except Exception: except Exception:
self.send_test_ipc_message() ipc_server.send_test_ipc_message()
try: try:
self.create_ipc_listener() ipc_server.create_ipc_listener()
except Exception as e: except Exception as e:
... ...

View File

@ -1,3 +0,0 @@
"""
Gtk Bound Signal Module
"""

View File

@ -14,9 +14,9 @@ from .editors_container import EditorsContainer
class CoreWidget(Gtk.Box): class BaseContainer(Gtk.Box):
def __init__(self): def __init__(self):
super(CoreWidget, self).__init__() super(BaseContainer, self).__init__()
builder = settings_manager.get_builder() builder = settings_manager.get_builder()
self.ctx = self.get_style_context() self.ctx = self.get_style_context()

View File

@ -0,0 +1,3 @@
"""
Controllers Module
"""

View File

@ -10,13 +10,14 @@ from gi.repository import Gdk
from gi.repository import GLib from gi.repository import GLib
# Application imports # Application imports
from .controller_data import ControllerData from ..mixins.signals_mixins import SignalsMixins
from .containers.core_widget import CoreWidget from ..containers.base_container import BaseContainer
from .mixins.signals_mixins import SignalsMixins
from .base_controller_data import BaseControllerData
class Controller(SignalsMixins, ControllerData): class BaseController(SignalsMixins, BaseControllerData):
def __init__(self, args, unknownargs): def __init__(self, args, unknownargs):
messages = [] messages = []
for arg in unknownargs + [args.new_tab,]: for arg in unknownargs + [args.new_tab,]:
@ -60,7 +61,7 @@ class Controller(SignalsMixins, ControllerData):
self.builder.expose_object("main_window", self.window) self.builder.expose_object("main_window", self.window)
settings_manager.set_builder(self.builder) settings_manager.set_builder(self.builder)
self.core_widget = CoreWidget() self.core_widget = BaseContainer()
settings_manager.register_signals_to_builder([self, self.core_widget]) settings_manager.register_signals_to_builder([self, self.core_widget])

View File

@ -9,9 +9,8 @@ from plugins.plugins_controller import PluginsController
class BaseControllerData:
class ControllerData: ''' BaseControllerData contains most of the state of the app at ay given time. It also has some support methods. '''
''' ControllerData contains most of the state of the app at ay given time. It also has some support methods. '''
def setup_controller_data(self) -> None: def setup_controller_data(self) -> None:
self.window = settings_manager.get_main_window() self.window = settings_manager.get_main_window()

View File

@ -39,7 +39,13 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']: if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
return False 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 return True
def do_get_priority(self): def do_get_priority(self):
@ -49,7 +55,9 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
return GtkSource.CompletionActivation.INTERACTIVE return GtkSource.CompletionActivation.INTERACTIVE
def do_populate(self, context, result = None): def do_populate(self, context, result = None):
result = event_system.emit_and_await("textDocument/completion", (self._source_view,))
proposals = [] proposals = []
if result: if result:
if not result.items is None: if not result.items is None:
for item in result.items: for item in result.items:
@ -87,5 +95,4 @@ class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
comp_item.set_icon( self.get_icon_for_type(item.kind) ) comp_item.set_icon( self.get_icon_for_type(item.kind) )
comp_item.set_info(item.documentation) comp_item.set_info(item.documentation)
return comp_item return comp_item

View File

@ -81,7 +81,7 @@ class KeyInputController:
return True 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": if keyname == "z":
self.keyboard_undo() self.keyboard_undo()
if keyname == "y": if keyname == "y":
@ -94,6 +94,12 @@ class KeyInputController:
self.toggle_highlight_line() self.toggle_highlight_line()
if keyname == "g": if keyname == "g":
self.go_to_call() 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": if keyname == "equal":
self.scale_up_text() self.scale_up_text()

View File

@ -6,12 +6,13 @@ import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('GtkSource', '4') gi.require_version('GtkSource', '4')
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gio from gi.repository import Gio
from gi.repository import GtkSource from gi.repository import GtkSource
# Application imports # 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: class FileEventsMixin:
@ -79,7 +80,7 @@ class FileEventsMixin:
self.update_labels(gfile) self.update_labels(gfile)
self._loading_file = False self._loading_file = False
self._file_loader.load_async(io_priority = 80, self._file_loader.load_async(io_priority = GLib.PRIORITY_HIGH,
cancellable = None, cancellable = None,
progress_callback = None, progress_callback = None,
progress_callback_data = None, progress_callback_data = None,
@ -140,8 +141,10 @@ class FileEventsMixin:
for provider in self._completion.get_providers(): for provider in self._completion.get_providers():
self._completion.remove_provider(provider) self._completion.remove_provider(provider)
uri = self._current_file.get_uri() uri = self._current_file.get_uri()
buffer = self.get_buffer() buffer = self.get_buffer()
buffer.uri = uri
buffer.language_id = self._current_filetype
event_system.emit("textDocument/didOpen", (self._current_filetype, uri,)) event_system.emit("textDocument/didOpen", (self._current_filetype, uri,))
@ -149,9 +152,12 @@ class FileEventsMixin:
word_completion.register(buffer) word_completion.register(buffer)
self._completion.add_provider(word_completion) self._completion.add_provider(word_completion)
lsp_completion_provider = LSPCompletionProvider(self) # lsp_completion_provider = LSPCompletionProvider(self)
self._completion.add_provider(lsp_completion_provider) # 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) self.got_to_line(buffer, line)
event_system.emit("buffer_changed_first_load", (buffer, ))

View File

@ -1,4 +1,5 @@
# Python imports # Python imports
import threading
# Lib imports # Lib imports
import gi import gi
@ -33,6 +34,9 @@ class SourceView(SourceViewControllerMixin, GtkSource.View):
self._current_filename: str = "" self._current_filename: str = ""
self._current_filepath: str = None self._current_filepath: str = None
self._current_filetype: str = "buffer" 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._skip_file_load = False
self._ignore_internal_change = False self._ignore_internal_change = False
@ -96,3 +100,19 @@ class SourceView(SourceViewControllerMixin, GtkSource.View):
def _load_widgets(self): 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 = ""

View File

@ -34,6 +34,57 @@ class SourceViewControllerMixin(KeyInputController, SourceViewEvents):
event_system.emit("textDocument/definition", (self.get_filetype(), uri, line, offset,)) 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): def update_cursor_position(self, buffer = None):
buffer = self.get_buffer() if not buffer else buffer buffer = self.get_buffer() if not buffer else buffer
@ -117,22 +168,67 @@ class SourceViewControllerMixin(KeyInputController, SourceViewEvents):
buffer.redo() buffer.redo()
def keyboard_move_lines_up(self): def keyboard_move_lines_up(self):
buffer = self.get_buffer() buffer = self.get_buffer()
self.begin_user_action(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,)) self.emit("move-lines", *(False,))
# unindent_lines if not had_selection:
# self.emit("move-words", *(self, 4,)) 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) self.end_user_action(buffer)
def keyboard_move_lines_down(self): def keyboard_move_lines_down(self):
buffer = self.get_buffer() buffer = self.get_buffer()
self.begin_user_action(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-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) self.end_user_action(buffer)

View File

@ -26,9 +26,8 @@ class SourceViewEvents(SourceViewDnDMixin, MarkEventsMixin, FileEventsMixin):
if not self._loading_file: if not self._loading_file:
event_system.emit("buffer_changed", (buffer, )) event_system.emit("buffer_changed", (buffer, ))
event_system.emit("textDocument/didChange", (file_type, buffer, )) # event_system.emit("textDocument/didChange", (file_type, buffer, ))
else: # event_system.emit("textDocument/completion", (self, ))
event_system.emit("buffer_changed_first_load", (buffer, ))
self.update_cursor_position(buffer) self.update_cursor_position(buffer)

View File

@ -1,5 +1,4 @@
# Python imports # Python imports
import time
import signal import signal
# Lib imports # Lib imports
@ -12,7 +11,8 @@ from gi.repository import Gdk
from gi.repository import GLib from gi.repository import GLib
# Application imports # Application imports
from core.controller import Controller from core.controllers.base_controller import BaseController
class ControllerStartExceptiom(Exception): class ControllerStartExceptiom(Exception):
@ -20,7 +20,6 @@ class ControllerStartExceptiom(Exception):
class Window(Gtk.ApplicationWindow): class Window(Gtk.ApplicationWindow):
"""docstring for Window.""" """docstring for Window."""
@ -30,12 +29,12 @@ class Window(Gtk.ApplicationWindow):
self._controller = None self._controller = None
self._set_window_data()
self._setup_styling() self._setup_styling()
self._setup_signals() self._setup_signals()
self._subscribe_to_events() self._subscribe_to_events()
self._load_widgets(args, unknownargs) self._load_widgets(args, unknownargs)
self._set_window_data()
self._set_size_constraints() self._set_size_constraints()
self.show() self.show()
@ -49,21 +48,26 @@ class Window(Gtk.ApplicationWindow):
ctx = self.get_style_context() ctx = self.get_style_context()
ctx.add_class("main-window") ctx.add_class("main-window")
ctx.add_class(f"mw_transparency_{settings.theming.transparency}")
def _setup_signals(self): 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) self.connect("delete-event", self._tear_down)
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down)
def _subscribe_to_events(self): def _subscribe_to_events(self):
event_system.subscribe("tear_down", self._tear_down) event_system.subscribe("tear_down", self._tear_down)
event_system.subscribe("load_interactive_debug", self._load_interactive_debug)
def _load_widgets(self, args, unknownargs): def _load_widgets(self, args, unknownargs):
if settings_manager.is_debug(): if settings_manager.is_debug():
self.set_interactive_debugging(True) self.set_interactive_debugging(True)
self._controller = Controller(args, unknownargs) self._controller = BaseController(args, unknownargs)
if not self._controller: 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() ) self.add( self._controller.get_core_widget() )
@ -83,7 +87,7 @@ class Window(Gtk.ApplicationWindow):
screen = self.get_screen() screen = self.get_screen()
visual = screen.get_rgba_visual() 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_visual(visual)
self.set_app_paintable(True) self.set_app_paintable(True)
self.connect("draw", self._area_draw) self.connect("draw", self._area_draw)
@ -102,6 +106,15 @@ class Window(Gtk.ApplicationWindow):
cr.set_operator(cairo.OPERATOR_OVER) 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): def _tear_down(self, widget = None, eve = None):
event_system.emit("shutting_down") event_system.emit("shutting_down")
@ -116,3 +129,6 @@ class Window(Gtk.ApplicationWindow):
settings_manager.clear_pid() settings_manager.clear_pid()
Gtk.main_quit() Gtk.main_quit()
def main(self):
Gtk.main()

View File

@ -12,12 +12,26 @@ function main() {
files=() files=()
for f in "$@"; do for f in "$@"; do
# ff=${f%:*}
# ii=${f#*:}
# target=$(readlink -f "${ff}")
target=$(readlink -f "${f}") target=$(readlink -f "${f}")
i="${#files[@]}" i="${#files[@]}"
size=$(($i + 1)) size=$(($i + 1))
files[$size]="${target}" files[$size]="${target}"
# if [ -n "$ii" ]; then
# files[$size]="${target}:${ii}"
# else
# files[$size]="${target}${ii}"
# fi
done done
echo "${files[@]}"
cd "/opt/" cd "/opt/"
python /opt/newton.zip "${files[@]}" python /opt/newton.zip "${files[@]}"
} }

View File

@ -81,6 +81,11 @@ notebook > stack > scrolledwindow > textview {
color: rgba(255, 255, 255, 1); color: rgba(255, 255, 255, 1);
} }
textview {
padding-bottom: 50em;
}
/* any popover */ /* any popover */
popover { popover {
background: rgba(39, 43, 52, 0.86); background: rgba(39, 43, 52, 0.86);