obsidian#debounce TypeScript Examples

The following examples show how to use obsidian#debounce. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: settings.ts    From folder-note-core with MIT License 6 votes vote down vote up
setIndexName = (containerEl: HTMLElement) => {
    new Setting(containerEl)
      .setName("Name for Index File")
      .setDesc("Set the note name to be recognized as index file for folders")
      .addText((text) => {
        const onChange = async (value: string) => {
          this.plugin.settings.indexName = value;
          this.plugin.trigger("folder-note:cfg-changed");
          await this.plugin.saveSettings();
        };
        text
          .setValue(this.plugin.settings.indexName)
          .onChange(debounce(onChange, 500, true));
      });
  };
Example #2
Source File: settings.ts    From folder-note-core with MIT License 6 votes vote down vote up
setTemplate = (containerEl: HTMLElement) => {
    new Setting(containerEl)
      .setName("Folder Note Template")
      .setDesc(
        createFragment((descEl) => {
          descEl.appendText("The template used to generate new folder note.");
          descEl.appendChild(document.createElement("br"));
          descEl.appendText("Supported placeholders:");
          descEl.appendChild(document.createElement("br"));
          descEl.appendText("{{FOLDER_NAME}} {{FOLDER_PATH}}");
        }),
      )
      .addTextArea((text) => {
        const onChange = async (value: string) => {
          this.plugin.settings.folderNoteTemplate = value;
          await this.plugin.saveSettings();
        };
        text
          .setValue(this.plugin.settings.folderNoteTemplate)
          .onChange(debounce(onChange, 500, true));
        text.inputEl.rows = 8;
        text.inputEl.cols = 30;
      });
  };
Example #3
Source File: MathModal.ts    From quickadd with MIT License 6 votes vote down vote up
private display() {
        this.containerEl.addClass('quickAddModal', 'qaMathModal')
        this.contentEl.empty();

        const mathDiv = this.contentEl.createDiv();
        mathDiv.className = "math math-block is-loaded";

        const tc = new TextAreaComponent(this.contentEl);
        tc.inputEl.style.width = "100%";
        tc.inputEl.style.height = "10rem";

        this.inputEl = tc.inputEl;

        tc.onChange(debounce(async value => await this.mathjaxLoop(mathDiv, value), 50));

        tc.inputEl.addEventListener('keydown', this.keybindListener);

        this.createButtonBar(this.contentEl.createDiv());
    }
Example #4
Source File: manager.ts    From better-word-count with MIT License 6 votes vote down vote up
constructor(
    statusBar: StatusBar,
    settings: BetterWordCountSettings,
    vault: Vault,
    metadataCache: MetadataCache
  ) {
    this.statusBar = statusBar;
    this.settings = settings;
    this.vault = vault;
    this.dataCollector = new DataCollector(vault, metadataCache);
    this.dataManager = new DataManager(vault, metadataCache);
    this.deboucer = debounce(
      (text: string) => this.updateStatusBar(text),
      20,
      false
    );
    this.expression = parse(this.settings.statusBarQuery);
  }
Example #5
Source File: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
constructor(
    private app: App,
    private settings: SwitcherPlusSettings,
    public exKeymap: Keymap,
  ) {
    const handlersByMode = new Map<Omit<Mode, 'Standard'>, Handler<AnySuggestion>>();
    this.handlersByMode = handlersByMode;
    handlersByMode.set(Mode.SymbolList, new SymbolHandler(app, settings));
    handlersByMode.set(Mode.WorkspaceList, new WorkspaceHandler(app, settings));
    handlersByMode.set(Mode.HeadingsList, new HeadingsHandler(app, settings));
    handlersByMode.set(Mode.EditorList, new EditorHandler(app, settings));
    handlersByMode.set(Mode.StarredList, new StarredHandler(app, settings));
    handlersByMode.set(Mode.CommandList, new CommandHandler(app, settings));
    handlersByMode.set(Mode.RelatedItemsList, new RelatedItemsHandler(app, settings));

    this.debouncedGetSuggestions = debounce(this.getSuggestions.bind(this), 400, true);
    this.reset();
  }
Example #6
Source File: PumlView.ts    From obsidian-plantuml with MIT License 6 votes vote down vote up
constructor(leaf: WorkspaceLeaf, plugin: PlantumlPlugin) {
        super(leaf);
        this.plugin = plugin;

        this.debounced = debounce(this.plugin.getProcessor().png, this.plugin.settings.debounce * 1000, true);

        this.sourceEl = this.contentEl.createDiv({cls: 'plantuml-source-view', attr: {'style': 'display: block'}});
        this.previewEl = this.contentEl.createDiv({cls: 'plantuml-preview-view', attr: {'style': 'display: none'}});

        const vault = (this.app.vault as any);

        if (vault.getConfig("showLineNumber")) {
            this.extensions.push(lineNumbers());
        }
        if(vault.getConfig("lineWrap")) {
            this.extensions.push(EditorView.lineWrapping);
        }

        this.editor = new EditorView({
            state: EditorState.create({
                extensions: this.extensions,
                doc: this.data,
            }),
            parent: this.sourceEl,
            dispatch: syncDispatch(views.length),
        });
        this.dispatchId = views.push(this.editor) - 1;

    }
