Compare commits

...

3 Commits

Author SHA1 Message Date
be53c209ea cache lines to speed up rendering 2024-04-23 10:28:44 +02:00
aa2bfa967e style fixes 2024-04-22 17:52:15 +02:00
2b91b19ef3 fix test test_column_to_char_ignore_nonspacing_mark_charaters 2024-04-22 17:51:12 +02:00
3 changed files with 38 additions and 11 deletions

View File

@@ -116,7 +116,7 @@ class Line:
# todo there are many other character combinations that should be skipped # todo there are many other character combinations that should be skipped
while i < len(self._line) and unicodedata.category(self._line[i]) == "Mn": while i < len(self._line) and unicodedata.category(self._line[i]) == "Mn":
self._char_to_column_cache[i] = result - 1 self._char_to_column_cache[i] = result - 1
if not result in self._column_to_char_cache: if (result - 1) not in self._column_to_char_cache:
self._column_to_char_cache[result - 1] = i self._column_to_char_cache[result - 1] = i
i = i + 1 i = i + 1

View File

@@ -7,6 +7,7 @@ from src.ui.bigtext.highlighting import Highlighting
from src.ui.bigtext.line import Line from src.ui.bigtext.line import Line
import os import os
from src.settings.settings import Settings from src.settings.settings import Settings
from functools import lru_cache
class LogFileModel: class LogFileModel:
@@ -21,6 +22,8 @@ class LogFileModel:
range_start = 0 range_start = 0
range_end = -1 range_end = -1
_line_cache = {}
def __init__(self, file: str, settings: Settings, original_file: str = False): def __init__(self, file: str, settings: Settings, original_file: str = False):
""" """
:param file: :param file:
@@ -133,6 +136,13 @@ class LogFileModel:
def _is_word_char(self, char: str) -> bool: def _is_word_char(self, char: str) -> bool:
return re.match(r"\w", char) is not None return re.match(r"\w", char) is not None
def prune_cache(self, range_start: int, range_end: int):
for key in list(self._line_cache.keys()):
line = self._line_cache[key]
if range_start > line.byte_end() or line.byte_offset() > range_end:
del self._line_cache[key]
def data(self, byte_offset: int, scroll_lines: int, lines: int, range_start: int, range_end: int) -> List[Line]: def data(self, byte_offset: int, scroll_lines: int, lines: int, range_start: int, range_end: int) -> List[Line]:
# print("data(%s, %s, %s)" % (byte_offset, scroll_lines, lines)) # print("data(%s, %s, %s)" % (byte_offset, scroll_lines, lines))
lines_before_offset: List[Line] = [] lines_before_offset: List[Line] = []
@@ -148,19 +158,36 @@ class LogFileModel:
offset = max(0, offset = max(0,
max(range_start - self.settings.max_line_length(), offset - self.settings.max_line_length())) max(range_start - self.settings.max_line_length(), offset - self.settings.max_line_length()))
self.prune_cache(range_start, range_end)
previous_line_is_complete = False
f.seek(offset) f.seek(offset)
while l := f.readline(): while True:
line: Line | None = self._line_cache.get(offset)
if line is None:
line_bytes = f.readline()
if not line_bytes:
break
new_offset = f.tell() new_offset = f.tell()
if 0 <= range_end < new_offset: if 0 <= range_end < new_offset:
break break
line = Line(offset, new_offset, l.decode("utf8", errors="ignore")) line = Line(offset, new_offset, line_bytes.decode("utf8", errors="ignore"))
if previous_line_is_complete: # only cache lines when we know they are complete
self._line_cache[offset] = line
offset = new_offset
previous_line_is_complete = True
else:
# print(f"loaded cached line at offset {offset}")
offset = line.byte_end() # line.byte_end() returns the end byte +1
f.seek(offset)
previous_line_is_complete = True
if line.byte_end() <= byte_offset: # line.byte_end() returns the end byte +1 if line.byte_end() <= byte_offset: # line.byte_end() returns the end byte +1
if line.byte_offset() >= range_start: # only add if in range if line.byte_offset() >= range_start: # only add if in range
lines_before_offset.append(line) lines_before_offset.append(line)
else: else:
lines_after_offset.append(line) lines_after_offset.append(line)
offset = f.tell()
if len(lines_after_offset) >= lines_to_find: if len(lines_after_offset) >= lines_to_find:
break break

View File

@@ -31,9 +31,9 @@ class MyTestCase(unittest.TestCase):
self.assertEqual(9, line.column_to_char(15)) # tab self.assertEqual(9, line.column_to_char(15)) # tab
self.assertEqual(10, line.column_to_char(16)) # g self.assertEqual(10, line.column_to_char(16)) # g
def test_column_to_char_ignore_nonspacing_mark_charaters(self): def test_column_to_char_ignore_nonspacing_mark_characters(self):
""" """
nonspacing mark charaters are those little decorations that are applied to the previous character, nonspacing mark characters are those little decorations that are applied to the previous character,
e.g. x\u0308 to make ẍ e.g. x\u0308 to make ẍ
:return: :return:
""" """
@@ -63,7 +63,7 @@ class MyTestCase(unittest.TestCase):
def test_char_to_column_ignore_nonspacing_mark_charaters(self): def test_char_to_column_ignore_nonspacing_mark_charaters(self):
""" """
nonspacing mark charaters are those little decorations that are applied to the previous character, nonspacing mark characters are those little decorations that are applied to the previous character,
e.g. x\u0308 to make ẍ e.g. x\u0308 to make ẍ
:return: :return:
""" """
@@ -119,7 +119,7 @@ class MyTestCase(unittest.TestCase):
for i in range(128): for i in range(128):
if unicodedata.category(chr(i)) == "Cc": if unicodedata.category(chr(i)) == "Cc":
# print(i, " -> ", ord(chr(i)), " --> ", chr(9216 + i)) # print(i, " -> ", ord(chr(i)), " --> ", chr(9216 + i))
if not i in [9, 10, 11, 13]: if i not in [9, 10, 11, 13]:
text = text + chr(i) text = text + chr(i)
line = Line(byte_offset=byte_offset, byte_end=byte_offset + len(text.encode("utf8")), line=text) line = Line(byte_offset=byte_offset, byte_end=byte_offset + len(text.encode("utf8")), line=text)