Overview

obsidian-export is a tool that I use in my workflow to host my digital garden. You can learn more about it at obsidian-export. You can find the source code of my fork on my Github.

My Changes

Remove Obsidian Comments

I added a new command line flag —comments=[keep-unchanged|remove] This is implemented as a post_processor removing anything between two % which Obsidian uses to denote comments. However if the comment is within a code block (as seen below) it won’t be removed.

This isn't a comment
%% This is a comment %%

I use regex in-order to find the comments in the text.

Source Code

pub fn remove_obsidian_comments(  
    _context: &mut Context,  
    events: &mut MarkdownEvents<'_>,  
) -> PostprocessorResult {  
    let mut output = Vec::with_capacity(events.len());  
    let mut inside_comment = false;  
    let mut inside_codeblock = false;  
    let re = LazyCell::new(|| Regex::new(r"%%.*?%%").unwrap());  
  
    for event in &mut *events {  
        output.push(event.to_owned());  
  
        match event {  
            Event::Text(ref text) => {  
                if !text.contains("%%") {  
                    if inside_comment {  
                        output.pop(); //Inside block comment so remove  
                    }  
                    continue;  
                }  
                if inside_codeblock {  
                    continue; //Skip anything inside codeblocks  
                }  
  
                output.pop();  
  
                if inside_comment {  
                    inside_comment = false;  
                    continue;  
                }  
                if !text.eq(&CowStr::from("%%")) {  
                    let result = re.replace_all(text, "").to_string();  
                    output.push(Event::Text(CowStr::from(result)));  
                    continue;  
                }  
                inside_comment = true;  
            }         
               
            Event::Start(Tag::CodeBlock(_)) => {  
                if inside_comment {  
                    output.pop();  
                } else {  
                    inside_codeblock = true;  
                }            
            }            
                
            Event::End(Tag::CodeBlock(_)) => {  
                if inside_comment {  
                    output.pop();  
                } else {  
                    inside_codeblock = false;  
                }            
            }
                        
            Event::End(Tag::Paragraph) => {  
                if output.len() >= 2  
                    && output.get(output.len() - 2) == Option::from(&Event::Start(Tag::Paragraph))  
                {                    
	                // If the comment was the only item on the line remove the start and end  
                    // paragraph events to remove the \n in the output file.                    output.pop();  
                    output.pop();  
                }            
            }            
                
            _ => {  
                if inside_comment {  
                    output.pop();  
                }            
            }        
        }    
    }    
    *events = output;  
    PostprocessorResult::Continue  
}

Remove table of contents

In my obsidian vault I have a table of contents at the top of most of my notes. However, I currently host my notes through Quartz which offers a table of contents on the right side of the screen. Because of this I don’t also need a table of contents on the top so I just remove it from the markdown file before publishing. I originally was going to render the table of contents myself (seen here Automatic Table of Contents Bug) but I decided this wasn’t worth the effort and to just use the quartz table of contents.

This code is heavily borrowed from an example provided by Nick Groenen, creator of obsidian-export. This option is invoked through the flag —remove-table-of-contents

Source Code

pub fn remove_toc(_context: &mut Context, events: &mut MarkdownEvents) -> PostprocessorResult {  
    let mut output = Vec::with_capacity(events.len());  
  
    for event in &mut *events {  
        output.push(event.to_owned());  
        match event {  
            Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref language_tag))) => {  
                if language_tag != &CowStr::from("toc") && language_tag != &CowStr::from("table-of-contents")  {                    
	                continue;  
            }                
            
            output.pop(); // Remove codeblock start tag that was pushed onto output              
            }  
            
            Event::End(Tag::CodeBlock(CodeBlockKind::Fenced(ref language_tag))) => {  
                if language_tag == &CowStr::from("toc") && language_tag != &CowStr::from("table-of-contents")  {                    
	                // The corresponding codeblock start tag for this is replaced with regular  
                    // text (containing the Hugo shortcode), so we must also pop this end tag.                    
                    output.pop();  
                }            
            }            
            _ => {}  
        }    
    }    
    *events = output;  
    PostprocessorResult::Continue  
}