Move change tracking into the screen class
This commit is contained in:
parent
c7b87f9174
commit
9b544f83e0
@ -20,7 +20,6 @@ from pyte import modes as mo
|
||||
from .char_grid import CharGrid
|
||||
from .keys import interpret_text_event, interpret_key_event
|
||||
from .screen import Screen
|
||||
from .tracker import ChangeTracker
|
||||
from .utils import resize_pty, create_pty
|
||||
|
||||
|
||||
@ -53,8 +52,7 @@ class Boss(Thread):
|
||||
self.queue_action(self.initialize)
|
||||
self.profile = args.profile
|
||||
self.window, self.opts = window, opts
|
||||
self.tracker = ChangeTracker()
|
||||
self.screen = Screen(self.opts, self.tracker, self)
|
||||
self.screen = Screen(self.opts, self)
|
||||
self.char_grid = CharGrid(self.screen, opts, window_width, window_height)
|
||||
sclass = DebugStream if args.dump_commands else Stream
|
||||
self.stream = sclass(self.screen)
|
||||
@ -179,10 +177,11 @@ class Boss(Thread):
|
||||
dispatch[r]()
|
||||
if writers:
|
||||
self.write_ready()
|
||||
if self.tracker.dirty:
|
||||
self.mark_dirtied()
|
||||
if self.pending_update_screen is not None and monotonic() > self.pending_update_screen:
|
||||
self.apply_update_screen()
|
||||
if self.pending_update_screen is not None:
|
||||
if monotonic() > self.pending_update_screen:
|
||||
self.apply_update_screen()
|
||||
elif self.screen.is_dirty:
|
||||
self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY
|
||||
|
||||
def close(self):
|
||||
if not self.shutting_down:
|
||||
@ -234,14 +233,9 @@ class Boss(Thread):
|
||||
def queue_write(self, data):
|
||||
self.write_buf = memoryview(self.write_buf.tobytes() + data)
|
||||
|
||||
def mark_dirtied(self):
|
||||
# Batch screen updates
|
||||
if self.pending_update_screen is None:
|
||||
self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY
|
||||
|
||||
def apply_update_screen(self):
|
||||
self.pending_update_screen = None
|
||||
changes = self.tracker.consolidate_changes()
|
||||
changes = self.screen.consolidate_changes()
|
||||
self.char_grid.update_cell_data(changes)
|
||||
glfw.glfwPostEmptyEvent()
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ from typing import Sequence
|
||||
from pyte import charsets as cs, graphics as g, modes as mo
|
||||
from .utils import wcwidth, is_simple_string, sanitize_title
|
||||
from .unicode import ignore_pat
|
||||
from .tracker import ChangeTracker
|
||||
from .fast_data_types import LineBuf, REVERSE, Cursor
|
||||
|
||||
|
||||
@ -55,21 +56,28 @@ class Screen:
|
||||
tracker_callbacks = 'cursor_changed update_screen update_line_range update_cell_range line_added_to_history'.split()
|
||||
_notify_cursor_position = True
|
||||
|
||||
def __init__(self, opts, tracker, callbacks=None, columns: int=80, lines: int=24):
|
||||
for attr in self.tracker_callbacks:
|
||||
setattr(self, attr, getattr(tracker, attr))
|
||||
def __init__(self, opts, callbacks=None, columns: int=80, lines: int=24):
|
||||
for attr in default_callbacks:
|
||||
setattr(self, attr, getattr(callbacks, attr, default_callbacks[attr]))
|
||||
self.main_savepoints, self.alt_savepoints = deque(), deque()
|
||||
self.savepoints = self.main_savepoints
|
||||
self.columns = columns
|
||||
self.lines = lines
|
||||
self.tracker = ChangeTracker()
|
||||
for attr in self.tracker_callbacks:
|
||||
setattr(self, attr, getattr(self.tracker, attr))
|
||||
self.consolidate_changes = self.tracker.consolidate_changes
|
||||
self.reset_dirty = self.tracker.reset
|
||||
sz = max(1000, opts.scrollback_lines)
|
||||
self.tophistorybuf = deque(maxlen=sz)
|
||||
self.main_linebuf, self.alt_linebuf = LineBuf(self.lines, self.columns), LineBuf(self.lines, self.columns)
|
||||
self.linebuf = self.main_linebuf
|
||||
self.reset(notify=False)
|
||||
|
||||
@property
|
||||
def is_dirty(self):
|
||||
return self.tracker.dirty
|
||||
|
||||
def apply_opts(self, opts):
|
||||
sz = max(1000, opts.scrollback_lines)
|
||||
if sz != self.tophistorybuf.maxlen:
|
||||
|
||||
@ -6,7 +6,6 @@ from collections import defaultdict
|
||||
from unittest import TestCase
|
||||
|
||||
from kitty.screen import Screen
|
||||
from kitty.tracker import ChangeTracker
|
||||
from kitty.config import defaults
|
||||
from kitty.fast_data_types import LineBuf, Cursor
|
||||
|
||||
@ -34,10 +33,9 @@ class BaseTest(TestCase):
|
||||
ae = TestCase.assertEqual
|
||||
|
||||
def create_screen(self, cols=5, lines=5, history_size=5):
|
||||
t = ChangeTracker()
|
||||
opts = defaults._replace(scrollback_lines=history_size)
|
||||
s = Screen(opts, t, columns=cols, lines=lines)
|
||||
return s, t
|
||||
s = Screen(opts, columns=cols, lines=lines)
|
||||
return s
|
||||
|
||||
def assertEqualAttributes(self, c1, c2):
|
||||
x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0
|
||||
@ -47,8 +45,8 @@ class BaseTest(TestCase):
|
||||
finally:
|
||||
c1.x, c1.y, c2.x, c2.y = x1, y1, x2, y2
|
||||
|
||||
def assertChanges(self, t, ignore='', **expected_changes):
|
||||
actual_changes = t.consolidate_changes()
|
||||
def assertChanges(self, s, ignore='', **expected_changes):
|
||||
actual_changes = s.consolidate_changes()
|
||||
ignore = frozenset(ignore.split())
|
||||
for k, v in actual_changes.items():
|
||||
if isinstance(v, defaultdict):
|
||||
|
||||
@ -11,11 +11,11 @@ class TestScreen(BaseTest):
|
||||
|
||||
def test_draw_fast(self):
|
||||
# Test in line-wrap, non-insert mode
|
||||
s, t = self.create_screen()
|
||||
s = self.create_screen()
|
||||
s.draw(b'a' * 5)
|
||||
self.ae(str(s.line(0)), 'a' * 5)
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
|
||||
s.draw(b'b' * 7)
|
||||
self.assertTrue(s.linebuf.is_continued(1))
|
||||
self.assertTrue(s.linebuf.is_continued(2))
|
||||
@ -23,124 +23,124 @@ class TestScreen(BaseTest):
|
||||
self.ae(str(s.line(1)), 'b' * 5)
|
||||
self.ae(str(s.line(2)), 'b' * 2 + ' ' * 3)
|
||||
self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2)
|
||||
self.assertChanges(t, ignore='cursor', cells={1: ((0, 4),), 2: ((0, 1),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={1: ((0, 4),), 2: ((0, 1),)})
|
||||
s.draw(b'c' * 15)
|
||||
self.ae(str(s.line(0)), 'b' * 5)
|
||||
self.ae(str(s.line(1)), 'bbccc')
|
||||
|
||||
# Now test without line-wrap
|
||||
s.reset(), t.reset()
|
||||
s.reset(), s.reset_dirty()
|
||||
s.reset_mode(mo.DECAWM)
|
||||
s.draw(b'0123456789')
|
||||
self.ae(str(s.line(0)), '01239')
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
|
||||
s.draw(b'ab')
|
||||
self.ae(str(s.line(0)), '0123b')
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((4, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((4, 4),)})
|
||||
|
||||
# Now test in insert mode
|
||||
s.reset(), t.reset()
|
||||
s.reset(), s.reset_dirty()
|
||||
s.set_mode(mo.IRM)
|
||||
s.draw(b'12345' * 5)
|
||||
s.cursor_back(5)
|
||||
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 4)
|
||||
t.reset()
|
||||
s.reset_dirty()
|
||||
s.draw(b'ab')
|
||||
self.ae(str(s.line(4)), 'ab123')
|
||||
self.ae((s.cursor.x, s.cursor.y), (2, 4))
|
||||
self.assertChanges(t, ignore='cursor', cells={4: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={4: ((0, 4),)})
|
||||
|
||||
def test_draw_char(self):
|
||||
# Test in line-wrap, non-insert mode
|
||||
s, t = self.create_screen()
|
||||
s = self.create_screen()
|
||||
s.draw('ココx'.encode('utf-8'))
|
||||
self.ae(str(s.line(0)), 'ココx')
|
||||
self.ae(tuple(map(s.line(0).width, range(5))), (2, 0, 2, 0, 1))
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
|
||||
s.draw('ニチハ'.encode('utf-8'))
|
||||
self.ae(str(s.line(0)), 'ココx')
|
||||
self.ae(str(s.line(1)), 'ニチ ')
|
||||
self.ae(str(s.line(2)), 'ハ ')
|
||||
self.assertChanges(t, ignore='cursor', cells={1: ((0, 3),), 2: ((0, 1),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={1: ((0, 3),), 2: ((0, 1),)})
|
||||
self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2)
|
||||
s.draw('Ƶ̧\u0308'.encode('utf-8'))
|
||||
self.ae(str(s.line(2)), 'ハƵ̧\u0308 ')
|
||||
self.ae(s.cursor.x, 3), self.ae(s.cursor.y, 2)
|
||||
self.assertChanges(t, ignore='cursor', cells={2: ((2, 2),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={2: ((2, 2),)})
|
||||
s.draw(b'xy'), s.draw('\u0306'.encode('utf-8'))
|
||||
self.ae(str(s.line(2)), 'ハƵ̧\u0308xy\u0306')
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 2)
|
||||
self.assertChanges(t, ignore='cursor', cells={2: ((3, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={2: ((3, 4),)})
|
||||
s.draw(b'c' * 15)
|
||||
self.ae(str(s.line(0)), 'ニチ ')
|
||||
|
||||
# Now test without line-wrap
|
||||
s.reset(), t.reset()
|
||||
s.reset(), s.reset_dirty()
|
||||
s.reset_mode(mo.DECAWM)
|
||||
s.draw('0\u030612345\u03066789\u0306'.encode('utf-8'))
|
||||
self.ae(str(s.line(0)), '0\u03061239\u0306')
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
|
||||
s.draw('ab\u0306'.encode('utf-8'))
|
||||
self.ae(str(s.line(0)), '0\u0306123b\u0306')
|
||||
self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((4, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((4, 4),)})
|
||||
|
||||
# Now test in insert mode
|
||||
s.reset(), t.reset()
|
||||
s.reset(), s.reset_dirty()
|
||||
s.set_mode(mo.IRM)
|
||||
s.draw('1\u03062345'.encode('utf-8') * 5)
|
||||
s.cursor_back(5)
|
||||
self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 4)
|
||||
t.reset()
|
||||
s.reset_dirty()
|
||||
s.draw('a\u0306b'.encode('utf-8'))
|
||||
self.ae(str(s.line(4)), 'a\u0306b1\u030623')
|
||||
self.ae((s.cursor.x, s.cursor.y), (2, 4))
|
||||
self.assertChanges(t, ignore='cursor', cells={4: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={4: ((0, 4),)})
|
||||
|
||||
def test_char_manipulation(self):
|
||||
s, t = self.create_screen()
|
||||
s = self.create_screen()
|
||||
|
||||
def init():
|
||||
s.reset(), t.reset()
|
||||
s.reset(), s.reset_dirty()
|
||||
s.draw(b'abcde')
|
||||
s.cursor.bold = True
|
||||
s.cursor_back(4)
|
||||
t.reset()
|
||||
s.reset_dirty()
|
||||
self.ae(s.cursor.x, 1)
|
||||
|
||||
init()
|
||||
s.insert_characters(2)
|
||||
self.ae(str(s.line(0)), 'a bc')
|
||||
self.assertTrue(s.line(0).cursor_from(1).bold)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((1, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((1, 4),)})
|
||||
s.cursor_back(1)
|
||||
s.insert_characters(20)
|
||||
self.ae(str(s.line(0)), ' ')
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
|
||||
s.draw('xココ'.encode('utf-8'))
|
||||
s.cursor_back(5)
|
||||
t.reset()
|
||||
s.reset_dirty()
|
||||
s.insert_characters(1)
|
||||
self.ae(str(s.line(0)), ' xコ ')
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)})
|
||||
|
||||
init()
|
||||
s.delete_characters(2)
|
||||
self.ae(str(s.line(0)), 'ade ')
|
||||
self.assertTrue(s.line(0).cursor_from(4).bold)
|
||||
self.assertFalse(s.line(0).cursor_from(2).bold)
|
||||
self.assertChanges(t, ignore='cursor', cells={0: ((1, 4),)})
|
||||
self.assertChanges(s, ignore='cursor', cells={0: ((1, 4),)})
|
||||
|
||||
init()
|
||||
s.erase_characters(2)
|
||||
self.ae(str(s.line(0)), 'a de')
|
||||
self.assertTrue(s.line(0).cursor_from(1).bold)
|
||||
self.assertFalse(s.line(0).cursor_from(4).bold)
|
||||
self.assertChanges(t, cells={0: ((1, 2),)})
|
||||
self.assertChanges(s, cells={0: ((1, 2),)})
|
||||
s.erase_characters(20)
|
||||
self.ae(str(s.line(0)), 'a ')
|
||||
|
||||
@ -149,54 +149,54 @@ class TestScreen(BaseTest):
|
||||
self.ae(str(s.line(0)), 'a ')
|
||||
self.assertTrue(s.line(0).cursor_from(1).bold)
|
||||
self.assertFalse(s.line(0).cursor_from(0).bold)
|
||||
self.assertChanges(t, cells={0: ((1, 4),)})
|
||||
self.assertChanges(s, cells={0: ((1, 4),)})
|
||||
init()
|
||||
s.erase_in_line(1)
|
||||
self.ae(str(s.line(0)), ' cde')
|
||||
self.assertChanges(t, cells={0: ((0, 1),)})
|
||||
self.assertChanges(s, cells={0: ((0, 1),)})
|
||||
init()
|
||||
s.erase_in_line(2)
|
||||
self.ae(str(s.line(0)), ' ')
|
||||
self.assertChanges(t, cells={0: ((0, 4),)})
|
||||
self.assertChanges(s, cells={0: ((0, 4),)})
|
||||
init()
|
||||
s.erase_in_line(2, private=True)
|
||||
self.ae((False, False, False, False, False), tuple(map(lambda i: s.line(0).cursor_from(i).bold, range(5))))
|
||||
|
||||
def test_erase_in_screen(self):
|
||||
s, t = self.create_screen()
|
||||
s = self.create_screen()
|
||||
|
||||
def init():
|
||||
s.reset()
|
||||
s.draw(b'12345' * 5)
|
||||
t.reset()
|
||||
s.reset_dirty()
|
||||
s.cursor.x, s.cursor.y = 2, 1
|
||||
s.cursor.bold = True
|
||||
|
||||
init()
|
||||
s.erase_in_display()
|
||||
self.ae(s.display, ('12345', '12 ', ' ', ' ', ' '))
|
||||
self.assertChanges(t, lines={2, 3, 4}, cells={1: ((2, 4),)})
|
||||
self.assertChanges(s, lines={2, 3, 4}, cells={1: ((2, 4),)})
|
||||
|
||||
init()
|
||||
s.erase_in_display(1)
|
||||
self.ae(s.display, (' ', ' 45', '12345', '12345', '12345'))
|
||||
self.assertChanges(t, lines={0}, cells={1: ((0, 2),)})
|
||||
self.assertChanges(s, lines={0}, cells={1: ((0, 2),)})
|
||||
|
||||
init()
|
||||
s.erase_in_display(2)
|
||||
self.ae(s.display, (' ', ' ', ' ', ' ', ' '))
|
||||
self.assertChanges(t, lines=set(range(5)))
|
||||
self.assertChanges(s, lines=set(range(5)))
|
||||
self.assertTrue(s.line(0).cursor_from(1).bold)
|
||||
init()
|
||||
s.erase_in_display(2, private=True)
|
||||
self.ae(s.display, (' ', ' ', ' ', ' ', ' '))
|
||||
self.assertChanges(t, lines=set(range(5)))
|
||||
self.assertChanges(s, lines=set(range(5)))
|
||||
self.assertFalse(s.line(0).cursor_from(1).bold)
|
||||
|
||||
def test_cursor_movement(self):
|
||||
s, t = self.create_screen()
|
||||
s = self.create_screen()
|
||||
s.draw(b'12345' * 5)
|
||||
t.reset()
|
||||
s.reset_dirty()
|
||||
s.cursor_up(2)
|
||||
self.ae((s.cursor.x, s.cursor.y), (4, 2))
|
||||
s.cursor_up1()
|
||||
@ -210,7 +210,7 @@ class TestScreen(BaseTest):
|
||||
s.cursor_down1(5)
|
||||
self.ae((s.cursor.x, s.cursor.y), (0, 4))
|
||||
|
||||
s, t = self.create_screen()
|
||||
s = self.create_screen()
|
||||
s.draw(b'12345' * 5)
|
||||
s.index()
|
||||
self.ae(str(s.line(4)), ' ' * 5)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user