Feedback wanted for Automatic MOC plugin I wrote (wondering if there's a better way)

Note: for my purposes, a MOC is just a table of contents. I’m organizing my vault w/ a very basic IMF structure:

  • each directory has a MOC which contains a list of internal links to notes in the same directory and/or other MOCs in child directories.

I got tired of updating my MOCs every time I added a new note or MOC so I wrote some code to update them for me (Note: I didn’t really write a whole plugin, I just pulled the code for the very awesome Templater plugin and added to it.

Here’s the Templater function I added code to:

async append_template_to_active_file(template_file: TFile): Promise<void> {
        const active_view =
            this.app.workspace.getActiveViewOfType(MarkdownView);
        if (active_view === null) {
            log_error(
                new TemplaterError("No active view, can't append templates.")
            );
            return;
        }
        
        const running_config = this.create_running_config(
            template_file,
            active_view.file,
            RunMode.AppendActiveFile
        );

        const output_content = await errorWrapper(
            async () => this.read_and_parse_template(running_config),
            "Template parsing error, aborting."
        );
        // errorWrapper failed
        if (output_content == null) {
            return;
        }

        const editor = active_view.editor;
        const doc = editor.getDoc();
        doc.replaceSelection(output_content);

        // CODE I ADDED
        const newFileIsMOC = template_file.name.includes("MOC")

        let mocToUpdate

        if (newFileIsMOC) {
            mocToUpdate = active_view.file.parent.parent.children.filter(f => f.name.includes("MOC"))[0]
        } else {
            mocToUpdate = active_view.file.parent.children.filter(f => f.name.includes("MOC"))[0]
        }

        if (mocToUpdate) {
            const linkTitle = newFileIsMOC ? active_view.file.basename.split('-')[1].trim() : active_view.file.basename
            const linkPath = active_view.file.path
            
            const mocToUpdateContent = (await this.app.vault.read(mocToUpdate)).trimEnd()
            const shouldAddNewLine = !((mocToUpdateContent.slice(mocToUpdateContent.length - 2)).match(/\d+\./))

            const newMocListItem = `${ shouldAddNewLine ? "\n1." : ""} [[${linkPath}|${linkTitle}]]`
            const newMocToUpdateContent = mocToUpdateContent + newMocListItem

            try {
                await this.app.vault.modify(mocToUpdate, newMocToUpdateContent)
                new Notice(`updated ${mocToUpdate.name}`)
            } catch(e) {
                new Notice(`error updating MOC: ${e}`)
            }
        }
        // END OF CODE I ADDED

        await this.plugin.editor_handler.jump_to_next_cursor_location(active_view.file, true);
    }

This works fine but I wonder if any folks have ideas for how I might improve it.

  1. I chose to tack this functionality onto the action of applying a template to file b/c at that point I know if the new file is meant to be a note or a MOC. Is there another point when I could make that determination? Like maybe I could setup some kind of pre-commit hook that would update the MOCs everytime I commit changes to my vault? Any other ideas?
  2. I would like to extract this functionality from Templater if possible (i.e. so I don’t have to re-insert it everytime I update the plugin), is there an event I could listen for to trigger the MOC update? Maybe I could fire an event in Templater and listen for the event in a different plugin?

Any thoughts/feedback would be appreciated!

2 Likes

In case you want to try and get this working for yourself, here’s what you’ll need:

  1. All your MOC files need to be titled in the format “000 MOC - (whatever title you want)”
  2. The template(s) for you MOC files need have “MOC” in the title
  3. The template(s) for you notes need to not have “MOC” in the title
  4. take a look at Create your first plugin | Obsidian Plugin Developer Docs to learn how to run your own plugins on your computer but instead of cloning their demo repo clone this one: GitHub - SilentVoid13/Templater: A template plugin for obsidian.
  5. insert the ^ code in the append_template_to_active_file method found at src/core/Templater.ts
  6. start applying templates to your notes and you should be good to go.

I am very, very interested in this. I used to use a hierarchical database app called Frontier which had an associated scripting language, and one of the things that was easy to do was to create index pages which listed the content of a directory (and listed subdirectories) automatically.
Having said that, I’m not a developer. :frowning:
I will try your directions to try to get this working and report back here.

In thinking about this further, it might be wise to post this in the plug-in ideas subforum, no?

Let me know if you get stuck and I’d be happy to try and help!