Example #7
Source File: base.ts    From alx-folder-note with MIT License 6 votes vote down vote up
protected execQueue(queueName: string) {
    if (!Object.keys(this.queues).includes(queueName)) return;
    const debouncer =
      this.debouncers[queueName] ??
      (this.debouncers[queueName] = debounce(
        () => this._execQueue(queueName),
        200,
        true,
      ));
    debouncer();
  }
Example #8
Source File: debouncedProcessors.ts    From obsidian-plantuml with MIT License 6 votes vote down vote up
processor = async (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext, filetype: string, processor: (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => Promise<void>) => {

        el.createEl("h6", {text: "Generating PlantUML diagram", cls: "puml-loading"});

        if (el.dataset.plantumlDebounce) {
            const debounceId = el.dataset.plantumlDebounce;
            if (this.debounceMap.has(debounceId)) {
                await this.debounceMap.get(debounceId)(source, el, ctx);
            }
        } else {
            const func = debounce(processor, this.debounceTime, true);
            const uuid = uuidv4();
            el.dataset.plantumlDebouce = uuid;
            this.debounceMap.set(uuid, func);
            source = this.plugin.replacer.replaceNonBreakingSpaces(source);
            source = this.plugin.replacer.replaceLinks(source, this.plugin.replacer.getPath(ctx), filetype);
            source = this.plugin.settings.header + "\r\n" + source;
            await processor(source, el, ctx);
        }
    }
Example #9
Source File: manager.ts    From better-word-count with MIT License 6 votes vote down vote up
constructor(vault: Vault, metadataCache: MetadataCache) {
    this.vault = vault;
    this.metadataCache = metadataCache;
    this.collector = new DataCollector(vault, metadataCache);
    this.debounceChange = debounce(
      (file: TFile, data: string) => this.change(file, data),
      1000,
      false
    );

    this.vault.adapter.exists(".vault-stats").then(async (exists) => {
      if (!exists) {
        this.vault.adapter.write(".vault-stats", "{}");
      }

      this.history = Object.assign(
        JSON.parse(await this.vault.adapter.read(".vault-stats"))
      );

      this.updateToday();
      this.update();
    });
  }
Example #10
Source File: debouncedProcessors.ts    From obsidian-plantuml with MIT License 5 votes vote down vote up
constructor(plugin: PlantumlPlugin) {
        this.plugin = plugin;
        const debounceTime = plugin.settings.debounce;
        this.debounceTime = debounceTime * this.SECONDS_TO_MS_FACTOR;
    }
Example #11
Source File: main.ts    From obsidian-dictionary with GNU Affero General Public License v3.0 5 votes vote down vote up
// Open the synonym popover if a word is selected
    // This is debounced to handle double clicks
    handlePointerUp = debounce(
        () => {

            const activeLeaf = this.app.workspace.activeLeaf;

            if (activeLeaf?.view instanceof MarkdownView) {
                const view = activeLeaf.view;

                if (view.getMode() === 'source') {
                    const editor = view.editor;
                    const selection = editor.getSelection();

                    // Return early if we don't have anything selected, or if
                    // multiple words are selected
                    if (!selection || /\s/.test(selection)) return;

                    const cursor = editor.getCursor('from');
                    const line = editor.getLine(cursor.line);

                    let coords: Coords;

                    // Get the cursor position using the appropriate CM5 or CM6 interface
                    if ((editor as any).cursorCoords) {
                        coords = (editor as any).cursorCoords(true, 'window');
                    } else if ((editor as any).coordsAtPos) {
                        const offset = editor.posToOffset(cursor);
                        coords = (editor as any).cm.coordsAtPos?.(offset) ?? (editor as any).coordsAtPos(offset);
                    } else {
                        return;
                    }

                    this.synonymPopover = new SynonymPopover({
                        apiManager: this.manager,
                        advancedPoS: this.settings.advancedSynonymAnalysis,
                        coords,
                        cursor,
                        line,
                        selection,
                        onSelect: (replacement) => {
                            editor.replaceSelection(matchCasing(replacement, selection));
                        }
                    });
                }
            }
        },
        300,
        true
    );
Example #12
Source File: SuperchargedLinksSettingTab.ts    From obsidian_supercharged_links with MIT License 5 votes vote down vote up
constructor(app: App, plugin: SuperchargedLinks) {
		super(app, plugin);
		this.plugin = plugin;
		this.debouncedGenerate = debounce(this._generateSnippet, 1000, true);
	}
Example #13
Source File: EmbedDecoration.ts    From obsidian-plantuml with MIT License 5 votes vote down vote up
debouncedUpdate = debounce(this.updateAsyncDecorations, 100, true);
Example #14
Source File: index.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
mockDebounce = mockFn<typeof debounce>().mockImplementation()
Example #15
Source File: main.ts    From obsidian-dataview with MIT License 5 votes vote down vote up
private updateRefreshSettings() {
        this.debouncedRefresh = debounce(
            () => this.app.workspace.trigger("dataview:refresh-views"),
            this.settings.refreshInterval,
            true
        );
    }
Example #16
Source File: modeHandler.test.ts    From obsidian-switcher-plus with GNU General Public License v3.0 4 votes vote down vote up
describe('modeHandler', () => {
  let mockApp: MockProxy<App>;
  let settings: SwitcherPlusSettings;
  let sut: ModeHandler;

  beforeAll(() => {
    const mockInternalPlugins = mock<InternalPlugins>();
    mockInternalPlugins.getPluginById.mockImplementation((_id) => {
      return {
        enabled: true,
        instance: null,
      };
    });

    const mockWorkspace = mock<Workspace>({ activeLeaf: null });
    mockWorkspace.iterateAllLeaves.mockImplementation((_callback) => {
      //noop
    });

    mockApp = mock<App>({
      internalPlugins: mockInternalPlugins,
      workspace: mockWorkspace,
    });

    settings = new SwitcherPlusSettings(null);

    jest.spyOn(settings, 'editorListCommand', 'get').mockReturnValue(editorTrigger);
    jest.spyOn(settings, 'symbolListCommand', 'get').mockReturnValue(symbolTrigger);
    jest.spyOn(settings, 'workspaceListCommand', 'get').mockReturnValue(workspaceTrigger);
    jest.spyOn(settings, 'headingsListCommand', 'get').mockReturnValue(headingsTrigger);
    jest.spyOn(settings, 'starredListCommand', 'get').mockReturnValue(starredTrigger);
    jest.spyOn(settings, 'commandListCommand', 'get').mockReturnValue(commandTrigger);
    jest
      .spyOn(settings, 'relatedItemsListCommand', 'get')
      .mockReturnValue(relatedItemsTrigger);
  });

  describe('opening and closing the modal', () => {
    const mockKeymap = mock<Keymap>();

    beforeAll(() => {
      sut = new ModeHandler(mockApp, settings, mockKeymap);
    });

    test('onOpen() should open the keymap', () => {
      mockKeymap.isOpen = false;

      sut.onOpen();

      expect(mockKeymap.isOpen).toBe(true);
    });

    test('onClose() should close the keymap', () => {
      mockKeymap.isOpen = true;

      sut.onClose();

      expect(mockKeymap.isOpen).toBe(false);
    });
  });

  describe('Starting sessions with explicit command string', () => {
    let commandStringSpy: jest.SpyInstance;

    beforeAll(() => {
      sut = new ModeHandler(mockApp, settings, null);
    });

    describe('setSessionOpenMode', () => {
      it('should save the command string for any Ex modes', () => {
        commandStringSpy = jest
          .spyOn(EditorHandler.prototype, 'commandString', 'get')
          .mockReturnValueOnce(editorTrigger);

        sut.setSessionOpenMode(Mode.EditorList, null);

        expect(commandStringSpy).toHaveBeenCalled();

        commandStringSpy.mockRestore();
      });

      it('should not save the command string for any Ex modes', () => {
        const sSpy = jest.spyOn(SymbolHandler.prototype, 'commandString', 'get');
        const eSpy = jest.spyOn(EditorHandler.prototype, 'commandString', 'get');
        const wSpy = jest.spyOn(WorkspaceHandler.prototype, 'commandString', 'get');
        const hSpy = jest.spyOn(HeadingsHandler.prototype, 'commandString', 'get');
        const starredSpy = jest.spyOn(StarredHandler.prototype, 'commandString', 'get');
        const commandsSpy = jest.spyOn(CommandHandler.prototype, 'commandString', 'get');
        const relatedItemsSpy = jest.spyOn(
          RelatedItemsHandler.prototype,
          'commandString',
          'get',
        );

        sut.setSessionOpenMode(Mode.Standard, null);

        expect(sSpy).not.toHaveBeenCalled();
        expect(eSpy).not.toHaveBeenCalled();
        expect(wSpy).not.toHaveBeenCalled();
        expect(hSpy).not.toHaveBeenCalled();
        expect(starredSpy).not.toHaveBeenCalled();
        expect(commandsSpy).not.toHaveBeenCalled();
        expect(relatedItemsSpy).not.toHaveBeenCalled();

        sSpy.mockRestore();
        eSpy.mockRestore();
        wSpy.mockRestore();
        hSpy.mockRestore();
        starredSpy.mockRestore();
        commandsSpy.mockRestore();
        relatedItemsSpy.mockRestore();
      });
    });

    describe('insertSessionOpenModeCommandString', () => {
      const mockInputEl = mock<HTMLInputElement>();

      it('should insert the command string into the input element', () => {
        mockInputEl.value = '';
        sut.setSessionOpenMode(Mode.EditorList, null);

        sut.insertSessionOpenModeCommandString(mockInputEl);

        expect(mockInputEl).toHaveProperty('value', editorTrigger);
      });

      it('should do nothing when sessionOpenModeString is falsy', () => {
        mockInputEl.value = '';
        sut.setSessionOpenMode(Mode.Standard, null);

        sut.insertSessionOpenModeCommandString(mockInputEl);

        expect(mockInputEl).toHaveProperty('value', '');
      });
    });
  });

  describe('determineRunMode', () => {
    beforeAll(() => {
      sut = new ModeHandler(mockApp, settings, null);
    });

    it('should reset on falsy input', () => {
      const input: string = null;
      const inputInfo = sut.determineRunMode(input, null, null);

      expect(inputInfo.mode).toBe(Mode.Standard);
      expect(inputInfo.searchQuery).toBeFalsy();
      expect(inputInfo.inputText).toBe('');
    });

    describe('should identify unicode triggers', () => {
      test.each(unicodeInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ editorTrigger, symbolTrigger, input, expected: { mode, parsedInput } }) => {
          const s = new SwitcherPlusSettings(null);
          const mh = new ModeHandler(mockApp, s, null);
          let cmdSpy: jest.SpyInstance;

          if (editorTrigger) {
            cmdSpy = jest
              .spyOn(s, 'editorListCommand', 'get')
              .mockReturnValue(editorTrigger);
          }

          if (symbolTrigger) {
            cmdSpy = jest
              .spyOn(s, 'symbolListCommand', 'get')
              .mockReturnValue(symbolTrigger);
          }

          const leaf = makeLeaf();
          const es: EditorSuggestion = {
            item: leaf,
            file: leaf.view.file,
            type: 'editor',
            match: {
              score: 0,
              matches: [[0, 0]],
            },
          };

          const inputInfo = mh.determineRunMode(input, es, makeLeaf());
          const parsed = inputInfo.parsedCommand().parsedInput;

          expect(cmdSpy).toHaveBeenCalled();
          expect(inputInfo.mode).toBe(mode);
          expect(parsed).toBe(parsedInput);
        },
      );
    });

    describe('should parse as standard mode', () => {
      test(`with excluded active view for input: "${symbolTrigger} test"`, () => {
        const mockLeaf = makeLeaf();
        const mockView = mockLeaf.view as MockProxy<View>;
        const excludedType = 'foo';
        const input = `${symbolTrigger} test`;

        const excludeViewTypesSpy = jest
          .spyOn(settings, 'excludeViewTypes', 'get')
          .mockReturnValue([excludedType]);

        mockView.getViewType.mockReturnValue(excludedType);

        const inputInfo = sut.determineRunMode(input, null, mockLeaf);

        expect(inputInfo.mode).toBe(Mode.Standard);
        expect(inputInfo.inputText).toBe(input);
        expect(excludeViewTypesSpy).toHaveBeenCalled();
        expect(mockView.getViewType).toHaveBeenCalled();

        excludeViewTypesSpy.mockRestore();
      });

      test.each(standardModeInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ input, expected: { mode } }) => {
          const inputInfo = sut.determineRunMode(input, null, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);
        },
      );
    });

    describe('should parse as editor mode', () => {
      test.each(editorPrefixOnlyInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const inputInfo = sut.determineRunMode(input, null, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const editorCmd = inputInfo.parsedCommand();
          expect(editorCmd.isValidated).toBe(isValidated);
          expect(editorCmd.parsedInput).toBe(parsedInput);
        },
      );
    });

    describe('should parse as symbol mode', () => {
      test.each(symbolPrefixOnlyInputFixture)(
        'with ACTIVE LEAF for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const mockLeaf = makeLeaf();
          const inputInfo = sut.determineRunMode(input, null, mockLeaf);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const symbolCmd = inputInfo.parsedCommand() as SourcedParsedCommand;
          expect(symbolCmd.isValidated).toBe(isValidated);
          expect(symbolCmd.parsedInput).toBe(parsedInput);

          const { source } = symbolCmd;
          expect(source.isValidSource).toBe(true);
          expect(source.file).toBe(mockLeaf.view.file);
          expect(source.leaf).toBe(mockLeaf);
          expect(source.suggestion).toBe(null);
        },
      );

      test.each(symbolModeInputFixture)(
        'with FILE SUGGESTION for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const fileSuggestion: FileSuggestion = {
            file: new TFile(),
            type: 'file',
            match: {
              score: 0,
              matches: [[0, 0]],
            },
          };

          const inputInfo = sut.determineRunMode(input, fileSuggestion, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const symbolCmd = inputInfo.parsedCommand() as SourcedParsedCommand;
          expect(symbolCmd.isValidated).toBe(isValidated);
          expect(symbolCmd.parsedInput).toBe(parsedInput);

          const { source } = symbolCmd;
          expect(source.isValidSource).toBe(true);
          expect(source.file).toBe(fileSuggestion.file);
          expect(source.leaf).toBe(null);
          expect(source.suggestion).toBe(fileSuggestion);
        },
      );

      test.each(symbolModeInputFixture)(
        'with EDITOR SUGGESTION for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const leaf = makeLeaf();
          const editorSuggestion: EditorSuggestion = {
            item: leaf,
            file: leaf.view.file,
            type: 'editor',
            match: {
              score: 0,
              matches: [[0, 0]],
            },
          };

          mockApp.workspace.activeLeaf = leaf;

          const inputInfo = sut.determineRunMode(input, editorSuggestion, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const symbolCmd = inputInfo.parsedCommand() as SourcedParsedCommand;
          expect(symbolCmd.isValidated).toBe(isValidated);
          expect(symbolCmd.parsedInput).toBe(parsedInput);

          const { source } = symbolCmd;
          expect(source.isValidSource).toBe(true);
          expect(source.file).toBe(leaf.view.file);
          expect(source.leaf).toBe(leaf);
          expect(source.suggestion).toBe(editorSuggestion);

          mockApp.workspace.activeLeaf = null;
        },
      );
    });

    describe('should parse as workspace mode', () => {
      test.each(workspacePrefixOnlyInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const inputInfo = sut.determineRunMode(input, null, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const workspaceCmd = inputInfo.parsedCommand();
          expect(workspaceCmd.isValidated).toBe(isValidated);
          expect(workspaceCmd.parsedInput).toBe(parsedInput);
        },
      );
    });

    describe('should parse as starred mode', () => {
      test.each(starredPrefixOnlyInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const inputInfo = sut.determineRunMode(input, null, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const starredCmd = inputInfo.parsedCommand();
          expect(starredCmd.isValidated).toBe(isValidated);
          expect(starredCmd.parsedInput).toBe(parsedInput);
        },
      );
    });

    describe('should parse as headings mode', () => {
      test.each(headingsPrefixOnlyInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const inputInfo = sut.determineRunMode(input, null, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const headingsCmd = inputInfo.parsedCommand();
          expect(headingsCmd.isValidated).toBe(isValidated);
          expect(headingsCmd.parsedInput).toBe(parsedInput);
        },
      );
    });

    describe('should parse as command mode', () => {
      test.each(commandPrefixOnlyInputFixture)(
        'for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const inputInfo = sut.determineRunMode(input, null, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const commandCmd = inputInfo.parsedCommand();
          expect(commandCmd.isValidated).toBe(isValidated);
          expect(commandCmd.parsedInput).toBe(parsedInput);
        },
      );
    });

    describe('should parse as related mode', () => {
      test.each(relatedItemsPrefixOnlyInputFixture)(
        'with ACTIVE LEAF for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const mockLeaf = makeLeaf();
          const inputInfo = sut.determineRunMode(input, null, mockLeaf);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const cmd = inputInfo.parsedCommand() as SourcedParsedCommand;
          expect(cmd.isValidated).toBe(isValidated);
          expect(cmd.parsedInput).toBe(parsedInput);

          const { source } = cmd;
          expect(source.isValidSource).toBe(true);
          expect(source.file).toBe(mockLeaf.view.file);
          expect(source.leaf).toBe(mockLeaf);
          expect(source.suggestion).toBe(null);
        },
      );

      test.each(relatedItemsModeInputFixture)(
        'with FILE SUGGESTION for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const fileSuggestion: FileSuggestion = {
            file: new TFile(),
            type: 'file',
            match: {
              score: 0,
              matches: [[0, 0]],
            },
          };

          const inputInfo = sut.determineRunMode(input, fileSuggestion, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const cmd = inputInfo.parsedCommand() as SourcedParsedCommand;
          expect(cmd.isValidated).toBe(isValidated);
          expect(cmd.parsedInput).toBe(parsedInput);

          const { source } = cmd;
          expect(source.isValidSource).toBe(true);
          expect(source.file).toBe(fileSuggestion.file);
          expect(source.leaf).toBe(null);
          expect(source.suggestion).toBe(fileSuggestion);
        },
      );

      test.each(relatedItemsModeInputFixture)(
        'with EDITOR SUGGESTION for input: "$input" (array data index: $#)',
        ({ input, expected: { mode, isValidated, parsedInput } }) => {
          const leaf = makeLeaf();
          const editorSuggestion: EditorSuggestion = {
            item: leaf,
            file: leaf.view.file,
            type: 'editor',
            match: {
              score: 0,
              matches: [[0, 0]],
            },
          };

          mockApp.workspace.activeLeaf = leaf;

          const inputInfo = sut.determineRunMode(input, editorSuggestion, null);

          expect(inputInfo.mode).toBe(mode);
          expect(inputInfo.inputText).toBe(input);

          const cmd = inputInfo.parsedCommand() as SourcedParsedCommand;
          expect(cmd.isValidated).toBe(isValidated);
          expect(cmd.parsedInput).toBe(parsedInput);

          const { source } = cmd;
          expect(source.isValidSource).toBe(true);
          expect(source.file).toBe(leaf.view.file);
          expect(source.leaf).toBe(leaf);
          expect(source.suggestion).toBe(editorSuggestion);

          mockApp.workspace.activeLeaf = null;
        },
      );
    });
  });

  describe('managing suggestions', () => {
    const editorSugg: EditorSuggestion = {
      type: 'editor',
      file: null,
      item: makeLeaf(),
      match: null,
    };

    const symbolSugg: SymbolSuggestion = {
      type: 'symbol',
      file: null,
      item: {
        type: 'symbolInfo',
        symbol: getHeadings()[0],
        symbolType: SymbolType.Heading,
        isSelected: false,
      },
      match: null,
    };

    const workspaceSugg: WorkspaceSuggestion = {
      type: 'workspace',
      item: {
        type: 'workspaceInfo',
        id: 'foo',
      },
      match: null,
    };

    const headingsSugg: HeadingSuggestion = {
      type: 'heading',
      item: makeHeading('foo', 1),
      file: null,
      match: null,
    };

    const starredSugg: StarredSuggestion = {
      type: 'starred',
      file: new TFile(),
      item: makeFileStarredItem(),
      match: null,
    };

    const commandSugg: CommandSuggestion = {
      type: 'command',
      item: makeCommandItem(),
      match: null,
    };

    const relatedItemSugg: RelatedItemsSuggestion = {
      type: 'relatedItems',
      relationType: 'diskLocation',
      file: new TFile(),
      match: null,
    };

    beforeAll(() => {
      sut = new ModeHandler(mockApp, settings, mock<Keymap>());
    });

    describe('updateSuggestions', () => {
      const mockChooser = mock<Chooser<AnySuggestion>>();
      const mockSetSuggestion = mockChooser.setSuggestions.mockImplementation();
      let getSuggestionSpy: jest.SpyInstance;

      test('with falsy input (Standard mode), it should return not handled', () => {
        const results = sut.updateSuggestions(null, null);
        expect(results).toBe(false);
      });

      it('should debounce in Headings mode with filter text', () => {
        const validateCommandSpy = jest
          .spyOn(HeadingsHandler.prototype, 'validateCommand')
          .mockImplementation((inputInfo) => {
            inputInfo.mode = Mode.HeadingsList;
            const cmd = inputInfo.parsedCommand(Mode.HeadingsList);
            cmd.parsedInput = 'foo';
          });

        const mockDebouncedFn = jest.fn();
        const mockDebounce = debounce as jest.Mock;
        mockDebounce.mockImplementation(() => mockDebouncedFn);
        sut = new ModeHandler(mockApp, settings, mock<Keymap>());

        const results = sut.updateSuggestions(headingsTrigger, mockChooser);

        expect(results).toBe(true);
        expect(mockDebounce).toHaveBeenCalled();
        expect(mockDebouncedFn).toHaveBeenCalled();
        expect(validateCommandSpy).toHaveBeenCalled();

        validateCommandSpy.mockRestore();
        mockDebounce.mockReset();
      });

      it('should get suggestions for Editor Mode', () => {
        const expectedSuggestions = [editorSugg];
        getSuggestionSpy = jest
          .spyOn(EditorHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const results = sut.updateSuggestions(editorTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should get suggestions for Symbol Mode', () => {
        const expectedSuggestions = [symbolSugg];
        getSuggestionSpy = jest
          .spyOn(SymbolHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const validateCommandSpy = jest
          .spyOn(SymbolHandler.prototype, 'validateCommand')
          .mockImplementation((inputInfo) => {
            inputInfo.mode = Mode.SymbolList;
          });

        const mockSetSelectedItem = mockChooser.setSelectedItem.mockImplementation();
        mockChooser.values = expectedSuggestions;

        const results = sut.updateSuggestions(symbolTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(validateCommandSpy).toHaveBeenCalled();
        expect(mockSetSelectedItem).not.toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        validateCommandSpy.mockRestore();
        mockSetSelectedItem.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should set the active suggestion in Symbol Mode', () => {
        const symbolSugg2: SymbolSuggestion = {
          type: 'symbol',
          file: null,
          item: {
            type: 'symbolInfo',
            symbol: getHeadings()[0],
            symbolType: SymbolType.Heading,
            isSelected: true, // <-- here
          },
          match: null,
        };

        const expectedSuggestions = [symbolSugg2];
        getSuggestionSpy = jest
          .spyOn(SymbolHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const validateCommandSpy = jest
          .spyOn(SymbolHandler.prototype, 'validateCommand')
          .mockImplementation((inputInfo) => {
            inputInfo.mode = Mode.SymbolList;
          });

        const mockSetSelectedItem = mockChooser.setSelectedItem.mockImplementation();
        mockChooser.values = expectedSuggestions;

        const results = sut.updateSuggestions(symbolTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(validateCommandSpy).toHaveBeenCalled();
        expect(mockSetSelectedItem).toHaveBeenCalledWith(0, true); // <-- here
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        validateCommandSpy.mockRestore();
        mockSetSelectedItem.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should get suggestions for Workspace Mode', () => {
        const expectedSuggestions = [workspaceSugg];
        getSuggestionSpy = jest
          .spyOn(WorkspaceHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const results = sut.updateSuggestions(workspaceTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should get suggestions for Starred Mode', () => {
        const expectedSuggestions = [starredSugg];
        getSuggestionSpy = jest
          .spyOn(StarredHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const results = sut.updateSuggestions(starredTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should get suggestions for Headings Mode', () => {
        const expectedSuggestions = [headingsSugg];
        getSuggestionSpy = jest
          .spyOn(HeadingsHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const results = sut.updateSuggestions(headingsTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should get suggestions for Command Mode', () => {
        const expectedSuggestions = [commandSugg];
        getSuggestionSpy = jest
          .spyOn(CommandHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const results = sut.updateSuggestions(commandTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        mockSetSuggestion.mockReset();
      });

      it('should get suggestions for RelatedItems Mode', () => {
        const expectedSuggestions = [relatedItemSugg];
        getSuggestionSpy = jest
          .spyOn(RelatedItemsHandler.prototype, 'getSuggestions')
          .mockReturnValue(expectedSuggestions);

        const validateCommandSpy = jest
          .spyOn(RelatedItemsHandler.prototype, 'validateCommand')
          .mockImplementation((inputInfo) => {
            inputInfo.mode = Mode.RelatedItemsList;
          });

        const results = sut.updateSuggestions(relatedItemsTrigger, mockChooser);

        expect(results).toBe(true);
        expect(getSuggestionSpy).toHaveBeenCalled();
        expect(mockSetSuggestion).toHaveBeenLastCalledWith(expectedSuggestions);

        getSuggestionSpy.mockRestore();
        validateCommandSpy.mockRestore();
        mockSetSuggestion.mockReset();
      });
    });

    describe('renderSuggestions', () => {
      const mockParentEl = mock<HTMLElement>();
      let renderSuggestionSpy: jest.SpyInstance;

      it('should return false with falsy input', () => {
        const result = sut.renderSuggestion(null, null);
        expect(result).toBe(false);
      });

      it('should render suggestions for Editor Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(EditorHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(editorSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(editorSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });

      it('should render suggestions for Symbol Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(SymbolHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(symbolSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(symbolSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });

      it('should render suggestions for Headings Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(HeadingsHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(headingsSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(headingsSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });

      it('should render suggestions for Workspace Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(WorkspaceHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(workspaceSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(workspaceSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });

      it('should render suggestions for Starred Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(StarredHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(starredSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(starredSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });

      it('should render suggestions for Command Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(CommandHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(commandSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(commandSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });

      it('should render suggestions for RelatedItems Mode', () => {
        renderSuggestionSpy = jest
          .spyOn(RelatedItemsHandler.prototype, 'renderSuggestion')
          .mockImplementation();

        const result = sut.renderSuggestion(relatedItemSugg, mockParentEl);

        expect(result).toBe(true);
        expect(renderSuggestionSpy).toHaveBeenCalledWith(relatedItemSugg, mockParentEl);

        renderSuggestionSpy.mockRestore();
      });
    });

    describe('onchooseSuggestions', () => {
      const mockEvt = mock<MouseEvent>();
      let onChooseSuggestionSpy: jest.SpyInstance;

      it('should return false with falsy input', () => {
        const result = sut.onChooseSuggestion(null, null);
        expect(result).toBe(false);
      });

      it('should action suggestions for Editor Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(EditorHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(editorSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(editorSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });

      it('should action suggestions for Symbol Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(SymbolHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(symbolSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(symbolSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });

      it('should action suggestions for Headings Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(HeadingsHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(headingsSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(headingsSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });

      it('should action suggestions for Workspace Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(WorkspaceHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(workspaceSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(workspaceSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });

      it('should action suggestions for Starred Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(StarredHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(starredSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(starredSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });

      it('should action suggestions for Command Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(CommandHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(commandSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(commandSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });

      it('should action suggestions for RelatedItems Mode', () => {
        onChooseSuggestionSpy = jest
          .spyOn(RelatedItemsHandler.prototype, 'onChooseSuggestion')
          .mockImplementation();

        const result = sut.onChooseSuggestion(relatedItemSugg, mockEvt);

        expect(result).toBe(true);
        expect(onChooseSuggestionSpy).toHaveBeenCalledWith(relatedItemSugg, mockEvt);

        onChooseSuggestionSpy.mockRestore();
      });
    });
  });
});
Example #17
Source File: main.ts    From obsidian_supercharged_links with MIT License 4 votes vote down vote up
async onload(): Promise<void> {
		console.log('Supercharged links loaded');
		await this.loadSettings();


		this.settings.presetFields.forEach(prop => {
			const property = new Field()
			Object.assign(property, prop)
			this.initialProperties.push(property)
		})
		this.addSettingTab(new SuperchargedLinksSettingTab(this.app, this));
		this.registerMarkdownPostProcessor((el, ctx) => {
			updateElLinks(this.app, this, el, ctx)
		});

		// Plugins watching
		this.registerEvent(this.app.metadataCache.on('changed', debounce((_file) => {
			updateVisibleLinks(this.app, this);
			this.observers.forEach(([observer, type, own_class ]) => {
				const leaves = this.app.workspace.getLeavesOfType(type);
				leaves.forEach(leaf => {
					this.updateContainer(leaf.view.containerEl, this, own_class);
				})
			});
			// Debounced to prevent lag when writing
		}, 4500, true)));


		// Live preview
		const ext = Prec.lowest(buildCMViewPlugin(this.app, this.settings));
		this.registerEditorExtension(ext);

		this.observers = [];

		this.app.workspace.onLayoutReady(() => {
			this.initViewObservers(this);
			this.initModalObservers(this);
		});
		this.registerEvent(this.app.workspace.on("layout-change", () => this.initViewObservers(this)));

		this.addCommand({
			id: "field_options",
			name: "field options",
			hotkeys: [
				{
					modifiers: ["Alt"],
					key: 'O',
				},
			],
			callback: () => {
				const leaf = this.app.workspace.activeLeaf
				if (leaf.view instanceof MarkdownView && leaf.view.file) {
					const fieldsOptionsModal = new NoteFieldsCommandsModal(this.app, this, leaf.view.file)
					fieldsOptionsModal.open()
				}
			},
		});

		/* TODO : add a context menu for fileClass files to show the same options as in FileClassAttributeSelectModal*/
		this.addCommand({
			id: "fileClassAttr_options",
			name: "fileClass attributes options",
			hotkeys: [
				{
					modifiers: ["Alt"],
					key: 'P',
				},
			],
			callback: () => {
				const leaf = this.app.workspace.activeLeaf
				if (leaf.view instanceof MarkdownView && leaf.view.file && `${leaf.view.file.parent.path}/` == this.settings.classFilesPath) {
					const modal = new FileClassAttributeSelectModal(this, leaf.view.file)
					modal.open()
				} else {
					const notice = new Notice("This is not a fileClass", 2500)
				}
			},
		});

		new linkContextMenu(this)
	}
Example #18
Source File: main.ts    From obsidian-readwise with GNU General Public License v3.0 4 votes vote down vote up
async onload() {
    // @ts-ignore
    if (!this.app.isMobile) {
      this.statusBar = new StatusBar(this.addStatusBarItem());
      this.registerInterval(
        window.setInterval(() => this.statusBar.display(), 1000)
      );
    }
    await this.loadSettings();
    this.refreshBookExport = debounce(
      this.refreshBookExport.bind(this),
      800,
      true
    );

    this.refreshBookExport(this.settings.booksToRefresh);
    this.app.vault.on("delete", async (file) => {
      const bookId = this.settings.booksIDsMap[file.path];
      if (bookId) {
        await this.addBookToRefresh(bookId);
      }
      this.refreshBookExport();
      delete this.settings.booksIDsMap[file.path];
      this.saveSettings();
    });
    this.app.vault.on("rename", (file, oldPath) => {
      const bookId = this.settings.booksIDsMap[oldPath];
      if (!bookId) {
        return;
      }
      this.settings.booksIDsMap[file.path] = bookId;
      delete this.settings.booksIDsMap[oldPath];
      this.saveSettings();
    });
    if (this.settings.isSyncing) {
      if (this.settings.currentSyncStatusID) {
        await this.getExportStatus();
      } else {
        // we probably got some unhandled error...
        this.settings.isSyncing = false;
        await this.saveSettings();
      }
    }
    this.addCommand({
      id: 'readwise-official-sync',
      name: 'Sync your data now',
      callback: () => {
        this.startSync();
      }
    });
    this.addCommand({
      id: 'readwise-official-format',
      name: 'Customize formatting',
      callback: () => window.open(`${baseURL}/export/obsidian/preferences`)
    });
    this.addCommand({
      id: 'readwise-official-reimport-file',
      name: 'Delete and reimport this document',
      editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView) => {
        const activeFilePath = view.file.path;
        const isRWfile = activeFilePath in this.settings.booksIDsMap;
        if (checking) {
          return isRWfile;
        }
        if (this.settings.reimportShowConfirmation) {
          const modal = new Modal(view.app);
          modal.contentEl.createEl(
            'p',
            {
              'text': 'Warning: Proceeding will delete this file entirely (including any changes you made) ' +
                'and then reimport a new copy of your highlights from Readwise.'
            });
          const buttonsContainer = modal.contentEl.createEl('div', {"cls": "rw-modal-btns"});
          const cancelBtn = buttonsContainer.createEl("button", {"text": "Cancel"});
          const confirmBtn = buttonsContainer.createEl("button", {"text": "Proceed", 'cls': 'mod-warning'});
          const showConfContainer = modal.contentEl.createEl('div', {'cls': 'rw-modal-confirmation'});
          showConfContainer.createEl("label", {"attr": {"for": "rw-ask-nl"}, "text": "Don't ask me in the future"});
          const showConf = showConfContainer.createEl("input", {"type": "checkbox", "attr": {"name": "rw-ask-nl"}});
          showConf.addEventListener('change', (ev) => {
            // @ts-ignore
            this.settings.reimportShowConfirmation = !ev.target.checked;
            this.saveSettings();
          });
          cancelBtn.onClickEvent(() => {
            modal.close();
          });
          confirmBtn.onClickEvent(() => {
            this.reimportFile(view.app.vault, activeFilePath);
            modal.close();
          });
          modal.open();
        } else {
          this.reimportFile(view.app.vault, activeFilePath);
        }
      }
    });
    this.registerMarkdownPostProcessor((el, ctx) => {
      if (!ctx.sourcePath.startsWith(this.settings.readwiseDir)) {
        return;
      }
      let matches: string[];
      try {
        // @ts-ignore
        matches = [...ctx.getSectionInfo(el).text.matchAll(/__(.+)__/g)].map((a) => a[1]);
      } catch (TypeError) {
        // failed interaction with a Dataview element
        return;
      }
      const hypers = el.findAll("strong").filter(e => matches.contains(e.textContent));
      hypers.forEach(strongEl => {
        const replacement = el.createEl('span');
        while (strongEl.firstChild) {
          replacement.appendChild(strongEl.firstChild);
        }
        replacement.addClass("rw-hyper-highlight");
        strongEl.replaceWith(replacement);
      });
    });
    this.addSettingTab(new ReadwiseSettingTab(this.app, this));
    await this.configureSchedule();
    if (this.settings.token && this.settings.triggerOnLoad && !this.settings.isSyncing) {
      await this.saveSettings();
      await this.requestArchive(null, null, true);
    }
  }