support line number in code block in both read and edit mode

This commit is contained in:
Le Tan 2017-09-12 20:28:40 +08:00
parent e9238c921e
commit 7fd2273aad
18 changed files with 212 additions and 19 deletions

View File

@ -22,19 +22,6 @@ struct HighlightingStyle
QTextCharFormat format; QTextCharFormat format;
}; };
enum HighlightBlockState
{
Normal = 0,
// A fenced code block.
CodeBlockStart,
CodeBlock,
CodeBlockEnd,
// This block is inside a HTML comment region.
Comment
};
// One continuous region for a certain markdown highlight style // One continuous region for a certain markdown highlight style
// within a QTextBlock. // within a QTextBlock.
// Pay attention to the change of HighlightingStyles[] // Pay attention to the change of HighlightingStyles[]

View File

@ -41,6 +41,8 @@ var updateHtml = function(html) {
} }
} }
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to // If you add new logics after handling MathJax, please pay attention to
// finishLoading logic. // finishLoading logic.
// MathJax may be not loaded for now. // MathJax may be not loaded for now.

View File

@ -94,6 +94,7 @@ var updateText = function(text) {
insertImageCaption(); insertImageCaption();
renderMermaid('lang-mermaid'); renderMermaid('lang-mermaid');
renderFlowchart('lang-flowchart'); renderFlowchart('lang-flowchart');
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to // If you add new logics after handling MathJax, please pay attention to
// finishLoading logic. // finishLoading logic.

View File

@ -24,6 +24,10 @@ if (typeof VEnableMathjax == 'undefined') {
VEnableMathjax = false; VEnableMathjax = false;
} }
if (typeof VEnableHighlightLineNumber == 'undefined') {
VEnableHighlightLineNumber = false;
}
// Add a caption (using alt text) under the image. // Add a caption (using alt text) under the image.
var VImageCenterClass = 'img-center'; var VImageCenterClass = 'img-center';
var VImageCaptionClass = 'img-caption'; var VImageCaptionClass = 'img-caption';
@ -805,3 +809,27 @@ var jumpTitle = function(forward, relativeLevel, repeat) {
content.setHeader(headers[targetIdx].getAttribute("id")); content.setHeader(headers[targetIdx].getAttribute("id"));
setTimeout("g_muteScroll = false", 100); setTimeout("g_muteScroll = false", 100);
}; };
var renderCodeBlockLineNumber = function() {
if (!VEnableHighlightLineNumber) {
return;
}
var codes = document.getElementsByTagName('code');
for (var i = 0; i < codes.length; ++i) {
var code = codes[i];
if (code.parentElement.tagName.toLowerCase() == 'pre') {
hljs.lineNumbersBlock(code);
}
}
// Delete the last extra row.
var tables = document.getElementsByTagName('table');
for (var i = 0; i < tables.length; ++i) {
var table = tables[i];
if (table.classList.contains("hljs-ln")) {
var rowCount = table.rows.length;
table.deleteRow(rowCount - 1);
}
}
};

View File

@ -49,6 +49,7 @@ var updateText = function(text) {
insertImageCaption(); insertImageCaption();
renderMermaid('lang-mermaid'); renderMermaid('lang-mermaid');
renderFlowchart('lang-flowchart'); renderFlowchart('lang-flowchart');
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to // If you add new logics after handling MathJax, please pay attention to
// finishLoading logic. // finishLoading logic.

View File

@ -76,6 +76,7 @@ var updateText = function(text) {
highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart); highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart);
renderMermaid('language-mermaid'); renderMermaid('language-mermaid');
renderFlowchart('language-flowchart'); renderFlowchart('language-flowchart');
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to // If you add new logics after handling MathJax, please pay attention to
// finishLoading logic. // finishLoading logic.

View File

@ -206,3 +206,33 @@ div.img-caption {
text-align: center; text-align: center;
line-height: 1.5; line-height: 1.5;
} }
/* For Highlight.js Line Number */
table.hljs-ln tr {
border: none;
background-color: transparent;
}
table.hljs-ln tr td {
border: none;
background-color: transparent;
}
table.hljs-ln tr td.hljs-ln-numbers {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: center;
color: #AAA;
border-right: 1px solid #CCC;
vertical-align: top;
padding-right: 5px;
}
table.hljs-ln tr td.hljs-ln-code {
padding-left: 10px;
}

View File

