mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-11 08:46:20 +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 striptags from 'striptags'
|
||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||
import markdownToc from 'browser/lib/markdown-toc-generator'
|
||||
|
||||
class MarkdownNoteDetail extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -47,6 +48,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
this.dispatchTimer = null
|
||||
|
||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||
this.generateToc = () => this.handleGenerateToc()
|
||||
}
|
||||
|
||||
focus () {
|
||||
@@ -59,6 +61,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
|
||||
this.handleSwitchMode(reversedType)
|
||||
})
|
||||
ee.on('code:generate-toc', this.generateToc)
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
@@ -75,6 +78,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
|
||||
componentWillUnmount () {
|
||||
ee.off('topbar:togglelockbutton', this.toggleLockButton)
|
||||
ee.off('code:generate-toc', this.generateToc)
|
||||
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) {
|
||||
this.focus()
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import InfoPanelTrashed from './InfoPanelTrashed'
|
||||
import { formatDate } from 'browser/lib/date-formatter'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||
import markdownToc from 'browser/lib/markdown-toc-generator'
|
||||
|
||||
const electron = require('electron')
|
||||
const { remote } = electron
|
||||
@@ -52,6 +53,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
this.scrollToNextTabThreshold = 0.7
|
||||
this.generateToc = () => this.handleGenerateToc()
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -65,6 +67,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
enableLeftArrow: allTabs.offsetLeft !== 0
|
||||
})
|
||||
}
|
||||
ee.on('code:generate-toc', this.generateToc)
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
@@ -91,6 +94,16 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
componentWillUnmount () {
|
||||
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) {
|
||||
@@ -441,7 +454,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
const isSuper = global.process.platform === 'darwin'
|
||||
? e.metaKey
|
||||
: e.ctrlKey
|
||||
if (isSuper && !e.shiftKey) {
|
||||
if (isSuper && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault()
|
||||
this.addSnippet()
|
||||
}
|
||||
|
||||
@@ -145,6 +145,16 @@ const file = {
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Generate/Update Markdown TOC',
|
||||
accelerator: 'Shift+Ctrl+T',
|
||||
click () {
|
||||
mainWindow.webContents.send('code:generate-toc')
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Print',
|
||||
accelerator: 'CommandOrControl+P',
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
"markdown-it-named-headers": "^0.0.4",
|
||||
"markdown-it-plantuml": "^1.1.0",
|
||||
"markdown-it-smartarrows": "^1.0.1",
|
||||
"markdown-toc": "^1.2.0",
|
||||
"mdurl": "^1.0.1",
|
||||
"mermaid": "^8.0.0-rc.8",
|
||||
"moment": "^2.10.3",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
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 = {
|
||||
// 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-plantuml',
|
||||
'markdown-it-admonition',
|
||||
'markdown-toc',
|
||||
'devtron',
|
||||
'@rokt33r/season',
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user