Compact tags refactor (#1021)

* Update translator to flag redundant tags instead of remove

* Update how compact tags are shown in the popup

* Pass compactTags option to note builder

* Update options templates

* Add options upgrade

* Add options upgrade test
This commit is contained in:
toasted-nutbread 2020-11-12 20:34:11 -05:00 committed by GitHub
parent f2ad94e54f
commit ec021964b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 153 additions and 38 deletions

View File

@ -1,6 +1,17 @@
{{#*inline "glossary-single"}} {{#*inline "glossary-single"}}
{{~#unless brief~}} {{~#unless brief~}}
{{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}} {{~#scope~}}
{{~#set "any" false}}{{/set~}}
{{~#if definitionTags~}}{{#each definitionTags~}}
{{~#if (op "||" (op "!" ../data.compactTags) (op "!" redundant))~}}
{{~#if (get "any")}}, {{else}}<i>({{/if~}}
{{name}}
{{~#set "any" true}}{{/set~}}
{{~/if~}}
{{~/each~}}
{{~#if (get "any")}})</i> {{/if~}}
{{~/if~}}
{{~/scope~}}
{{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}} {{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}}
{{~/unless~}} {{~/unless~}}
{{~#if glossary.[1]~}} {{~#if glossary.[1]~}}
@ -92,18 +103,18 @@
{{~else~}} {{~else~}}
{{~#if group~}} {{~#if group~}}
{{~#if definition.definitions.[1]~}} {{~#if definition.definitions.[1]~}}
<ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief compactGlossaries=../compactGlossaries}}</li>{{/each}}</ol> <ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief compactGlossaries=../compactGlossaries data=../.}}</li>{{/each}}</ol>
{{~else~}} {{~else~}}
{{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries~}} {{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~/if~}} {{~/if~}}
{{~else if merge~}} {{~else if merge~}}
{{~#if definition.definitions.[1]~}} {{~#if definition.definitions.[1]~}}
<ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief compactGlossaries=../compactGlossaries}}</li>{{/each}}</ol> <ol>{{#each definition.definitions}}<li>{{> glossary-single brief=../brief compactGlossaries=../compactGlossaries data=../.}}</li>{{/each}}</ol>
{{~else~}} {{~else~}}
{{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries~}} {{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~/if~}} {{~/if~}}
{{~else~}} {{~else~}}
{{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries~}} {{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~/if~}} {{~/if~}}
{{~/if~}} {{~/if~}}
</div> </div>

View File

@ -35,6 +35,7 @@ class AnkiNoteBuilder {
duplicateScope='collection', duplicateScope='collection',
resultOutputMode='split', resultOutputMode='split',
compactGlossaries=false, compactGlossaries=false,
compactTags=false,
modeOptions: {fields, deck, model}, modeOptions: {fields, deck, model},
audioDetails=null, audioDetails=null,
screenshotDetails=null, screenshotDetails=null,
@ -70,7 +71,7 @@ class AnkiNoteBuilder {
} }
}; };
const data = this._createNoteData(definition, mode, context, resultOutputMode, compactGlossaries); const data = this._createNoteData(definition, mode, context, resultOutputMode, compactGlossaries, compactTags);
const formattedFieldValuePromises = []; const formattedFieldValuePromises = [];
for (const [, fieldValue] of fieldEntries) { for (const [, fieldValue] of fieldEntries) {
const formattedFieldValuePromise = this._formatField(fieldValue, data, templates, errors); const formattedFieldValuePromise = this._formatField(fieldValue, data, templates, errors);
@ -104,7 +105,7 @@ class AnkiNoteBuilder {
// Private // Private
_createNoteData(definition, mode, context, resultOutputMode, compactGlossaries) { _createNoteData(definition, mode, context, resultOutputMode, compactGlossaries, compactTags) {
const pitches = DictionaryDataUtil.getPitchAccentInfos(definition); const pitches = DictionaryDataUtil.getPitchAccentInfos(definition);
const pitchCount = pitches.reduce((i, v) => i + v.pitches.length, 0); const pitchCount = pitches.reduce((i, v) => i + v.pitches.length, 0);
return { return {
@ -118,6 +119,7 @@ class AnkiNoteBuilder {
modeTermKana: mode === 'term-kana', modeTermKana: mode === 'term-kana',
modeKanji: mode === 'kanji', modeKanji: mode === 'kanji',
compactGlossaries, compactGlossaries,
compactTags,
context context
}; };
} }

View File

@ -1650,7 +1650,7 @@ class Backend {
const {wildcard} = details; const {wildcard} = details;
const enabledDictionaryMap = this._getTranslatorEnabledDictionaryMap(options); const enabledDictionaryMap = this._getTranslatorEnabledDictionaryMap(options);
const { const {
general: {compactTags, mainDictionary}, general: {mainDictionary},
scanning: {alphanumeric}, scanning: {alphanumeric},
translation: { translation: {
convertHalfWidthCharacters, convertHalfWidthCharacters,
@ -1663,7 +1663,6 @@ class Backend {
} = options; } = options;
return { return {
wildcard, wildcard,
compactTags,
mainDictionary, mainDictionary,
alphanumeric, alphanumeric,
convertHalfWidthCharacters, convertHalfWidthCharacters,

View File

@ -618,7 +618,35 @@ class OptionsUtil {
options.global.showPopupPreview = false; options.global.showPopupPreview = false;
for (const profile of options.profiles) { for (const profile of options.profiles) {
profile.options.anki.checkForDuplicates = true; profile.options.anki.checkForDuplicates = true;
const fieldTemplates = profile.options.anki.fieldTemplates;
if (typeof fieldTemplates === 'string') {
profile.options.anki.fieldTemplates = this._updateVersion6AnkiTemplatesCompactTags(fieldTemplates);
}
} }
return options; return options;
} }
_updateVersion6AnkiTemplatesCompactTags(templates) {
const rawPattern1 = '{{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}}';
const pattern1 = new RegExp(`((\r?\n)?[ \t]*)${escapeRegExp(rawPattern1)}`, 'g');
const replacement1 = (
// eslint-disable-next-line indent
`{{~#scope~}}
{{~#set "any" false}}{{/set~}}
{{~#if definitionTags~}}{{#each definitionTags~}}
{{~#if (op "||" (op "!" ../data.compactTags) (op "!" redundant))~}}
{{~#if (get "any")}}, {{else}}<i>({{/if~}}
{{name}}
{{~#set "any" true}}{{/set~}}
{{~/if~}}
{{~/each~}}
{{~#if (get "any")}})</i> {{/if~}}
{{~/if~}}
{{~/scope~}}`
);
const simpleNewline = /\n/g;
templates = templates.replace(pattern1, (g0, space) => (space + replacement1.replace(simpleNewline, space)));
templates = templates.replace(/\bcompactGlossaries=((?:\.*\/)*)compactGlossaries\b/g, (g0, g1) => `${g0} data=${g1}.`);
return templates;
}
} }

View File

@ -183,7 +183,7 @@ class AnkiTemplatesController {
const ankiNoteBuilder = new AnkiNoteBuilder({ const ankiNoteBuilder = new AnkiNoteBuilder({
renderTemplate: this._renderTemplate.bind(this) renderTemplate: this._renderTemplate.bind(this)
}); });
const {general: {resultOutputMode, compactGlossaries}} = options; const {general: {resultOutputMode, compactGlossaries, compactTags}} = options;
const note = await ankiNoteBuilder.createNote({ const note = await ankiNoteBuilder.createNote({
definition, definition,
mode, mode,
@ -191,6 +191,7 @@ class AnkiTemplatesController {
templates, templates,
resultOutputMode, resultOutputMode,
compactGlossaries, compactGlossaries,
compactTags,
modeOptions: { modeOptions: {
fields: {field}, fields: {field},
deck: '', deck: '',

View File

@ -59,7 +59,6 @@ class Translator {
* @param options An object using the following structure: * @param options An object using the following structure:
* { * {
* wildcard: (null or string), * wildcard: (null or string),
* compactTags: (boolean),
* mainDictionary: (string), * mainDictionary: (string),
* alphanumeric: (boolean), * alphanumeric: (boolean),
* convertHalfWidthCharacters: (enum: 'false', 'true', 'variant'), * convertHalfWidthCharacters: (enum: 'false', 'true', 'variant'),
@ -156,24 +155,22 @@ class Translator {
} }
async _findTermsGrouped(text, options) { async _findTermsGrouped(text, options) {
const {compactTags, enabledDictionaryMap} = options; const {enabledDictionaryMap} = options;
const [definitions, length] = await this._findTermsInternal(text, enabledDictionaryMap, options); const [definitions, length] = await this._findTermsInternal(text, enabledDictionaryMap, options);
const groupedDefinitions = this._groupTerms(definitions, enabledDictionaryMap); const groupedDefinitions = this._groupTerms(definitions, enabledDictionaryMap);
await this._buildTermMeta(groupedDefinitions, enabledDictionaryMap); await this._buildTermMeta(groupedDefinitions, enabledDictionaryMap);
this._sortDefinitions(groupedDefinitions, false); this._sortDefinitions(groupedDefinitions, false);
if (compactTags) {
for (const definition of groupedDefinitions) { for (const definition of groupedDefinitions) {
this._compressDefinitionTags(definition.definitions); this._flagRedundantDefinitionTags(definition.definitions);
}
} }
return [groupedDefinitions, length]; return [groupedDefinitions, length];
} }
async _findTermsMerged(text, options) { async _findTermsMerged(text, options) {
const {compactTags, mainDictionary, enabledDictionaryMap} = options; const {mainDictionary, enabledDictionaryMap} = options;
const secondarySearchDictionaryMap = this._getSecondarySearchDictionaryMap(enabledDictionaryMap); const secondarySearchDictionaryMap = this._getSecondarySearchDictionaryMap(enabledDictionaryMap);
const [definitions, length] = await this._findTermsInternal(text, enabledDictionaryMap, options); const [definitions, length] = await this._findTermsInternal(text, enabledDictionaryMap, options);
@ -212,10 +209,8 @@ class Translator {
await this._buildTermMeta(definitionsMerged, enabledDictionaryMap); await this._buildTermMeta(definitionsMerged, enabledDictionaryMap);
this._sortDefinitions(definitionsMerged, false); this._sortDefinitions(definitionsMerged, false);
if (compactTags) {
for (const definition of definitionsMerged) { for (const definition of definitionsMerged) {
this._compressDefinitionTags(definition.definitions); this._flagRedundantDefinitionTags(definition.definitions);
}
} }
return [definitionsMerged, length]; return [definitionsMerged, length];
@ -541,7 +536,7 @@ class Translator {
} }
} }
_compressDefinitionTags(definitions) { _flagRedundantDefinitionTags(definitions) {
let lastDictionary = ''; let lastDictionary = '';
let lastPartOfSpeech = ''; let lastPartOfSpeech = '';
const removeCategoriesSet = new Set(); const removeCategoriesSet = new Set();
@ -564,7 +559,7 @@ class Translator {
} }
if (removeCategoriesSet.size > 0) { if (removeCategoriesSet.size > 0) {
this._removeTagsWithCategory(definitionTags, removeCategoriesSet); this._flagTagsWithCategoryAsRedundant(definitionTags, removeCategoriesSet);
removeCategoriesSet.clear(); removeCategoriesSet.clear();
} }
} }
@ -749,7 +744,7 @@ class Translator {
const meta = tagMetaList[i]; const meta = tagMetaList[i];
const name = names[i]; const name = names[i];
const {category, notes, order, score} = (meta !== null ? meta : {}); const {category, notes, order, score} = (meta !== null ? meta : {});
const tag = this._createTag(name, category, notes, order, score, dictionary); const tag = this._createTag(name, category, notes, order, score, dictionary, false);
results.push(tag); results.push(tag);
} }
return results; return results;
@ -893,13 +888,11 @@ class Translator {
return results; return results;
} }
_removeTagsWithCategory(tags, removeCategoriesSet) { _flagTagsWithCategoryAsRedundant(tags, removeCategoriesSet) {
for (let i = 0, ii = tags.length; i < ii; ++i) { for (const tag of tags) {
const {category} = tags[i]; if (removeCategoriesSet.has(tag.category)) {
if (!removeCategoriesSet.has(category)) { continue; } tag.redundant = true;
tags.splice(i, 1); }
--i;
--ii;
} }
} }
@ -970,8 +963,8 @@ class Translator {
// Common data creation and cloning functions // Common data creation and cloning functions
_cloneTag(tag) { _cloneTag(tag) {
const {name, category, notes, order, score, dictionary} = tag; const {name, category, notes, order, score, dictionary, redundant} = tag;
return this._createTag(name, category, notes, order, score, dictionary); return this._createTag(name, category, notes, order, score, dictionary, redundant);
} }
_cloneTags(tags) { _cloneTags(tags) {
@ -987,17 +980,18 @@ class Translator {
} }
_createDictionaryTag(name) { _createDictionaryTag(name) {
return this._createTag(name, 'dictionary', '', 100, 0, name); return this._createTag(name, 'dictionary', '', 100, 0, name, false);
} }
_createTag(name, category, notes, order, score, dictionary) { _createTag(name, category, notes, order, score, dictionary, redundant) {
return { return {
name, name,
category: (typeof category === 'string' && category.length > 0 ? category : 'default'), category: (typeof category === 'string' && category.length > 0 ? category : 'default'),
notes: (typeof notes === 'string' ? notes : ''), notes: (typeof notes === 'string' ? notes : ''),
order: (typeof order === 'number' ? order : 0), order: (typeof order === 'number' ? order : 0),
score: (typeof score === 'number' ? score : 0), score: (typeof score === 'number' ? score : 0),
dictionary: (typeof dictionary === 'string' ? dictionary : null) dictionary: (typeof dictionary === 'string' ? dictionary : null),
redundant
}; };
} }

View File

@ -562,6 +562,10 @@ button.action-button {
display: inline; display: inline;
} }
:root[data-compact-tags=true] .tag[data-redundant=true] {
display: none;
}
.term-glossary-separator, .term-glossary-separator,
.term-reason-separator { .term-reason-separator {
display: inline; display: inline;

View File

@ -349,6 +349,7 @@ class DisplayGenerator {
node.title = details.notes; node.title = details.notes;
inner.textContent = details.name; inner.textContent = details.name;
node.dataset.category = details.category; node.dataset.category = details.category;
if (details.redundant) { node.dataset.redundant = true; }
return node; return node;
} }

View File

@ -726,6 +726,7 @@ class Display extends EventDispatcher {
data.ankiEnabled = `${options.anki.enable}`; data.ankiEnabled = `${options.anki.enable}`;
data.audioEnabled = `${options.audio.enabled && options.audio.sources.length > 0}`; data.audioEnabled = `${options.audio.enabled && options.audio.sources.length > 0}`;
data.compactGlossaries = `${options.general.compactGlossaries}`; data.compactGlossaries = `${options.general.compactGlossaries}`;
data.compactTags = `${options.general.compactTags}`;
data.enableSearchTags = `${options.scanning.enableSearchTags}`; data.enableSearchTags = `${options.scanning.enableSearchTags}`;
data.showPitchAccentDownstepNotation = `${options.general.showPitchAccentDownstepNotation}`; data.showPitchAccentDownstepNotation = `${options.general.showPitchAccentDownstepNotation}`;
data.showPitchAccentPositionNotation = `${options.general.showPitchAccentPositionNotation}`; data.showPitchAccentPositionNotation = `${options.general.showPitchAccentPositionNotation}`;
@ -1421,7 +1422,7 @@ class Display extends EventDispatcher {
async _createNote(definition, mode, context, options, templates, injectMedia) { async _createNote(definition, mode, context, options, templates, injectMedia) {
const { const {
general: {resultOutputMode, compactGlossaries}, general: {resultOutputMode, compactGlossaries, compactTags},
anki: {tags, checkForDuplicates, duplicateScope, kanji, terms, screenshot: {format, quality}}, anki: {tags, checkForDuplicates, duplicateScope, kanji, terms, screenshot: {format, quality}},
audio: {sources, customSourceUrl} audio: {sources, customSourceUrl}
} = options; } = options;
@ -1462,6 +1463,7 @@ class Display extends EventDispatcher {
duplicateScope, duplicateScope,
resultOutputMode, resultOutputMode,
compactGlossaries, compactGlossaries,
compactTags,
modeOptions modeOptions
}); });
} }

View File

@ -608,6 +608,79 @@ ${update2}
${update4} ${update4}
${update6} ${update6}
{{~> (lookup . "marker") ~}}`.trimStart() {{~> (lookup . "marker") ~}}`.trimStart()
},
// Definition tags update
{
old: `
{{#*inline "glossary-single"}}
{{~#unless brief~}}
{{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}}
{{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}}
{{~/unless~}}
{{/inline}}
{{#*inline "glossary-single2"}}
{{~#unless brief~}}
{{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}}
{{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}}
{{~/unless~}}
{{/inline}}
{{#*inline "glossary"}}
{{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries~}}
{{~> glossary-single definition brief=brief compactGlossaries=../compactGlossaries~}}
{{/inline}}
{{~> (lookup . "marker") ~}}
`.trimStart(),
expected: `
{{#*inline "glossary-single"}}
{{~#unless brief~}}
{{~#scope~}}
{{~#set "any" false}}{{/set~}}
{{~#if definitionTags~}}{{#each definitionTags~}}
{{~#if (op "||" (op "!" ../data.compactTags) (op "!" redundant))~}}
{{~#if (get "any")}}, {{else}}<i>({{/if~}}
{{name}}
{{~#set "any" true}}{{/set~}}
{{~/if~}}
{{~/each~}}
{{~#if (get "any")}})</i> {{/if~}}
{{~/if~}}
{{~/scope~}}
{{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}}
{{~/unless~}}
{{/inline}}
{{#*inline "glossary-single2"}}
{{~#unless brief~}}
{{~#scope~}}
{{~#set "any" false}}{{/set~}}
{{~#if definitionTags~}}{{#each definitionTags~}}
{{~#if (op "||" (op "!" ../data.compactTags) (op "!" redundant))~}}
{{~#if (get "any")}}, {{else}}<i>({{/if~}}
{{name}}
{{~#set "any" true}}{{/set~}}
{{~/if~}}
{{~/each~}}
{{~#if (get "any")}})</i> {{/if~}}
{{~/if~}}
{{~/scope~}}
{{~#if only~}}({{#each only}}{{{.}}}{{#unless @last}}, {{/unless}}{{/each}} only) {{/if~}}
{{~/unless~}}
{{/inline}}
{{#*inline "glossary"}}
{{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~> glossary-single definition brief=brief compactGlossaries=../compactGlossaries data=../.~}}
{{/inline}}
${update2}
${update4}
${update6}
{{~> (lookup . "marker") ~}}
`.trimStart()
} }
]; ];