@ -57,7 +57,7 @@ enable_vim_mode=false
enable_smart_im_in_vim_mode=true enable_smart_im_in_vim_mode=true
; Display an area besides the editor area to show line number ; Display an area besides the editor area to show line number
; 0 - None, 1 - Absolute, 2 - Relative ; 0 - None, 1 - Absolute, 2 - Relative, 3 - CodeBlock
editor_line_number=0 editor_line_number=0
; Whether minimize to system tray when closing the app ; Whether minimize to system tray when closing the app
@ -90,6 +90,9 @@ enable_heading_sequence=false
; 0 - no color column ; 0 - no color column
color_column=0 color_column=0
; Whether display line number of code block in read mode
enable_code_block_line_number=false
[session] [session]
tools_dock_checked=true tools_dock_checked=true

View File

@ -0,0 +1 @@
!function(e){"use strict";function t(){var e=document.createElement("style");e.type="text/css",e.innerHTML=".{0}{border-collapse:collapse}.{0} td{padding:0}.{1}:before{content:attr({2})}".format(s,d,u),document.getElementsByTagName("head")[0].appendChild(e)}function n(){"complete"===document.readyState?r():e.addEventListener("DOMContentLoaded",r)}function r(){try{var e=document.querySelectorAll("code.hljs");for(var t in e)e.hasOwnProperty(t)&&o(e[t])}catch(n){console.error("LineNumbers error: ",n)}}function o(e){if("object"==typeof e){var t=l(e.innerHTML);if(t.length>1){for(var n="",r=0;r<t.length;r++)n+='<tr><td class="{0}"><div class="{1} {2}" {3}="{5}"></div></td><td class="{4}"><div class="{1}">{6}</div></td></tr>'.format(c,a,d,u,i,r+1,t[r].length>0?t[r]:" ");e.innerHTML='<table class="{0}">{1}</table>'.format(s,n)}}}function l(e){return 0===e.length?[]:e.split(/\r\n|\r|\n/g)}var s="hljs-ln",a="hljs-ln-line",i="hljs-ln-code",c="hljs-ln-numbers",d="hljs-ln-n",u="data-line-number";String.prototype.format=String.prototype.f=function(){var e=arguments;return this.replace(/\{(\d+)\}/g,function(t,n){return e[n]?e[n]:t})},"undefined"==typeof e.hljs?console.error("highlight.js not detected!"):(e.hljs.initLineNumbersOnLoad=n,e.hljs.lineNumbersBlock=o,t())}(window);

View File

@ -551,6 +551,11 @@ QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType, bool p_exp
extraFile += "<script>var VEnableImageCaption = true;</script>\n"; extraFile += "<script>var VEnableImageCaption = true;</script>\n";
} }
if (g_config->getEnableCodeBlockLineNumber()) {
extraFile += "<script src=\"qrc" + VNote::c_highlightjsLineNumberExtraFile + "\"></script>\n" +
"<script>var VEnableHighlightLineNumber = true;</script>\n";
}
QString htmlTemplate; QString htmlTemplate;
if (p_exportPdf) { if (p_exportPdf) {
htmlTemplate = VNote::s_markdownTemplatePDF; htmlTemplate = VNote::s_markdownTemplatePDF;

View File

@ -179,6 +179,9 @@ void VConfigManager::initialize()
"enable_heading_sequence").toBool(); "enable_heading_sequence").toBool();
m_colorColumn = getConfigFromSettings("global", "color_column").toInt(); m_colorColumn = getConfigFromSettings("global", "color_column").toInt();
m_enableCodeBlockLineNumber = getConfigFromSettings("global",
"enable_code_block_line_number").toBool();
} }
void VConfigManager::readPredefinedColorsFromSettings() void VConfigManager::readPredefinedColorsFromSettings()

View File

@ -241,6 +241,9 @@ public:
const QString &getEditorColorColumnBg() const; const QString &getEditorColorColumnBg() const;
const QString &getEditorColorColumnFg() const; const QString &getEditorColorColumnFg() const;
bool getEnableCodeBlockLineNumber() const;
void setEnableCodeBlockLineNumber(bool p_enabled);
// Return the configured key sequence of @p_operation. // Return the configured key sequence of @p_operation.
// Return empty if there is no corresponding config. // Return empty if there is no corresponding config.
QString getShortcutKeySequence(const QString &p_operation) const; QString getShortcutKeySequence(const QString &p_operation) const;
@ -484,6 +487,9 @@ private:
// The column to style in code block. // The column to style in code block.
int m_colorColumn; int m_colorColumn;
// Whether display line number of code block in read mode.
bool m_enableCodeBlockLineNumber;
// The background color of the color column. // The background color of the color column.
QString m_editorColorColumnBg; QString m_editorColorColumnBg;
@ -1266,4 +1272,21 @@ inline const QString &VConfigManager::getEditorColorColumnFg() const
return m_editorColorColumnFg; return m_editorColorColumnFg;
} }
inline bool VConfigManager::getEnableCodeBlockLineNumber() const
{
return m_enableCodeBlockLineNumber;
}
inline void VConfigManager::setEnableCodeBlockLineNumber(bool p_enabled)
{
if (m_enableCodeBlockLineNumber == p_enabled) {
return;
}
m_enableCodeBlockLineNumber = p_enabled;
setConfigToSettings("global",
"enable_code_block_line_number",
m_enableCodeBlockLineNumber);
}
#endif // VCONFIGMANAGER_H #endif // VCONFIGMANAGER_H

