obsidian#Chooser TypeScript Examples

The following examples show how to use obsidian#Chooser. 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: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
updateSuggestions(query: string, chooser: Chooser<AnySuggestion>): boolean {
    let handled = false;
    const {
      exKeymap,
      app: {
        workspace: { activeLeaf },
      },
    } = this;

    const activeSugg = ModeHandler.getActiveSuggestion(chooser);
    const inputInfo = this.determineRunMode(query, activeSugg, activeLeaf);

    const { mode } = inputInfo;
    exKeymap.updateKeymapForMode(mode);

    if (mode !== Mode.Standard) {
      if (mode === Mode.HeadingsList && inputInfo.parsedCommand().parsedInput?.length) {
        // if headings mode and user is typing a query, delay getting suggestions
        this.debouncedGetSuggestions(inputInfo, chooser);
      } else {
        this.getSuggestions(inputInfo, chooser);
      }

      handled = true;
    }

    return handled;
  }
Example #2
Source File: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
private getSuggestions(inputInfo: InputInfo, chooser: Chooser<AnySuggestion>): void {
    this.inputInfo = inputInfo;
    const { mode } = inputInfo;

    chooser.setSuggestions([]);

    const suggestions = this.getHandler(mode).getSuggestions(inputInfo);

    chooser.setSuggestions(suggestions);
    ModeHandler.setActiveSuggestion(mode, chooser);
  }
Example #3
Source File: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
private static setActiveSuggestion(mode: Mode, chooser: Chooser<AnySuggestion>): void {
    // only symbol mode currently sets an active selection
    if (mode === Mode.SymbolList) {
      const index = chooser.values
        .filter((v): v is SymbolSuggestion => isSymbolSuggestion(v))
        .findIndex((v) => v.item.isSelected);

      if (index !== -1) {
        chooser.setSelectedItem(index, true);
      }
    }
  }
Example #4
Source File: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
private static getActiveSuggestion(chooser: Chooser<AnySuggestion>): AnySuggestion {
    let activeSuggestion: AnySuggestion = null;

    if (chooser?.values) {
      activeSuggestion = chooser.values[chooser.selectedItem];
    }

    return activeSuggestion;
  }
Example #5
Source File: switcherPlus.test.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
protected chooser: Chooser<AnySuggestion>;
Example #6
Source File: switcherPlus.test.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
mockChooser = mock<Chooser<AnySuggestion>>()
Example #7
Source File: keymap.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
constructor(
    private scope: Scope,
    private chooser: Chooser<AnySuggestion>,
    private modalContainerEl: HTMLElement,
  ) {
    this.registerBindings(scope);
  }
Example #8
Source File: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
setSessionOpenMode(mode: Mode, chooser: Chooser<AnySuggestion>): void {
    this.reset();
    chooser?.setSuggestions([]);

    if (mode !== Mode.Standard) {
      this.sessionOpenModeString = this.getHandler(mode).commandString;
    }
  }
