generated from mirrors/plugin-sample-vite-svelte
Smarter document loading
All checks were successful
Build on Push and create Release on Tag / build (push) Successful in 1m5s
All checks were successful
Build on Push and create Release on Tag / build (push) Successful in 1m5s
This commit is contained in:
parent
095e2a2bb9
commit
631cfd14bf
6 changed files with 155 additions and 98 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@ dev
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
tmp
|
tmp
|
||||||
|
.lingma
|
||||||
|
|
|
||||||
68
src/index.ts
68
src/index.ts
|
|
@ -1,5 +1,5 @@
|
||||||
import {Plugin, showMessage} from 'siyuan';
|
import {IProtyle, Plugin, showMessage} from 'siyuan';
|
||||||
import {ProtyleHelpers} from "@/protyleHelpers";
|
import {ProtyleHelper} from "@/protyleHelper";
|
||||||
import {Icons} from "@/icons";
|
import {Icons} from "@/icons";
|
||||||
import {Settings} from "@/settings";
|
import {Settings} from "@/settings";
|
||||||
import {SettingUtils} from "@/libs/setting-utils";
|
import {SettingUtils} from "@/libs/setting-utils";
|
||||||
|
|
@ -15,6 +15,7 @@ import {Language} from "@/spellChecker";
|
||||||
export default class SpellCheckPlugin extends Plugin {
|
export default class SpellCheckPlugin extends Plugin {
|
||||||
|
|
||||||
private menus: Menus
|
private menus: Menus
|
||||||
|
private currentlyEditing: { protyle: ProtyleHelper, enabled: boolean, language: string };
|
||||||
|
|
||||||
public settingsUtil: SettingUtils;
|
public settingsUtil: SettingUtils;
|
||||||
public suggestions: SuggestionEngine
|
public suggestions: SuggestionEngine
|
||||||
|
|
@ -62,30 +63,34 @@ export default class SpellCheckPlugin extends Plugin {
|
||||||
}`
|
}`
|
||||||
window.document.head.appendChild(style);
|
window.document.head.appendChild(style);
|
||||||
|
|
||||||
if(window.siyuan.config.editor.spellcheck) {
|
if (window.siyuan.config.editor.spellcheck) {
|
||||||
showMessage(this.i18nx.errors.builtInEnabled, -1, 'error')
|
showMessage(this.i18nx.errors.builtInEnabled, -1, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventBus.on('ws-main', async (event) => {
|
this.eventBus.on('ws-main', async (event) => {
|
||||||
|
|
||||||
if (event.detail.cmd != 'transactions') { return }
|
if (event.detail.cmd != 'transactions') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const operation = event.detail.data[0].doOperations[0]
|
const operation = event.detail.data[0].doOperations[0]
|
||||||
const action = operation.action
|
const action = operation.action
|
||||||
const blockID = operation.id
|
const blockID = operation.id
|
||||||
|
|
||||||
if(action != 'update') { return }
|
if (action != 'update' || !this.currentlyEditing.enabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.suggestions.storeBlocks(this.currentlyEditing.protyle, this.currentlyEditing.language)
|
||||||
await this.suggestions.suggestAndRender(blockID)
|
await this.suggestions.suggestAndRender(blockID)
|
||||||
void this.suggestions.forAllBlocksSuggest(this.suggestions.documentID, false, true, false)
|
void this.suggestions.forAllBlocksSuggest(false, true)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.eventBus.on('open-menu-content', async (event) => {
|
this.eventBus.on('open-menu-content', async (event) => {
|
||||||
|
|
||||||
if(!this.suggestions.documentEnabled) { return }
|
|
||||||
void this.analytics.sendEvent('menu-open-any');
|
void this.analytics.sendEvent('menu-open-any');
|
||||||
const blockID = ProtyleHelpers.getNodeId(event.detail.range.startContainer.parentElement)
|
const blockID = ProtyleHelper.getNodeId(event.detail.range.startContainer.parentElement)
|
||||||
|
|
||||||
const suggNo = this.suggestions.getSuggestionNumber(blockID, event.detail.range)
|
const suggNo = this.suggestions.getSuggestionNumber(blockID, event.detail.range)
|
||||||
this.menus.addCorrectionsToParagraphMenu(blockID, suggNo, event.detail.menu)
|
this.menus.addCorrectionsToParagraphMenu(blockID, suggNo, event.detail.menu)
|
||||||
|
|
@ -93,31 +98,46 @@ export default class SpellCheckPlugin extends Plugin {
|
||||||
})
|
})
|
||||||
|
|
||||||
this.eventBus.on('open-menu-doctree', async (event) => {
|
this.eventBus.on('open-menu-doctree', async (event) => {
|
||||||
const docID = ProtyleHelpers.getNodeId(event.detail.elements[0]) // @TODO this is ugly, why does the event not carry the docID?
|
const docID = ProtyleHelper.getNodeId(event.detail.elements[0]) // @TODO this is ugly, why does the event not carry the docID?
|
||||||
void this.menus.addSettingsToDocMenu(docID, event.detail.menu)
|
void this.menus.addSettingsToDocMenu(docID, event.detail.menu)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.eventBus.on('switch-protyle', async (event) => {
|
this.eventBus.on('switch-protyle', async (event) => {
|
||||||
|
void this.suggestions.forAllBlocksSuggest(false, true)
|
||||||
|
const settings = await ProtyleHelper.getDocumentSettings(event.detail.protyle.block.id,
|
||||||
|
this.settingsUtil.get('enabledByDefault'), this.settingsUtil.get('defaultLanguage'))
|
||||||
|
this.currentlyEditing = {
|
||||||
|
protyle: new ProtyleHelper(event.detail.protyle.contentElement),
|
||||||
|
enabled: settings.enabled,
|
||||||
|
language: settings.language
|
||||||
|
}
|
||||||
|
new ResizeObserver(
|
||||||
|
this.suggestions.forAllBlocksSuggest.bind(this.suggestions)
|
||||||
|
).observe(event.detail.protyle.contentElement)
|
||||||
|
})
|
||||||
|
|
||||||
const docID = event.detail.protyle.block.id
|
this.eventBus.on('loaded-protyle-static', async (event) => {
|
||||||
const settings = await ProtyleHelpers.getDocumentSettings(docID, this.settingsUtil.get('enabledByDefault'), this.settingsUtil.get('defaultLanguage'))
|
await this.protyleLoad(event)
|
||||||
|
})
|
||||||
this.suggestions.documentID = docID
|
this.eventBus.on('loaded-protyle-dynamic', async (event) => {
|
||||||
this.suggestions.documentEnabled = settings.enabled
|
await this.protyleLoad(event)
|
||||||
this.suggestions.documentLanguage = settings.language
|
|
||||||
|
|
||||||
this.suggestions.clearStorage()
|
|
||||||
void this.suggestions.forAllBlocksSuggest(docID, true, true, false)
|
|
||||||
|
|
||||||
const activeEditor = document.querySelector('.fn__flex-1.protyle:not([class*="fn__none"])')
|
|
||||||
new ResizeObserver(this.reRenderSuggestions.bind(this)).observe(activeEditor)
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private reRenderSuggestions() {
|
private async protyleLoad(event: CustomEvent<{ protyle: IProtyle; }>) {
|
||||||
void this.suggestions.forAllBlocksSuggest(this.suggestions.documentID, false, true, false)
|
|
||||||
|
const protyle = new ProtyleHelper(event.detail.protyle.contentElement)
|
||||||
|
const docID = event.detail.protyle.block.id
|
||||||
|
|
||||||
|
const settings = await ProtyleHelper.getDocumentSettings(docID,
|
||||||
|
this.settingsUtil.get('enabledByDefault'), this.settingsUtil.get('defaultLanguage'))
|
||||||
|
|
||||||
|
if(settings.enabled) {
|
||||||
|
await this.suggestions.storeBlocks(protyle, settings.language)
|
||||||
|
void this.suggestions.forAllBlocksSuggest(true, true)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ import {Menu, showMessage, subMenu} from 'siyuan';
|
||||||
import SpellCheckPlugin from "@/index";
|
import SpellCheckPlugin from "@/index";
|
||||||
import {getBlockAttrs, setBlockAttrs} from "@/api";
|
import {getBlockAttrs, setBlockAttrs} from "@/api";
|
||||||
import {Settings} from "@/settings";
|
import {Settings} from "@/settings";
|
||||||
import {ProtyleHelpers} from "@/protyleHelpers";
|
import {ProtyleHelper} from "@/protyleHelper";
|
||||||
import {SuggestionEngine} from "@/suggestions";
|
|
||||||
|
|
||||||
export class Menus {
|
export class Menus {
|
||||||
|
|
||||||
|
|
@ -37,7 +36,7 @@ export class Menus {
|
||||||
label: this.plugin.i18nx.textMenu.addToDictionary,
|
label: this.plugin.i18nx.textMenu.addToDictionary,
|
||||||
click: async () => {
|
click: async () => {
|
||||||
void this.plugin.analytics.sendEvent('menu-click-add-to-dictionary');
|
void this.plugin.analytics.sendEvent('menu-click-add-to-dictionary');
|
||||||
const word = SuggestionEngine.suggestionToWrongText(suggestion, blockID)
|
const word = this.plugin.suggestions.suggestionToWrongText(suggestion, blockID)
|
||||||
await Settings.addToDictionary(word, this.plugin.settingsUtil)
|
await Settings.addToDictionary(word, this.plugin.settingsUtil)
|
||||||
showMessage(this.plugin.i18nx.textMenu.addedToDictionary + word, 5000, 'info')
|
showMessage(this.plugin.i18nx.textMenu.addedToDictionary + word, 5000, 'info')
|
||||||
await this.plugin.suggestions.renderSuggestions(blockID)
|
await this.plugin.suggestions.renderSuggestions(blockID)
|
||||||
|
|
@ -72,7 +71,7 @@ export class Menus {
|
||||||
icon: 'info',
|
icon: 'info',
|
||||||
label: this.plugin.i18nx.docMenu.documentStatus,
|
label: this.plugin.i18nx.docMenu.documentStatus,
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const settings = await ProtyleHelpers.getDocumentSettings(docID, this.plugin.settingsUtil.get('enabledByDefault'), this.plugin.settingsUtil.get('defaultLanguage'))
|
const settings = await ProtyleHelper.getDocumentSettings(docID, this.plugin.settingsUtil.get('enabledByDefault'), this.plugin.settingsUtil.get('defaultLanguage'))
|
||||||
if(settings == null) {
|
if(settings == null) {
|
||||||
void this.plugin.analytics.sendEvent('docmenu-click-info-notebook');
|
void this.plugin.analytics.sendEvent('docmenu-click-info-notebook');
|
||||||
showMessage(this.plugin.i18nx.errors.notImplementedNotebookSettings, 5000, 'info')
|
showMessage(this.plugin.i18nx.errors.notImplementedNotebookSettings, 5000, 'info')
|
||||||
|
|
@ -93,7 +92,7 @@ export class Menus {
|
||||||
click: async () => {
|
click: async () => {
|
||||||
void this.plugin.analytics.sendEvent('docmenu-click-toggle');
|
void this.plugin.analytics.sendEvent('docmenu-click-toggle');
|
||||||
const attrs = await getBlockAttrs(docID)
|
const attrs = await getBlockAttrs(docID)
|
||||||
const settings = await ProtyleHelpers.getDocumentSettings(docID, this.plugin.settingsUtil.get('enabledByDefault'), this.plugin.settingsUtil.get('defaultLanguage'))
|
const settings = await ProtyleHelper.getDocumentSettings(docID, this.plugin.settingsUtil.get('enabledByDefault'), this.plugin.settingsUtil.get('defaultLanguage'))
|
||||||
if(settings == null) {
|
if(settings == null) {
|
||||||
void this.plugin.analytics.sendEvent('docmenu-click-info-notebook');
|
void this.plugin.analytics.sendEvent('docmenu-click-info-notebook');
|
||||||
showMessage(this.plugin.i18nx.errors.notImplementedNotebookSettings, 5000, 'info')
|
showMessage(this.plugin.i18nx.errors.notImplementedNotebookSettings, 5000, 'info')
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
import {getBlockAttrs} from "@/api";
|
import {getBlockAttrs} from "@/api";
|
||||||
import SpellCheckPlugin from "@/index";
|
import SpellCheckPlugin from "@/index";
|
||||||
|
|
||||||
export class ProtyleHelpers {
|
export class ProtyleHelper {
|
||||||
|
|
||||||
// We shouldn't use JavaScript elements to get and set data in blocks, but the kernel API is noticeably too slow for this.
|
private contentElement: Element
|
||||||
|
|
||||||
|
constructor(contentElement?: Element) {
|
||||||
|
this.contentElement = contentElement || document.documentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We shouldn't use JavaScript elements to get data in blocks, but the kernel API is noticeably too slow for this.
|
||||||
// We must try to keep the dependency to the HTML to a minimum.
|
// We must try to keep the dependency to the HTML to a minimum.
|
||||||
|
|
||||||
// doesn't use kernel API, so it's faster
|
// doesn't use kernel API, so it's faster
|
||||||
public static fastGetBlockElement(blockID: string): Element {
|
public fastGetBlockElement(blockID: string): Element {
|
||||||
const wrapper = Array.from(
|
const wrapper = Array.from(
|
||||||
document.querySelectorAll(`div[data-node-id="${blockID}"]`)
|
this.contentElement.querySelectorAll(`div[data-node-id="${blockID}"]`)
|
||||||
).find(el =>
|
).find(el =>
|
||||||
!el.closest('.protyle-wysiwyg__embed') // true = not inside that class
|
!el.closest('.protyle-wysiwyg__embed') // true = not inside that class
|
||||||
);
|
);
|
||||||
|
|
@ -17,16 +23,16 @@ export class ProtyleHelpers {
|
||||||
return wrapper?.querySelector(':scope > [contenteditable="true"]') ?? null;
|
return wrapper?.querySelector(':scope > [contenteditable="true"]') ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fastGetBlockHTML(blockID: string): string {
|
public fastGetBlockHTML(blockID: string): string {
|
||||||
return this.fastGetBlockElement(blockID).innerHTML
|
return this.fastGetBlockElement(blockID).innerHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fastGetBlockText(blockID: string): string {
|
public fastGetBlockText(blockID: string): string {
|
||||||
return this.fastGetBlockElement(blockID)?.textContent
|
return this.fastGetBlockElement(blockID)?.textContent
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fastGetTitleElement(docID: string) {
|
public fastGetTitleElement() {
|
||||||
const container = document.querySelector(`div.protyle-title.protyle-wysiwyg--attr[data-node-id="${docID}"]`);
|
const container = this.contentElement.querySelector(`div.protyle-title.protyle-wysiwyg--attr`); // [data-node-id="${docID}"]
|
||||||
if (!container) return null;
|
if (!container) return null;
|
||||||
return container.querySelector('div.protyle-title__input[contenteditable="true"]');
|
return container.querySelector('div.protyle-title__input[contenteditable="true"]');
|
||||||
}
|
}
|
||||||
|
|
@ -57,9 +63,16 @@ export class ProtyleHelpers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isProtyleReady(docID: string): boolean {
|
public getBlockElements(): Element[] {
|
||||||
const protyleTitleContainer = document.querySelector(`div[class="protyle-title protyle-wysiwyg--attr"]`)
|
const allElements: Set<Element> = new Set();
|
||||||
return protyleTitleContainer.getAttribute('data-node-id') == docID
|
this.contentElement.querySelectorAll('[data-node-id]').forEach(el => {
|
||||||
|
allElements.add(el);
|
||||||
|
});
|
||||||
|
return Array.from(allElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toNode(): Node {
|
||||||
|
return this.contentElement
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,28 +1,31 @@
|
||||||
import {ProtyleHelpers} from "@/protyleHelpers";
|
import {ProtyleHelper} from "@/protyleHelper";
|
||||||
|
|
||||||
export class SpellCheckerUI {
|
export class SpellCheckerUI {
|
||||||
|
|
||||||
private readonly blockID: string;
|
private readonly blockID: string;
|
||||||
private readonly docID: string;
|
private readonly protyle: ProtyleHelper;
|
||||||
private block: HTMLElement;
|
private block: HTMLElement;
|
||||||
private overlay: HTMLElement;
|
private overlay: HTMLElement;
|
||||||
|
|
||||||
constructor(blockID: string, docID: string) {
|
constructor(blockID: string, protyle: ProtyleHelper) {
|
||||||
this.blockID = blockID;
|
this.blockID = blockID;
|
||||||
this.docID = docID;
|
this.protyle = protyle;
|
||||||
this.setBlock()
|
this.setBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
private setBlock() {
|
private setBlock() {
|
||||||
|
|
||||||
this.block = <HTMLElement>ProtyleHelpers.fastGetBlockElement(this.blockID)
|
this.block = <HTMLElement>this.protyle.fastGetBlockElement(this.blockID)
|
||||||
let overlay = <HTMLElement>ProtyleHelpers.fastGetOverlayElement(this.blockID)
|
if(this.block == null) {
|
||||||
|
throw new Error(`Block ${this.blockID} not found`);
|
||||||
|
}
|
||||||
|
let overlay = <HTMLElement>ProtyleHelper.fastGetOverlayElement(this.blockID)
|
||||||
|
|
||||||
if(overlay == null) {
|
if(overlay == null) {
|
||||||
this.overlay = document.createElement('div')
|
this.overlay = document.createElement('div')
|
||||||
this.overlay.className = 'underline-overlay';
|
this.overlay.className = 'underline-overlay';
|
||||||
this.overlay.setAttribute('for-block-id', this.blockID)
|
this.overlay.setAttribute('for-block-id', this.blockID)
|
||||||
const protyleTitle = ProtyleHelpers.fastGetTitleElement(this.docID)
|
const protyleTitle = this.protyle.fastGetTitleElement()
|
||||||
protyleTitle?.append(this.overlay)
|
protyleTitle?.append(this.overlay)
|
||||||
}else{
|
}else{
|
||||||
if(this.overlay == null) {
|
if(this.overlay == null) {
|
||||||
|
|
@ -144,7 +147,7 @@ export class SpellCheckerUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
let overlay = <HTMLElement>ProtyleHelpers.fastGetOverlayElement(this.blockID)
|
let overlay = <HTMLElement>ProtyleHelper.fastGetOverlayElement(this.blockID)
|
||||||
overlay?.remove();
|
overlay?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {ProtyleHelpers} from "@/protyleHelpers";
|
import {ProtyleHelper} from "@/protyleHelper";
|
||||||
import {Settings} from "@/settings";
|
import {Settings} from "@/settings";
|
||||||
import {getChildBlocks, updateBlock} from "@/api";
|
import {updateBlock} from "@/api";
|
||||||
import {SpellCheckerUI} from "@/spellCheckerUI";
|
import {SpellCheckerUI} from "@/spellCheckerUI";
|
||||||
import {showMessage} from "siyuan";
|
import {showMessage} from "siyuan";
|
||||||
import SpellCheckPlugin from "@/index";
|
import SpellCheckPlugin from "@/index";
|
||||||
|
|
@ -8,7 +8,9 @@ import {Suggestion} from "@/spellChecker";
|
||||||
|
|
||||||
interface StoredBlock {
|
interface StoredBlock {
|
||||||
spellChecker: SpellCheckerUI;
|
spellChecker: SpellCheckerUI;
|
||||||
suggestions: Suggestion[];
|
language: string;
|
||||||
|
suggestions: Suggestion[] | null;
|
||||||
|
protyle: ProtyleHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlockStorage = Record<string, StoredBlock>;
|
type BlockStorage = Record<string, StoredBlock>;
|
||||||
|
|
@ -18,10 +20,6 @@ export class SuggestionEngine {
|
||||||
private blockStorage: BlockStorage = {};
|
private blockStorage: BlockStorage = {};
|
||||||
private plugin: SpellCheckPlugin;
|
private plugin: SpellCheckPlugin;
|
||||||
|
|
||||||
public documentID: string;
|
|
||||||
public documentEnabled: boolean = false;
|
|
||||||
public documentLanguage: string = 'auto';
|
|
||||||
|
|
||||||
constructor(plugin: SpellCheckPlugin) {
|
constructor(plugin: SpellCheckPlugin) {
|
||||||
this.plugin = plugin
|
this.plugin = plugin
|
||||||
}
|
}
|
||||||
|
|
@ -36,72 +34,79 @@ export class SuggestionEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async discoverBlocks(blockID: string) {
|
public getProtyle(blockID: string) {
|
||||||
const children = await getChildBlocks(blockID)
|
if(!(blockID in this.blockStorage)) { return null }
|
||||||
if(children.length == 0) {
|
return this.blockStorage[blockID].protyle
|
||||||
if(!(blockID in this.blockStorage)) {
|
|
||||||
const spellChecker = new SpellCheckerUI(blockID, this.documentID)
|
|
||||||
this.blockStorage[blockID] = {
|
|
||||||
spellChecker: spellChecker,
|
|
||||||
suggestions: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
for (const child of children) {
|
|
||||||
await this.discoverBlocks(child.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async forAllBlocksSuggest(docID: string, suggest: boolean, render: boolean, remove: boolean) {
|
public async storeBlocks(protyle: ProtyleHelper, documentLanguage: string) {
|
||||||
if(!this.documentEnabled) { return }
|
const blocks = protyle.getBlockElements()
|
||||||
if(suggest) {
|
blocks.forEach(block => {
|
||||||
await this.discoverBlocks(docID) // updates this.blockStorage
|
const blockID = ProtyleHelper.getNodeId(block)
|
||||||
}
|
if(!blockID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(!(blockID in this.blockStorage)) {
|
||||||
|
try {
|
||||||
|
const spellChecker = new SpellCheckerUI(blockID, protyle)
|
||||||
|
this.blockStorage[blockID] = {
|
||||||
|
spellChecker: spellChecker,
|
||||||
|
language: documentLanguage,
|
||||||
|
suggestions: null,
|
||||||
|
protyle: protyle
|
||||||
|
}
|
||||||
|
}catch (_) {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async forAllBlocksSuggest(suggest: boolean = false, render: boolean = true) {
|
||||||
const blockPromises = Object.keys(this.blockStorage).map(async (blockID) => {
|
const blockPromises = Object.keys(this.blockStorage).map(async (blockID) => {
|
||||||
if(suggest) {
|
if(!(blockID in this.blockStorage)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(suggest && this.blockStorage[blockID].suggestions == null) {
|
||||||
await this.suggestForBlock(blockID)
|
await this.suggestForBlock(blockID)
|
||||||
}
|
}
|
||||||
if(render) {
|
if(render) {
|
||||||
await this.renderSuggestions(blockID)
|
await this.renderSuggestions(blockID)
|
||||||
}
|
}
|
||||||
if(remove) {
|
|
||||||
await this.removeSuggestionsAndRender(blockID)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
await Promise.all(blockPromises);
|
await Promise.all(blockPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async suggestAndRender(blockID: string) {
|
public async suggestAndRender(blockID: string) {
|
||||||
if(!this.documentEnabled) { return }
|
|
||||||
await this.suggestForBlock(blockID)
|
await this.suggestForBlock(blockID)
|
||||||
await this.renderSuggestions(blockID)
|
await this.renderSuggestions(blockID)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async suggestForBlock(blockID: string) {
|
public async suggestForBlock(blockID: string) {
|
||||||
|
|
||||||
let suggestions: Suggestion[]
|
if(!(blockID in this.blockStorage)) {
|
||||||
const text = ProtyleHelpers.fastGetBlockText(blockID)
|
|
||||||
if(text == null || !this.documentEnabled) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(!(blockID in this.blockStorage)) {
|
const thisBlock = this.blockStorage[blockID]
|
||||||
await this.discoverBlocks(blockID)
|
thisBlock.suggestions = [] // we change from null so that it doesn't run again in forAllBlocksSuggest if we're waiting for the spell checker
|
||||||
return this.suggestForBlock(blockID)
|
|
||||||
|
let suggestions: Suggestion[]
|
||||||
|
const text = thisBlock.protyle.fastGetBlockText(blockID)
|
||||||
|
if(text == null || text == '') {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.plugin.settingsUtil.get('offline')) {
|
if(this.plugin.settingsUtil.get('offline')) {
|
||||||
suggestions = await this.plugin.offlineSpellChecker.check(text, [this.documentLanguage])
|
suggestions = await this.plugin.offlineSpellChecker.check(text, [thisBlock.language])
|
||||||
|
thisBlock.suggestions = suggestions
|
||||||
}else{
|
}else{
|
||||||
try {
|
try {
|
||||||
suggestions = await this.plugin.onlineSpellChecker.check(text, [this.documentLanguage])
|
suggestions = await this.plugin.onlineSpellChecker.check(text, [thisBlock.language])
|
||||||
|
thisBlock.suggestions = suggestions
|
||||||
}catch (_) {
|
}catch (_) {
|
||||||
showMessage(this.plugin.i18nx.errors.checkServer, 5000, 'error')
|
showMessage(this.plugin.i18nx.errors.checkServer, 5000, 'error')
|
||||||
|
thisBlock.suggestions = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blockStorage[blockID].suggestions = suggestions
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async removeSuggestionsAndRender(blockID: string) {
|
public async removeSuggestionsAndRender(blockID: string) {
|
||||||
|
|
@ -109,19 +114,35 @@ export class SuggestionEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async renderSuggestions(blockID: string) {
|
public async renderSuggestions(blockID: string) {
|
||||||
if(!(blockID in this.blockStorage) || !this.documentEnabled) {
|
|
||||||
|
if(!(blockID in this.blockStorage)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.blockStorage[blockID].spellChecker.clearUnderlines()
|
const thisBlock = this.blockStorage[blockID]
|
||||||
this.blockStorage[blockID].suggestions.forEach(suggestion => {
|
if(!document.contains(thisBlock.protyle.toNode())) {
|
||||||
if(!Settings.isInCustomDictionary(SuggestionEngine.suggestionToWrongText(suggestion, blockID), this.plugin.settingsUtil)) {
|
delete this.blockStorage[blockID]
|
||||||
this.blockStorage[blockID].spellChecker.highlightCharacterRange(suggestion.offset, suggestion.offset + suggestion.length)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
thisBlock.spellChecker.clearUnderlines()
|
||||||
|
|
||||||
|
thisBlock.suggestions?.forEach(suggestion => {
|
||||||
|
if(!Settings.isInCustomDictionary(this.suggestionToWrongText(suggestion, blockID), this.plugin.settingsUtil)) {
|
||||||
|
try {
|
||||||
|
thisBlock.spellChecker.highlightCharacterRange(suggestion.offset, suggestion.offset + suggestion.length)
|
||||||
|
}catch (_) {
|
||||||
|
delete this.blockStorage[blockID]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static suggestionToWrongText(suggestion: Suggestion, blockID: string): string {
|
public suggestionToWrongText(suggestion: Suggestion, blockID: string): string {
|
||||||
const blockTxt = ProtyleHelpers.fastGetBlockText(blockID)
|
if(!(blockID in this.blockStorage)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const blockTxt = this.blockStorage[blockID].protyle.fastGetBlockText(blockID)
|
||||||
return blockTxt.slice(suggestion.offset, suggestion.offset + suggestion.length)
|
return blockTxt.slice(suggestion.offset, suggestion.offset + suggestion.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,7 +187,7 @@ export class SuggestionEngine {
|
||||||
console.log("dbg " + blockID + ' ' + suggestionNumber + ' ' + correctionNumber)
|
console.log("dbg " + blockID + ' ' + suggestionNumber + ' ' + correctionNumber)
|
||||||
console.log(this.blockStorage)
|
console.log(this.blockStorage)
|
||||||
const suggestion = this.blockStorage[blockID].suggestions[suggestionNumber]
|
const suggestion = this.blockStorage[blockID].suggestions[suggestionNumber]
|
||||||
const rich = ProtyleHelpers.fastGetBlockHTML(blockID)
|
const rich = new ProtyleHelper().fastGetBlockHTML(blockID)
|
||||||
const fixedOffset = this.adjustIndexForTags(rich, suggestion.offset)
|
const fixedOffset = this.adjustIndexForTags(rich, suggestion.offset)
|
||||||
const newStr = rich.slice(0, fixedOffset) + suggestion.replacements[correctionNumber] + rich.slice(fixedOffset + suggestion.length)
|
const newStr = rich.slice(0, fixedOffset) + suggestion.replacements[correctionNumber] + rich.slice(fixedOffset + suggestion.length)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue