obsidian#MarkdownView TypeScript Examples

The following examples show how to use obsidian#MarkdownView. 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: utils.ts    From obsidian-map-view with GNU General Public License v3.0 7 votes vote down vote up
export async function getEditor(
    app: App,
    leafToUse?: WorkspaceLeaf
): Promise<Editor> {
    let view =
        leafToUse && leafToUse.view instanceof MarkdownView
            ? leafToUse.view
            : app.workspace.getActiveViewOfType(MarkdownView);
    if (view) return view.editor;
    return null;
}
Example #2
Source File: utility.ts    From quickadd with MIT License 7 votes vote down vote up
export function appendToCurrentLine(toAppend: string, app: App) {
    try {
        const activeView = app.workspace.getActiveViewOfType(MarkdownView);

        if (!activeView) {
            log.logError(`unable to append '${toAppend}' to current line.`);
            return;
        }

        activeView.editor.replaceSelection(toAppend);
    } catch {
       log.logError(`unable to append '${toAppend}' to current line.`);
    }
}
Example #3
Source File: active-folder.ts    From alx-folder-note with MIT License 6 votes vote down vote up
private handleActiveLeafChange(leaf: WorkspaceLeaf | null) {
    let folder;
    if (
      leaf &&
      leaf.view instanceof MarkdownView &&
      (folder = this.fncApi.getFolderFromNote(leaf.view.file))
    ) {
      this.activeFolder = folder;
    } else {
      this.activeFolder = null;
    }
  }
Example #4
Source File: main.ts    From luhman-obsidian-plugin with GNU General Public License v3.0 6 votes vote down vote up
async makeNote(
    path: string,
    title: string,
    content: string,
    placeCursorAtStartOfContent: boolean
  ) {
    let app = this.app;
    let titleContent = "# " + title + "\n\n";
    let fullContent = titleContent + content;
    let file = await this.app.vault.create(path, fullContent);
    let active = app.workspace.getLeaf();
    if (active == null) {
      return;
    }

    await active.openFile(file);

    let editor = app.workspace.getActiveViewOfType(MarkdownView)?.editor;
    if (editor == null) {
      return;
    }

    if (placeCursorAtStartOfContent) {
      let position: EditorPosition = { line: 2, ch: 0 };
      editor.setCursor(position);
    } else {
      editor.exec("goEnd");
    }
  }
Example #5
Source File: main.ts    From obsidian-banners with MIT License 6 votes vote down vote up
// Helper to refresh views
  refreshViews() {
    // Reconfigure CM6 extensions and update Live Preview views
    this.extensions[0] = bannerDecorFacet.of(this.settings);
    this.extensions[1] = iconDecorFacet.of(this.settings);
    this.workspace.updateOptions();

    // Rerender Reading views
    this.workspace.getLeavesOfType('markdown').forEach((leaf) => {
      if (leaf.getViewState().state.mode.includes('preview')) {
        (leaf.view as MarkdownView).previewMode.rerender(true);
      }
    });

    // Refresh banners' drag state
    this.isDragModHeld();
  }
Example #6
Source File: commands.ts    From nldates-obsidian with MIT License 6 votes vote down vote up
export function insertMomentCommand(
  plugin: NaturalLanguageDates,
  date: Date,
  format: string
) {
  const { workspace } = plugin.app;
  const activeView = workspace.getActiveViewOfType(MarkdownView);

  if (activeView) {
    // The active view might not be a markdown view
    const editor = activeView.editor;
    editor.replaceSelection(window.moment(date).format(format));
  }
}
Example #7
Source File: main.ts    From obsidian-ics with MIT License 6 votes vote down vote up
async onload() {
		console.log('loading ics plugin');
		await this.loadSettings();
		this.addSettingTab(new ICSSettingsTab(this.app, this));
		this.addCommand({
			id: "import_events",
			name: "import events",
			hotkeys: [{
				modifiers: ["Alt", "Shift"],
				key: 'T',
			}, ],
			callback: async () => {
				const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
				const fileDate = getDateFromFile(activeView.file, "day").format("YYYY-MM-DD");
				var mdArray: string[] = [];

				for (const calendar in this.data.calendars) {
					const calendarSetting = this.data.calendars[calendar];
					console.log(calendarSetting);
					var icsArray: any[] = [];
					var icsArray = parseIcs(await request({
						url: calendarSetting.icsUrl
					}));
					const todayEvents = filterMatchingEvents(icsArray, fileDate);
					console.log(todayEvents);
	
					todayEvents.forEach((e) => {
						mdArray.push(`- [ ] ${moment(e.start).format("HH:mm")} ${calendarSetting.icsName} ${e.summary} ${e.location}`.trim());
					});
				}

				activeView.editor.replaceRange(mdArray.sort().join("\n"), activeView.editor.getCursor());
			}
		});
	}
Example #8
Source File: date-suggest.ts    From nldates-obsidian with MIT License 6 votes vote down vote up
selectSuggestion(suggestion: IDateCompletion, event: KeyboardEvent | MouseEvent): void {
    const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
    if (!activeView) {
      return;
    }

    const includeAlias = event.shiftKey;
    let dateStr = "";
    let makeIntoLink = this.plugin.settings.autosuggestToggleLink;

    if (suggestion.label.startsWith("time:")) {
      const timePart = suggestion.label.substring(5);
      dateStr = this.plugin.parseTime(timePart).formattedString;
      makeIntoLink = false;
    } else {
      dateStr = this.plugin.parseDate(suggestion.label).formattedString;
    }

    if (makeIntoLink) {
      dateStr = generateMarkdownLink(
        this.app,
        dateStr,
        includeAlias ? suggestion.label : undefined
      );
    }

    activeView.editor.replaceRange(dateStr, this.context.start, this.context.end);
  }
Example #9
Source File: linkAttributes.ts    From obsidian_supercharged_links with MIT License 6 votes vote down vote up
export function updateVisibleLinks(app: App, plugin: SuperchargedLinks) {
    const settings = plugin.settings;
    app.workspace.iterateRootLeaves((leaf) => {
        if (leaf.view instanceof MarkdownView && leaf.view.file) {
            const file: TFile = leaf.view.file;
            const cachedFile = app.metadataCache.getFileCache(file)
            if (cachedFile.links) {
                cachedFile.links.forEach((link: LinkCache) => {
                    const fileName = file.path.replace(/(.*).md/, "$1")
                    const dest = app.metadataCache.getFirstLinkpathDest(link.link, fileName)
                    if (dest) {
                        const new_props = fetchTargetAttributesSync(app, settings, dest, false)
                        const internalLinks = leaf.view.containerEl.querySelectorAll(`a.internal-link[href="${link.link}"]`)
                        internalLinks.forEach((internalLink: HTMLElement) => setLinkNewProps(internalLink, new_props))
                    }
                })
            }
        }
    })
}
Example #10
Source File: EditorCommand.ts    From quickadd with MIT License 6 votes vote down vote up
static getActiveMarkdownView(app: App): MarkdownView {
        const activeView = app.workspace.getActiveViewOfType(MarkdownView);

        if (!activeView) {
            log.logError("no active markdown view.");
            return;
        }

        return activeView;
    }
Example #11
Source File: main.ts    From quick_latex_obsidian with MIT License 6 votes vote down vote up
private addCasesBlock(editor: Editor) {
		const view = this.app.workspace.getActiveViewOfType(MarkdownView);
		if (!view) return;
		if (!this.settings.addCasesBlock_toggle) return;
		const selected_text = editor.getSelection()
		editor.replaceSelection(
			'\\begin{cases}\n' +
			selected_text +
			'\n\\end{cases}'
		);
		const position = editor.getCursor();
		editor.setCursor({ line: position.line - 1, ch: editor.getLine(position.line - 1).length })
	}
Example #12
Source File: handler.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
/**
   * Retrieves the position of the cursor, given that view is in a Mode that supports cursors.
   * @param  {View} view
   * @returns EditorPosition
   */
  getCursorPosition(view: View): EditorPosition {
    let cursor: EditorPosition = null;

    if (view?.getViewType() === 'markdown') {
      const md = view as MarkdownView;

      if (md.getMode() !== 'preview') {
        const { editor } = md;
        cursor = editor.getCursor('head');
      }
    }

    return cursor;
  }
Example #13
Source File: files.ts    From obsidian-checklist-plugin with MIT License 6 votes vote down vote up
navToFile = async (app: App, path: string, ev: MouseEvent, line?: number) => {
  path = ensureMdExtension(path)
  const file = getFileFromPath(app.vault, path)
  if (!file) return
  const leaf = isMetaPressed(ev) ? app.workspace.splitActiveLeaf() : app.workspace.getUnpinnedLeaf()
  await leaf.openFile(file)
  if (line) {
    app.workspace.getActiveViewOfType(MarkdownView)?.currentMode?.applyScroll(line)
  }
}
Example #14
Source File: main.ts    From quick_latex_obsidian with MIT License 6 votes vote down vote up
private addMatrixBlock(editor: Editor) {
		const view = this.app.workspace.getActiveViewOfType(MarkdownView);
		if (!view) return;
		if (!this.settings.addMatrixBlock_toggle) return;
		editor.replaceSelection(
			'\\begin{' + this.settings.addMatrixBlock_parameter + '}' +
			'\\end{' + this.settings.addMatrixBlock_parameter + '}'
		);
		const position = editor.getCursor();
		const retract_length = ('\\end{' + this.settings.addMatrixBlock_parameter + '}').length
		editor.setCursor({ line: position.line, ch: position.ch - retract_length })
	}
Example #15
Source File: main.ts    From obsidian-consistent-attachments-and-links with MIT License 6 votes vote down vote up
async collectAttachmentsCurrentNote(editor: Editor, view: MarkdownView) {
		let note = view.file;
		if (this.isPathIgnored(note.path)) {
			new Notice("Note path is ignored");
			return;
		}

		let result = await this.fh.collectAttachmentsForCachedNote(
			note.path,
			this.settings.attachmentsSubfolder,
			this.settings.deleteExistFilesWhenMoveNote);

		if (result && result.movedAttachments && result.movedAttachments.length > 0) {
			await this.lh.updateChangedPathsInNote(note.path, result.movedAttachments)
		}

		if (result.movedAttachments.length == 0)
			new Notice("No files found that need to be moved");
		else
			new Notice("Moved " + result.movedAttachments.length + " attachment" + (result.movedAttachments.length > 1 ? "s" : ""));
	}
Example #16
Source File: main.ts    From quick_latex_obsidian with MIT License 6 votes vote down vote up
private addAlignBlock(editor: Editor) {
		const view = this.app.workspace.getActiveViewOfType(MarkdownView);
		if (!view) return;
		if (!this.settings.addAlignBlock_toggle) return;
		const selected_text = editor.getSelection()
		editor.replaceSelection(
			'\\begin{' + this.settings.addAlignBlock_parameter + '}\n' +
			selected_text +
			'\n\\end{' + this.settings.addAlignBlock_parameter + '}'
		);
		const position = editor.getCursor();
		editor.setCursor({ line: position.line - 1, ch: editor.getLine(position.line - 1).length })
	}
