1 #include "MarkdownHighlighter.h"
4 MarkdownHighlighter::MarkdownHighlighter(QTextDocument *document,
FormattingOptions *options) : QSyntaxHighlighter(document),
9 connect(
this, &MarkdownHighlighter::highlightBlockAtPosition,
this, &MarkdownHighlighter::onHighlightBlockAtPosition);
12 font.setFamily(m_options->get_fontFamily());
13 font.setWeight(m_options->get_fontWeight());
14 font.setItalic(
false);
15 font.setPointSizeF(12.0);
16 font.setStyleStrategy(QFont::PreferAntialias);
17 defaultFormat.setFont(font);
18 defaultFormat.setForeground(QBrush(m_options->get_textColor()));
22 for (
int i = 0; i < MarkdownToken::Last; i++)
24 applyStyleToMarkup[i] =
false;
25 emphasizeToken[i] =
false;
26 strongToken[i] =
false;
27 strongMarkup[i] =
false;
28 strikethroughToken[i] =
false;
29 fontSizeIncrease[i] = 0;
32 for (
int i = MarkdownToken::AtxHeading1; i <= MarkdownToken::AtxHeading6; i++)
34 applyStyleToMarkup[i] =
true;
37 applyStyleToMarkup[MarkdownToken::Emphasis] =
true;
38 applyStyleToMarkup[MarkdownToken::Strong] =
true;
39 applyStyleToMarkup[MarkdownToken::AtxHeading1] =
true;
40 applyStyleToMarkup[MarkdownToken::AtxHeading2] =
true;
41 applyStyleToMarkup[MarkdownToken::AtxHeading3] =
true;
42 applyStyleToMarkup[MarkdownToken::AtxHeading4] =
true;
43 applyStyleToMarkup[MarkdownToken::AtxHeading5] =
true;
44 applyStyleToMarkup[MarkdownToken::AtxHeading6] =
true;
46 emphasizeToken[MarkdownToken::Emphasis] =
true;
47 emphasizeToken[MarkdownToken::Blockquote] =
false;
48 strongToken[MarkdownToken::Strong] =
true;
49 strongToken[MarkdownToken::Mention] =
true;
50 strongToken[MarkdownToken::AtxHeading1] =
true;
51 strongToken[MarkdownToken::AtxHeading2] =
true;
52 strongToken[MarkdownToken::AtxHeading3] =
true;
53 strongToken[MarkdownToken::AtxHeading4] =
true;
54 strongToken[MarkdownToken::AtxHeading5] =
true;
55 strongToken[MarkdownToken::AtxHeading6] =
true;
56 strongToken[MarkdownToken::SetextHead1Line1] =
true;
57 strongToken[MarkdownToken::SetextHead2Line1] =
true;
58 strongToken[MarkdownToken::SetextHead1Line2] =
true;
59 strongToken[MarkdownToken::SetextHead2Line2] =
true;
60 strongToken[MarkdownToken::TableHeader] =
true;
61 strikethroughToken[MarkdownToken::Strikethrough] =
true;
63 setupHeadingFontSize(m_options->get_enableLargeHeadingSizes());
65 strongMarkup[MarkdownToken::NumberedList] =
true;
66 strongMarkup[MarkdownToken::Blockquote] =
true;
67 strongMarkup[MarkdownToken::BulletList] =
true;
70 void MarkdownHighlighter::highlightBlock(
const QString &text)
72 MarkdownTokenizer::TokenState lastState = (MarkdownTokenizer::TokenState)currentBlockState();
74 setFormat(0, text.length(), defaultFormat);
76 if (!m_tokenizer.isNull())
78 m_tokenizer.data()->clear();
80 QTextBlock block = currentBlock();
81 MarkdownTokenizer::TokenState nextState = MarkdownTokenizer::Unknown;
82 MarkdownTokenizer::TokenState previousState = (MarkdownTokenizer::TokenState)previousBlockState();
84 if (block.next().isValid())
86 nextState = (MarkdownTokenizer::TokenState)block.next().userState();
89 m_tokenizer->tokenize(text, lastState, previousState, nextState);
90 setCurrentBlockState(m_tokenizer->state());
92 if (m_tokenizer->state() == MarkdownTokenizer::Blockquote)
94 m_inBlockquote =
true;
98 m_inBlockquote =
false;
101 for (
const auto token : m_tokenizer->tokens()) {
102 switch (token.type())
104 case MarkdownToken::Unknown:
105 qWarning(
"Highlighter found unknown token type in text block.");
108 applyFormattingForToken(token);
113 if (m_tokenizer->shouldBackTrack())
115 QTextBlock previous = currentBlock().previous();
116 emit highlightBlockAtPosition(previous.position());
121 void MarkdownHighlighter::onHighlightBlockAtPosition(
int pos)
123 QTextBlock block = document()->findBlock(pos);
124 rehighlightBlock(block);
127 void MarkdownHighlighter::setupTokenColors()
129 for (
int i = 0; i < MarkdownToken::Last; i++)
131 colorForToken[i] = m_options->get_textColor();
134 colorForToken[MarkdownToken::HtmlTag] = m_options->get_markupColor();
135 colorForToken[MarkdownToken::HtmlEntity] = m_options->get_markupColor();
136 colorForToken[MarkdownToken::AutomaticLink] = m_options->get_linkColor();
137 colorForToken[MarkdownToken::InlineLink] = m_options->get_linkColor();
138 colorForToken[MarkdownToken::ReferenceLink] = m_options->get_linkColor();
139 colorForToken[MarkdownToken::ReferenceDefinition] = m_options->get_linkColor();
140 colorForToken[MarkdownToken::Image] = m_options->get_linkColor();
141 colorForToken[MarkdownToken::Mention] = m_options->get_linkColor();
142 colorForToken[MarkdownToken::HtmlComment] = m_options->get_markupColor();
143 colorForToken[MarkdownToken::HorizontalRule] = m_options->get_markupColor();
144 colorForToken[MarkdownToken::GFMCodeFence] = m_options->get_markupColor();
145 colorForToken[MarkdownToken::CodeFenceEnd] = m_options->get_markupColor();
146 colorForToken[MarkdownToken::SetextHead1Line2] = m_options->get_markupColor();
147 colorForToken[MarkdownToken::SetextHead2Line2] = m_options->get_markupColor();
148 colorForToken[MarkdownToken::TableDiv] = m_options->get_markupColor();
149 colorForToken[MarkdownToken::TablePipe] = m_options->get_markupColor();
152 void MarkdownHighlighter::setupHeadingFontSize(
bool useLargeHeadings)
154 if (useLargeHeadings)
156 fontSizeIncrease[MarkdownToken::SetextHead1Line1] = 6;
157 fontSizeIncrease[MarkdownToken::SetextHead2Line1] = 5;
158 fontSizeIncrease[MarkdownToken::SetextHead1Line2] = 6;
159 fontSizeIncrease[MarkdownToken::SetextHead2Line2] = 5;
160 fontSizeIncrease[MarkdownToken::AtxHeading1] = 6;
161 fontSizeIncrease[MarkdownToken::AtxHeading2] = 5;
162 fontSizeIncrease[MarkdownToken::AtxHeading3] = 4;
163 fontSizeIncrease[MarkdownToken::AtxHeading4] = 3;
164 fontSizeIncrease[MarkdownToken::AtxHeading5] = 2;
165 fontSizeIncrease[MarkdownToken::AtxHeading6] = 1;
169 fontSizeIncrease[MarkdownToken::SetextHead1Line1] = 0;
170 fontSizeIncrease[MarkdownToken::SetextHead2Line1] = 0;
171 fontSizeIncrease[MarkdownToken::SetextHead1Line2] = 0;
172 fontSizeIncrease[MarkdownToken::SetextHead2Line2] = 0;
173 fontSizeIncrease[MarkdownToken::AtxHeading1] = 0;
174 fontSizeIncrease[MarkdownToken::AtxHeading2] = 0;
175 fontSizeIncrease[MarkdownToken::AtxHeading3] = 0;
176 fontSizeIncrease[MarkdownToken::AtxHeading4] = 0;
177 fontSizeIncrease[MarkdownToken::AtxHeading5] = 0;
178 fontSizeIncrease[MarkdownToken::AtxHeading6] = 0;
182 bool MarkdownHighlighter::isHeadingBlockState(MarkdownTokenizer::TokenState state)
const
185 case MarkdownTokenizer::AtxHeading1:
186 case MarkdownTokenizer::AtxHeading2:
187 case MarkdownTokenizer::AtxHeading3:
188 case MarkdownTokenizer::AtxHeading4:
189 case MarkdownTokenizer::AtxHeading5:
190 case MarkdownTokenizer::AtxHeading6:
191 case MarkdownTokenizer::SetextHead1Line1:
192 case MarkdownTokenizer::SetextHead2Line1:
199 void MarkdownHighlighter::applyFormattingForToken(
const MarkdownToken &token)
201 if (token.type() != MarkdownToken::Unknown) {
202 MarkdownToken::TokenType tokenType = token.type();
203 QTextCharFormat fmt = this->format(token.position());
205 QColor tokenColor = colorForToken[tokenType];
207 fmt.setForeground(QBrush(tokenColor));
209 if (strongToken[tokenType])
211 fmt.setFontWeight(QFont::Bold);
214 if (emphasizeToken[tokenType])
216 if (m_options->get_useUnderlineForEmp() && (tokenType != MarkdownToken::Blockquote))
218 fmt.setFontUnderline(
true);
222 fmt.setFontItalic(
true);
226 if (strikethroughToken[tokenType])
228 fmt.setFontStrikeOut(
true);
231 fmt.setFontPointSize(fmt.fontPointSize()
232 + (qreal) fontSizeIncrease[tokenType]);
234 QTextCharFormat markupFormat;
238 applyStyleToMarkup[tokenType] &&
239 (!emphasizeToken[tokenType] || !m_options->get_useUnderlineForEmp())
246 markupFormat = this->format(token.position());
249 QColor adjustedMarkupColor = m_options->get_markupColor().lighter();
251 markupFormat.setForeground(QBrush(adjustedMarkupColor));
253 if (strongMarkup[tokenType])
255 markupFormat.setFontWeight(QFont::Bold);
258 if (token.openingLength() > 0)
262 (MarkdownToken::Blockquote == tokenType)
265 markupFormat.setBackground(QBrush(adjustedMarkupColor.lighter()));
266 QString text = currentBlock().text();
268 for (
int i = token.position(); i < token.openingLength(); i++)
270 if (!text[i].isSpace())
286 token.openingLength(),
294 token.position() + token.openingLength(),
296 - token.openingLength()
297 - token.closingLength(),
301 if (token.closingLength() > 0)
305 token.position() + token.length()
306 - token.closingLength(),
307 token.closingLength(),
314 qWarning(
"Highlighter::applyFormattingForToken() was passed in a "
315 "token of unknown type.");