hipstersmoothie / obsidian-plugin-toc

Create a tables of contents for a note.
286 stars 20 forks source link

Ignore the [[]] link syntax in a Heading that is a link. #32

Open chriselderer opened 1 year ago

chriselderer commented 1 year ago

currently # [[My Heading Link]] turns to [[#[[My Heading Link]]|[[My Heading Link]]]]

The Stripping the [[]] from the heading when you link it works just fine

should look like: # [[My Heading Link]] -> [[#My Heading Link|My Heading Link]]

wimiam1 commented 1 year ago

Hi I was experiencing the same issue. My solution was just to create a function to strip the link syntax from any header names.

function removeBrackets(str) {
    if (typeof str == 'string') {
      return str.replace(/^[\[\]]+|[\[\]]+$/g, '');
    }
  }

Then in the if statement around line 214 where you see the final return for each line of the ToC, I called that function on the headings. Like so:

if (typeof previousLevelHeading == "undefined") {
      return `${indent}${itemIndication} [[#${removeBrackets(heading.heading)}|${removeBrackets(heading.heading)}]]`;
    } else {
      return `${indent}${itemIndication} [[#${removeBrackets(previousLevelHeading.heading)}#${removeBrackets(heading.heading)}|${removeBrackets(heading.heading)}]]`;
    }

You could also define strings with the stripped text use those, but make sure to initialize the one for previousLevelHeading within the if statement that checks if one exists.

hipstersmoothie commented 1 year ago

@wimiam1 Would you want to submit a PR for that?

visualcurrent commented 1 month ago

Yeah, headings with links continues to break this TOC extension. I ended up writing a more robust ToC script in python. Sorry, I don't know how to make an obsidian extension.

import re
import sys
import glob

def process_markdown_file(input_file_name):
    # Extract page title from input file name
    page_title = input_file_name.split('.')[0]

    # Capture the file extension
    extension = input_file_name.split('.')[-1]

    # Initialize empty lists to store processed lines
    link_parts = []
    presentation_parts = []
    heading_levels = []

    # Initialize flag to track whether we're inside a code block
    inside_code_block = False

    # Read input file and process lines
    with open(input_file_name, 'r') as input_file:
        for line in input_file:
            # Check if we're entering or exiting a code block
            if line.strip().startswith('```'):
                inside_code_block = not inside_code_block
                continue

            # Only process lines if we're not inside a code block
            if not inside_code_block:
                match = re.match(r'^(#+)\s+(.+)', line)
                if match:
                    heading_level = len(match.group(1))
                    link_content = match.group(2).replace('[', '').replace(']', '')

                    if '|' in link_content or '#' in link_content:
                        # Process link content with pipe and/or hash
                        if '#' in link_content:
                            link_content = link_content.replace('#', '')
                        parts = [part.strip() for part in link_content.split('|')]
                        link_part = ' '.join(parts)
                        presentation_part = parts[-1]
                    else:
                        # Process link content without pipe and/or hash
                        link_part = link_content.strip()
                        presentation_part = link_content.strip()

                    link_parts.append(link_part)
                    presentation_parts.append(presentation_part)
                    heading_levels.append(heading_level)

    # Build new lines
    new_lines = []
    for i, (link_part, presentation_part, heading_level) in enumerate(zip(link_parts, presentation_parts, heading_levels)):
        new_line = '\t' * (heading_level - 1) + '- [[' + page_title + '#' + link_part + '|' + presentation_part + ']]'
        new_lines.append(new_line)

    # Write output file
    output_file_name = input_file_name.replace('.' + extension, 'TOC.' + extension)
    with open(output_file_name, 'w') as output_file:
        output_file.write('\n'.join(new_lines))

    # Print completion message
    print(f'Output written to {output_file_name}')

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Usage: python3 extractTOC.py <input_file_name> or python3 extractTOC.py <pattern>')
        sys.exit(1)

    input_files = []
    for arg in sys.argv[1:]:
        input_files.extend(glob.glob(arg))

    if not input_files:
        print('No files found matching the input pattern')
        sys.exit(1)

    for input_file_name in input_files:
        process_markdown_file(input_file_name)

Maybe it's not perfect but it has worked great for everything that I've thrown at it so far.