mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
highlight: highlight MathJax formula in editor
This commit is contained in:
parent
05e4159530
commit
9566e6f5d2
@ -35,6 +35,7 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &s
|
||||
parsing(0),
|
||||
m_blockHLResultReady(false),
|
||||
waitInterval(waitInterval),
|
||||
m_enableMathjax(false),
|
||||
content(NULL),
|
||||
capacity(0),
|
||||
result(NULL)
|
||||
@ -42,6 +43,9 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &s
|
||||
codeBlockStartExp = QRegExp(VUtils::c_fencedCodeBlockStartRegExp);
|
||||
codeBlockEndExp = QRegExp(VUtils::c_fencedCodeBlockEndRegExp);
|
||||
|
||||
m_mathjaxInlineExp = QRegExp(VUtils::c_mathjaxInlineRegExp);
|
||||
m_mathjaxBlockExp = QRegExp(VUtils::c_mathjaxBlockRegExp);
|
||||
|
||||
m_codeBlockFormat.setForeground(QBrush(Qt::darkYellow));
|
||||
for (int index = 0; index < styles.size(); ++index) {
|
||||
const pmh_element_type &eleType = styles[index].type;
|
||||
@ -58,6 +62,8 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &s
|
||||
m_colorColumnFormat.setForeground(QColor(g_config->getEditorColorColumnFg()));
|
||||
m_colorColumnFormat.setBackground(QColor(g_config->getEditorColorColumnBg()));
|
||||
|
||||
m_mathjaxFormat.setForeground(QColor(g_config->getEditorMathjaxFg()));
|
||||
|
||||
m_headerStyles.resize(6);
|
||||
for (auto const & it : highlightingStyles) {
|
||||
if (it.type >= pmh_H1 && it.type <= pmh_H6) {
|
||||
@ -108,6 +114,9 @@ void HGMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &p
|
||||
if (!blockData) {
|
||||
blockData = new VTextBlockData();
|
||||
setCurrentBlockUserData(blockData);
|
||||
} else {
|
||||
blockData->setCodeBlockIndentation(-1);
|
||||
blockData->clearMathjax();
|
||||
}
|
||||
|
||||
if (blockData->getPreviews().isEmpty()) {
|
||||
@ -115,8 +124,6 @@ void HGMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &p
|
||||
} else {
|
||||
m_possiblePreviewBlocks.insert(p_blockNum);
|
||||
}
|
||||
|
||||
blockData->setCodeBlockIndentation(-1);
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::highlightBlock(const QString &text)
|
||||
@ -171,10 +178,13 @@ void HGMarkdownHighlighter::highlightBlock(const QString &text)
|
||||
setCurrentBlockState(HighlightBlockState::Normal);
|
||||
highlightCodeBlock(curBlock, text);
|
||||
|
||||
if (currentBlockState() == HighlightBlockState::Normal
|
||||
&& isVerbatimBlock(curBlock)) {
|
||||
if (currentBlockState() == HighlightBlockState::Normal) {
|
||||
if (isVerbatimBlock(curBlock)) {
|
||||
setCurrentBlockState(HighlightBlockState::Verbatim);
|
||||
goto exit;
|
||||
} else if (m_enableMathjax) {
|
||||
highlightMathJax(curBlock, text);
|
||||
}
|
||||
}
|
||||
|
||||
// PEG Markdown Highlight does not handle links with spaces in the URL.
|
||||
@ -184,6 +194,10 @@ void HGMarkdownHighlighter::highlightBlock(const QString &text)
|
||||
|
||||
highlightHeaderFast(blockNum, text);
|
||||
|
||||
if (currentBlockState() != HighlightBlockState::CodeBlock) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Highlight CodeBlock using VCodeBlockHighlightHelper.
|
||||
if (m_codeBlockHighlights.size() > blockNum) {
|
||||
const QVector<HLUnitStyle> &units = m_codeBlockHighlights[blockNum];
|
||||
@ -436,7 +450,7 @@ void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos,
|
||||
}
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::highlightCodeBlock(const QTextBlock &p_block, const QString &text)
|
||||
void HGMarkdownHighlighter::highlightCodeBlock(const QTextBlock &p_block, const QString &p_text)
|
||||
{
|
||||
VTextBlockData *blockData = currentBlockData();
|
||||
Q_ASSERT(blockData);
|
||||
@ -449,10 +463,10 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QTextBlock &p_block, const
|
||||
if (preState != HighlightBlockState::CodeBlock
|
||||
&& preState != HighlightBlockState::CodeBlockStart) {
|
||||
// Need to find a new code block start.
|
||||
index = codeBlockStartExp.indexIn(text);
|
||||
index = codeBlockStartExp.indexIn(p_text);
|
||||
if (index >= 0 && !isVerbatimBlock(p_block)) {
|
||||
// Start a new code block.
|
||||
length = text.length();
|
||||
length = p_text.length();
|
||||
state = HighlightBlockState::CodeBlockStart;
|
||||
|
||||
// The leading spaces of code block start and end must be identical.
|
||||
@ -471,18 +485,18 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QTextBlock &p_block, const
|
||||
startLeadingSpaces = preBlockData->getCodeBlockIndentation();
|
||||
}
|
||||
|
||||
index = codeBlockEndExp.indexIn(text);
|
||||
index = codeBlockEndExp.indexIn(p_text);
|
||||
|
||||
// The closing ``` should have the same indentation as the open ```.
|
||||
if (index >= 0
|
||||
&& startLeadingSpaces == codeBlockEndExp.capturedTexts()[1].size()) {
|
||||
// End of code block.
|
||||
length = text.length();
|
||||
length = p_text.length();
|
||||
state = HighlightBlockState::CodeBlockEnd;
|
||||
} else {
|
||||
// Within code block.
|
||||
index = 0;
|
||||
length = text.length();
|
||||
length = p_text.length();
|
||||
state = HighlightBlockState::CodeBlock;
|
||||
}
|
||||
|
||||
@ -493,6 +507,101 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QTextBlock &p_block, const
|
||||
setFormat(index, length, m_codeBlockFormat);
|
||||
}
|
||||
|
||||
static bool intersect(const QList<QPair<int, int>> &p_indices, int &p_start, int &p_end)
|
||||
{
|
||||
for (auto const & range : p_indices) {
|
||||
if (p_end <= range.first) {
|
||||
return false;
|
||||
} else if (p_start < range.second) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QString &p_text)
|
||||
{
|
||||
const int blockMarkLength = 2;
|
||||
const int inlineMarkLength = 1;
|
||||
|
||||
int startIdx = 0;
|
||||
// Next position to search.
|
||||
int pos = 0;
|
||||
HighlightBlockState state = (HighlightBlockState)previousBlockState();
|
||||
|
||||
QList<QPair<int, int>> blockIdices;
|
||||
|
||||
// Mathjax block formula.
|
||||
if (state != HighlightBlockState::MathjaxBlock) {
|
||||
startIdx = m_mathjaxBlockExp.indexIn(p_text);
|
||||
pos = startIdx + m_mathjaxBlockExp.matchedLength();
|
||||
startIdx = pos - blockMarkLength;
|
||||
}
|
||||
|
||||
while (startIdx >= 0) {
|
||||
int endIdx = m_mathjaxBlockExp.indexIn(p_text, pos);
|
||||
int mathLength = 0;
|
||||
if (endIdx == -1) {
|
||||
setCurrentBlockState(HighlightBlockState::MathjaxBlock);
|
||||
mathLength = p_text.length() - startIdx;
|
||||
} else {
|
||||
mathLength = endIdx - startIdx + m_mathjaxBlockExp.matchedLength();
|
||||
}
|
||||
|
||||
pos = startIdx + mathLength;
|
||||
|
||||
blockIdices.append(QPair<int, int>(startIdx, pos));
|
||||
|
||||
setFormat(startIdx, mathLength, m_mathjaxFormat);
|
||||
startIdx = m_mathjaxBlockExp.indexIn(p_text, pos);
|
||||
pos = startIdx + m_mathjaxBlockExp.matchedLength();
|
||||
startIdx = pos - blockMarkLength;
|
||||
}
|
||||
|
||||
// Mathjax inline formula.
|
||||
startIdx = 0;
|
||||
pos = 0;
|
||||
if (state != HighlightBlockState::MathjaxInline) {
|
||||
startIdx = m_mathjaxInlineExp.indexIn(p_text);
|
||||
pos = startIdx + m_mathjaxInlineExp.matchedLength();
|
||||
startIdx = pos - inlineMarkLength;
|
||||
}
|
||||
|
||||
while (startIdx >= 0) {
|
||||
int endIdx = m_mathjaxInlineExp.indexIn(p_text, pos);
|
||||
int mathLength = 0;
|
||||
if (endIdx == -1) {
|
||||
setCurrentBlockState(HighlightBlockState::MathjaxBlock);
|
||||
mathLength = p_text.length() - startIdx;
|
||||
} else {
|
||||
mathLength = endIdx - startIdx + m_mathjaxInlineExp.matchedLength();
|
||||
}
|
||||
|
||||
pos = startIdx + mathLength;
|
||||
// Check if it intersect with blocks.
|
||||
if (!intersect(blockIdices, startIdx, pos)) {
|
||||
// A valid inline mathjax.
|
||||
if (endIdx == -1) {
|
||||
setCurrentBlockState(HighlightBlockState::MathjaxInline);
|
||||
}
|
||||
|
||||
setFormat(startIdx, mathLength, m_mathjaxFormat);
|
||||
|
||||
startIdx = m_mathjaxInlineExp.indexIn(p_text, pos);
|
||||
pos = startIdx + m_mathjaxInlineExp.matchedLength();
|
||||
startIdx = pos - inlineMarkLength;
|
||||
} else {
|
||||
// Make the second mark as the first one and try again.
|
||||
if (endIdx == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
startIdx = pos - inlineMarkLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HGMarkdownHighlighter::highlightCodeBlockColorColumn(const QString &p_text)
|
||||
{
|
||||
int cc = g_config->getColorColumn();
|
||||
|
@ -155,6 +155,8 @@ public:
|
||||
|
||||
QVector<HighlightingStyle> &getHighlightingStyles();
|
||||
|
||||
void setMathjaxEnabled(bool p_enabled);
|
||||
|
||||
signals:
|
||||
void highlightCompleted();
|
||||
|
||||
@ -197,10 +199,15 @@ private:
|
||||
|
||||
QRegExp codeBlockStartExp;
|
||||
QRegExp codeBlockEndExp;
|
||||
|
||||
QRegExp m_mathjaxInlineExp;
|
||||
QRegExp m_mathjaxBlockExp;
|
||||
|
||||
QTextCharFormat m_codeBlockFormat;
|
||||
QTextCharFormat m_linkFormat;
|
||||
QTextCharFormat m_imageFormat;
|
||||
QTextCharFormat m_colorColumnFormat;
|
||||
QTextCharFormat m_mathjaxFormat;
|
||||
|
||||
QTextDocument *document;
|
||||
|
||||
@ -253,6 +260,8 @@ private:
|
||||
// Block number of those blocks which possible contains previewed image.
|
||||
QSet<int> m_possiblePreviewBlocks;
|
||||
|
||||
bool m_enableMathjax;
|
||||
|
||||
char *content;
|
||||
int capacity;
|
||||
pmh_element **result;
|
||||
@ -260,7 +269,10 @@ private:
|
||||
static const int initCapacity;
|
||||
|
||||
void resizeBuffer(int newCap);
|
||||
void highlightCodeBlock(const QTextBlock &p_block, const QString &text);
|
||||
|
||||
void highlightCodeBlock(const QTextBlock &p_block, const QString &p_text);
|
||||
|
||||
void highlightMathJax(const QTextBlock &p_block, const QString &p_text);
|
||||
|
||||
// Highlight links using regular expression.
|
||||
// PEG Markdown Highlight treat URLs with spaces illegal. This function is
|
||||
@ -375,4 +387,9 @@ inline bool HGMarkdownHighlighter::isVerbatimBlock(const QTextBlock &p_block) co
|
||||
{
|
||||
return m_verbatimBlocks.contains(p_block.blockNumber());
|
||||
}
|
||||
|
||||
inline void HGMarkdownHighlighter::setMathjaxEnabled(bool p_enabled)
|
||||
{
|
||||
m_enableMathjax = p_enabled;
|
||||
}
|
||||
#endif
|
||||
|
@ -35,8 +35,10 @@ incremental-searched-word-background: ce93d8
|
||||
# [VNote] Style for color column in fenced code block
|
||||
color-column-background: c9302c
|
||||
color-column-foreground: eeeeee
|
||||
# [VNote} Style for preview image line
|
||||
# [VNote] Style for preview image line
|
||||
preview-image-line-foreground: 6f5799
|
||||
# [VNote] Style for MathJax
|
||||
mathjax-foreground: ce93d8
|
||||
|
||||
editor-selection
|
||||
foreground: dadada
|
||||
|
@ -34,8 +34,10 @@ incremental-searched-word-background: ce93d8
|
||||
# [VNote] Style for color column in fenced code block
|
||||
color-column-background: dd0000
|
||||
color-column-foreground: ffff00
|
||||
# [VNote} Style for preview image line
|
||||
# [VNote] Style for preview image line
|
||||
preview-image-line-foreground: 9575cd
|
||||
# [VNote] Style for MathJax
|
||||
mathjax-foreground: 8e24aa
|
||||
|
||||
editor-selection
|
||||
foreground: eeeeee
|
||||
|
@ -35,8 +35,10 @@ incremental-searched-word-background: ce93d8
|
||||
# [VNote] Style for color column in fenced code block
|
||||
color-column-background: dd0000
|
||||
color-column-foreground: ffff00
|
||||
# [VNote} Style for preview image line
|
||||
# [VNote] Style for preview image line
|
||||
preview-image-line-foreground: 9575cd
|
||||
# [VNote] Style for MathJax
|
||||
mathjax-foreground: 8e24aa
|
||||
|
||||
editor-selection
|
||||
foreground: eeeeee
|
||||
|
@ -47,6 +47,10 @@ const QString VUtils::c_fencedCodeBlockStartRegExp = QString("^(\\s*)```([^`\\s]
|
||||
|
||||
const QString VUtils::c_fencedCodeBlockEndRegExp = QString("^(\\s*)```$");
|
||||
|
||||
const QString VUtils::c_mathjaxInlineRegExp = QString("(?:^|[^\\$\\\\]|(?:^|[^\\\\])(?:\\\\\\\\)+)\\$(?!\\$)");
|
||||
|
||||
const QString VUtils::c_mathjaxBlockRegExp = QString("(?:^|[^\\$\\\\]|(?:^|[^\\\\])(?:\\\\\\\\)+)\\$\\$(?!\\$)");
|
||||
|
||||
const QString VUtils::c_previewImageBlockRegExp = QString("[\\n|^][ |\\t]*\\xfffc[ |\\t]*(?=\\n)");
|
||||
|
||||
const QString VUtils::c_headerRegExp = QString("^(#{1,6})\\s+(((\\d+\\.)+(?=\\s))?\\s*(\\S.*)?)$");
|
||||
|
@ -340,6 +340,14 @@ public:
|
||||
static const QString c_fencedCodeBlockStartRegExp;
|
||||
static const QString c_fencedCodeBlockEndRegExp;
|
||||
|
||||
// Regular expression for inline mathjax formula.
|
||||
// $..$
|
||||
static const QString c_mathjaxInlineRegExp;
|
||||
|
||||
// Regular expression for block mathjax formula.
|
||||
// $$..$$
|
||||
static const QString c_mathjaxBlockRegExp;
|
||||
|
||||
// Regular expression for preview image block.
|
||||
static const QString c_previewImageBlockRegExp;
|
||||
|
||||
|
@ -635,6 +635,7 @@ void VConfigManager::updateMarkdownEditStyle()
|
||||
m_editorColorColumnBg = defaultColor;
|
||||
m_editorColorColumnFg = defaultColor;
|
||||
m_editorPreviewImageLineFg = defaultColor;
|
||||
m_editorMathjaxFg = defaultColor;
|
||||
|
||||
auto editorIt = styles.find("editor");
|
||||
if (editorIt != styles.end()) {
|
||||
@ -707,6 +708,11 @@ void VConfigManager::updateMarkdownEditStyle()
|
||||
if (it != editorIt->end()) {
|
||||
m_editorPreviewImageLineFg = "#" + *it;
|
||||
}
|
||||
|
||||
it = editorIt->find("mathjax-foreground");
|
||||
if (it != editorIt->end()) {
|
||||
m_editorMathjaxFg = "#" + *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,6 +316,8 @@ public:
|
||||
|
||||
const QString &getEditorPreviewImageLineFg() const;
|
||||
|
||||
const QString &getEditorMathjaxFg() const;
|
||||
|
||||
bool getEnableCodeBlockLineNumber() const;
|
||||
void setEnableCodeBlockLineNumber(bool p_enabled);
|
||||
|
||||
@ -779,6 +781,9 @@ private:
|
||||
// The foreground color of the preview image line.
|
||||
QString m_editorPreviewImageLineFg;
|
||||
|
||||
// The foreground color of the MathJax.
|
||||
QString m_editorMathjaxFg;
|
||||
|
||||
// Icon size of tool bar in pixels.
|
||||
int m_toolBarIconSize;
|
||||
|
||||
@ -1826,6 +1831,11 @@ inline const QString &VConfigManager::getEditorPreviewImageLineFg() const
|
||||
return m_editorPreviewImageLineFg;
|
||||
}
|
||||
|
||||
inline const QString &VConfigManager::getEditorMathjaxFg() const
|
||||
{
|
||||
return m_editorMathjaxFg;
|
||||
}
|
||||
|
||||
inline bool VConfigManager::getEnableCodeBlockLineNumber() const
|
||||
{
|
||||
return m_enableCodeBlockLineNumber;
|
||||
|
@ -121,7 +121,11 @@ enum HighlightBlockState
|
||||
Comment,
|
||||
|
||||
// Verbatim code block.
|
||||
Verbatim
|
||||
Verbatim,
|
||||
|
||||
// Mathjax. It means the pending state of the block.
|
||||
MathjaxBlock,
|
||||
MathjaxInline
|
||||
};
|
||||
|
||||
// Pages to open on start up.
|
||||
|
@ -63,6 +63,7 @@ VMdEditor::VMdEditor(VFile *p_file,
|
||||
g_config->getCodeBlockStyles(),
|
||||
g_config->getMarkdownHighlightInterval(),
|
||||
document());
|
||||
m_mdHighlighter->setMathjaxEnabled(g_config->getEnableMathjax());
|
||||
|
||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::headersUpdated,
|
||||
this, &VMdEditor::updateHeaders);
|
||||
|
@ -129,6 +129,45 @@ struct VPreviewInfo
|
||||
};
|
||||
|
||||
|
||||
struct MathjaxInfo
|
||||
{
|
||||
public:
|
||||
MathjaxInfo()
|
||||
: m_isBlock(false),
|
||||
m_index(-1),
|
||||
m_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return m_index >= 0 && m_length > 0;
|
||||
}
|
||||
|
||||
bool isBlock() const
|
||||
{
|
||||
return m_isBlock;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_isBlock = false;
|
||||
m_index = -1;
|
||||
m_length = 0;
|
||||
}
|
||||
|
||||
// Inline or block formula.
|
||||
bool m_isBlock;
|
||||
|
||||
// Start index wihtin block, including the start mark.
|
||||
int m_index;
|
||||
|
||||
// Length of this mathjax, including the end mark.
|
||||
int m_length;
|
||||
};
|
||||
|
||||
|
||||
// User data for each block.
|
||||
class VTextBlockData : public QTextBlockUserData
|
||||
{
|
||||
@ -153,6 +192,16 @@ public:
|
||||
|
||||
void setCodeBlockIndentation(int p_indent);
|
||||
|
||||
void clearMathjax();
|
||||
|
||||
const MathjaxInfo &getPendingMathjax() const;
|
||||
|
||||
void setPendingMathjax(const MathjaxInfo &p_info);
|
||||
|
||||
const QVector<MathjaxInfo> getMathjax() const;
|
||||
|
||||
void addMathjax(const MathjaxInfo &p_info);
|
||||
|
||||
private:
|
||||
// Check the order of elements.
|
||||
bool checkOrder() const;
|
||||
@ -162,6 +211,12 @@ private:
|
||||
|
||||
// Indentation of the this code block if this block is a fenced code block.
|
||||
int m_codeBlockIndentation;
|
||||
|
||||
// Pending Mathjax info, such as this block is the start of a Mathjax formula.
|
||||
MathjaxInfo m_pendingMathjax;
|
||||
|
||||
// Mathjax info ends in this block.
|
||||
QVector<MathjaxInfo> m_mathjax;
|
||||
};
|
||||
|
||||
inline const QVector<VPreviewInfo *> &VTextBlockData::getPreviews() const
|
||||
@ -178,4 +233,31 @@ inline void VTextBlockData::setCodeBlockIndentation(int p_indent)
|
||||
{
|
||||
m_codeBlockIndentation = p_indent;
|
||||
}
|
||||
|
||||
inline void VTextBlockData::clearMathjax()
|
||||
{
|
||||
m_pendingMathjax.clear();
|
||||
m_mathjax.clear();
|
||||
}
|
||||
|
||||
inline const MathjaxInfo &VTextBlockData::getPendingMathjax() const
|
||||
{
|
||||
return m_pendingMathjax;
|
||||
}
|
||||
|
||||
inline void VTextBlockData::setPendingMathjax(const MathjaxInfo &p_info)
|
||||
{
|
||||
m_pendingMathjax = p_info;
|
||||
}
|
||||
|
||||
inline const QVector<MathjaxInfo> VTextBlockData::getMathjax() const
|
||||
{
|
||||
return m_mathjax;
|
||||
}
|
||||
|
||||
inline void VTextBlockData::addMathjax(const MathjaxInfo &p_info)
|
||||
{
|
||||
m_mathjax.append(p_info);
|
||||
}
|
||||
|
||||
#endif // VTEXTBLOCKDATA_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user