Example #17
Source File: functions.ts    From obsidian-rss with GNU General Public License v3.0 6 votes vote down vote up
export async function pasteToNote(plugin: RssReaderPlugin, item: RssFeedItem): Promise<void> {
    const file = plugin.app.workspace.getActiveFile();
    if (file === null) {
        new Notice(t("no_file_active"));
        return;
    }

    const view = plugin.app.workspace.getActiveViewOfType(MarkdownView);
    if (view) {
        const appliedTemplate = applyTemplate(plugin, item, plugin.settings.pasteTemplate);

        const editor = view.editor;
        editor.replaceRange(appliedTemplate, editor.getCursor());

        item.created = true;
        const items = plugin.settings.items;
        await plugin.writeFeedContent(() => {
            return items;
        });

        new Notice(t("RSS_Reader") + t("inserted_article"));
    }
}
Example #18
Source File: renderer.ts    From obsidian-pandoc with MIT License 6 votes vote down vote up
// Note: parentFiles is for internal use (to prevent recursively embedded notes)
// inputFile must be an absolute file path
export default async function render (plugin: PandocPlugin, view: MarkdownView,
    inputFile: string, outputFormat: string, parentFiles: string[] = []):
    Promise<{ html: string, metadata: { [index: string]: string } }>
{
    // Use Obsidian's markdown renderer to render to a hidden <div>
    const markdown = view.data;
    const wrapper = document.createElement('div');
    wrapper.style.display = 'hidden';
    document.body.appendChild(wrapper);
    await MarkdownRenderer.renderMarkdown(markdown, wrapper, path.dirname(inputFile), view);

    // Post-process the HTML in-place
    await postProcessRenderedHTML(plugin, inputFile, wrapper, outputFormat,
        parentFiles, await mermaidCSS(plugin.settings, plugin.vaultBasePath()));
    let html = wrapper.innerHTML;
    document.body.removeChild(wrapper);

    // If it's a top level note, make the HTML a standalone document - inject CSS, a <title>, etc.
    const metadata = getYAMLMetadata(markdown);
    metadata.title ??= fileBaseName(inputFile);
    if (parentFiles.length === 0) {
        html = await standaloneHTML(plugin.settings, html, metadata.title, plugin.vaultBasePath());
    }

    return { html, metadata };
}
Example #19
Source File: main.ts    From luhman-obsidian-plugin with GNU General Public License v3.0 6 votes vote down vote up
insertTextIntoCurrentNote(text: string) {
    let view = this.app.workspace.getActiveViewOfType(MarkdownView);

    if (view) {
      let editor = view!.editor;

      let position: EditorPosition;
      var prefix: string = "";

      if (editor.getSelection()) {
        let selectionPos = editor.listSelections()[0];
        let positionCH = Math.max(selectionPos.head.ch, selectionPos.anchor.ch);
        position = { line: selectionPos.anchor.line, ch: positionCH + 1 };
        prefix = " ";
      } else {
        position = editor.getCursor();
      }

      editor.replaceRange(" " + text, position, position);
    }
  }
Example #20
Source File: CursorJumper.ts    From Templater with GNU Affero General Public License v3.0 6 votes vote down vote up
set_cursor_location(positions: EditorPosition[]): void {
        const active_view =
            this.app.workspace.getActiveViewOfType(MarkdownView);
        if (!active_view) {
            return;
        }

        const editor = active_view.editor;

        const selections: Array<EditorRangeOrCaret> = [];
        for (const pos of positions) {
            selections.push({ from: pos });
        }

        const transaction: EditorTransaction = {
            selections: selections,
        };
        editor.transaction(transaction);
    }
Example #21
Source File: main.ts    From obsidian-custom-attachment-location with GNU General Public License v3.0 6 votes vote down vote up
async handleDrop(event: DragEvent, editor: Editor, view: MarkdownView){
        console.log('Handle Drop');

        let mdFileName = view.file.basename;
        let mdFolderPath: string = Path.dirname(view.file.path);

        let path = this.getAttachmentFolderPath(mdFileName);
        let fullPath = this.getAttachmentFolderFullPath(mdFolderPath, mdFileName);

        if(!this.useRelativePath && !await this.adapter.exists(fullPath))
            await this.app.vault.createFolder(fullPath);
        
        this.updateAttachmentFolderConfig(path);
    }
