Skip to content

Commit

Permalink
support find and replace (vnotex#1593)
Browse files Browse the repository at this point in the history
TODO: we may need to transform the `QRegularExpression` usage to the `RegExp` in JS.
  • Loading branch information
tamlok authored Dec 13, 2020
1 parent 3d7406f commit 847e3d6
Show file tree
Hide file tree
Showing 34 changed files with 690 additions and 72 deletions.
2 changes: 2 additions & 0 deletions src/core/editorconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ namespace vnotex
Outline,
RichPaste,
FindAndReplace,
FindNext,
FindPrevious,
MaxShortcut
};
Q_ENUM(Shortcut)
Expand Down
9 changes: 5 additions & 4 deletions src/core/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ namespace vnotex
enum FindOption
{
None = 0,
CaseSensitive = 0x1U,
WholeWordOnly = 0x2U,
RegularExpression = 0x4U,
IncrementalSearch = 0x8U
FindBackward = 0x1U,
CaseSensitive = 0x2U,
WholeWordOnly = 0x4U,
RegularExpression = 0x8U,
IncrementalSearch = 0x10U
};
Q_DECLARE_FLAGS(FindOptions, FindOption);

Expand Down
11 changes: 6 additions & 5 deletions src/core/htmltemplatehelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <core/markdowneditorconfig.h>
#include <core/configmgr.h>
#include <utils/utils.h>
#include <utils/fileutils.h>
#include <utils/pathutils.h>
#include <core/thememgr.h>
Expand All @@ -16,11 +17,11 @@ HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
QString WebGlobalOptions::toJavascriptObject() const
{
return QStringLiteral("window.vxOptions = {\n")
+ QString("webPlantUml: %1,\n").arg(boolToString(m_webPlantUml))
+ QString("webGraphviz: %1,\n").arg(boolToString(m_webGraphviz))
+ QString("constrainImageWidthEnabled: %1,\n").arg(boolToString(m_constrainImageWidthEnabled))
+ QString("protectFromXss: %1,\n").arg(boolToString(m_protectFromXss))
+ QString("sectionNumberEnabled: %1\n").arg(boolToString(m_sectionNumberEnabled))
+ QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml))
+ QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz))
+ QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled))
+ QString("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss))
+ QString("sectionNumberEnabled: %1\n").arg(Utils::boolToString(m_sectionNumberEnabled))
+ QStringLiteral("}");
}

Expand Down
5 changes: 0 additions & 5 deletions src/core/htmltemplatehelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ namespace vnotex

bool m_protectFromXss = false;

QString boolToString(bool p_val) const
{
return p_val ? QStringLiteral("true") : QStringLiteral("false");
}

QString toJavascriptObject() const;
};

Expand Down
1 change: 1 addition & 0 deletions src/core/widgetconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ QJsonObject WidgetConfig::toJson() const
{
QJsonObject obj;
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
obj[QStringLiteral("find_and_replace_options")] = static_cast<int>(m_findAndReplaceOptions);
return obj;
}

