Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion entrypoints/spte.content.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,10 @@ export function getGlossaryRegex(ctx, glossary) {
const badWordsRegexPattern = ctx.rulesById.get('badWords').regex.source;
// On duplique chaque mot avec un s final pour pouvoir traiter les pluriels.
const glossaryWithPlurals = glossary.reduce((a, i) => a.concat(i, `${i}s`), []);
const glossaryRegexPattern = `${glossaryWithPlurals.join('(?=[\\s,:;"\']|$)|(?<=[\\s,:;"\']|^)(?<!«\\s)')}(?=[\\s,.:;"']|$)`;
// Un seul lookbehind/lookahead pour tous les termes (et non un par séparateur de jointure) : sinon le premier terme n'a pas
// de lookbehind et seul le dernier accepte un point final. Parenthèses et ponctuation finale acceptées (« (uploads) », « uploads. »).
// Le point n'est une borne que suivi d'un espace ou de la fin, pour ne pas signaler « widget.php ». Voir issue #116.
const glossaryRegexPattern = `(?<=[\\s,:;"'(]|^)(?<!«\\s)(?:${glossaryWithPlurals.join('|')})(?=[\\s,:;!?)»"']|\\.(?:\\s|$)|$)`;
// Flag « i » indispensable : les termes du glossaire sont en minuscules (et rgxBadWords l'a déjà), sinon un mot capitalisé n'est pas repéré.
const newRgxBadWords = new RegExp(`${badWordsRegexPattern}|${glossaryRegexPattern}`, 'gmi');
ctx.rulesById.get('badWords').regex = newRgxBadWords;
Expand Down
29 changes: 29 additions & 0 deletions tests/spte.content.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@ describe('getGlossaryRegex', () => {
getGlossaryRegex(ctx, ['notice']);
expect('Une Notice ici').toMatch(badWords.regex);
});

// Issue #116 : « (uploads) » et « uploads. » n'étaient pas repérés (parenthèses et point final absents des bornes,
// point final accepté seulement pour le dernier terme de la liste).
describe('bornes du terme', () => {
const glossaryMatches = (text) => {
const badWords = { regex: /^original$/gm };
const ctx = { rulesById: new Map([['badWords', badWords]]) };
getGlossaryRegex(ctx, ['upload', 'widget', 'notice']);
return [...text.matchAll(badWords.regex)].map((m) => m[0]);
};
it.each([
['entre parenthèses', 'le dossier de téléversement (uploads) est vide', ['uploads']],
['avant un point final', 'dans le dossier uploads.', ['uploads']],
['premier terme de la liste avant un point final', 'Un upload.', ['upload']],
['terme du milieu avant un point final', 'Un widget.', ['widget']],
['avant un point d’exclamation', 'Un widget!', ['widget']],
['en début de chaîne', 'widget ici', ['widget']],
['entre guillemets droits', 'Le "widget" ici', ['widget']],
])('repère un terme %s', (_label, text, expected) => {
expect(glossaryMatches(text)).toEqual(expected);
});
it.each([
['dans un nom de fichier', 'Voir widget.php pour le détail'],
['dans un mot plus long', 'Un widgetisé ici'],
['dans une citation « … »', 'On dit « widget » ici'],
])('ne repère pas un terme %s', (_label, text) => {
expect(glossaryMatches(text)).toEqual([]);
});
});
});

describe('getUnambiguousGlossaryTerms', () => {
Expand Down
Loading