From eb5f9390fc43e6aa8c841f15fb972ea1ab5d0c0b Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Wed, 22 Jun 2022 18:09:50 +0200 Subject: [PATCH 01/15] Implement support for setting a customized sidebar item name from the markdown content --- docs/helpers.md | 6 ++++++ src/core/render/compiler.js | 10 ++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/helpers.md b/docs/helpers.md index 6bd8a069d..356df6ec0 100644 --- a/docs/helpers.md +++ b/docs/helpers.md @@ -127,6 +127,12 @@ Only when you set both the `routerMode: 'history'` and `externalLinkTarget: '_se ### Hello, world! :id=hello-world ``` +## Customise item name for sidebar + +```md +### How would I write a "hello, world" example? :sidebar="Hello, world?" +``` + ## Markdown in html tag You need to insert a space between the html and markdown content. diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index 255c49642..3300ae250 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -207,38 +207,36 @@ export class Compiler { */ origin.heading = renderer.heading = function (text, level) { let { str, config } = getAndRemoveConfig(text); - const nextToc = { level, title: removeAtag(str) }; + const nextToc = { level }; if (//g.test(str)) { str = str.replace('', ''); - nextToc.title = removeAtag(str); nextToc.ignoreSubHeading = true; } if (/{docsify-ignore}/g.test(str)) { str = str.replace('{docsify-ignore}', ''); - nextToc.title = removeAtag(str); nextToc.ignoreSubHeading = true; } if (//g.test(str)) { str = str.replace('', ''); - nextToc.title = removeAtag(str); nextToc.ignoreAllSubs = true; } if (/{docsify-ignore-all}/g.test(str)) { str = str.replace('{docsify-ignore-all}', ''); - nextToc.title = removeAtag(str); nextToc.ignoreAllSubs = true; } + const title = config.sidebar || str; const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); + nextToc.title = removeAtag(title); nextToc.slug = url; _self.toc.push(nextToc); - return `${str}`; + return `${title}`; }; origin.code = highlightCodeCompiler({ renderer }); From a764b02156150878c21d978c2ec53f131ddd7579 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 11:31:12 +0200 Subject: [PATCH 02/15] Push both title and label to toc (=table of contents) array --- src/core/render/compiler.js | 6 +++--- src/core/render/tpl.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index 3300ae250..d7db551ec 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -229,14 +229,14 @@ export class Compiler { nextToc.ignoreAllSubs = true; } - const title = config.sidebar || str; const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); - nextToc.title = removeAtag(title); + nextToc.label = removeAtag(config.sidebar || str); + nextToc.title = removeAtag(str); nextToc.slug = url; _self.toc.push(nextToc); - return `${title}`; + return `${str}`; }; origin.code = highlightCodeCompiler({ renderer }); diff --git a/src/core/render/tpl.js b/src/core/render/tpl.js index 47ceeab74..c30f21c30 100644 --- a/src/core/render/tpl.js +++ b/src/core/render/tpl.js @@ -92,7 +92,7 @@ export function tree(toc, tpl = '') { let innerHTML = ''; toc.forEach(node => { const title = node.title.replace(/(<([^>]+)>)/g, ''); - innerHTML += `
  • ${node.title}
  • `; + innerHTML += `
  • ${node.label}
  • `; if (node.children) { innerHTML += tree(node.children, tpl); } From cfca427e994b3137a9f33bddc4130aa0b172efb5 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 11:33:45 +0200 Subject: [PATCH 03/15] Apply changes to duplicate code block --- src/core/render/compiler/headline.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/render/compiler/headline.js b/src/core/render/compiler/headline.js index 61e4b3fb9..01fb547b3 100644 --- a/src/core/render/compiler/headline.js +++ b/src/core/render/compiler/headline.js @@ -4,34 +4,32 @@ import { slugify } from './slugify'; export const headingCompiler = ({ renderer, router, _self }) => (renderer.code = (text, level) => { let { str, config } = getAndRemoveConfig(text); - const nextToc = { level, title: removeAtag(str) }; + const nextToc = { level }; if (//g.test(str)) { str = str.replace('', ''); - nextToc.title = removeAtag(str); nextToc.ignoreSubHeading = true; } if (/{docsify-ignore}/g.test(str)) { str = str.replace('{docsify-ignore}', ''); - nextToc.title = removeAtag(str); nextToc.ignoreSubHeading = true; } if (//g.test(str)) { str = str.replace('', ''); - nextToc.title = removeAtag(str); nextToc.ignoreAllSubs = true; } if (/{docsify-ignore-all}/g.test(str)) { str = str.replace('{docsify-ignore-all}', ''); - nextToc.title = removeAtag(str); nextToc.ignoreAllSubs = true; } const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); + nextToc.label = removeAtag(config.sidebar || str); + nextToc.title = removeAtag(str); nextToc.slug = url; _self.toc.push(nextToc); From cdca395b179110b83831ac918a7b6c46cf6e1582 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 11:35:21 +0200 Subject: [PATCH 04/15] Apply new functionality to doc file --- docs/helpers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/helpers.md b/docs/helpers.md index 356df6ec0..910846345 100644 --- a/docs/helpers.md +++ b/docs/helpers.md @@ -127,7 +127,7 @@ Only when you set both the `routerMode: 'history'` and `externalLinkTarget: '_se ### Hello, world! :id=hello-world ``` -## Customise item name for sidebar +## Customise the title for the sidebar items :sidebar="Customise title for sidebar" ```md ### How would I write a "hello, world" example? :sidebar="Hello, world?" From f536e724fd88d7bf2d05d7daf31ed8048ad6cdc6 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 11:50:26 +0200 Subject: [PATCH 05/15] Refactor for better readability --- src/core/render/compiler.js | 2 +- src/core/render/compiler/headline.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index d7db551ec..e047b61e5 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -231,8 +231,8 @@ export class Compiler { const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); - nextToc.label = removeAtag(config.sidebar || str); nextToc.title = removeAtag(str); + nextToc.label = config.sidebar || nextToc.title; nextToc.slug = url; _self.toc.push(nextToc); diff --git a/src/core/render/compiler/headline.js b/src/core/render/compiler/headline.js index 01fb547b3..f8643e776 100644 --- a/src/core/render/compiler/headline.js +++ b/src/core/render/compiler/headline.js @@ -28,8 +28,8 @@ export const headingCompiler = ({ renderer, router, _self }) => const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); - nextToc.label = removeAtag(config.sidebar || str); nextToc.title = removeAtag(str); + nextToc.label = config.sidebar || nextToc.title; nextToc.slug = url; _self.toc.push(nextToc); From e581df73c2ec048e17467527549d844614a49147 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 22:03:38 +0200 Subject: [PATCH 06/15] Support quoted strings for config value arguments --- src/core/render/utils.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/core/render/utils.js b/src/core/render/utils.js index 42fbfa078..564f8bcc2 100644 --- a/src/core/render/utils.js +++ b/src/core/render/utils.js @@ -23,16 +23,21 @@ export function getAndRemoveConfig(str = '') { if (str) { str = str + .replace( + /(?:^|\s):([\w-]+:?)=?([\w-%]+|"(?:[^"\\]|\\.)*")?/g, + (m, key, value) => { + if (key.indexOf(':') === -1) { + config[key] = + (value && value.replace(/"/g, '').replace(/(^"|"$)/g, '')) || + true; + return ''; + } + + return m; + } + ) .replace(/^('|")/, '') .replace(/('|")$/, '') - .replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g, (m, key, value) => { - if (key.indexOf(':') === -1) { - config[key] = (value && value.replace(/"/g, '')) || true; - return ''; - } - - return m; - }) .trim(); } From 27513e059efce08c06147de6acc4579e054260bf Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 22:42:55 +0200 Subject: [PATCH 07/15] Update render-util tests --- test/unit/render-util.test.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/test/unit/render-util.test.js b/test/unit/render-util.test.js index 574e0a8ef..0d7ae81a3 100644 --- a/test/unit/render-util.test.js +++ b/test/unit/render-util.test.js @@ -58,6 +58,19 @@ describe('core/render/utils', () => { str: `[filename](_media/example.md ":include")`, }); }); + + test('parse config with quoted string arguments', () => { + const result = getAndRemoveConfig( + `[filename](_media/example.md ':include :foo="bar :baz test"')` + ); + + expect(result).toMatchObject({ + config: { + foo: 'bar :baz test', + }, + str: `[filename](_media/example.md ':include')`, + }); + }); }); }); @@ -68,22 +81,32 @@ describe('core/render/tpl', () => { level: 2, slug: '#/cover?id=basic-usage', title: 'Basic usage', + label: 'Basic usage', }, { level: 2, slug: '#/cover?id=custom-background', title: 'Custom background', + label: 'Custom background', }, { level: 2, slug: '#/cover?id=test', title: 'icoTest', + label: + 'icoTest', + }, + { + level: 2, + slug: '#/cover?id=different-title-and-label', + title: 'Long title string', + label: 'Short string', }, ]); expect(result).toBe( - `` + `` ); }); }); From cf9e00bc051b5b0b5f0f346c51ef902e51fc3d63 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Thu, 23 Jun 2022 22:47:50 +0200 Subject: [PATCH 08/15] replace function chaining with more modified regex --- src/core/render/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/render/utils.js b/src/core/render/utils.js index 564f8bcc2..a495eb300 100644 --- a/src/core/render/utils.js +++ b/src/core/render/utils.js @@ -28,8 +28,7 @@ export function getAndRemoveConfig(str = '') { (m, key, value) => { if (key.indexOf(':') === -1) { config[key] = - (value && value.replace(/"/g, '').replace(/(^"|"$)/g, '')) || - true; + (value && value.replace(/(^"|"|"$)/g, '')) || true; return ''; } From 83e579578ed5b6a813af7c48d1402334d2e53375 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 10:19:17 +0200 Subject: [PATCH 09/15] replace 'label' with 'text' for more intuitive variable naming --- src/core/render/compiler.js | 2 +- src/core/render/compiler/headline.js | 2 +- src/core/render/tpl.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index e047b61e5..c59f5a1c9 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -232,7 +232,7 @@ export class Compiler { const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); nextToc.title = removeAtag(str); - nextToc.label = config.sidebar || nextToc.title; + nextToc.text = config.sidebar || nextToc.title; nextToc.slug = url; _self.toc.push(nextToc); diff --git a/src/core/render/compiler/headline.js b/src/core/render/compiler/headline.js index f8643e776..bbe9be19a 100644 --- a/src/core/render/compiler/headline.js +++ b/src/core/render/compiler/headline.js @@ -29,7 +29,7 @@ export const headingCompiler = ({ renderer, router, _self }) => const slug = slugify(config.id || str); const url = router.toURL(router.getCurrentPath(), { id: slug }); nextToc.title = removeAtag(str); - nextToc.label = config.sidebar || nextToc.title; + nextToc.text = config.sidebar || nextToc.title; nextToc.slug = url; _self.toc.push(nextToc); diff --git a/src/core/render/tpl.js b/src/core/render/tpl.js index c30f21c30..ff5668179 100644 --- a/src/core/render/tpl.js +++ b/src/core/render/tpl.js @@ -92,7 +92,7 @@ export function tree(toc, tpl = '
      {inner}
    ') { let innerHTML = ''; toc.forEach(node => { const title = node.title.replace(/(<([^>]+)>)/g, ''); - innerHTML += `
  • ${node.label}
  • `; + innerHTML += `
  • ${node.text}
  • `; if (node.children) { innerHTML += tree(node.children, tpl); } From b838fc36a7345af53d02893c602891eeb257e38f Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 10:20:09 +0200 Subject: [PATCH 10/15] Fix incorrect (escaped) quoted string parsing --- src/core/render/utils.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/render/utils.js b/src/core/render/utils.js index a495eb300..513a9208f 100644 --- a/src/core/render/utils.js +++ b/src/core/render/utils.js @@ -24,11 +24,10 @@ export function getAndRemoveConfig(str = '') { if (str) { str = str .replace( - /(?:^|\s):([\w-]+:?)=?([\w-%]+|"(?:[^"\\]|\\.)*")?/g, + /(?:^|\s):([\w-]+:?)=?([\w-%]+|"(?:[^"\\]|\\.)*")?/g, (m, key, value) => { if (key.indexOf(':') === -1) { - config[key] = - (value && value.replace(/(^"|"|"$)/g, '')) || true; + config[key] = (value && value.replace(/"/g, '')) || true; return ''; } From da7937e340ec44b514fc397b3c1b1734eb938952 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 10:53:41 +0200 Subject: [PATCH 11/15] Fix logic for html-escaped str argument Because the provided `str` argument has been html-escaped, with backslashes stripped, we can unfortunately not support escaped characters in quoted strings. --- src/core/render/utils.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/render/utils.js b/src/core/render/utils.js index 513a9208f..e2052c681 100644 --- a/src/core/render/utils.js +++ b/src/core/render/utils.js @@ -23,19 +23,20 @@ export function getAndRemoveConfig(str = '') { if (str) { str = str + .replace(/"/g, '"') .replace( - /(?:^|\s):([\w-]+:?)=?([\w-%]+|"(?:[^"\\]|\\.)*")?/g, + /(?:^|\s):([\w-]+:?)=?([\w-%]+|"[^"]*")?/g, // Note: because the provided `str` argument has been html-escaped, with backslashes stripped, we cannot support escaped characters in quoted strings :-( (m, key, value) => { if (key.indexOf(':') === -1) { - config[key] = (value && value.replace(/"/g, '')) || true; + config[key] = (value && value.replace(/"/g, '')) || true; return ''; } return m; } ) - .replace(/^('|")/, '') - .replace(/('|")$/, '') + .replace(/^('|")|('|")$/g, '') + .replace(/"/g, '"') .trim(); } From d66d22dc31713dd98eb3b7f724b4d6497da262b3 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 11:43:05 +0200 Subject: [PATCH 12/15] Fix test arguments to match reality --- test/unit/render-util.test.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/unit/render-util.test.js b/test/unit/render-util.test.js index 0d7ae81a3..df741b6bf 100644 --- a/test/unit/render-util.test.js +++ b/test/unit/render-util.test.js @@ -81,27 +81,26 @@ describe('core/render/tpl', () => { level: 2, slug: '#/cover?id=basic-usage', title: 'Basic usage', - label: 'Basic usage', + text: 'Basic usage', }, { level: 2, slug: '#/cover?id=custom-background', title: 'Custom background', - label: 'Custom background', + text: 'Custom background', }, { level: 2, slug: '#/cover?id=test', title: 'icoTest', - label: - 'icoTest', + text: 'icoTest', }, { level: 2, slug: '#/cover?id=different-title-and-label', title: 'Long title string', - label: 'Short string', + text: 'Short string', }, ]); From 71288be4902a3eee2ceed1695f8a6f55e6fc28c9 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 11:50:41 +0200 Subject: [PATCH 13/15] Change test to provide html-escaped string --- test/unit/render-util.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/render-util.test.js b/test/unit/render-util.test.js index df741b6bf..f4f1fc405 100644 --- a/test/unit/render-util.test.js +++ b/test/unit/render-util.test.js @@ -61,7 +61,7 @@ describe('core/render/utils', () => { test('parse config with quoted string arguments', () => { const result = getAndRemoveConfig( - `[filename](_media/example.md ':include :foo="bar :baz test"')` + `[filename](_media/example.md ':include :foo="bar :baz test"')` ); expect(result).toMatchObject({ From 79e738ace57d2b3f2baaaef40344d7ac514ae9d2 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 11:51:27 +0200 Subject: [PATCH 14/15] Change regex to work directly on html-escaped string --- src/core/render/utils.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/render/utils.js b/src/core/render/utils.js index e2052c681..bc3e61f5a 100644 --- a/src/core/render/utils.js +++ b/src/core/render/utils.js @@ -23,12 +23,11 @@ export function getAndRemoveConfig(str = '') { if (str) { str = str - .replace(/"/g, '"') .replace( - /(?:^|\s):([\w-]+:?)=?([\w-%]+|"[^"]*")?/g, // Note: because the provided `str` argument has been html-escaped, with backslashes stripped, we cannot support escaped characters in quoted strings :-( + /(?:^|\s):([\w-]+:?)=?([\w-%]+|"((?!").)*")?/g, // Note: because the provided `str` argument has been html-escaped, with backslashes stripped, we cannot support escaped characters in quoted strings :-( (m, key, value) => { if (key.indexOf(':') === -1) { - config[key] = (value && value.replace(/"/g, '')) || true; + config[key] = (value && value.replace(/"/g, '')) || true; return ''; } @@ -36,7 +35,6 @@ export function getAndRemoveConfig(str = '') { } ) .replace(/^('|")|('|")$/g, '') - .replace(/"/g, '"') .trim(); } From a6333ec636f77a827e3f67014eaa31b418347c28 Mon Sep 17 00:00:00 2001 From: Jacco-V Date: Mon, 4 Jul 2022 12:22:07 +0200 Subject: [PATCH 15/15] Add support for marked.md `smartypants` 'smart' quotes --- src/core/render/utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/render/utils.js b/src/core/render/utils.js index bc3e61f5a..beedeea54 100644 --- a/src/core/render/utils.js +++ b/src/core/render/utils.js @@ -24,10 +24,10 @@ export function getAndRemoveConfig(str = '') { if (str) { str = str .replace( - /(?:^|\s):([\w-]+:?)=?([\w-%]+|"((?!").)*")?/g, // Note: because the provided `str` argument has been html-escaped, with backslashes stripped, we cannot support escaped characters in quoted strings :-( - (m, key, value) => { + /(?:^|\s):([\w-]+:?)=?([\w-%]+|"((?!").)*"|[“”][^“”]*[“”])?/g, // Note: because the provided `str` argument has been html-escaped, with backslashes stripped, we cannot support escaped characters in quoted strings :-( + function (m, key, value) { if (key.indexOf(':') === -1) { - config[key] = (value && value.replace(/"/g, '')) || true; + config[key] = (value && value.replace(/"|[“”]/g, '')) || true; return ''; }