Accept apostrophes in localized search operator names

Allow straight and curly apostrophes in operator names so Klingon and other apostrophe-bearing localized keywords can be parsed. Keep quoted value parsing and unknown-operator errors unchanged.

Validation: actual Query runtime tests cover localized names, quoted values, normal operators, plain quoted text and unknown names; card-number search regressions pass.

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu committed 2026-09-13 17:11:59 +03:00
1 parent a4b53d2466
commit 0026390bba
2 files changed
+28 -2

No files matched your search

+2 -2
View File
@@ -304,11 +304,11 @@ export class Query {
}
const reOperator1 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)',
'^((?<operator>[\\p{Letter}\\p{Mark}\\x27\\u2019]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)',
'iu',
);
const reOperator2 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)',
'^((?<operator>[\\p{Letter}\\p{Mark}\\x27\\u2019]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)',
'iu',
);
const reText = new RegExp('^(?<text>\\S+)(\\s+|$)', 'u');
+26
View File
@@ -0,0 +1,26 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const path = require('node:path');
const root = path.resolve(__dirname, '..');
const read = filename => fs.readFileSync(path.join(root, filename), 'utf8');
const en = JSON.parse(read('imports/i18n/data/en.i18n.json'));
const labels = { ...en, 'operator-user': "lo'wI'", 'operator-title': "pong’le'" };
const context = { TAPi18n: { __: key => labels[key] || key }, Boards: { colorMap: () => ({}) }, console };
vm.createContext(context);
vm.runInContext(read('config/search-const.js').replace(/export /g, '') + '\n' +
read('config/query-classes.js').replace(/import[\s\S]*?from ['"][^'"]+['"];\s*/g, '').replace(/export /g, '') +
'\nthis.Query = Query;', context);
function query(text) {
const result = new context.Query();
result.buildParams(text);
return result;
}
assert.equal(query("lo'wI':Alice").getQueryParams().getPredicate('user'), 'Alice');
assert.equal(query("pong’le':\"two words\"").getQueryParams().getPredicate('title'), 'two words');
assert.equal(query('board:Roadmap').getQueryParams().getPredicate('board'), 'Roadmap');
assert.equal(query('board:"two words"').getQueryParams().getPredicate('board'), 'two words');
assert.equal(query("unknown'operator:value").hasErrors(), true);
assert.equal(query("'plain words'").getQueryParams().text, 'plain words');
console.log('localizedSearchApostrophes: localized operators, quoted values, ordinary operators and unknown names verified');