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):
|
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,11 +155,12 @@ 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 []
|
||||||
|
|
||||||
|
|
||||||
def load_lsp_server(self, language_id):
|
def load_lsp_server(self, language_id):
|
||||||
if not language_id in self.lsp_servers_config.keys():
|
if not language_id in self.lsp_servers_config.keys():
|
||||||
|
@ -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()
|
||||||
@ -86,32 +74,34 @@ class Plugin(PluginBase):
|
|||||||
start.forward_line()
|
start.forward_line()
|
||||||
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)
|
||||||
|
@ -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''.
|
||||||
|
@ -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()
|
34
src/app.py
34
src/app.py
@ -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:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -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):
|
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()
|
3
src/core/controllers/__init__.py
Normal file
3
src/core/controllers/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Controllers Module
|
||||||
|
"""
|
@ -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])
|
||||||
|
|
@ -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()
|
@ -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:
|
||||||
@ -86,6 +94,5 @@ 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
|
|
@ -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()
|
||||||
|
@ -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,
|
||||||
@ -139,19 +140,24 @@ class FileEventsMixin:
|
|||||||
def _document_loaded(self, line: int = 0):
|
def _document_loaded(self, line: int = 0):
|
||||||
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,))
|
||||||
|
|
||||||
word_completion = GtkSource.CompletionWords.new("word_completion")
|
word_completion = GtkSource.CompletionWords.new("word_completion")
|
||||||
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, ))
|
||||||
|
|
@ -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
|
||||||
@ -95,4 +99,20 @@ 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 = ""
|
||||||
|
@ -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)
|
@ -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)
|
||||||
|
|
||||||
|
@ -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()
|
@ -70,4 +70,4 @@ class EventSystem(Singleton):
|
|||||||
if not response in (None, ''):
|
if not response in (None, ''):
|
||||||
break
|
break
|
||||||
|
|
||||||
return response
|
return response
|
@ -12,13 +12,27 @@ 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[@]}"
|
||||||
}
|
}
|
||||||
main "$@";
|
main "$@";
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user