Example #9
Source File: modeHandler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
private debouncedGetSuggestions: Debouncer<[InputInfo, Chooser<AnySuggestion>]>;
Example #10
Source File: sharedTypes.ts    From obsidian-switcher-plus with GNU General Public License v3.0 5 votes vote down vote up
protected chooser: Chooser<AnySuggestion>;
Example #11
Source File: keymap.test.ts    From obsidian-switcher-plus with GNU General Public License v3.0 4 votes vote down vote up
describe('keymap', () => {
  const mockScope = mock<Scope>();
  const mockChooser = mock<Chooser<AnySuggestion>>();
  const mockModalContainer = mock<HTMLElement>();

  describe('isOpen property', () => {
    let sut: Keymap;

    beforeAll(() => {
      sut = new Keymap(mockScope, mockChooser, mockModalContainer);
    });

    it('should save the value provided for isOpen', () => {
      sut.isOpen = true;
      const result = sut.isOpen;
      expect(result).toBe(true);
    });
  });

  describe('Next/Previous keyboard navigation', () => {
    beforeEach(() => {
      mockReset(mockScope);
      mockReset(mockChooser);
    });

    it('should register ctrl-n/p for navigating forward/backward', () => {
      new Keymap(mockScope, null, null);

      expect(mockScope.register).toHaveBeenCalledWith(
        expect.arrayContaining(['Ctrl']),
        'n',
        expect.anything(),
      );

      expect(mockScope.register).toHaveBeenCalledWith(
        expect.arrayContaining(['Ctrl']),
        'p',
        expect.anything(),
      );
    });

    test('when Open, it should change the selected item with ctrl-n/p keyboard navigation', () => {
      const selectedIndex = 1;
      const navPairs: Record<string, KeymapEventListener> = {
        n: null,
        p: null,
      };

      mockScope.register.mockImplementation((_m, key, func) => {
        if (key in navPairs) {
          navPairs[key] = func;
        }

        return null;
      });

      mockChooser.selectedItem = selectedIndex;

      const sut = new Keymap(mockScope, mockChooser, null);
      sut.isOpen = true; // here

      navPairs.n(mock<KeyboardEvent>(), mock<KeymapContext>({ key: 'n' }));
      navPairs.p(mock<KeyboardEvent>(), mock<KeymapContext>({ key: 'p' }));

      expect(mockChooser.setSelectedItem).toHaveBeenCalledWith(selectedIndex + 1, true);
      expect(mockChooser.setSelectedItem).toHaveBeenCalledWith(selectedIndex - 1, true);
    });

    test('when not Open, it should not change the selected item with ctrl-n/p keyboard navigation', () => {
      const selectedIndex = 1;
      const navPairs: Record<string, KeymapEventListener> = {
        n: null,
        p: null,
      };

      mockScope.register.mockImplementation((_m, key, func) => {
        if (key in navPairs) {
          navPairs[key] = func;
        }

        return null;
      });

      mockChooser.selectedItem = selectedIndex;

      const sut = new Keymap(mockScope, mockChooser, null);
      sut.isOpen = false; // here

      navPairs.n(mock<KeyboardEvent>(), mock<KeymapContext>({ key: 'n' }));
      navPairs.p(mock<KeyboardEvent>(), mock<KeymapContext>({ key: 'p' }));

      expect(mockChooser.setSelectedItem).not.toHaveBeenCalled();
      expect(mockChooser.setSelectedItem).not.toHaveBeenCalled();
    });
  });

  describe('updateKeymapForMode', () => {
    const selector = '.prompt-instructions';
    const mockInstructionsEl = mock<HTMLElement>();
    const mockMetaEnter = mock<Hotkey>({
      modifiers: ['Meta'],
      key: 'Enter',
    });
    const mockShiftEnter = mock<Hotkey>({
      modifiers: ['Shift'],
      key: 'Enter',
    });

    beforeEach(() => {
      mockReset(mockScope);
      mockReset(mockModalContainer);
    });

    it('should do nothing if the helper text (prompt instructions) element is not found', () => {
      const mockQuerySelector =
        mockModalContainer.querySelector.mockReturnValueOnce(null);

      const sut = new Keymap(mockScope, mockChooser, mockModalContainer);

      expect(() => sut.updateKeymapForMode(Mode.Standard)).not.toThrow();
      expect(mockQuerySelector).toHaveBeenCalledWith(selector);
    });

    it('should hide the helper text (prompt instructions) in non-standard modes', () => {
      const mockQuerySelector =
        mockModalContainer.querySelector.mockReturnValueOnce(mockInstructionsEl);

      const sut = new Keymap(mockScope, mockChooser, mockModalContainer);

      sut.updateKeymapForMode(Mode.EditorList);

      expect(mockQuerySelector).toHaveBeenCalledWith(selector);
      expect(mockInstructionsEl.style.display).toBe('none');
    });

    it('should show the helper text (prompt instructions) in standard modes', () => {
      const mockQuerySelector =
        mockModalContainer.querySelector.mockReturnValueOnce(mockInstructionsEl);

      const sut = new Keymap(mockScope, mockChooser, mockModalContainer);

      sut.updateKeymapForMode(Mode.Standard);

      expect(mockQuerySelector).toHaveBeenCalledWith(selector);
      expect(mockInstructionsEl.style.display).toBe('');
    });

    it('should not remove Enter hotkey without shift/meta modifier', () => {
      const mockEnter = mock<Hotkey>({
        modifiers: [],
        key: 'Enter',
      });

      mockScope.keys = [mockEnter];
      const sut = new Keymap(mockScope, null, mockModalContainer);

      sut.updateKeymapForMode(Mode.EditorList);

      expect(mockScope.keys).toContain(mockEnter);
    });

    it('should remove the shift-enter hotkey in non-standard modes', () => {
      mockScope.keys = [mockMetaEnter, mockShiftEnter];
      const sut = new Keymap(mockScope, null, mockModalContainer);

      sut.updateKeymapForMode(Mode.EditorList);

      expect(mockScope.keys).toHaveLength(1);
    });

    it('should keep the meta-enter hotkey registered in non-standard modes', () => {
      mockScope.keys = [mockMetaEnter, mockShiftEnter];
      const sut = new Keymap(mockScope, null, mockModalContainer);

      sut.updateKeymapForMode(Mode.StarredList);

      expect(mockScope.keys).toHaveLength(1);
      expect(mockScope.keys).toContain(mockMetaEnter);
    });

    it('should restore the shift/meta hotkey in standard mode', () => {
      mockScope.keys = [mockMetaEnter, mockShiftEnter];
      const sut = new Keymap(mockScope, null, mockModalContainer);

      // should first remove shift-enter in non-standard mode
      sut.updateKeymapForMode(Mode.EditorList);
      const extendedModeKeyCount = mockScope.keys.length;

      // should restore all hotkeys in standard mode
      sut.updateKeymapForMode(Mode.Standard);

      expect(extendedModeKeyCount).toBe(1);
      expect(mockScope.keys).toContain(mockMetaEnter);
      expect(mockScope.keys).toContain(mockShiftEnter);
    });
  });
});
Example #12
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();
      });
    });
  });
});