diff --git a/bigtext.py b/bigtext.py index 57ea5f3..1522856 100644 --- a/bigtext.py +++ b/bigtext.py @@ -11,6 +11,7 @@ from PyQt6.QtGui import * from PyQt6.QtGui import QMouseEvent from PyQt6.QtWidgets import * +import constants from ScaledScrollBar import ScaledScrollBar from conversion import humanbytes from highlight import Highlight @@ -283,7 +284,8 @@ class InnerBigText(QWidget): if line_number < len(self.lines): line = self.lines[line_number] column_in_line = self.x_pos_to_column(e.pos().x()) + self._left_offset - char_in_line = min(column_in_line, line.length() - 1) + column_in_line = min(column_in_line, line.length() - 1) # x was behind the last column of this line + char_in_line = line.column_to_char(column_in_line) # print("%s in line %s" % (char_in_line, line_number)) byte_in_line = line.char_index_to_byte(char_in_line) current_byte = line.byte_offset() + byte_in_line @@ -347,17 +349,19 @@ class InnerBigText(QWidget): # print("paintEvent") painter = QPainter(self) # painter.setFont(self.model.settings.font()) - #print("%s" % QFontDatabase.families()) + # print("%s" % QFontDatabase.families()) # Courier New, DejaVu Sans Mono painter.setFont(QFont("Courier New", self.model.settings.getint_session('general', "font_size"))) painter.setPen(QColor(0, 0, 0)) self.update_font_metrics(painter) + tab_string = " " * constants.tab_width + lines_to_show = self.lines_shown() # print("%s / %s = %s" %(self.height(), float(self.char_height), lines_to_show)) self.lines = self.model.data(self._byte_offset, self.scroll_lines, lines_to_show) - #print("lines_to_show: %d returned: %d" % (lines_to_show, len(self.lines))) + # print("lines_to_show: %d returned: %d" % (lines_to_show, len(self.lines))) self.scroll_lines = 0 self._byte_offset = self.lines[0].byte_offset() if len(self.lines) > 0 else 0 # print("new byte offset: ", self._byte_offset) @@ -389,7 +393,8 @@ class InnerBigText(QWidget): left_offset = int(-1 * self._left_offset * self.char_width) y_line_offset = self.char_height; for l in self.lines: - painter.drawText(left_offset, y_line_offset, l.line()) + text = l.line().replace("\t", tab_string) + painter.drawText(left_offset, y_line_offset, text) y_line_offset = y_line_offset + self.char_height painter.end() @@ -406,13 +411,13 @@ class InnerBigText(QWidget): self.highlight_background(painter, rect, highlight.get_brush_full_line()) for highlight in highlights: - left_offset = -1 * self._left_offset * self.char_width + left_offset = self._left_offset * self.char_width x1 = highlight.get_start() * self.char_width width = highlight.get_width() * self.char_width y1 = y_line_offset - self.char_height + self.char_height / 7 height = self.char_height - rect = QRect(round(left_offset + x1), round(y1), round(width), round(height)) + rect = QRect(round(x1 - left_offset), round(y1), round(width), round(height)) self.highlight_background(painter, rect, highlight.get_brush()) def highlight_background(self, painter: QPainter, rect: QRect, brush: QBrush): diff --git a/constants.py b/constants.py index deaa2b1..3d2bab0 100644 --- a/constants.py +++ b/constants.py @@ -1 +1,3 @@ -raven_icon = "icon8_burned_sky.png" +raven_icon = "icon8c.png" + +tab_width = 4 diff --git a/highlight_regex.py b/highlight_regex.py index a609cce..0b1f5f8 100644 --- a/highlight_regex.py +++ b/highlight_regex.py @@ -36,11 +36,15 @@ class HighlightRegex(Highlight): match_iter = re.finditer(self.regex, line.line()) for match in match_iter: # print("%s" % match) - start = match.start(0) - end = match.end(0) + start_char = match.start(0) + end_char = match.end(0) + + start_column = line.char_to_column(start_char) + end_column = line.char_to_column(end_char) + result.append(HighlightedRange( - start, - end - start, + start_column, + end_column - start_column, highlight_full_line=True, brush=self.brush(self.hit_background_color), brush_full_line=self.brush(self.line_background_color) diff --git a/highlight_selection.py b/highlight_selection.py index aa744db..3ba2f44 100644 --- a/highlight_selection.py +++ b/highlight_selection.py @@ -44,9 +44,11 @@ class HighlightSelection(Highlight): # it just means that we render the highlight into the invisible range on the right end_char = start_char + length_in_bytes - length_in_chars = end_char - start_char + start_column = line.char_to_column(start_char) + end_column = line.char_to_column(end_char) + length_in_columns = end_column - start_column - return [HighlightedRange(start_char, length_in_chars, brush=QBrush(QColor(156, 215, 255, 192)), + return [HighlightedRange(start_column, length_in_columns, brush=QBrush(QColor(156, 215, 255, 192)), pen=Qt.PenStyle.NoPen)] else: return None diff --git a/line.py b/line.py index a8e4e7b..542c72b 100644 --- a/line.py +++ b/line.py @@ -1,3 +1,6 @@ +import constants + + class Line: def __init__(self, byte_offset: int, byte_end: int, line: str): self._byte_offset = byte_offset @@ -24,12 +27,37 @@ class Line: prefix_chars = prefix_bytes.decode("utf8", errors="ignore") return len(prefix_chars) + def column_to_char(self, column_in_line: int) -> int: + i = 0 + result = 0 + while i < column_in_line: + char = self._line[result] + if char == "\t": + i = i + constants.tab_width - 1 # jump the additional 7 columns of the tab width + if i >= column_in_line: + break; + i = i + 1 + result = result + 1 + + return result + + def char_to_column(self, char_in_line: int) -> int: + result = 0 + i = 0 + while i < char_in_line: + if i < len(self._line) and self._line[i] == "\t": + result = result + constants.tab_width + else: + result = result + 1 + i = i + 1 + return result + def includes_byte(self, byte: int) -> bool: return self._byte_offset <= byte <= self._byte_end def intersects(self, start_byte: int, end_byte: int): result = start_byte < self._byte_end and end_byte > self._byte_offset - #print("%d,%d in %d,%d" % (start_byte, end_byte, self._byte_offset, self._byte_end)) + # print("%d,%d in %d,%d" % (start_byte, end_byte, self._byte_offset, self._byte_end)) return result def prefix(self, index: int) -> str: diff --git a/testline.py b/testline.py new file mode 100644 index 0000000..2229a2b --- /dev/null +++ b/testline.py @@ -0,0 +1,30 @@ +import unittest + +from line import Line + + +class MyTestCase(unittest.TestCase): + def test_column_to_char(self): + byte_offset = 123 + text = "\tabc" # will be rendered as: ........abc where . represents a whitespace column + line = Line(byte_offset=byte_offset, byte_end=byte_offset + len(text.encode("utf8")), line=text) + + self.assertEqual(0, line.column_to_char(0)) # the tab + self.assertEqual(0, line.column_to_char(1)) # the tab + self.assertEqual(0, line.column_to_char(3)) # last column of the tab + self.assertEqual(1, line.column_to_char(4)) # a + self.assertEqual(2, line.column_to_char(5)) # b + self.assertEqual(3, line.column_to_char(6)) # c + + def test_char_to_column(self): + byte_offset = 123 + text = "\tabc" # will be rendered as: ........abc where . represents a whitespace column + line = Line(byte_offset=byte_offset, byte_end=byte_offset + len(text.encode("utf8")), line=text) + self.assertEqual(0, line.char_to_column(0)) + self.assertEqual(4, line.char_to_column(1)) + self.assertEqual(5, line.char_to_column(2)) + self.assertEqual(6, line.char_to_column(3)) + + +if __name__ == '__main__': + unittest.main()