Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fragment plugin and parse fragmented list #148

Merged
merged 3 commits into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Added

- Parse lists in `*` and `1)` marker as fragmented list ([#145](https://github.com/marp-team/marpit/issues/145), [#148](https://github.com/marp-team/marpit/pull/148))

### Changed

- Upgrade dependent packages to the latest version ([#143](https://github.com/marp-team/marpit/pull/143))
Expand Down
52 changes: 52 additions & 0 deletions src/markdown/fragment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/** @module */
import marpitPlugin from './marpit_plugin'

const fragmentedListMarkups = ['*', ')']

/**
* Marpit fragment plugin.
*
* @alias module:markdown/fragment
* @param {MarkdownIt} md markdown-it instance.
*/
function fragment(md) {
// Fragmented list
md.core.ruler.after('marpit_directives_parse', 'marpit_fragment', state => {
if (state.inlineMode) return

for (const token of state.tokens) {
if (
token.type === 'list_item_open' &&
fragmentedListMarkups.includes(token.markup)
) {
token.meta = token.meta || {}
token.meta.marpitFragment = true
}
}
})

// Add data-marpit-fragment(s) attributes to token
md.core.ruler.after('marpit_fragment', 'marpit_apply_fragment', state => {
if (state.inlineMode) return

const fragments = { slide: undefined, count: 0 }

for (const token of state.tokens) {
if (token.meta && token.meta.marpitSlideElement === 1) {
fragments.slide = token
fragments.count = 0
} else if (token.meta && token.meta.marpitSlideElement === -1) {
if (fragments.slide && fragments.count > 0) {
fragments.slide.attrSet('data-marpit-fragments', fragments.count)
}
} else if (token.meta && token.meta.marpitFragment) {
fragments.count += 1

token.meta.marpitFragment = fragments.count
token.attrSet('data-marpit-fragment', fragments.count)
}
}
})
}

export default marpitPlugin(fragment)
4 changes: 3 additions & 1 deletion src/marpit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import marpitBackgroundImage from './markdown/background_image'
import marpitCollect from './markdown/collect'
import marpitComment from './markdown/comment'
import marpitContainerPlugin from './markdown/container'
import marpitFragment from './markdown/fragment'
import marpitHeaderAndFooter from './markdown/header_and_footer'
import marpitHeadingDivider from './markdown/heading_divider'
import marpitInlineSVG from './markdown/inline_svg'
Expand Down Expand Up @@ -179,8 +180,9 @@ class Marpit {
.use(marpitSweep)
.use(marpitInlineSVG)
.use(marpitStyleAssign)
.use(marpitCollect)
.use(marpitBackgroundImage)
.use(marpitFragment)
.use(marpitCollect)
}

/**
Expand Down
84 changes: 84 additions & 0 deletions test/markdown/fragment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import cheerio from 'cheerio'
import MarkdownIt from 'markdown-it'
import fragment from '../../src/markdown/fragment'
import slide from '../../src/markdown/slide'

describe('Marpit fragment plugin', () => {
const md = () => {
const instance = new MarkdownIt('commonmark')
instance.marpit = {}

return instance
.use(slide)
.use(m => m.core.ruler.push('marpit_directives_parse', () => {}))
.use(fragment)
}

describe('Fragmented unordered list', () => {
context('when using "*" markup', () => {
const markdown = '* A\n* B\n* C'
const $ = cheerio.load(md().render(markdown))

it('adds data-marpit-fragment attribute to <li> with index', () => {
const li = $('ul > li[data-marpit-fragment]')
expect(li).toHaveLength(3)

const indexes = li.map((_, el) => $(el).data('marpit-fragment')).get()
expect(indexes).toStrictEqual([1, 2, 3])
})

it('adds data-marpit-fragments attribute to <section> with count of fragments', () => {
const section = $('section[data-marpit-fragments]')

expect(section).toHaveLength(1)
expect(section.data('marpit-fragments')).toBe(3)
})
})

for (const char of ['-', '+']) {
context(`when using "${char}" markup`, () => {
const markdown = `${char} A\n${char} B\n${char} C`
const $ = cheerio.load(md().render(markdown))

it('does not add data-marpit-fragment attribute', () =>
expect($('[data-marpit-fragment]')).toHaveLength(0))

it('does not add data-marpit-fragments attribute', () =>
expect($('[data-marpit-fragments]')).toHaveLength(0))
})
}
})

describe('Fragmented ordered list', () => {
context('when using "1)" markup', () => {
const markdown = '1) A\n1) B\n1) C'
const $ = cheerio.load(md().render(markdown))

it('adds data-marpit-fragment attribute to <li> with index', () => {
const li = $('ol > li[data-marpit-fragment]')
expect(li).toHaveLength(3)

const indexes = li.map((_, el) => $(el).data('marpit-fragment')).get()
expect(indexes).toStrictEqual([1, 2, 3])
})

it('adds data-marpit-fragments attribute to <section> with count of fragments', () => {
const section = $('section[data-marpit-fragments]')

expect(section).toHaveLength(1)
expect(section.data('marpit-fragments')).toBe(3)
})
})

context(`when using "1." markup`, () => {
const markdown = `1. A\n1. B\n1. C`
const $ = cheerio.load(md().render(markdown))

it('does not add data-marpit-fragment attribute', () =>
expect($('[data-marpit-fragment]')).toHaveLength(0))

it('does not add data-marpit-fragments attribute', () =>
expect($('[data-marpit-fragments]')).toHaveLength(0))
})
})
})