Move change tracking into the screen class

This commit is contained in:
Kovid Goyal 2016-11-11 18:35:26 +05:30
parent c7b87f9174
commit 9b544f83e0
4 changed files with 63 additions and 63 deletions

View File

@ -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()

View File

@ -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:

View File

@ -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):

View File

@ -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)