Example #22
Source File: InternalModuleFile.ts    From Templater with GNU Affero General Public License v3.0 6 votes vote down vote up
generate_cursor_append(): (content: string) => void {
        return (content: string): string => {
            const active_view =
                this.app.workspace.getActiveViewOfType(MarkdownView);
            if (active_view === null) {
                log_error(
                    new TemplaterError(
                        "No active view, can't append to cursor."
                    )
                );
                return;
            }

            const editor = active_view.editor;
            const doc = editor.getDoc();
            doc.replaceSelection(content);
            return "";
        };
    }
Example #23
Source File: localDictionaryBuilder.ts    From obsidian-dictionary with GNU Affero General Public License v3.0 6 votes vote down vote up
onOpen() {
        this.contentEl.appendChild(createEl("p", { text: t("A existing File with the same Name was found, do you want to overwrite it?"), cls: "dictionarycenter" }));
        const buttonDiv = this.contentEl.appendChild(createDiv({ cls: "dictionarybuttons" }))
        buttonDiv.appendChild(createEl("button", { text: t("Yes, overwrite the old File."), cls: "mod-cta" })).onClickEvent(async () => {
            this.app.vault.modify(this.app.vault.getAbstractFileByPath(this.path) as TFile, this.content);
            let oldPaneOpen = false;
            this.app.workspace.iterateAllLeaves((leaf) => {
                if (leaf.view instanceof MarkdownView) {
                    if ((leaf.getViewState().state.file as string).endsWith(this.path)) {
                        oldPaneOpen = true;
                        this.app.workspace.setActiveLeaf(leaf);
                    }
                }
            });
            if (!oldPaneOpen && this.openNote) {
                const leaf = this.app.workspace.splitActiveLeaf();
                await leaf.openFile(this.app.vault.getAbstractFileByPath(this.path) as TFile);
                this.app.workspace.setActiveLeaf(leaf);
            }
            this.close();
        });
        buttonDiv.appendChild(createEl("button", { text: t("No, keep the old File."), cls: "mod-cta" })).onClickEvent(() => {
            this.close();
        });
    }
Example #24
Source File: main.ts    From obsidian-tracker with MIT License 5 votes vote down vote up
addCodeBlock(outputType: GraphType): void {
        const currentView = this.app.workspace.activeLeaf.view;

        if (!(currentView instanceof MarkdownView)) {
            return;
        }

        let codeblockToInsert = "";
        switch (outputType) {
            case GraphType.Line:
                codeblockToInsert = `\`\`\` tracker
searchType: tag
searchTarget: tagName
folder: /
startDate:
endDate:
line:
    title: "Line Chart"
    xAxisLabel: Date
    yAxisLabel: Value
\`\`\``;
                break;
            case GraphType.Bar:
                codeblockToInsert = `\`\`\` tracker
searchType: tag
searchTarget: tagName
folder: /
startDate:
endDate:
bar:
    title: "Bar Chart"
    xAxisLabel: Date
    yAxisLabel: Value
\`\`\``;
                break;
            case GraphType.Summary:
                codeblockToInsert = `\`\`\` tracker
searchType: tag
searchTarget: tagName
folder: /
startDate:
endDate:
summary:
    template: "Average value of tagName is {{average}}"
    style: "color:white;"
\`\`\``;
                break;
            default:
                break;
        }

        if (codeblockToInsert !== "") {
            let textInserted = this.insertToNextLine(codeblockToInsert);
            if (!textInserted) {
            }
        }
    }
