MarkdownCodeBlockProcessor behaviour when there is more than one code blocks

(text via translator, so that readers’ eyes won’t be too hurt by my English writing skills)

I’m new to both JavaScript (typescript) and plugin writing. I tried to find an answer to my question in the documentation, but it’s shockingly incomplete. The gist of the question is this: I’m developing a post processor for custom code blocks that look like this:

style: positive
prop: track
1:5

HTML is re-rendered every time a property specified in the code block changes. Everything works fine as long as there is only one code block in the file. If there are two or more, only the conditionally “active” one is rendered (in live preview mode, the one with the cursor last). I suspect that the solution to the problem lies in MarkdownRenderChild, but I have absolutely no idea how to use it.

Dirty code with very bad style, in the middle of finding a solution (I would appreciate hints on what can be changed in the structure or syntax):

import { Plugin, FileManager, Workspace } from 'obsidian';

const colors = {
	'positive': ['blue', 'green', 'yellow', 'orange', 'red']
}

export default class SegmentedProgressBar extends Plugin {
fileManager: FileManager;

  async onload() {

    this.registerMarkdownCodeBlockProcessor('seg-bar', async (source, el, ctx) => {
    	const file = this.app.workspace.getActiveFile();
			this.element = el;
			this.context = ctx;
			this.row = this.element.createEl('div', { cls: 'segments-row' });

    	const args = source.split('\n').filter((arg) => arg.length > 0);

    	this.style = args[0].replace('style: ', '');
    	this.prop_name = args[1].replace('prop: ', '');
    	this.seg = args[2].split(':');

			const cache = this.app.metadataCache;

    	if (file) {
				await this.drawContent();
    	}
    });

		this.app.metadataCache.on('changed', this.handleMetadata.bind(this));
  }

  async processFrontMatter(file: TFile) {
  	const frontMatter = this.app.metadataCache.getFileCache(file)?.frontmatter
  	return frontMatter;
  }

	async drawContent() {
		const stats = await this.processFrontMatter(this.app.workspace.getActiveFile());
		console.log(this.element.getElementsByTagName('div'));

		const row = this.element.createEl('div', { cls: 'segments-row' });
		const segments = [];

		let class_sig = 'segment ' + colors[this.style][stats[this.prop_name]-1];

		for (let i = 1; i <= this.seg[1]; i++) {
			if (i > stats[this.prop_name]) {
				class_sig = 'segment empty';
			}
			segments.push(row.createEl('div', { cls: class_sig }));
		}

		for (let el of this.element.getElementsByTagName('div')) {
			if (el.className == 'segments-row') {
				console.log(el.className);
				el.replaceWith(row);
			}
		}
	}

	handleMetadata(file: TFile) {
		if (file === this.app.workspace.getActiveFile()) {
			this.drawContent();
		}
	}
}

this.element and this.context are global variables, they get reassigned whenever you swap to the other codeblock.

Pass them as variables instead of putting them on the global context.

1 Like

Okay, idk how exactly, but now it works, thank you!

import { Plugin, FileManager, Workspace } from 'obsidian';

const colors = {
	'positive': ['blue', 'green', 'yellow', 'orange', 'red']
}

export default class SegmentedProgressBar extends Plugin {
fileManager: FileManager;

  async onload() {

    this.registerMarkdownCodeBlockProcessor('seg-bar', async (source, el, ctx) => {
    	const file = this.app.workspace.getActiveFile();
			this.row = el.createEl('div', { cls: 'segments-row' });

    	const args = source.split('\n').filter((arg) => arg.length > 0);

    	const style = args[0].replace('style: ', '');
    	const prop_name = args[1].replace('prop: ', '');
    	const seg = args[2].split(':');

			const cache = this.app.metadataCache;

    	if (file) {
				await this.drawContent(el, ctx, style, prop_name, seg);
    	}

			this.app.metadataCache.on('changed', this.drawContent.bind(this, el, ctx, style, prop_name, seg));
    });


  }

  async processFrontMatter() {
		const file = this.app.workspace.getActiveFile();
  	const frontMatter = this.app.metadataCache.getFileCache(file)?.frontmatter;
  	return frontMatter;
  }

	async drawContent(el, ctx, style, prop_name, seg) {
		const stats = await this.processFrontMatter();

		el.empty();

		const row = el.createEl('div', { cls: 'segments-row' });
		const segments = [];

		let class_sig = 'segment ' + colors[style][stats[prop_name]-1];

		for (let i = 1; i <= seg[1]; i++) {
			if (i > stats[prop_name]) {
				class_sig = 'segment empty';
			}
			segments.push(row.createEl('div', { cls: class_sig }));
		}
	}
}

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.