mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Improve search keyword highlighting
This commit is contained in:
parent
8dc0b34fdc
commit
96cd56548e
1
.gitignore
vendored
1
.gitignore
vendored
@ -40,4 +40,5 @@ Tools/github_oauth_token.txt
|
|||||||
_releases
|
_releases
|
||||||
ReactNativeClient/lib/csstojs/
|
ReactNativeClient/lib/csstojs/
|
||||||
ElectronClient/app/gui/note-viewer/fonts/
|
ElectronClient/app/gui/note-viewer/fonts/
|
||||||
|
ElectronClient/app/gui/note-viewer/lib.js
|
||||||
Tools/commit_hook.txt
|
Tools/commit_hook.txt
|
@ -2,6 +2,7 @@ const fs = require('fs-extra');
|
|||||||
const spawnSync = require('child_process').spawnSync;
|
const spawnSync = require('child_process').spawnSync;
|
||||||
|
|
||||||
const babelPath = __dirname + '/node_modules/.bin/babel' + (process.platform === 'win32' ? '.cmd' : '');
|
const babelPath = __dirname + '/node_modules/.bin/babel' + (process.platform === 'win32' ? '.cmd' : '');
|
||||||
|
const basePath = __dirname + '/../..';
|
||||||
const guiPath = __dirname + '/gui';
|
const guiPath = __dirname + '/gui';
|
||||||
|
|
||||||
function fileIsNewerThan(path1, path2) {
|
function fileIsNewerThan(path1, path2) {
|
||||||
@ -27,7 +28,7 @@ fs.readdirSync(guiPath).forEach((filename) => {
|
|||||||
|
|
||||||
if (fileIsNewerThan(jsxPath, jsPath)) {
|
if (fileIsNewerThan(jsxPath, jsPath)) {
|
||||||
console.info('Compiling ' + jsxPath + '...');
|
console.info('Compiling ' + jsxPath + '...');
|
||||||
const result = spawnSync(babelPath, ['--presets', 'react', '--out-file',jsPath, jsxPath]);
|
const result = spawnSync(babelPath, ['--presets', 'react', '--out-file', jsPath, jsxPath]);
|
||||||
if (result.status !== 0) {
|
if (result.status !== 0) {
|
||||||
const msg = [];
|
const msg = [];
|
||||||
if (result.stdout) msg.push(result.stdout.toString());
|
if (result.stdout) msg.push(result.stdout.toString());
|
||||||
@ -38,3 +39,5 @@ fs.readdirSync(guiPath).forEach((filename) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fs.copySync(basePath + '/ReactNativeClient/lib/string-utils-common.js', __dirname + '/gui/note-viewer/lib.js');
|
@ -37,6 +37,7 @@
|
|||||||
<div id="hlScriptContainer"></div>
|
<div id="hlScriptContainer"></div>
|
||||||
<div id="markScriptContainer"></div>
|
<div id="markScriptContainer"></div>
|
||||||
<div id="content" ondragstart="return false;" ondrop="return false;"></div>
|
<div id="content" ondragstart="return false;" ondrop="return false;"></div>
|
||||||
|
<script src="./lib.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const contentElement = document.getElementById('content');
|
const contentElement = document.getElementById('content');
|
||||||
@ -226,10 +227,6 @@
|
|||||||
function setMarkers(keywords, options = null) {
|
function setMarkers(keywords, options = null) {
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
|
|
||||||
// TODO: It should highlight queries without accents - eg "penche*" should highlight "penchés"
|
|
||||||
// TODO: It should highlight Chinese, Japanese characters, etc.
|
|
||||||
// TODO: It should highlight Russian
|
|
||||||
// TODO: not working - "oue*" doesn't highlight "ouéé"
|
|
||||||
// TODO: Search engine support to mobile app (sync tables)
|
// TODO: Search engine support to mobile app (sync tables)
|
||||||
// TODO: Update everywhere that uses allParsedQueryTerms to support new format
|
// TODO: Update everywhere that uses allParsedQueryTerms to support new format
|
||||||
// TODO: Add support for scriptType on mobile and CLI
|
// TODO: Add support for scriptType on mobile and CLI
|
||||||
@ -275,12 +272,20 @@
|
|||||||
const isBasicSearch = ['ja', 'zh', 'ko'].indexOf(keyword.scriptType) >= 0;
|
const isBasicSearch = ['ja', 'zh', 'ko'].indexOf(keyword.scriptType) >= 0;
|
||||||
|
|
||||||
if (keyword.type === 'regex') {
|
if (keyword.type === 'regex') {
|
||||||
let regexString = '\\b' + keyword.value + '\\b';
|
const b = '[' + pregQuote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']+';
|
||||||
|
|
||||||
|
// The capturing groups are a hack to go around the strange behaviour of the ignoreGroups property. What we want is to
|
||||||
|
// exclude the first and last matches (the boundaries). What ignoreGroups does is ignore the first X groups. So
|
||||||
|
// we put the first boundary and the keyword inside a group, that way the first groups is ignored (ignoreGroups = 1)
|
||||||
|
// the second is included. And the last boundary is dropped because it's not in any group (it's important NOT to
|
||||||
|
// put this one in a group because otherwise it cannot be excluded).
|
||||||
|
let regexString = '(' + b + ')' + '(' + replaceRegexDiacritics(keyword.value) + ')' + b;
|
||||||
if (isBasicSearch) regexString = keyword.value;
|
if (isBasicSearch) regexString = keyword.value;
|
||||||
|
|
||||||
mark_.markRegExp(new RegExp(regexString, 'gmi'), {
|
mark_.markRegExp(new RegExp(regexString, 'gmi'), {
|
||||||
each: onEachElement,
|
each: onEachElement,
|
||||||
acrossElements: true,
|
acrossElements: true,
|
||||||
|
ignoreGroups: 1,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let accuracy = keyword.accuracy ? keyword.accuracy : 'exactly';
|
let accuracy = keyword.accuracy ? keyword.accuracy : 'exactly';
|
||||||
|
2
ElectronClient/app/package-lock.json
generated
2
ElectronClient/app/package-lock.json
generated
@ -4594,7 +4594,7 @@
|
|||||||
},
|
},
|
||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.10.0",
|
"version": "2.10.0",
|
||||||
"resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||||
},
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
"pack": "node_modules/.bin/electron-builder --dir",
|
"pack": "node_modules/.bin/electron-builder --dir",
|
||||||
"dist": "node_modules/.bin/electron-builder",
|
"dist": "node_modules/.bin/electron-builder",
|
||||||
"publish": "build -p always",
|
"publish": "build -p always",
|
||||||
"postinstall": "node compile-jsx.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts && install-app-deps",
|
"postinstall": "node compile.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts && install-app-deps",
|
||||||
"compile": "node compile-jsx.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts"
|
"compile": "node compile.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
11
ElectronClient/package-lock.json
generated
Normal file
11
ElectronClient/package-lock.json
generated
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"diacritics": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
|
||||||
|
"integrity": "sha1-PvqHMj67hj5mls67AILUj/PW96E="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -226,8 +226,8 @@ class SearchEngine {
|
|||||||
|
|
||||||
let regexString = pregQuote(term);
|
let regexString = pregQuote(term);
|
||||||
if (regexString[regexString.length - 1] === '*') {
|
if (regexString[regexString.length - 1] === '*') {
|
||||||
// regexString = regexString.substr(0, regexString.length - 2) + '[^' + pregQuote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']' + '*';
|
regexString = regexString.substr(0, regexString.length - 2) + '[^' + pregQuote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']' + '*?';
|
||||||
regexString = regexString.substr(0, regexString.length - 2) + '.*?';
|
// regexString = regexString.substr(0, regexString.length - 2) + '.*?';
|
||||||
}
|
}
|
||||||
|
|
||||||
return regexString;
|
return regexString;
|
||||||
|
55
ReactNativeClient/lib/string-utils-common.js
Normal file
55
ReactNativeClient/lib/string-utils-common.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
function pregQuote(str, delimiter = '') {
|
||||||
|
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceRegexDiacritics(regexString) {
|
||||||
|
if (!regexString) return '';
|
||||||
|
|
||||||
|
const diacriticReplacements = {
|
||||||
|
'a': '[aàáâãäåāą]',
|
||||||
|
'A': '[AÀÁÂÃÄÅĀĄ]',
|
||||||
|
'c': '[cçćč]',
|
||||||
|
'C': '[CÇĆČ]',
|
||||||
|
'd': '[dđď]',
|
||||||
|
'D': '[DĐĎ]',
|
||||||
|
'e': '[eèéêëěēę]',
|
||||||
|
'E': '[EÈÉÊËĚĒĘ]',
|
||||||
|
'i': '[iìíîïī]',
|
||||||
|
'I': '[IÌÍÎÏĪ]',
|
||||||
|
'l': '[lł]',
|
||||||
|
'L': '[LŁ]',
|
||||||
|
'n': '[nñňń]',
|
||||||
|
'N': '[NÑŇŃ]',
|
||||||
|
'o': '[oòóôõöøō]',
|
||||||
|
'O': '[OÒÓÔÕÖØŌ]',
|
||||||
|
'r': '[rř]',
|
||||||
|
'R': '[RŘ]',
|
||||||
|
's': '[sšś]',
|
||||||
|
'S': '[SŠŚ]',
|
||||||
|
't': '[tť]',
|
||||||
|
'T': '[TŤ]',
|
||||||
|
'u': '[uùúûüůū]',
|
||||||
|
'U': '[UÙÚÛÜŮŪ]',
|
||||||
|
'y': '[yÿý]',
|
||||||
|
'Y': '[YŸÝ]',
|
||||||
|
'z': '[zžżź]',
|
||||||
|
'Z': '[ZŽŻŹ]',
|
||||||
|
};
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
for (let i = 0; i < regexString.length; i++) {
|
||||||
|
let c = regexString[i];
|
||||||
|
const r = diacriticReplacements[c];
|
||||||
|
if (r) {
|
||||||
|
output += r;
|
||||||
|
} else {
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports = { pregQuote, replaceRegexDiacritics };
|
||||||
|
}
|
@ -223,10 +223,6 @@ function escapeHtml(s) {
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function pregQuote(str, delimiter = '') {
|
|
||||||
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
|
|
||||||
}
|
|
||||||
|
|
||||||
function surroundKeywords(keywords, text, prefix, suffix) {
|
function surroundKeywords(keywords, text, prefix, suffix) {
|
||||||
if (!keywords.length) return text;
|
if (!keywords.length) return text;
|
||||||
|
|
||||||
@ -242,54 +238,6 @@ function surroundKeywords(keywords, text, prefix, suffix) {
|
|||||||
return text.replace(re, prefix + '$1' + suffix);
|
return text.replace(re, prefix + '$1' + suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceRegexDiacritics(regexString) {
|
|
||||||
if (!regexString) return '';
|
|
||||||
|
|
||||||
const diacriticReplacements = {
|
|
||||||
'a': '[aàáâãäåāą]',
|
|
||||||
'A': '[AÀÁÂÃÄÅĀĄ]',
|
|
||||||
'c': '[cçćč]',
|
|
||||||
'C': '[CÇĆČ]',
|
|
||||||
'd': '[dđď]',
|
|
||||||
'D': '[DĐĎ]',
|
|
||||||
'e': '[eèéêëěēę]',
|
|
||||||
'E': '[EÈÉÊËĚĒĘ]',
|
|
||||||
'i': '[iìíîïī]',
|
|
||||||
'I': '[IÌÍÎÏĪ]',
|
|
||||||
'l': '[lł]',
|
|
||||||
'L': '[LŁ]',
|
|
||||||
'n': '[nñňń]',
|
|
||||||
'N': '[NÑŇŃ]',
|
|
||||||
'o': '[oòóôõöøō]',
|
|
||||||
'O': '[OÒÓÔÕÖØŌ]',
|
|
||||||
'r': '[rř]',
|
|
||||||
'R': '[RŘ]',
|
|
||||||
's': '[sšś]',
|
|
||||||
'S': '[SŠŚ]',
|
|
||||||
't': '[tť]',
|
|
||||||
'T': '[TŤ]',
|
|
||||||
'u': '[uùúûüůū]',
|
|
||||||
'U': '[UÙÚÛÜŮŪ]',
|
|
||||||
'y': '[yÿý]',
|
|
||||||
'Y': '[YŸÝ]',
|
|
||||||
'z': '[zžżź]',
|
|
||||||
'Z': '[ZŽŻŹ]',
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = '';
|
|
||||||
for (let i = 0; i < regexString.length; i++) {
|
|
||||||
let c = regexString[i];
|
|
||||||
const r = diacriticReplacements[c];
|
|
||||||
if (r) {
|
|
||||||
output += r;
|
|
||||||
} else {
|
|
||||||
output += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
const REGEX_JAPANESE = /[\u3000-\u303f]|[\u3040-\u309f]|[\u30a0-\u30ff]|[\uff00-\uff9f]|[\u4e00-\u9faf]|[\u3400-\u4dbf]/;
|
const REGEX_JAPANESE = /[\u3000-\u303f]|[\u3040-\u309f]|[\u30a0-\u30ff]|[\uff00-\uff9f]|[\u4e00-\u9faf]|[\u3400-\u4dbf]/;
|
||||||
const REGEX_CHINESE = /[\u4e00-\u9fff]|[\u3400-\u4dbf]|[\u{20000}-\u{2a6df}]|[\u{2a700}-\u{2b73f}]|[\u{2b740}-\u{2b81f}]|[\u{2b820}-\u{2ceaf}]|[\uf900-\ufaff]|[\u3300-\u33ff]|[\ufe30-\ufe4f]|[\uf900-\ufaff]|[\u{2f800}-\u{2fa1f}]/u;
|
const REGEX_CHINESE = /[\u4e00-\u9fff]|[\u3400-\u4dbf]|[\u{20000}-\u{2a6df}]|[\u{2a700}-\u{2b73f}]|[\u{2b740}-\u{2b81f}]|[\u{2b820}-\u{2ceaf}]|[\uf900-\ufaff]|[\u3300-\u33ff]|[\ufe30-\ufe4f]|[\uf900-\ufaff]|[\u{2f800}-\u{2fa1f}]/u;
|
||||||
const REGEX_KOREAN = /[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/;
|
const REGEX_KOREAN = /[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/;
|
||||||
@ -301,4 +249,7 @@ function scriptType(s) {
|
|||||||
return 'en';
|
return 'en';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { removeDiacritics, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, urlDecode, escapeHtml, pregQuote, surroundKeywords, scriptType, replaceRegexDiacritics };
|
module.exports = Object.assign(
|
||||||
|
{ removeDiacritics, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase, urlDecode, escapeHtml, surroundKeywords, scriptType },
|
||||||
|
require('./string-utils-common.js'),
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user