mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 14:18:26 +02:00
Add threaded job infrastructure to tui
This commit is contained in:
@@ -7,7 +7,6 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
from kitty.key_encoding import ESCAPE
|
from kitty.key_encoding import ESCAPE
|
||||||
@@ -28,14 +27,7 @@ class DiffHandler(Handler):
|
|||||||
self.report_traceback_on_exit = None
|
self.report_traceback_on_exit = None
|
||||||
|
|
||||||
def create_collection(self):
|
def create_collection(self):
|
||||||
try:
|
self.start_job('diff', create_collection, self.left, self.right)
|
||||||
self.collection = create_collection(self.left, self.right)
|
|
||||||
except Exception:
|
|
||||||
self.report_traceback_on_exit = traceback.format_exc()
|
|
||||||
self.quit_loop(1)
|
|
||||||
else:
|
|
||||||
self.state = READY
|
|
||||||
self.wakeup()
|
|
||||||
|
|
||||||
def init_terminal_state(self):
|
def init_terminal_state(self):
|
||||||
self.write(set_line_wrapping(False))
|
self.write(set_line_wrapping(False))
|
||||||
@@ -45,9 +37,7 @@ class DiffHandler(Handler):
|
|||||||
Handler.initialize(self, *args)
|
Handler.initialize(self, *args)
|
||||||
self.init_terminal_state()
|
self.init_terminal_state()
|
||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
t = Thread(target=self.create_collection, name='CreatingCollection')
|
self.create_collection()
|
||||||
t.daemon = True
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
def draw_screen(self):
|
def draw_screen(self):
|
||||||
if self.state is INITIALIZING:
|
if self.state is INITIALIZING:
|
||||||
@@ -65,8 +55,14 @@ class DiffHandler(Handler):
|
|||||||
self.quit_loop(0)
|
self.quit_loop(0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_wakeup(self):
|
def on_job_done(self, job_id, job_result):
|
||||||
self.draw_screen()
|
if 'tb' in job_result:
|
||||||
|
self.report_traceback_on_exit = traceback.format_exc()
|
||||||
|
self.quit_loop(1)
|
||||||
|
if job_id == 'diff':
|
||||||
|
self.collection = job_result['result']
|
||||||
|
self.state = READY
|
||||||
|
self.write(clear_screen())
|
||||||
|
|
||||||
def on_interrupt(self):
|
def on_interrupt(self):
|
||||||
self.quit_loop(1)
|
self.quit_loop(1)
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
|
|
||||||
def initialize(self, screen_size, quit_loop, wakeup):
|
def initialize(self, screen_size, quit_loop, wakeup, start_job):
|
||||||
self.screen_size, self.quit_loop = screen_size, quit_loop
|
self.screen_size, self.quit_loop = screen_size, quit_loop
|
||||||
self.wakeup = wakeup
|
self.wakeup = wakeup
|
||||||
|
self.start_job = start_job
|
||||||
|
|
||||||
def on_resize(self, screen_size):
|
def on_resize(self, screen_size):
|
||||||
self.screen_size = screen_size
|
self.screen_size = screen_size
|
||||||
@@ -33,6 +34,9 @@ class Handler:
|
|||||||
def on_wakeup(self):
|
def on_wakeup(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_job_done(self, job_id, job_result):
|
||||||
|
pass
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import tty
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import closing, contextmanager
|
from contextlib import closing, contextmanager
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from queue import Empty, Queue
|
||||||
|
|
||||||
from kitty.fast_data_types import parse_input_from_terminal, safe_pipe
|
from kitty.fast_data_types import parse_input_from_terminal, safe_pipe
|
||||||
from kitty.icat import screen_size
|
from kitty.icat import screen_size
|
||||||
@@ -24,7 +25,7 @@ from kitty.key_encoding import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .handler import Handler
|
from .handler import Handler
|
||||||
from .operations import init_state, reset_state, clear_screen
|
from .operations import clear_screen, init_state, reset_state
|
||||||
|
|
||||||
|
|
||||||
def log(*a, **kw):
|
def log(*a, **kw):
|
||||||
@@ -120,8 +121,8 @@ class UnhandledException(Handler):
|
|||||||
def __init__(self, tb):
|
def __init__(self, tb):
|
||||||
self.tb = tb
|
self.tb = tb
|
||||||
|
|
||||||
def initialize(self, screen_size, quit_loop, wakeup):
|
def initialize(self, *args):
|
||||||
Handler.initialize(self, screen_size, quit_loop, wakeup)
|
Handler.initialize(self, *args)
|
||||||
self.write(clear_screen())
|
self.write(clear_screen())
|
||||||
self.write(self.tb.replace('\n', '\r\n'))
|
self.write(self.tb.replace('\n', '\r\n'))
|
||||||
self.write('\r\n')
|
self.write('\r\n')
|
||||||
@@ -167,6 +168,24 @@ class Loop:
|
|||||||
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
|
self.sanitize_bracketed_paste = bool(sanitize_bracketed_paste)
|
||||||
if self.sanitize_bracketed_paste:
|
if self.sanitize_bracketed_paste:
|
||||||
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
|
self.sanitize_ibp_pat = re.compile(sanitize_bracketed_paste)
|
||||||
|
self.jobs_queue = Queue()
|
||||||
|
|
||||||
|
def start_job(self, job_id, func, *args, **kw):
|
||||||
|
from threading import Thread
|
||||||
|
t = Thread(target=partial(self._run_job, job_id, func), args=args, kwargs=kw, name='LoopJob')
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
def _run_job(self, job_id, func, *args, **kw):
|
||||||
|
try:
|
||||||
|
result = func(*args, **kw)
|
||||||
|
except Exception as err:
|
||||||
|
import traceback
|
||||||
|
entry = {'id': job_id, 'exception': err, 'tb': traceback.format_exc()}
|
||||||
|
else:
|
||||||
|
entry = {'id': job_id, 'result': result}
|
||||||
|
self.jobs_queue.put(entry)
|
||||||
|
self._wakeup_write(b'j')
|
||||||
|
|
||||||
def _read_ready(self, handler):
|
def _read_ready(self, handler):
|
||||||
if not self.read_allowed:
|
if not self.read_allowed:
|
||||||
@@ -283,6 +302,15 @@ class Loop:
|
|||||||
handler.on_term()
|
handler.on_term()
|
||||||
if b'i' in data:
|
if b'i' in data:
|
||||||
handler.on_interrupt()
|
handler.on_interrupt()
|
||||||
|
if b'j' in data:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
entry = self.jobs_queue.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
job_id = entry.pop('id')
|
||||||
|
handler.on_job_done(job_id, entry)
|
||||||
if b'1' in data:
|
if b'1' in data:
|
||||||
handler.on_wakeup()
|
handler.on_wakeup()
|
||||||
|
|
||||||
@@ -331,7 +359,7 @@ class Loop:
|
|||||||
handler._term_manager = term_manager
|
handler._term_manager = term_manager
|
||||||
keep_going = True
|
keep_going = True
|
||||||
try:
|
try:
|
||||||
handler.initialize(screen_size(), self.quit, self.wakeup)
|
handler.initialize(screen_size(), self.quit, self.wakeup, self.start_job)
|
||||||
except Exception:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
@@ -363,7 +391,7 @@ class Loop:
|
|||||||
handler = UnhandledException(tb)
|
handler = UnhandledException(tb)
|
||||||
handler.write_buf = []
|
handler.write_buf = []
|
||||||
handler._term_manager = term_manager
|
handler._term_manager = term_manager
|
||||||
handler.initialize(screen_size(), self.quit, self.wakeup)
|
handler.initialize(screen_size(), self.quit, self.wakeup, self.start_job)
|
||||||
while True:
|
while True:
|
||||||
has_data_to_write = bool(handler.write_buf)
|
has_data_to_write = bool(handler.write_buf)
|
||||||
if not has_data_to_write and not self.read_allowed:
|
if not has_data_to_write and not self.read_allowed:
|
||||||
|
|||||||
Reference in New Issue
Block a user