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"}}
{{~#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~}}
{{~/unless~}}
{{~#if glossary.[1]~}}
@ -92,18 +103,18 @@
{{~else~}}
{{~#if group~}}
{{~#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~}}
{{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries~}}
{{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~/if~}}
{{~else if merge~}}
{{~#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~}}
{{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries~}}
{{~> glossary-single definition.definitions.[0] brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~/if~}}
{{~else~}}
{{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries~}}
{{~> glossary-single definition brief=brief compactGlossaries=compactGlossaries data=.~}}
{{~/if~}}
{{~/if~}}
</div>

View File

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

View File

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

View File

@ -618,7 +618,35 @@ class OptionsUtil {
options.global.showPopupPreview = false;
for (const profile of options.profiles) {
profile.options.anki.checkForDuplicates = true;
const fieldTemplates = profile.options.anki.fieldTemplates;
if (typeof fieldTemplates === 'string') {
profile.options.anki.fieldTemplates = this._updateVersion6AnkiTemplatesCompactTags(fieldTemplates);
}
}
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({
renderTemplate: this._renderTemplate.bind(this)
});
const {general: {resultOutputMode, compactGlossaries}} = options;
const {general: {resultOutputMode, compactGlossaries, compactTags}} = options;
const note = await ankiNoteBuilder.createNote({
definition,
mode,
@ -191,6 +191,7 @@ class AnkiTemplatesController {
templates,
resultOutputMode,
compactGlossaries,
compactTags,
modeOptions: {
fields: {field},
deck: '',

View File

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

View File

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

View File

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

View File

@ -608,6 +608,79 @@ ${update2}
${update4}
${update6}
{{~> (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()
}
];