Example #25
Source File: main.ts    From quick_latex_obsidian with MIT License 5 votes vote down vote up
private readonly handleKeyPress = (
		cm: CodeMirror.Editor,
		event: KeyboardEvent,
	): void => {

		if (['{', '[', '(', 'm'].contains(event.key)) {
			const view = this.app.workspace.getActiveViewOfType(MarkdownView);
			if (!view) return;

			const editor = view.editor;
			if (this.withinMath(editor)) {
				const position = editor.getCursor();
				const brackets = [['(', ')'], ['{', '}'], ['[', ']']];
				const next_char = editor.getRange(
					{ line: position.line, ch: position.ch },
					{ line: position.line, ch: position.ch+1 });
				const next_2char = editor.getRange(
					{ line: position.line, ch: position.ch },
					{ line: position.line, ch: position.ch+2 });
				const followed_by_$spacetabnonedoubleslash = (['$',' ','	',''].contains(next_char) || next_2char == '\\\\');
				switch (event.key) {
					case '{':
						if (this.settings.autoCloseCurly_toggle) {
							if (!this.withinAnyBrackets_inline(editor, brackets) && followed_by_$spacetabnonedoubleslash) {
								editor.replaceSelection('{}');
								editor.setCursor({line:position.line, ch:position.ch+1});
								event.preventDefault();
								return;
							};
						}
						return;
					case '[':
						if (this.settings.autoCloseSquare_toggle) {
							if (!this.withinAnyBrackets_inline(editor, brackets) && followed_by_$spacetabnonedoubleslash) {
								editor.replaceSelection('[]');
								editor.setCursor({line:position.line, ch:position.ch+1});
								event.preventDefault();
								return;
							};
						}
						return;
					case '(':
						if (this.settings.autoCloseRound_toggle) {
							if (!this.withinAnyBrackets_inline(editor, brackets) && followed_by_$spacetabnonedoubleslash) {
								editor.replaceSelection('()');
								editor.setCursor({line:position.line, ch:position.ch+1});
								event.preventDefault();
								return;
							};
						}	
						return;
					case 'm':
						if (!this.settings.autoSumLimit_toggle) return;
						if (editor.getRange(
							{ line: position.line, ch: position.ch - 3 },
							{ line: position.line, ch: position.ch }) == '\\su') {
							editor.replaceSelection('m\\limits')
							event.preventDefault()
							return;
						};
				};
			};
		};
	};
Example #26
Source File: ImgurPlugin.ts    From obsidian-imgur-plugin with MIT License 5 votes vote down vote up
private customPasteEventCallback = async (
    e: ClipboardEvent,
    _: Editor,
    markdownView: MarkdownView
  ) => {
    if (e instanceof PasteEventCopy) return;

    if (!this.imgUploader) {
      ImgurPlugin.showUnconfiguredPluginNotice();
      return;
    }

    const { files } = e.clipboardData;
    if (files.length === 0 || !files[0].type.startsWith("image")) {
      return;
    }

    e.preventDefault();

    if (this.settings.showRemoteUploadConfirmation) {
      const modal = new RemoteUploadConfirmationDialog(this.app);
      modal.open();

      const userResp = await modal.response();
      switch (userResp.shouldUpload) {
        case undefined:
          return;
        case true:
          if (userResp.alwaysUpload) {
            this.settings.showRemoteUploadConfirmation = false;
            this.saveSettings()
              .then(() => {})
              .catch(() => {});
          }
          break;
        case false:
          markdownView.currentMode.clipboardManager.handlePaste(
            new PasteEventCopy(e)
          );
          return;
        default:
          return;
      }
    }

    for (let i = 0; i < files.length; i += 1) {
      this.uploadFileAndEmbedImgurImage(files[i]).catch(() => {
        markdownView.currentMode.clipboardManager.handlePaste(
          new PasteEventCopy(e)
        );
      });
    }
  };