Expand Down
14 changes: 12 additions & 2 deletions src/data/core/vnotex.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
"TypeTable" : "Ctrl+/",
"Outline" : "Ctrl+G, O",
"RichPaste" : "Ctrl+Shift+V",
"FindAndReplace" : "Ctrl+F"
"FindAndReplace" : "Ctrl+F",
"FindNext" : "F3",
"FindPrevious" : "Shift+F3"
}
},
"text_editor" : {
Expand Down Expand Up @@ -195,6 +197,14 @@
"web/js/turndown/turndown-plugin-gfm.js",
"web/js/turndown.js"
]
},
{
"name" : "mark.js",
"enabled" : true,
"scripts" : [
"web/js/mark.js/mark.min.js",
"web/js/markjs.js"
]
}
]
},
Expand Down Expand Up @@ -226,6 +236,6 @@
"//comment" : "Level of the heading in outline that should expand to automatically (1-6)",
"outline_auto_expanded_level" : 6,
"//comment" : "Default find options in FindAndReplace",
"find_and_replace_options" : 8
"find_and_replace_options" : 16
}
}
2 changes: 1 addition & 1 deletion src/data/extra/docs/en/about_vnotex.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p>
VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote will share most of the code base with VNoteX since version 3.0 and continue to be open source.
VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote shares most of the code base with VNoteX since version 3.0 and continue to be open source.
<br/><br/>
Source code of VNote could be found at <a href="https://github.com/vnotex/vnote">GitHub</a>.
<br/><br/>
Expand Down
2 changes: 2 additions & 0 deletions src/data/extra/extra.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
<file>web/js/turndown/turndown.js</file>
<file>web/js/turndown/turndown-plugin-gfm.js</file>
<file>web/js/turndown.js</file>
<file>web/js/mark.js/mark.min.js</file>
<file>web/js/markjs.js</file>
<file>syntax-highlighting/themes/markdown-default.theme</file>
<file>syntax-highlighting/themes/default.theme</file>
<file>syntax-highlighting/themes/breeze-dark.theme</file>
Expand Down
32 changes: 1 addition & 31 deletions src/data/extra/themes/native/editor-highlight.theme
Original file line number Diff line number Diff line change
Expand Up @@ -140,35 +140,5 @@
"text-color" : "#006e28",
"selected-text-color" : "#006e28"
}
},
"editor-colors": {
"background-color" : "#ffffff",
"code-folding" : "#94caef",
"bracket-matching" : "#ffff00",
"current-line" : "#f8f7f6",
"icon-border" : "#f0f0f0",
"indentation-line" : "#d2d2d2",
"line-numbers" : "#a0a0a0",
"current-line-number" : "#1e1e1e",
"mark-bookmark" : "#0000ff",
"mark-breakpoint-active" : "#ff0000",
"mark-breakpoint-reached" : "#ffff00",
"mark-breakpoint-disabled" : "#ff00ff",
"mark-execution" : "#a0a0a4",
"mark-warning" : "#00ff00",
"mark-error" : "#ff0000",
"modified-lines" : "#fdbc4b",
"replace-highlight" : "#00ff00",
"saved-lines" : "#2ecc71",
"search-highlight" : "#ffff00",
"selection" : "#94caef",
"separator" : "#898887",
"spell-checking" : "#bf0303",
"tab-marker" : "#d2d2d2",
"template-background" : "#d6d2d0",
"template-placeholder" : "#baf8ce",
"template-focused-placeholder" : "#76da98",
"template-read-only-placeholder" : "#f6e6e6",
"word-wrap-marker" : "#ededed"
}
}
}
15 changes: 15 additions & 0 deletions src/data/extra/themes/native/text-editor.theme
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@
},
"FoldingHighlight" : {
"text-color" : "#ffa9c4f5"
},
"IncrementalSearch" : {
"//comment" : "Incremental search highlight",
"text-color" : "#222222",
"background-color" : "#ce93d8"
},
"Search" : {
"//comment" : "Search highlight",
"text-color" : "#222222",
"background-color" : "#4db6ac"
},
"SearchUnderCursor" : {
"//comment" : "Search highlight under cursor",
"text-color" : "#222222",
"background-color" : "#66bb6a"
}
},
"//comment" : "Override the Text style in editor-styles",
Expand Down
10 changes: 10 additions & 0 deletions src/data/extra/themes/native/web.css
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,13 @@ span.modal-close:hover,
span.modal-close:focus {
color: #222222;
}

#vx-content span.vx-search-match {
color: #222222;
background-color: #4db6ac;
}

#vx-content span.vx-current-search-match {
color: #222222;
background-color: #66bb6a;
}
3 changes: 3 additions & 0 deletions src/data/extra/web/js/mark.js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# [mark.js](https://github.com/julmot/mark.js)
v8.11.1
Julian Kühnel
13 changes: 13 additions & 0 deletions src/data/extra/web/js/mark.js/mark.min.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/data/extra/web/js/markdownviewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ new QWebChannel(qt.webChannelTransport,
window.vnotex.crossCopy(p_id, p_timeStamp, p_target, p_baseUrl, p_html);
});

adapter.findTextRequested.connect(function(p_text, p_options) {
window.vnotex.findText(p_text, p_options);
});

