diff --git a/browser/lib/markdown-toc-generator.js b/browser/lib/markdown-toc-generator.js index eae448ec..af1c833f 100644 --- a/browser/lib/markdown-toc-generator.js +++ b/browser/lib/markdown-toc-generator.js @@ -2,51 +2,27 @@ * @fileoverview Markdown table of contents generator */ +import { EOL } from 'os' import toc from 'markdown-toc' -import diacritics from 'diacritics-map' -import stripColor from 'strip-color' import mdlink from 'markdown-link' +import slugify from './slugify' -const EOL = require('os').EOL +const hasProp = Object.prototype.hasOwnProperty /** - * @caseSensitiveSlugify Custom slugify function - * Same implementation that the original used by markdown-toc (node_modules/markdown-toc/lib/utils.js), - * but keeps original case to properly handle https://github.com/BoostIO/Boostnote/issues/2067 + * From @enyaxu/markdown-it-anchor */ -function caseSensitiveSlugify (str) { - function replaceDiacritics (str) { - return str.replace(/[À-ž]/g, function (ch) { - return diacritics[ch] || ch - }) - } - - function getTitle (str) { - if (/^\[[^\]]+\]\(/.test(str)) { - var m = /^\[([^\]]+)\]/.exec(str) - if (m) return m[1] - } - return str - } - - str = getTitle(str) - str = stripColor(str) - // str = str.toLowerCase() //let's be case sensitive - - // `.split()` is often (but not always) faster than `.replace()` - str = str.split(' ').join('-') - str = str.split(/\t/).join('--') - str = str.split(/<\/?[^>]+>/).join('') - str = str.split(/[|$&`~=\\\/@+*!?({[\]})<>=.,;:'"^]/).join('') - str = str.split(/[。?!,、;:“”【】()〔〕[]﹃﹄“ ”‘’﹁﹂—…-~《》〈〉「」]/).join('') - str = replaceDiacritics(str) - return str +function uniqueSlug (slug, slugs, opts) { + let uniq = slug + let i = opts.uniqueSlugStartIndex + while (hasProp.call(slugs, uniq)) uniq = `${slug}-${i++}` + slugs[uniq] = true + return uniq } -function linkify (tok, text, slug, opts) { - var uniqeID = opts.num === 0 ? '' : '-' + opts.num - tok.content = mdlink(text, '#' + slug + uniqeID) - return tok +function linkify (token) { + token.content = mdlink(token.content, '#' + token.slug) + return token } const TOC_MARKER_START = '' @@ -91,8 +67,23 @@ export function generateInEditor (editor) { * @returns generatedTOC String containing generated TOC */ export function generate (markdownText) { - const generatedToc = toc(markdownText, {slugify: caseSensitiveSlugify, linkify: linkify}) - return TOC_MARKER_START + EOL + EOL + generatedToc.content + EOL + EOL + TOC_MARKER_END + const slugs = {} + const opts = { + uniqueSlugStartIndex: 1 + } + + const result = toc(markdownText, { + slugify: title => { + return uniqueSlug(slugify(title), slugs, opts) + }, + linkify: false + }) + + const md = toc.bullets(result.json.map(linkify), { + highest: result.highest + }) + + return TOC_MARKER_START + EOL + EOL + md + EOL + EOL + TOC_MARKER_END } function wrapTocWithEol (toc, editor) { diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 2a7b66b0..0f7d58b5 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -7,7 +7,6 @@ import _ from 'lodash' import ConfigManager from 'browser/main/lib/ConfigManager' import katex from 'katex' import { lastFindInArray } from './utils' -import anchor from '@enyaxu/markdown-it-anchor' function createGutter (str, firstLineNumber) { if (Number.isNaN(firstLineNumber)) firstLineNumber = 1 @@ -118,14 +117,8 @@ class Markdown { this.md.use(require('markdown-it-imsize')) this.md.use(require('markdown-it-footnote')) this.md.use(require('markdown-it-multimd-table')) - this.md.use(anchor, { - slugify: (title) => { - var slug = encodeURI(title.trim() - .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') - .replace(/\s+/g, '-')) - .replace(/\-+$/, '') - return slug - } + this.md.use(require('@enyaxu/markdown-it-anchor'), { + slugify: require('./slugify') }) this.md.use(require('markdown-it-kbd')) this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']}) diff --git a/browser/lib/slugify.js b/browser/lib/slugify.js new file mode 100644 index 00000000..a3447a90 --- /dev/null +++ b/browser/lib/slugify.js @@ -0,0 +1,17 @@ +import diacritics from 'diacritics-map' + +function replaceDiacritics (str) { + return str.replace(/[À-ž]/g, function (ch) { + return diacritics[ch] || ch + }) +} + +module.exports = function slugify (title) { + let slug = title.trim() + + slug = replaceDiacritics(slug) + + slug = slug.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-') + + return encodeURI(slug).replace(/\-+$/, '') +}