Example #27
Source File: main.ts    From luhman-obsidian-plugin with GNU General Public License v3.0 5 votes vote down vote up
makeNoteFunction(idGenerator: (file: TFile) => string) {
    var file = this.app.workspace.getActiveFile();
    if (file == null) {
      return;
    }
    if (this.isZettelFile(file.name)) {
      let fileID = this.fileToId(file.basename);
      let fileLink = "[[" + file.basename + "]]";

      let editor = this.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
      if (editor == null) {
        return;
      }

      let selection = editor.getSelection();

      let nextID = idGenerator.bind(this, file)();
      let nextPath = (title: string) =>
        file?.path
          ? this.app.fileManager.getNewFileParent(file.path).path +
              "/" +
              nextID +
              (this.settings.addTitle
                ? this.settings.separator + title
                : ''
              ) +
              ".md"
          : '';
      let newLink = "[[" + nextID + "]]";

      if (selection) {
        let title = selection
          .split(/\s+/)
          .map((w) => w[0].toUpperCase() + w.slice(1))
          .join(" ");
        let selectionPos = editor!.listSelections()[0];
        let positionCH = Math.max(selectionPos.head.ch, selectionPos.anchor.ch);
        let position: EditorPosition = {
          line: selectionPos.anchor.line,
          ch: positionCH + 1,
        };
        editor!.replaceRange(" " + newLink, position, position);
        this.makeNote(nextPath(title), title, fileLink, true);
      } else {
        new NewZettelModal(this.app, (title: string) => {
          this.insertTextIntoCurrentNote(newLink);
          this.makeNote(nextPath(title), title, fileLink, true);
        }).open();
      }
    } else {

    }
  }
Example #28
Source File: ImgurPlugin.ts    From obsidian-imgur-plugin with MIT License 5 votes vote down vote up
private getEditor(): Editor {
    const mdView = this.app.workspace.activeLeaf.view as MarkdownView;
    return mdView.editor;
  }
Example #29
Source File: main.ts    From obsidian-linter with MIT License 5 votes vote down vote up
async onload() {
      console.log('Loading Linter plugin');
      await this.loadSettings();

      this.addCommand({
        id: 'lint-file',
        name: 'Lint the current file',
        editorCallback: (editor) => this.runLinterEditor(editor),
        hotkeys: [
          {
            modifiers: ['Mod', 'Alt'],
            key: 'l',
          },
        ],
      });

      this.addCommand({
        id: 'lint-all-files',
        name: 'Lint all files in the vault',
        callback: () => {
          const startMessage = 'This will edit all of your files and may introduce errors.';
          const submitBtnText = 'Lint All';
          const submitBtnNoticeText = 'Linting all files...';
          new LintConfirmationModal(this.app, startMessage, submitBtnText, submitBtnNoticeText, () =>{
            return this.runLinterAllFiles(this.app);
          }).open();
        },
      });

      this.addCommand({
        id: 'lint-all-files-in-folder',
        name: 'Lint all files in the current folder',
        editorCallback: (_) => {
          this.createFolderLintModal(this.app.workspace.getActiveFile().parent);
        },
      });

      // https://github.com/mgmeyers/obsidian-kanban/blob/main/src/main.ts#L239-L251
      this.registerEvent(
          this.app.workspace.on('file-menu', (menu, file: TFile) => {
            // Add a menu item to the folder context menu to create a board
            if (file instanceof TFolder) {
              menu.addItem((item) => {
                item
                    .setTitle('Lint folder')
                    .setIcon('wrench-screwdriver-glyph')
                    .onClick(() => this.createFolderLintModal(file));
              });
            }
          }),
      );

      this.eventRef = this.app.workspace.on('file-menu',
          (menu, file, source) => this.onMenuOpenCallback(menu, file, source));
      this.registerEvent(this.eventRef);

      // Source for save setting
      // https://github.com/hipstersmoothie/obsidian-plugin-prettier/blob/main/src/main.ts
      const saveCommandDefinition = (this.app as any).commands?.commands?.[
        'editor:save-file'
      ];
      const save = saveCommandDefinition?.callback;

      if (typeof save === 'function') {
        saveCommandDefinition.callback = () => {
          if (this.settings.lintOnSave) {
            const editor = this.app.workspace.getActiveViewOfType(MarkdownView).editor;
            const file = this.app.workspace.getActiveFile();

            if (!this.shouldIgnoreFile(file)) {
              this.runLinterEditor(editor);
            }
          }
        };
      }

      this.addSettingTab(new SettingTab(this.app, this));
    }