mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
Merge pull request #2281 from mbarczak/features/toc_generator
Automatic table of contents generation for Markdown
This commit is contained in:
100
browser/lib/markdown-toc-generator.js
Normal file
100
browser/lib/markdown-toc-generator.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Markdown table of contents generator
|
||||||
|
*/
|
||||||
|
|
||||||
|
import toc from 'markdown-toc'
|
||||||
|
import diacritics from 'diacritics-map'
|
||||||
|
import stripColor from 'strip-color'
|
||||||
|
|
||||||
|
const EOL = require('os').EOL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
const TOC_MARKER_START = '<!-- toc -->'
|
||||||
|
const TOC_MARKER_END = '<!-- tocstop -->'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes care of proper updating given editor with TOC.
|
||||||
|
* If TOC doesn't exit in the editor, it's inserted at current caret position.
|
||||||
|
* Otherwise,TOC is updated in place.
|
||||||
|
* @param editor CodeMirror editor to be updated with TOC
|
||||||
|
*/
|
||||||
|
export function generateInEditor (editor) {
|
||||||
|
const tocRegex = new RegExp(`${TOC_MARKER_START}[\\s\\S]*?${TOC_MARKER_END}`)
|
||||||
|
|
||||||
|
function tocExistsInEditor () {
|
||||||
|
return tocRegex.test(editor.getValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateExistingToc () {
|
||||||
|
const toc = generate(editor.getValue())
|
||||||
|
const search = editor.getSearchCursor(tocRegex)
|
||||||
|
while (search.findNext()) {
|
||||||
|
search.replace(toc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTocAtCursorPosition () {
|
||||||
|
const toc = generate(editor.getRange(editor.getCursor(), {line: Infinity}))
|
||||||
|
editor.replaceRange(wrapTocWithEol(toc, editor), editor.getCursor())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tocExistsInEditor()) {
|
||||||
|
updateExistingToc()
|
||||||
|
} else {
|
||||||
|
addTocAtCursorPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates MD TOC based on MD document passed as string.
|
||||||
|
* @param markdownText MD document
|
||||||
|
* @returns generatedTOC String containing generated TOC
|
||||||
|
*/
|
||||||
|
export function generate (markdownText) {
|
||||||
|
const generatedToc = toc(markdownText, {slugify: caseSensitiveSlugify})
|
||||||
|
return TOC_MARKER_START + EOL + EOL + generatedToc.content + EOL + EOL + TOC_MARKER_END
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapTocWithEol (toc, editor) {
|
||||||
|
const leftWrap = editor.getCursor().ch === 0 ? '' : EOL
|
||||||
|
const rightWrap = editor.getLine(editor.getCursor().line).length === editor.getCursor().ch ? '' : EOL
|
||||||
|
return leftWrap + toc + rightWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
generate,
|
||||||
|
generateInEditor
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import { formatDate } from 'browser/lib/date-formatter'
|
|||||||
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
|
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
|
||||||
import striptags from 'striptags'
|
import striptags from 'striptags'
|
||||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||||
|
import markdownToc from 'browser/lib/markdown-toc-generator'
|
||||||
|
|
||||||
class MarkdownNoteDetail extends React.Component {
|
class MarkdownNoteDetail extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -47,6 +48,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
this.dispatchTimer = null
|
this.dispatchTimer = null
|
||||||
|
|
||||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||||
|
this.generateToc = () => this.handleGenerateToc()
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus () {
|
||||||
@@ -59,6 +61,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
|
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
|
||||||
this.handleSwitchMode(reversedType)
|
this.handleSwitchMode(reversedType)
|
||||||
})
|
})
|
||||||
|
ee.on('code:generate-toc', this.generateToc)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
@@ -75,6 +78,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
ee.off('topbar:togglelockbutton', this.toggleLockButton)
|
ee.off('topbar:togglelockbutton', this.toggleLockButton)
|
||||||
|
ee.off('code:generate-toc', this.generateToc)
|
||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +266,11 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleGenerateToc () {
|
||||||
|
const editor = this.refs.content.refs.code.editor
|
||||||
|
markdownToc.generateInEditor(editor)
|
||||||
|
}
|
||||||
|
|
||||||
handleFocus (e) {
|
handleFocus (e) {
|
||||||
this.focus()
|
this.focus()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import InfoPanelTrashed from './InfoPanelTrashed'
|
|||||||
import { formatDate } from 'browser/lib/date-formatter'
|
import { formatDate } from 'browser/lib/date-formatter'
|
||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||||
|
import markdownToc from 'browser/lib/markdown-toc-generator'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { remote } = electron
|
const { remote } = electron
|
||||||
@@ -52,6 +53,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.scrollToNextTabThreshold = 0.7
|
this.scrollToNextTabThreshold = 0.7
|
||||||
|
this.generateToc = () => this.handleGenerateToc()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
@@ -65,6 +67,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
enableLeftArrow: allTabs.offsetLeft !== 0
|
enableLeftArrow: allTabs.offsetLeft !== 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
ee.on('code:generate-toc', this.generateToc)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
@@ -91,6 +94,16 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
|
ee.off('code:generate-toc', this.generateToc)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGenerateToc () {
|
||||||
|
const { note, snippetIndex } = this.state
|
||||||
|
const currentMode = note.snippets[snippetIndex].mode
|
||||||
|
if (currentMode.includes('Markdown')) {
|
||||||
|
const currentEditor = this.refs[`code-${snippetIndex}`].refs.code.editor
|
||||||
|
markdownToc.generateInEditor(currentEditor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange (e) {
|
handleChange (e) {
|
||||||
@@ -441,7 +454,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
const isSuper = global.process.platform === 'darwin'
|
const isSuper = global.process.platform === 'darwin'
|
||||||
? e.metaKey
|
? e.metaKey
|
||||||
: e.ctrlKey
|
: e.ctrlKey
|
||||||
if (isSuper && !e.shiftKey) {
|
if (isSuper && !e.shiftKey && !e.altKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.addSnippet()
|
this.addSnippet()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,16 @@ const file = {
|
|||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Generate/Update Markdown TOC',
|
||||||
|
accelerator: 'Shift+Ctrl+T',
|
||||||
|
click () {
|
||||||
|
mainWindow.webContents.send('code:generate-toc')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Print',
|
label: 'Print',
|
||||||
accelerator: 'CommandOrControl+P',
|
accelerator: 'CommandOrControl+P',
|
||||||
|
|||||||
@@ -80,6 +80,7 @@
|
|||||||
"markdown-it-named-headers": "^0.0.4",
|
"markdown-it-named-headers": "^0.0.4",
|
||||||
"markdown-it-plantuml": "^1.1.0",
|
"markdown-it-plantuml": "^1.1.0",
|
||||||
"markdown-it-smartarrows": "^1.0.1",
|
"markdown-it-smartarrows": "^1.0.1",
|
||||||
|
"markdown-toc": "^1.2.0",
|
||||||
"mdurl": "^1.0.1",
|
"mdurl": "^1.0.1",
|
||||||
"mermaid": "^8.0.0-rc.8",
|
"mermaid": "^8.0.0-rc.8",
|
||||||
"moment": "^2.10.3",
|
"moment": "^2.10.3",
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
import browserEnv from 'browser-env'
|
import browserEnv from 'browser-env'
|
||||||
browserEnv(['window', 'document'])
|
browserEnv(['window', 'document', 'navigator'])
|
||||||
|
|
||||||
|
// for CodeMirror mockup
|
||||||
|
document.body.createTextRange = function () {
|
||||||
|
return {
|
||||||
|
setEnd: function () {},
|
||||||
|
setStart: function () {},
|
||||||
|
getBoundingClientRect: function () {
|
||||||
|
return {right: 0}
|
||||||
|
},
|
||||||
|
getClientRects: function () {
|
||||||
|
return {
|
||||||
|
length: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.localStorage = {
|
window.localStorage = {
|
||||||
// polyfill
|
// polyfill
|
||||||
|
|||||||
668
tests/lib/markdown-toc-generator-test.js
Normal file
668
tests/lib/markdown-toc-generator-test.js
Normal file
@@ -0,0 +1,668 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Unit test for browser/lib/markdown-toc-generator
|
||||||
|
*/
|
||||||
|
|
||||||
|
import CodeMirror from 'codemirror'
|
||||||
|
require('codemirror/addon/search/searchcursor.js')
|
||||||
|
const test = require('ava')
|
||||||
|
const markdownToc = require('browser/lib/markdown-toc-generator')
|
||||||
|
const EOL = require('os').EOL
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
/**
|
||||||
|
* Contains array of test cases in format :
|
||||||
|
* [
|
||||||
|
* test title
|
||||||
|
* input markdown,
|
||||||
|
* expected toc
|
||||||
|
* ]
|
||||||
|
* @type {*[]}
|
||||||
|
*/
|
||||||
|
const testCases = [
|
||||||
|
[
|
||||||
|
'***************************** empty note',
|
||||||
|
`
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** single level',
|
||||||
|
`
|
||||||
|
# one
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** two levels',
|
||||||
|
`
|
||||||
|
# one
|
||||||
|
# two
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
- [two](#two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** 3 levels with children',
|
||||||
|
`
|
||||||
|
# one
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
## two two
|
||||||
|
# three
|
||||||
|
## three three
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
- [three](#three)
|
||||||
|
* [three three](#three-three)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** 3 levels, 3rd with 6 sub-levels',
|
||||||
|
`
|
||||||
|
# one
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
## two two
|
||||||
|
# three
|
||||||
|
## three three
|
||||||
|
### three three three
|
||||||
|
#### three three three three
|
||||||
|
##### three three three three three
|
||||||
|
###### three three three three three three
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
- [three](#three)
|
||||||
|
* [three three](#three-three)
|
||||||
|
+ [three three three](#three-three-three)
|
||||||
|
- [three three three three](#three-three-three-three)
|
||||||
|
* [three three three three three](#three-three-three-three-three)
|
||||||
|
+ [three three three three three three](#three-three-three-three-three-three)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** multilevel with texts in between',
|
||||||
|
`
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
# three
|
||||||
|
this is a level three three text
|
||||||
|
this is a level three three text
|
||||||
|
## three three
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
### three three three
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
### three three three 2
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
#### three three three three
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
#### three three three three 2
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
##### three three three three three
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
##### three three three three three 2
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
###### three three three three three three
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
this is a text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
- [three](#three)
|
||||||
|
* [three three](#three-three)
|
||||||
|
+ [three three three](#three-three-three)
|
||||||
|
+ [three three three 2](#three-three-three-2)
|
||||||
|
- [three three three three](#three-three-three-three)
|
||||||
|
- [three three three three 2](#three-three-three-three-2)
|
||||||
|
* [three three three three three](#three-three-three-three-three)
|
||||||
|
* [three three three three three 2](#three-three-three-three-three-2)
|
||||||
|
+ [three three three three three three](#three-three-three-three-three-three)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** outdated TOC',
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
|
||||||
|
# one modified
|
||||||
|
## one one
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one modified](#one-modified)
|
||||||
|
* [one one](#one-one)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** properly generated case sensitive TOC',
|
||||||
|
`
|
||||||
|
# onE
|
||||||
|
## oNe one
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [onE](#onE)
|
||||||
|
* [oNe one](#oNe-one)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** position of TOC is stable (do not use elements above toc marker)',
|
||||||
|
`
|
||||||
|
# title
|
||||||
|
|
||||||
|
this is a text
|
||||||
|
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [onE](#onE)
|
||||||
|
* [oNe one](#oNe-one)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
|
||||||
|
# onE
|
||||||
|
## oNe one
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [onE](#onE)
|
||||||
|
* [oNe one](#oNe-one)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'***************************** properly handle generation of not completed TOC',
|
||||||
|
`
|
||||||
|
# hoge
|
||||||
|
|
||||||
|
##
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [hoge](#hoge)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
testCases.forEach(testCase => {
|
||||||
|
const title = testCase[0]
|
||||||
|
const inputMd = testCase[1].trim()
|
||||||
|
const expectedToc = testCase[2].trim()
|
||||||
|
const generatedToc = markdownToc.generate(inputMd)
|
||||||
|
|
||||||
|
t.is(generatedToc, expectedToc, `generate test : ${title} , generated : ${EOL}${generatedToc}, expected : ${EOL}${expectedToc}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
/**
|
||||||
|
* Contains array of test cases in format :
|
||||||
|
* [
|
||||||
|
* title
|
||||||
|
* cursor
|
||||||
|
* inputMd
|
||||||
|
* expectedMd
|
||||||
|
* ]
|
||||||
|
* @type {*[]}
|
||||||
|
*/
|
||||||
|
const testCases = [
|
||||||
|
[
|
||||||
|
`***************************** Empty note, cursor at the top`,
|
||||||
|
{line: 0, ch: 0},
|
||||||
|
``,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Two level note,TOC at the beginning `,
|
||||||
|
{line: 0, ch: 0},
|
||||||
|
`
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Two level note, cursor just after 'header text' `,
|
||||||
|
{line: 1, ch: 12},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Two level note, cursor at empty line under 'header text' `,
|
||||||
|
{line: 2, ch: 0},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Two level note, cursor just before 'text' word`,
|
||||||
|
{line: 1, ch: 8},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
text
|
||||||
|
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Already generated TOC without header file, regenerate TOC in place, no changes`,
|
||||||
|
{line: 13, ch: 0},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Already generated TOC, needs updating in place`,
|
||||||
|
{line: 0, ch: 0},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [one](#one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# This is the one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [This is the one](#This-is-the-one)
|
||||||
|
* [one one](#one-one)
|
||||||
|
- [two](#two)
|
||||||
|
* [two two](#two-two)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# This is the one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Document with cursor at the last line, expecting empty TOC `,
|
||||||
|
{line: 13, ch: 30},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# This is the one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# This is the one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`***************************** Empty, not actual TOC , should be supplemented with two new points beneath`,
|
||||||
|
{line: 0, ch: 0},
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# This is the one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# new point included in toc
|
||||||
|
## new subpoint
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
# header
|
||||||
|
header text
|
||||||
|
|
||||||
|
# This is the one
|
||||||
|
this is a level one text
|
||||||
|
this is a level one text
|
||||||
|
|
||||||
|
## one one
|
||||||
|
# two
|
||||||
|
this is a level two text
|
||||||
|
this is a level two text
|
||||||
|
## two two
|
||||||
|
this is a level two two text
|
||||||
|
this is a level two two text
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
|
- [new point included in toc](#new-point-included-in-toc)
|
||||||
|
* [new subpoint](#new-subpoint)
|
||||||
|
|
||||||
|
<!-- tocstop -->
|
||||||
|
# new point included in toc
|
||||||
|
## new subpoint
|
||||||
|
`
|
||||||
|
]
|
||||||
|
]
|
||||||
|
testCases.forEach(testCase => {
|
||||||
|
const title = testCase[0]
|
||||||
|
const cursor = testCase[1]
|
||||||
|
const inputMd = testCase[2].trim()
|
||||||
|
const expectedMd = testCase[3].trim()
|
||||||
|
|
||||||
|
const editor = CodeMirror()
|
||||||
|
editor.setValue(inputMd)
|
||||||
|
editor.setCursor(cursor)
|
||||||
|
markdownToc.generateInEditor(editor)
|
||||||
|
|
||||||
|
t.is(expectedMd, editor.getValue(), `generateInEditor test : ${title} , generated : ${EOL}${editor.getValue()}, expected : ${EOL}${expectedMd}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -41,6 +41,7 @@ var config = {
|
|||||||
'markdown-it-kbd',
|
'markdown-it-kbd',
|
||||||
'markdown-it-plantuml',
|
'markdown-it-plantuml',
|
||||||
'markdown-it-admonition',
|
'markdown-it-admonition',
|
||||||
|
'markdown-toc',
|
||||||
'devtron',
|
'devtron',
|
||||||
'@rokt33r/season',
|
'@rokt33r/season',
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user