2020-11-05 18:58:23 +02:00
|
|
|
import Setting from '../../models/Setting';
|
|
|
|
import CommandService from '../CommandService';
|
|
|
|
import SpellCheckerServiceDriverBase from './SpellCheckerServiceDriverBase';
|
|
|
|
import { _, countryDisplayName } from '../../locale';
|
2020-11-08 03:08:33 +02:00
|
|
|
import KvStore from '../KvStore';
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
export default class SpellCheckerService {
|
|
|
|
|
|
|
|
private driver_:SpellCheckerServiceDriverBase;
|
2020-11-08 03:08:33 +02:00
|
|
|
private latestSelectedLanguages_:string[] = [];
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
private static instance_:SpellCheckerService;
|
|
|
|
|
|
|
|
public static instance():SpellCheckerService {
|
|
|
|
if (this.instance_) return this.instance_;
|
|
|
|
this.instance_ = new SpellCheckerService();
|
|
|
|
return this.instance_;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async initialize(driver:SpellCheckerServiceDriverBase) {
|
|
|
|
this.driver_ = driver;
|
2020-11-08 03:08:33 +02:00
|
|
|
this.latestSelectedLanguages_ = await this.loadLatestSelectedLanguages();
|
2020-11-05 18:58:23 +02:00
|
|
|
this.setupDefaultLanguage();
|
|
|
|
this.applyStateToDriver();
|
|
|
|
}
|
|
|
|
|
|
|
|
private get defaultLanguage():string {
|
|
|
|
return 'en-US';
|
|
|
|
}
|
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
private async loadLatestSelectedLanguages():Promise<string[]> {
|
|
|
|
const result = await KvStore.instance().value<string>('spellCheckerService.latestSelectedLanguages');
|
|
|
|
if (!result) return [];
|
|
|
|
return JSON.parse(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async addLatestSelectedLanguage(language:string) {
|
|
|
|
const languages = this.latestSelectedLanguages_.slice();
|
|
|
|
if (languages.length > 5) languages.splice(0, 1);
|
|
|
|
|
|
|
|
if (languages.includes(language)) {
|
|
|
|
languages.splice(languages.indexOf(language), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
languages.splice(0, 0, language);
|
|
|
|
|
|
|
|
this.latestSelectedLanguages_ = languages;
|
|
|
|
await KvStore.instance().setValue('spellCheckerService.latestSelectedLanguages', JSON.stringify(this.latestSelectedLanguages_));
|
|
|
|
}
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
public setupDefaultLanguage() {
|
|
|
|
if (!Setting.value('spellChecker.language')) {
|
|
|
|
const l = this.driver_.language;
|
|
|
|
this.setLanguage(l ? l : this.defaultLanguage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public get availableLanguages():string[] {
|
|
|
|
return this.driver_.availableLanguages;
|
|
|
|
}
|
|
|
|
|
|
|
|
private applyStateToDriver() {
|
|
|
|
this.driver_.setLanguage(this.enabled ? this.language : '');
|
|
|
|
}
|
|
|
|
|
|
|
|
public setLanguage(language:string) {
|
|
|
|
Setting.setValue('spellChecker.language', language);
|
|
|
|
this.applyStateToDriver();
|
2020-11-08 03:08:33 +02:00
|
|
|
this.addLatestSelectedLanguage(language);
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public get language():string {
|
|
|
|
return Setting.value('spellChecker.language');
|
|
|
|
}
|
|
|
|
|
|
|
|
public get enabled():boolean {
|
|
|
|
return Setting.value('spellChecker.enabled');
|
|
|
|
}
|
|
|
|
|
|
|
|
public toggleEnabled() {
|
|
|
|
Setting.toggle('spellChecker.enabled');
|
|
|
|
this.applyStateToDriver();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async addToDictionary(language:string, word:string) {
|
|
|
|
this.driver_.addWordToSpellCheckerDictionary(language, word);
|
|
|
|
}
|
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
public contextMenuItems(misspelledWord:string, dictionarySuggestions:string[]):any[] {
|
2020-11-05 18:58:23 +02:00
|
|
|
if (!misspelledWord) return [];
|
|
|
|
|
|
|
|
const output = [];
|
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
output.push({ type: 'separator' });
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
if (dictionarySuggestions.length) {
|
|
|
|
for (const suggestion of dictionarySuggestions) {
|
2020-11-08 03:08:33 +02:00
|
|
|
output.push({
|
2020-11-05 18:58:23 +02:00
|
|
|
label: suggestion,
|
|
|
|
click: () => {
|
|
|
|
CommandService.instance().execute('replaceSelection', suggestion);
|
|
|
|
},
|
2020-11-08 03:08:33 +02:00
|
|
|
});
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
} else {
|
2020-11-08 03:08:33 +02:00
|
|
|
output.push({
|
2020-11-05 18:58:23 +02:00
|
|
|
label: `(${_('No suggestions')})`,
|
|
|
|
enabled: false,
|
|
|
|
click: () => {},
|
2020-11-08 03:08:33 +02:00
|
|
|
});
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
output.push({ type: 'separator' });
|
2020-11-05 18:58:23 +02:00
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
output.push({
|
2020-11-05 18:58:23 +02:00
|
|
|
label: _('Add to dictionary'),
|
|
|
|
click: () => {
|
|
|
|
this.addToDictionary(this.language, misspelledWord);
|
|
|
|
},
|
2020-11-08 03:08:33 +02:00
|
|
|
});
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
private changeLanguageMenuItem(language:string, enabled:boolean, checked:boolean) {
|
|
|
|
return {
|
|
|
|
label: countryDisplayName(language),
|
|
|
|
type: 'radio',
|
|
|
|
checked: checked,
|
|
|
|
enabled: enabled,
|
|
|
|
click: () => {
|
|
|
|
this.setLanguage(language);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
private changeLanguageMenuItems(selectedLanguage:string, enabled:boolean) {
|
|
|
|
const languageMenuItems = [];
|
|
|
|
|
|
|
|
for (const locale of this.driver_.availableLanguages) {
|
2020-11-08 03:08:33 +02:00
|
|
|
languageMenuItems.push(this.changeLanguageMenuItem(locale, enabled, locale === selectedLanguage));
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
languageMenuItems.sort((a:any, b:any) => {
|
|
|
|
return a.label < b.label ? -1 : +1;
|
|
|
|
});
|
|
|
|
|
2020-11-08 03:08:33 +02:00
|
|
|
return languageMenuItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
public spellCheckerConfigMenuItems(selectedLanguage:string, useSpellChecker:boolean) {
|
|
|
|
const latestLanguageItems = this.latestSelectedLanguages_.map((language:string) => {
|
|
|
|
return this.changeLanguageMenuItem(language, true, language === selectedLanguage);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (latestLanguageItems.length) latestLanguageItems.splice(0, 0, { type: 'separator' } as any);
|
|
|
|
|
|
|
|
latestLanguageItems.sort((a:any, b:any) => {
|
|
|
|
return a.label < b.label ? -1 : +1;
|
|
|
|
});
|
|
|
|
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
label: _('Use spell checker'),
|
|
|
|
type: 'checkbox',
|
|
|
|
checked: useSpellChecker,
|
|
|
|
click: () => {
|
|
|
|
this.toggleEnabled();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
...latestLanguageItems,
|
|
|
|
|
|
|
|
{
|
|
|
|
type: 'separator',
|
|
|
|
},
|
|
|
|
|
|
|
|
// Can be removed once it does work
|
|
|
|
{
|
|
|
|
label: '⚠ Spell checker doesn\'t work in Markdown editor ⚠',
|
|
|
|
enabled: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
type: 'separator',
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
label: _('Change language'),
|
|
|
|
submenu: this.changeLanguageMenuItems(selectedLanguage, useSpellChecker),
|
|
|
|
},
|
|
|
|
];
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public spellCheckerConfigMenuItem(selectedLanguage:string, useSpellChecker:boolean) {
|
2020-11-08 03:08:33 +02:00
|
|
|
return {
|
2020-11-05 18:58:23 +02:00
|
|
|
label: _('Spell checker'),
|
2020-11-08 03:08:33 +02:00
|
|
|
submenu: this.spellCheckerConfigMenuItems(selectedLanguage, useSpellChecker),
|
|
|
|
};
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|