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
|
||||
ReactNativeClient/lib/csstojs/
|
||||
ElectronClient/app/gui/note-viewer/fonts/
|
||||
ElectronClient/app/gui/note-viewer/lib.js
|
||||
Tools/commit_hook.txt
|
@ -2,6 +2,7 @@ const fs = require('fs-extra');
|
||||
const spawnSync = require('child_process').spawnSync;
|
||||
|
||||
const babelPath = __dirname + '/node_modules/.bin/babel' + (process.platform === 'win32' ? '.cmd' : '');
|
||||
const basePath = __dirname + '/../..';
|
||||
const guiPath = __dirname + '/gui';
|
||||
|
||||
function fileIsNewerThan(path1, path2) {
|
||||
@ -27,7 +28,7 @@ fs.readdirSync(guiPath).forEach((filename) => {
|
||||
|
||||
if (fileIsNewerThan(jsxPath, jsPath)) {
|
||||
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) {
|
||||
const msg = [];
|
||||
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="markScriptContainer"></div>
|
||||
<div id="content" ondragstart="return false;" ondrop="return false;"></div>
|
||||
<script src="./lib.js"></script>
|
||||
|
||||
<script>
|
||||
const contentElement = document.getElementById('content');
|
||||
@ -226,10 +227,6 @@
|
||||
function setMarkers(keywords, options = null) {
|
||||
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: Update everywhere that uses allParsedQueryTerms to support new format
|
||||
// TODO: Add support for scriptType on mobile and CLI
|
||||
@ -275,12 +272,20 @@
|
||||
const isBasicSearch = ['ja', 'zh', 'ko'].indexOf(keyword.scriptType) >= 0;
|
||||
|
||||
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;
|
||||
|
||||
mark_.markRegExp(new RegExp(regexString, 'gmi'), {
|
||||
each: onEachElement,
|
||||
acrossElements: true,
|
||||
ignoreGroups: 1,
|
||||
});
|
||||
} else {
|
||||
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": {
|
||||
"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=="
|
||||
},
|
||||
"nanomatch": {
|
||||
|
@ -8,8 +8,8 @@
|
||||
"pack": "node_modules/.bin/electron-builder --dir",
|
||||
"dist": "node_modules/.bin/electron-builder",
|
||||
"publish": "build -p always",
|
||||
"postinstall": "node compile-jsx.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"
|
||||
"postinstall": "node compile.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts && install-app-deps",
|
||||
"compile": "node compile.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts"
|
||||
},
|
||||
"repository": {
|
||||
"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);
|
||||
if (regexString[regexString.length - 1] === '*') {
|
||||
// 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) + '[^' + pregQuote(' \t\n\r,.,+-*?!={}<>|:"\'()[]') + ']' + '*?';
|
||||
// regexString = regexString.substr(0, regexString.length - 2) + '.*?';
|
||||
}
|
||||
|
||||
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, "'");
|
||||
}
|
||||
|
||||
function pregQuote(str, delimiter = '') {
|
||||
return (str + '').replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&');
|
||||
}
|
||||
|
||||
function surroundKeywords(keywords, text, prefix, suffix) {
|
||||
if (!keywords.length) return text;
|
||||
|
||||
@ -242,54 +238,6 @@ function surroundKeywords(keywords, text, prefix, 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_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]/;
|
||||
@ -301,4 +249,7 @@ function scriptType(s) {
|
||||
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