1
0
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:
Laurent Cozic 2019-01-17 19:01:35 +00:00
parent 8dc0b34fdc
commit 96cd56548e
9 changed files with 90 additions and 64 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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) {
@ -38,3 +39,5 @@ fs.readdirSync(guiPath).forEach((filename) => {
}
}
});
fs.copySync(basePath + '/ReactNativeClient/lib/string-utils-common.js', __dirname + '/gui/note-viewer/lib.js');

View File

@ -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';

View File

@ -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": {

View File

@ -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
View 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="
}
}
}

View File

@ -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;

View 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 };
}

View File

@ -223,10 +223,6 @@ function escapeHtml(s) {
.replace(/'/g, "&#039;");
}
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'),
);