View File

@ -61,4 +61,25 @@ enum class PreviewImageType { Block, Inline, Invalid };
enum class PreviewImageSource { Image, CodeBlock, Invalid }; enum class PreviewImageSource { Image, CodeBlock, Invalid };
enum HighlightBlockState
{
Normal = 0,
// A fenced code block.
CodeBlockStart,
CodeBlock,
CodeBlockEnd,
// This block is inside a HTML comment region.
Comment
};
enum class LineNumberType
{
None = 0,
Absolute,
Relative,
CodeBlock
};
#endif #endif

View File

@ -1011,7 +1011,8 @@ void VEdit::resizeEvent(QResizeEvent *p_event)
void VEdit::lineNumberAreaPaintEvent(QPaintEvent *p_event) void VEdit::lineNumberAreaPaintEvent(QPaintEvent *p_event)
{ {
if (!g_config->getEditorLineNumber()) { int lineNumberType = g_config->getEditorLineNumber();
if (!lineNumberType) {
updateLineNumberAreaMargin(); updateLineNumberAreaMargin();
m_lineNumberArea->hide(); m_lineNumberArea->hide();
return; return;
@ -1020,8 +1021,7 @@ void VEdit::lineNumberAreaPaintEvent(QPaintEvent *p_event)
QPainter painter(m_lineNumberArea); QPainter painter(m_lineNumberArea);
painter.fillRect(p_event->rect(), g_config->getEditorLineNumberBg()); painter.fillRect(p_event->rect(), g_config->getEditorLineNumberBg());
QTextDocument *doc = document(); QAbstractTextDocumentLayout *layout = document()->documentLayout();
QAbstractTextDocumentLayout *layout = doc->documentLayout();
QTextBlock block = firstVisibleBlock(); QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber(); int blockNumber = block.blockNumber();
@ -1033,16 +1033,78 @@ void VEdit::lineNumberAreaPaintEvent(QPaintEvent *p_event)
int eventBtm = p_event->rect().bottom(); int eventBtm = p_event->rect().bottom();
const int digitHeight = m_lineNumberArea->getDigitHeight(); const int digitHeight = m_lineNumberArea->getDigitHeight();
const int curBlockNumber = textCursor().block().blockNumber(); const int curBlockNumber = textCursor().block().blockNumber();
const bool relative = g_config->getEditorLineNumber() == 2;
const QString &fg = g_config->getEditorLineNumberFg(); const QString &fg = g_config->getEditorLineNumberFg();
const int lineDistanceHeight = m_config.m_lineDistanceHeight; const int lineDistanceHeight = m_config.m_lineDistanceHeight;
painter.setPen(fg); painter.setPen(fg);
// Display line number only in code block.
if (lineNumberType == 3) {
if (m_file->getDocType() != DocType::Markdown) {
return;
}
int number = 0;
while (block.isValid() && top <= eventBtm) {
int blockState = block.userState();
switch (blockState) {
case HighlightBlockState::CodeBlockStart:
Q_ASSERT(number == 0);
number = 1;
break;
case HighlightBlockState::CodeBlockEnd:
number = 0;
break;
case HighlightBlockState::CodeBlock:
if (number == 0) {
// Need to find current line number in code block.
QTextBlock startBlock = block.previous();
while (startBlock.isValid()) {
if (startBlock.userState() == HighlightBlockState::CodeBlockStart) {
number = block.blockNumber() - startBlock.blockNumber();
break;
}
startBlock = startBlock.previous();
}
}
break;
default:
break;
}
if (blockState == HighlightBlockState::CodeBlock) {
if (block.isVisible() && bottom >= eventTop) {
QString numberStr = QString::number(number);
painter.drawText(0,
top + 2,
m_lineNumberArea->width(),
digitHeight,
Qt::AlignRight,
numberStr);
}
++number;
}
block = block.next();
top = bottom;
bottom = top + (int)layout->blockBoundingRect(block).height() + lineDistanceHeight;
}
return;
}
// Handle lineNumberType 1 and 2.
Q_ASSERT(lineNumberType == 1 || lineNumberType == 2);
while (block.isValid() && top <= eventBtm) { while (block.isValid() && top <= eventBtm) {
if (block.isVisible() && bottom >= eventTop) { if (block.isVisible() && bottom >= eventTop) {
bool currentLine = false; bool currentLine = false;
int number = blockNumber + 1; int number = blockNumber + 1;
if (relative) { if (lineNumberType == 2) {
number = blockNumber - curBlockNumber; number = blockNumber - curBlockNumber;
if (number == 0) { if (number == 0) {
currentLine = true; currentLine = true;

View File

@ -639,6 +639,16 @@ void VMainWindow::initMarkdownMenu()
markdownMenu->addAction(codeBlockAct); markdownMenu->addAction(codeBlockAct);
codeBlockAct->setChecked(g_config->getEnableCodeBlockHighlight()); codeBlockAct->setChecked(g_config->getEnableCodeBlockHighlight());
QAction *lineNumberAct = new QAction(tr("Display Line Number in Code Blocks"), this);
lineNumberAct->setToolTip(tr("Enable line number in code blocks in read mode"));
lineNumberAct->setCheckable(true);
connect(lineNumberAct, &QAction::triggered,
this, [this](bool p_checked){
g_config->setEnableCodeBlockLineNumber(p_checked);
});
markdownMenu->addAction(lineNumberAct);
lineNumberAct->setChecked(g_config->getEnableCodeBlockLineNumber());
QAction *previewImageAct = new QAction(tr("Preview Images In Edit Mode"), this); QAction *previewImageAct = new QAction(tr("Preview Images In Edit Mode"), this);
previewImageAct->setToolTip(tr("Enable image preview in edit mode (re-open current tabs to make it work)")); previewImageAct->setToolTip(tr("Enable image preview in edit mode (re-open current tabs to make it work)"));
previewImageAct->setCheckable(true); previewImageAct->setCheckable(true);
@ -1329,6 +1339,15 @@ void VMainWindow::initEditorLineNumberMenu(QMenu *p_menu)
if (lineNumberMode == 2) { if (lineNumberMode == 2) {
act->setChecked(true); act->setChecked(true);
} }
act = lineNumAct->addAction(tr("CodeBlock"));
act->setToolTip(tr("Display line number in code block in edit mode (for Markdown only)"));
act->setCheckable(true);
act->setData(3);
lineNumMenu->addAction(act);
if (lineNumberMode == 3) {
act->setChecked(true);
}
} }
void VMainWindow::updateEditorStyleMenu() void VMainWindow::updateEditorStyleMenu()

View File

@ -45,6 +45,8 @@ const QString VNote::c_raphaelJsFile = ":/utils/flowchart.js/raphael.min.js";
const QString VNote::c_mathjaxJsFile = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML"; const QString VNote::c_mathjaxJsFile = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML";
const QString VNote::c_highlightjsLineNumberExtraFile = ":/utils/highlightjs/highlightjs-line-numbers.min.js";
const QString VNote::c_shortcutsDocFile_en = ":/resources/docs/shortcuts_en.md"; const QString VNote::c_shortcutsDocFile_en = ":/resources/docs/shortcuts_en.md";
const QString VNote::c_shortcutsDocFile_zh = ":/resources/docs/shortcuts_zh.md"; const QString VNote::c_shortcutsDocFile_zh = ":/resources/docs/shortcuts_zh.md";

View File

@ -64,6 +64,9 @@ public:
// Mathjax // Mathjax
static const QString c_mathjaxJsFile; static const QString c_mathjaxJsFile;
// Highlight.js line number plugin
static const QString c_highlightjsLineNumberExtraFile;
static const QString c_shortcutsDocFile_en; static const QString c_shortcutsDocFile_en;
static const QString c_shortcutsDocFile_zh; static const QString c_shortcutsDocFile_zh;

View File

@ -120,5 +120,6 @@
<file>resources/icons/editing_modified.svg</file> <file>resources/icons/editing_modified.svg</file>
<file>resources/docs/markdown_guide_en.md</file> <file>resources/docs/markdown_guide_en.md</file>
<file>resources/docs/markdown_guide_zh.md</file> <file>resources/docs/markdown_guide_zh.md</file>
<file>utils/highlightjs/highlightjs-line-numbers.min.js</file>
</qresource> </qresource>
</RCC> </RCC>