How to create a gutter for obsidian codeblocks?

333 Views Asked by At

Please don't be angry, I don't know what I am doing. My end goal is to create a simple plugin for Obsidian which adds line numbering to codeblocks in the editor/live-preview mode, and I can highlight specific lines. As far as I know Obsidian uses CodeMirror editor. I also found that there is theoretically a simple way to add a gutter as described here: https://codemirror.net/examples/gutter/ but I couldn't make it work. The below code does work (sort of...). It does add line numbering, but the text which the codeblocks contains disappears. I would really appreciate if someone could provide a simple example, or correct my code, or point me in the right direction.

My code so far:

class EmojiListPlugin implements PluginValue {
  decorations: DecorationSet;

  constructor(view: EditorView) {
    this.decorations = this.buildDecorations(view);
  }

  update(update: ViewUpdate) {
    if (update.docChanged || update.viewportChanged) {
      this.decorations = this.buildDecorations(update.view);
    }
  }
  
  destroy() {}

  buildDecorations(view: EditorView): DecorationSet {
    const builder = new RangeSetBuilder<Decoration>();
    let lineNumber = 1;

    for (let { from, to } of view.visibleRanges) {
      syntaxTree(view.state).iterate({ from, to, enter(node) {
          console.log("+++ node enter");
          if (node.type.id === 3 ) {
            // opening ```
          }
          if (node.type.id === 5 ) {         
            builder.add(
              node.from,
              node.to,
              Decoration.replace({
                widget: new LineNumberingWidget(lineNumber ),
              })
            );
            lineNumber++;
          }
          if (node.type.id === 6 ) {
            // closing ```
            lineNumber = 1;
          }
        },
        leave(node)
        {
         // console.log("--- node exit");
        },
      });
    }
    return builder.finish();
  }// buildDecorations
}// EmojiListPlugin

const pluginSpec: PluginSpec<EmojiListPlugin> = {
  decorations: (value: EmojiListPlugin) => value.decorations,
  widget: (value: EmojiListPlugin) => new LineNumberingWidget(value.lineNumber),
};

class LineNumberingWidget extends WidgetType {
  lineNumber: number;
  constructor(lineNumber: number ) {
    super();
    this.lineNumber = lineNumber;
  }

  toDOM(view: EditorView): HTMLElement {   
    // create an element for the line number
    const lineNumberElement = document.createElement("span");
    lineNumberElement.classList.add("gutter");
    lineNumberElement.innerText = `${this.lineNumber}: `;

    return lineNumberElement;
  }// toDOM
}// LineNumberingWidget

export const emojiListPlugin = ViewPlugin.fromClass(
  EmojiListPlugin,
  pluginSpec
);

export default class LineNumberingPlugin extends Plugin  {
  settings: LinkifySettings;
  viewPlugins: LinkifyViewPlugin[] = [];
  async onload() {
    this.registerEditorExtension( ViewPlugin.fromClass(EmojiListPlugin, pluginSpec));
    console.log("Plugin is registered");
  }
}
0

There are 0 best solutions below