console.log('QWebChannel has been set up');
if (window.vnotex.initialized) {
window.vnotex.kickOffMarkdown();
Expand Down
128 changes: 128 additions & 0 deletions src/data/extra/web/js/markjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
class MarkJs {
constructor(p_adapter, p_container) {
this.className = 'vx-search-match';
this.currentMatchClassName = 'vx-current-search-match';
this.adapter = p_adapter;
this.container = p_container;
this.markjs = null;
this.cache = null;
this.matchedNodes = null;

this.adapter.on('basicMarkdownRendered', () => {
this.clearCache();
});
}

// @p_options: {
// findBackward,
// caseSensitive,
// wholeWordOnly,
// regularExpression
// }
findText(p_text, p_options) {
if (!this.markjs) {
this.markjs = new Mark(this.container);
}

if (!p_text) {
// Clear the cache and highlight.
this.clearCache();
return;
}

if (this.findInCache(p_text, p_options)) {
return;
}

// A new find.
this.clearCache();

let callbackFunc = function(markjs, text, options) {
let _markjs = markjs;
let _text = text;
let _options = options;
return function(totalMatches) {
if (!_markjs.matchedNodes) {
_markjs.matchedNodes = _markjs.container.getElementsByClassName(_markjs.className);
}

// Update cache.
_markjs.cache = {
text: _text,
options: _options,
currentIdx: -1
}

_markjs.updateCurrentMatch(_text, !_options.findBackward);
};
}
let opt = {
'element': 'span',
'className': this.className,
'caseSensitive': p_options.caseSensitive,
'accuracy': p_options.wholeWordOnly ? 'exactly' : 'partially',
'done': callbackFunc(this, p_text, p_options)
}

if (p_options.regularExpression) {
// TODO: may need transformation from QRegularExpression to RegExp.
this.markjs.markRegExp(new RegExp(p_text), opt);
} else {
this.markjs.mark(p_text, opt);
}
}

clearCache() {
if (!this.markjs) {
return;
}

this.cache = null;
this.markjs.unmark();
}

findInCache(p_text, p_options) {
if (!this.cache) {
return false;
}

if (this.cache.text === p_text
&& this.cache.options.caseSensitive == p_options.caseSensitive
&& this.cache.options.wholeWordOnly == p_options.wholeWordOnly
&& this.cache.options.regularExpression == p_options.regularExpression) {
// Matched. Move current match forward or backward.
this.updateCurrentMatch(p_text, !p_options.findBackward);
return true;
}

return false;
}

updateCurrentMatch(p_text, p_forward) {
let matches = this.matchedNodes.length;
if (matches == 0) {
this.adapter.showFindResult(p_text, 0, 0);
return;
}
if (this.cache.currentIdx >= 0) {
this.matchedNodes[this.cache.currentIdx].classList.remove(this.currentMatchClassName);
}
if (p_forward) {
this.cache.currentIdx += 1;
if (this.cache.currentIdx >= matches) {
this.cache.currentIdx = 0;
}
} else {
this.cache.currentIdx -= 1;
if (this.cache.currentIdx < 0) {
this.cache.currentIdx = matches - 1;
}
}
let node = this.matchedNodes[this.cache.currentIdx];
node.classList.add(this.currentMatchClassName);
if (!Utils.isVisible(node)) {
node.scrollIntoView();
}
this.adapter.showFindResult(p_text, matches, this.cache.currentIdx);
}
}
2 changes: 1 addition & 1 deletion src/data/extra/web/js/nodelinemapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class NodeLineMapper {
this.headingNodes = this.container.querySelectorAll("h1, h2, h3, h4, h5, h6");
let headings = [];
let needSectionNumber = window.vxOptions.sectionNumberEnabled;
let regExp = /^(?:\d\.)+ /;
let regExp = /^\d(?:\.\d)*\.? /;
for (let i = 0; i < this.headingNodes.length; ++i) {
let node = this.headingNodes[i];
headings.push({
Expand Down
10 changes: 10 additions & 0 deletions src/data/extra/web/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,14 @@ class Utils {
height: rect.height
};
}

static isVisible(p_node) {
let rect = p_node.getBoundingClientRect();
let vrect = this.viewPortRect();
if (rect.top < 0 || rect.left < 0
|| rect.bottom > vrect.height || rect.right > vrect.width) {
return false;
}
return true;
}
}
10 changes: 10 additions & 0 deletions src/data/extra/web/js/vnotex.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class VNoteX extends EventEmitter {

this.crossCopyer = new CrossCopy(this);

this.searcher = new MarkJs(this, this.contentContainer);

this.initialized = true;

// Signal out.
Expand Down Expand Up @@ -250,6 +252,14 @@ class VNoteX extends EventEmitter {
window.vxMarkdownAdapter.setCrossCopyResult(p_id, p_timeStamp, p_html);
}

findText(p_text, p_options) {
this.searcher.findText(p_text, p_options);
}

showFindResult(p_text, p_totalMatches, p_currentMatchIndex) {
window.vxMarkdownAdapter.setFindText(p_text, p_totalMatches, p_currentMatchIndex);
}

static detectOS() {
let osName="Unknown OS";
if (navigator.appVersion.indexOf("Win")!=-1) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/pathutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ QString PathUtils::concatenateFilePath(const QString &p_dirPath, const QString &

QString PathUtils::dirName(const QString &p_path)
{
Q_ASSERT(QFileInfo(p_path).isDir());
Q_ASSERT(!QFileInfo::exists(p_path) || QFileInfo(p_path).isDir());
return QDir(p_path).dirName();
}

Expand Down
Loading

0 comments on commit 847e3d6

Please sign in to comment.