correctly highlight lines with tabs
This commit is contained in:
17
bigtext.py
17
bigtext.py
@@ -11,6 +11,7 @@ from PyQt6.QtGui import *
|
|||||||
from PyQt6.QtGui import QMouseEvent
|
from PyQt6.QtGui import QMouseEvent
|
||||||
from PyQt6.QtWidgets import *
|
from PyQt6.QtWidgets import *
|
||||||
|
|
||||||
|
import constants
|
||||||
from ScaledScrollBar import ScaledScrollBar
|
from ScaledScrollBar import ScaledScrollBar
|
||||||
from conversion import humanbytes
|
from conversion import humanbytes
|
||||||
from highlight import Highlight
|
from highlight import Highlight
|
||||||
@@ -283,7 +284,8 @@ class InnerBigText(QWidget):
|
|||||||
if line_number < len(self.lines):
|
if line_number < len(self.lines):
|
||||||
line = self.lines[line_number]
|
line = self.lines[line_number]
|
||||||
column_in_line = self.x_pos_to_column(e.pos().x()) + self._left_offset
|
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))
|
# print("%s in line %s" % (char_in_line, line_number))
|
||||||
byte_in_line = line.char_index_to_byte(char_in_line)
|
byte_in_line = line.char_index_to_byte(char_in_line)
|
||||||
current_byte = line.byte_offset() + byte_in_line
|
current_byte = line.byte_offset() + byte_in_line
|
||||||
@@ -347,17 +349,19 @@ class InnerBigText(QWidget):
|
|||||||
# print("paintEvent")
|
# print("paintEvent")
|
||||||
painter = QPainter(self)
|
painter = QPainter(self)
|
||||||
# painter.setFont(self.model.settings.font())
|
# painter.setFont(self.model.settings.font())
|
||||||
#print("%s" % QFontDatabase.families())
|
# print("%s" % QFontDatabase.families())
|
||||||
# Courier New, DejaVu Sans Mono
|
# Courier New, DejaVu Sans Mono
|
||||||
painter.setFont(QFont("Courier New", self.model.settings.getint_session('general', "font_size")))
|
painter.setFont(QFont("Courier New", self.model.settings.getint_session('general', "font_size")))
|
||||||
painter.setPen(QColor(0, 0, 0))
|
painter.setPen(QColor(0, 0, 0))
|
||||||
self.update_font_metrics(painter)
|
self.update_font_metrics(painter)
|
||||||
|
|
||||||
|
tab_string = " " * constants.tab_width
|
||||||
|
|
||||||
lines_to_show = self.lines_shown()
|
lines_to_show = self.lines_shown()
|
||||||
# print("%s / %s = %s" %(self.height(), float(self.char_height), lines_to_show))
|
# 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)
|
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.scroll_lines = 0
|
||||||
self._byte_offset = self.lines[0].byte_offset() if len(self.lines) > 0 else 0
|
self._byte_offset = self.lines[0].byte_offset() if len(self.lines) > 0 else 0
|
||||||
# print("new byte offset: ", self._byte_offset)
|
# print("new byte offset: ", self._byte_offset)
|
||||||
@@ -389,7 +393,8 @@ class InnerBigText(QWidget):
|
|||||||
left_offset = int(-1 * self._left_offset * self.char_width)
|
left_offset = int(-1 * self._left_offset * self.char_width)
|
||||||
y_line_offset = self.char_height;
|
y_line_offset = self.char_height;
|
||||||
for l in self.lines:
|
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
|
y_line_offset = y_line_offset + self.char_height
|
||||||
|
|
||||||
painter.end()
|
painter.end()
|
||||||
@@ -406,13 +411,13 @@ class InnerBigText(QWidget):
|
|||||||
self.highlight_background(painter, rect, highlight.get_brush_full_line())
|
self.highlight_background(painter, rect, highlight.get_brush_full_line())
|
||||||
|
|
||||||
for highlight in highlights:
|
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
|
x1 = highlight.get_start() * self.char_width
|
||||||
width = highlight.get_width() * self.char_width
|
width = highlight.get_width() * self.char_width
|
||||||
y1 = y_line_offset - self.char_height + self.char_height / 7
|
y1 = y_line_offset - self.char_height + self.char_height / 7
|
||||||
height = self.char_height
|
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())
|
self.highlight_background(painter, rect, highlight.get_brush())
|
||||||
|
|
||||||
def highlight_background(self, painter: QPainter, rect: QRect, brush: QBrush):
|
def highlight_background(self, painter: QPainter, rect: QRect, brush: QBrush):
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
raven_icon = "icon8_burned_sky.png"
|
raven_icon = "icon8c.png"
|
||||||
|
|
||||||
|
tab_width = 4
|
||||||
|
|||||||
@@ -36,11 +36,15 @@ class HighlightRegex(Highlight):
|
|||||||
match_iter = re.finditer(self.regex, line.line())
|
match_iter = re.finditer(self.regex, line.line())
|
||||||
for match in match_iter:
|
for match in match_iter:
|
||||||
# print("%s" % match)
|
# print("%s" % match)
|
||||||
start = match.start(0)
|
start_char = match.start(0)
|
||||||
end = match.end(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(
|
result.append(HighlightedRange(
|
||||||
start,
|
start_column,
|
||||||
end - start,
|
end_column - start_column,
|
||||||
highlight_full_line=True,
|
highlight_full_line=True,
|
||||||
brush=self.brush(self.hit_background_color),
|
brush=self.brush(self.hit_background_color),
|
||||||
brush_full_line=self.brush(self.line_background_color)
|
brush_full_line=self.brush(self.line_background_color)
|
||||||
|
|||||||
@@ -44,9 +44,11 @@ class HighlightSelection(Highlight):
|
|||||||
# it just means that we render the highlight into the invisible range on the right
|
# it just means that we render the highlight into the invisible range on the right
|
||||||
end_char = start_char + length_in_bytes
|
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)]
|
pen=Qt.PenStyle.NoPen)]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
30
line.py
30
line.py
@@ -1,3 +1,6 @@
|
|||||||
|
import constants
|
||||||
|
|
||||||
|
|
||||||
class Line:
|
class Line:
|
||||||
def __init__(self, byte_offset: int, byte_end: int, line: str):
|
def __init__(self, byte_offset: int, byte_end: int, line: str):
|
||||||
self._byte_offset = byte_offset
|
self._byte_offset = byte_offset
|
||||||
@@ -24,12 +27,37 @@ class Line:
|
|||||||
prefix_chars = prefix_bytes.decode("utf8", errors="ignore")
|
prefix_chars = prefix_bytes.decode("utf8", errors="ignore")
|
||||||
return len(prefix_chars)
|
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:
|
def includes_byte(self, byte: int) -> bool:
|
||||||
return self._byte_offset <= byte <= self._byte_end
|
return self._byte_offset <= byte <= self._byte_end
|
||||||
|
|
||||||
def intersects(self, start_byte: int, end_byte: int):
|
def intersects(self, start_byte: int, end_byte: int):
|
||||||
result = start_byte < self._byte_end and end_byte > self._byte_offset
|
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
|
return result
|
||||||
|
|
||||||
def prefix(self, index: int) -> str:
|
def prefix(self, index: int) -> str:
|
||||||
|
|||||||
30
testline.py
Normal file
30
testline.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user