Skip to content

Commit f95c5a0

Browse files
committed
WIP first version of markdown highlighter
1 parent 097d8da commit f95c5a0

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed

qtribu/gui/markdown_highlighter.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import re
2+
3+
from qgis.PyQt.QtGui import (
4+
QBrush,
5+
QColor,
6+
QFont,
7+
QFontMetricsF,
8+
QPalette,
9+
QSyntaxHighlighter,
10+
QTextCharFormat,
11+
QTextCursor,
12+
QTextLayout,
13+
)
14+
15+
16+
class MarkdownHighlighter(QSyntaxHighlighter):
17+
18+
MARKDOWN_KEYS_REGEX = {
19+
"Bold": re.compile("(?P<delim>\*\*)(?P<text>.+)(?P=delim)"),
20+
"uBold": re.compile("(?P<delim>__)(?P<text>[^_]{2,})(?P=delim)"),
21+
"Italic": re.compile("(?P<delim>\*)(?P<text>[^*]{2,})(?P=delim)"),
22+
"uItalic": re.compile("(?P<delim>_)(?P<text>[^_]+)(?P=delim)"),
23+
"Link": re.compile("(?u)(^|(?P<pre>[^!]))\[.*?\]:?[ \t]*\(?[^)]+\)?"),
24+
"Image": re.compile("(?u)!\[.*?\]\(.+?\)"),
25+
"HeaderAtx": re.compile("(?u)^\#{1,6}(.*?)\#*(\n|$)"),
26+
"Header": re.compile("^(.+)[ \t]*\n(=+|-+)[ \t]*\n+"),
27+
"CodeBlock": re.compile("^([ ]{4,}|\t).*"),
28+
"UnorderedList": re.compile("(?u)^\s*(\* |\+ |- )+\s*"),
29+
"UnorderedListStar": re.compile("^\s*(\* )+\s*"),
30+
"OrderedList": re.compile("(?u)^\s*(\d+\. )\s*"),
31+
"BlockQuote": re.compile("(?u)^\s*>+\s*"),
32+
"BlockQuoteCount": re.compile("^[ \t]*>[ \t]?"),
33+
"CodeSpan": re.compile("(?P<delim>`+).+?(?P=delim)"),
34+
"HR": re.compile("(?u)^(\s*(\*|-)\s*){3,}$"),
35+
"eHR": re.compile("(?u)^(\s*(\*|=)\s*){3,}$"),
36+
"Html": re.compile("<.+?>"),
37+
}
38+
39+
def __init__(self, parent):
40+
super().__init__(parent)
41+
self.parent = parent
42+
parent.setTabStopDistance(
43+
QFontMetricsF(parent.font()).horizontalAdvance(" ") * 4
44+
)
45+
46+
self.defaultTheme = {
47+
"background-color": "#d7d7d7",
48+
"color": "#191970",
49+
"bold": {"color": "#859900", "font-weight": "bold", "font-style": "normal"},
50+
"emphasis": {
51+
"color": "#b58900",
52+
"font-weight": "bold",
53+
"font-style": "italic",
54+
},
55+
"link": {
56+
"color": "#cb4b16",
57+
"font-weight": "normal",
58+
"font-style": "normal",
59+
},
60+
"image": {
61+
"color": "#cb4b16",
62+
"font-weight": "normal",
63+
"font-style": "normal",
64+
},
65+
"header": {
66+
"color": "#2aa198",
67+
"font-weight": "bold",
68+
"font-style": "normal",
69+
},
70+
"unorderedlist": {
71+
"color": "#dc322f",
72+
"font-weight": "normal",
73+
"font-style": "normal",
74+
},
75+
"orderedlist": {
76+
"color": "#dc322f",
77+
"font-weight": "normal",
78+
"font-style": "normal",
79+
},
80+
"blockquote": {
81+
"color": "#dc322f",
82+
"font-weight": "normal",
83+
"font-style": "normal",
84+
},
85+
"codespan": {
86+
"color": "#dc322f",
87+
"font-weight": "normal",
88+
"font-style": "normal",
89+
},
90+
"codeblock": {
91+
"color": "#ff9900",
92+
"font-weight": "normal",
93+
"font-style": "normal",
94+
},
95+
"line": {
96+
"color": "#2aa198",
97+
"font-weight": "normal",
98+
"font-style": "normal",
99+
},
100+
"html": {
101+
"color": "#c000c0",
102+
"font-weight": "normal",
103+
"font-style": "normal",
104+
},
105+
}
106+
self.setTheme(self.defaultTheme)
107+
108+
def setTheme(self, theme):
109+
self.theme = theme
110+
self.MARKDOWN_KWS_FORMAT = {}
111+
112+
pal = self.parent.palette()
113+
pal.setColor(QPalette.Base, QColor(theme["background-color"]))
114+
self.parent.setPalette(pal)
115+
self.parent.setTextColor(QColor(theme["color"]))
116+
117+
text_char_format = QTextCharFormat()
118+
text_char_format.setForeground(QBrush(QColor(theme["header"]["color"])))
119+
text_char_format.setFontWeight(
120+
QFont.Bold if theme["header"]["font-weight"] == "bold" else QFont.Normal
121+
)
122+
text_char_format.setFontItalic(
123+
True if theme["header"]["font-style"] == "italic" else False
124+
)
125+
self.MARKDOWN_KWS_FORMAT["HeaderAtx"] = text_char_format
126+
127+
self.rehighlight()
128+
129+
def highlightBlock(self, text):
130+
text = str(text)
131+
self.highlightMarkdown(text, 0)
132+
133+
def highlightMarkdown(self, text, strt):
134+
cursor = QTextCursor(self.document())
135+
bf = cursor.blockFormat()
136+
self.setFormat(0, len(text), QColor(self.theme["color"]))
137+
138+
if self.highlightAtxHeader(text, cursor, bf, strt):
139+
return
140+
141+
def highlightEmptyLine(self, text, cursor, bf, strt):
142+
textAscii = str(text.replace("\u2029", "\n"))
143+
if textAscii.strip():
144+
return False
145+
else:
146+
return True
147+
148+
def highlightAtxHeader(self, text, cursor, bf, strt):
149+
found = False
150+
for mo in re.finditer(self.MARKDOWN_KEYS_REGEX["HeaderAtx"], text):
151+
# bf.setBackground(QBrush(QColor(7,54,65)))
152+
# cursor.movePosition(QTextCursor.End)
153+
# cursor.mergeBlockFormat(bf)
154+
self.setFormat(
155+
mo.start() + strt,
156+
mo.end() - mo.start(),
157+
self.MARKDOWN_KWS_FORMAT["HeaderAtx"],
158+
)
159+
found = True
160+
return found

0 commit comments

Comments
 (0)