我开始使用标记来做笔记。

我用标记来查看我的笔记,它很漂亮。

但是随着我的笔记变长,我发现很难找到我想要的东西。

我知道markdown可以创建表,但它是否能够创建目录,跳转到部分,或定义页面部分markdown?

或者,是否有降价阅读器/编辑器可以做这些事情。搜索也是一个不错的功能。

简而言之,我想让它成为我很棒的笔记工具,功能就像写一本书一样。


当前回答

作为手工制作链接列表的替代方案,让我们概述所有可用的开箱即用的解决方案来插入目录(请评论和扩展以保持最新):

在咕噜5版本中,markdown支持这一点:

<!-- assure you have a blank line before -->
[[_TOC_]]

这也适用于Azure DevOps wiki。


由于Gitlab将降价引擎从Redcarpet切换到Kramdown,他们现在支持以下语法

- TOC
{:toc}

看到https://about.gitlab.com/handbook/markdown-guide/ table-of-contents-toc


MultiMarkdown在4.7有一个下面的宏:

{{TOC}}

根据Jannik的回答: 如果你的Markdown文件是显示在bitbucket.org上的回购,你可以在你想要你的目录(更多信息在这里)的位置使用以下:

[TOC]

根据Paul Jurczak的回答: 当您在文档中写入[TOC]时,Markdown编辑器Typora也会生成一个目录。


我知道,我的回答有点晚了。然而,我自己却错过了这样一个概述。我对尼古拉斯·特里的回答进行了编辑,将其扩展为概述,但被拒绝了。

其他回答

MultiMarkdown Composer似乎生成了一个目录来辅助编辑。

也可能存在这样或那样的库,它们可以生成TOC:参见Python Markdown TOC扩展。

基于albertodebortoli answer创建了附加检查和标点符号替换功能。

# @fn       def generate_table_of_contents markdown # {{{
# @brief    Generates table of contents for given markdown text
#
# @param    [String]  markdown Markdown string e.g. File.read('README.md')
#
# @return   [String]  Table of content in markdown format.
#
def generate_table_of_contents markdown
  table_of_contents = ""
  i_section = 0
  # to track markdown code sections, because e.g. ruby comments also start with #
  inside_code_section = false
  markdown.each_line do |line|
    inside_code_section = !inside_code_section if line.start_with?('```')

    forbidden_words = ['Table of contents', 'define', 'pragma']
    next if !line.start_with?('#') || inside_code_section || forbidden_words.any? { |w| line =~ /#{w}/ }

    title = line.gsub("#", "").strip
    href = title.gsub(/(^[!.?:\(\)]+|[!.?:\(\)]+$)/, '').gsub(/[!.,?:; \(\)-]+/, "-").downcase

    bullet = line.count("#") > 1 ? " *" : "#{i_section += 1}."
    table_of_contents << "  " * (line.count("#") - 1) + "#{bullet} [#{title}](\##{href})\n"
  end
  table_of_contents
end

使用toc.py,这是一个小的python脚本,它为你的markdown生成一个目录。

用法:

在Markdown文件中,将<toc>添加到您希望放置目录的位置。 $python toc.py README。md(使用您的markdown文件名而不是README.md)

干杯!

只需使用带有插件的文本编辑器。

你的编辑器很可能有一个包/插件来为你处理这个问题。例如,在Emacs中,可以安装markdown-toc TOC生成器。然后在编辑时,只需反复调用M-x markdown-toc-generate-or-refresh-toc。如果你想经常这样做,那就值得一个键绑定。它擅长生成一个简单的TOC,而不会用HTML锚污染您的文档。

其他编辑器也有类似的插件,所以流行的列表是这样的:

器name: markdown-toc Vim: markdown-toc Atom: markdown-toc VSCode: markdown-toc

这是一个小的nodejs脚本,它生成目录,并考虑重复的标题:

const fs = require('fs')
const { mdToPdf } = require('md-to-pdf');

const stringtoreplace = '<toc/>'

const processTitleRepetitions = (contents, titleMap) => {
  for (const content of contents) {
    titleMap[content.link] = typeof titleMap[content.link] === 'undefined'
      ? 0
      : titleMap[content.link] + 1
    if (titleMap[content.link] > 0) {
      content.link = `${content.link}-${titleMap[content.link]}`
    }
  }
}

const convertContentToPdf = async (targetFile) => {
  const pdf = await mdToPdf({path: targetFile}).catch(console.error)
  if(pdf) {
    const pdfFile = `${targetFile.replace(/\.md/, '')}.pdf`
    fs.writeFileSync(pdfFile, pdf.content)
    return pdfFile
  }
  throw new Error("PDF generation failed.")
}

const generateTOC = (file, targetFile) => {
  // Extract headers
  const fileContent = fs.readFileSync(file, 'utf-8')
  const titleLine = /((?<=^)#+)\s(.+)/
  const contents = fileContent.split(/\r?\n/).
    map(line => line.match(titleLine)).
    filter(match => match).
    filter(match => match[1].length > 1).
    map(regExpMatchArray => {
      return {
        level: regExpMatchArray[1].length, text: regExpMatchArray[2],
        link: '#' + regExpMatchArray[2].replace(/(\s+|[.,\/#!$%^&*;:{}=\-_`~()]+)/g, '-').toLowerCase(),
      }
    })
  const titleMap = {}
  processTitleRepetitions(contents, titleMap)
  // Write content
  let toctext = '## Table of Contents\n'
  // Find the toplevel to adjust the level of the table of contents.
  const topLevel = contents.reduce((maxLevel, content) => Math.min(content['level'], maxLevel), 1000)
  levelCounter = {}
  contents.forEach(item => {
    let currentLevel = parseInt(item.level)
    levelCounter[currentLevel] = levelCounter[currentLevel] ? levelCounter[currentLevel] + 1 : 1
    Object.entries(levelCounter).forEach(e => {
      if(currentLevel < parseInt(e[0])) {
        levelCounter[e[0]] = 0
      }
    })
    const level = Array(currentLevel - topLevel).fill('\t').join('')
    const text = `${levelCounter[currentLevel]}. [${item['text']}](${item['link']}) \n`
    toctext += level + text
  })

  const updatedContent = fileContent.toString().replace(stringtoreplace, toctext)
  fs.writeFileSync(targetFile, updatedContent)
  convertContentToPdf(targetFile).then((pdfFile) => {
    console.info(`${pdfFile} has been generated.`)
  })
}

const args = process.argv.slice(2)

if(args.length < 2) {
  console.error("Please provide the name of the markdown file from which the headers should be extracted and the name of the file with the new table of contents")
  console.info("Example: node MD_TOC.js <source_md> <target_md>")
  process.exit(1)
}

const source_md = args[0]
const target_md = args[1]

generateTOC(source_md, target_md)

要使用它,您需要在markdown文件中注入<toc/>。

下面是你如何使用它:

generateTOC('../README.md', '../README_toc.md')

第一个参数是源markdown文件,第二个参数是带有markdown的文件。