diff --git a/src/breadcrumb-nav.ts b/src/breadcrumb-nav.ts deleted file mode 100644 index 6e8c2df..0000000 --- a/src/breadcrumb-nav.ts +++ /dev/null @@ -1,64 +0,0 @@ - -import { marked } from 'marked'; -import { ParsedAttributes, parse_attributes } from './attrs'; -import { MarkdownOptions } from './render'; - -// todo: deprecate this - -export interface BreadcrumbNavToken extends marked.Tokens.Generic { - text: string; - attrs: ParsedAttributes; - items: marked.Token[][]; -} - -export function breadcrumb_nav_ext(renderer: marked.Renderer, opts: MarkdownOptions) : marked.TokenizerExtension & marked.RendererExtension { - return { - name: 'breadcrumb_nav', - level: 'block', - start: (src) => src.match(/^\/\/\//)?.index, - tokenizer(src, tokens) { - const rule = /^\/\/\/(\/*)([^\n]+)?(?:\n)((?:[^\/]|\/\/?(?!\/\1))+)\/\/\/\1/; - const match = rule.exec(src); - - if (match) { - const token: BreadcrumbNavToken = { - type: 'breadcrumb_nav', - raw: match[0], - text: match[3], - attrs: parse_attributes(match[2] || ''), - tokens: [ ], - items: [ ], - }; - - const lines = match[3].trim().split('\n'); - - for (const line of lines) { - const tokens = this.lexer.inlineTokens(line, [ ]); - token.tokens.push(...tokens); - token.items.push(tokens); - } - - return token; - } - }, - renderer(token: BreadcrumbNavToken) { - return ``; - } - }; -} diff --git a/src/description-list.ts b/src/description-list.ts index 9b1afbf..6eef6d2 100644 --- a/src/description-list.ts +++ b/src/description-list.ts @@ -3,14 +3,19 @@ import { marked } from 'marked'; import { MarkdownOptions } from './render'; export interface DescriptionListToken extends marked.Tokens.Generic { + type: 'description_list'; items: (DescriptionTermToken | DescriptionDetailToken)[]; } +export type DescriptionElemToken = DescriptionTermToken | DescriptionDetailToken; + export interface DescriptionTermToken extends marked.Tokens.Generic { + type: 'description_term'; text: string; } export interface DescriptionDetailToken extends marked.Tokens.Generic { + type: 'description_detail'; text: string; } @@ -18,72 +23,107 @@ export function description_list_ext(renderer: marked.Renderer, opts: MarkdownOp return { name: 'description_list', level: 'block', - start: (src) => src.match(/^:[:#-]/)?.index, + start: (src) => src.match(/^: /)?.index, tokenizer(src, tokens) { - const rule = /^(?::[:#-](?:\s[^\n]*)?(?:\n|$))+/; - const match = rule.exec(src); + const start = src.match(/^: /)?.index; + const lines = src.slice(start).split(/\n/g); + const token: DescriptionListToken = { + type: 'description_list', + raw: '', + items: [ ] + }; - if (match) { - const token: DescriptionListToken = { - type: 'description_list', - raw: match[0], - items: [ ] - }; + let current: DescriptionElemToken; + const render_current = () => { + if (current) { + this.lexer.blockTokens(current.text, current.tokens); + current = null; + } + }; - const items = token.raw.trim().split('\n'); - const raw_buffer: string[] = [ ]; - const text_buffer: string[] = [ ]; + for (const line of lines) { + // Skip empty lines + if (! line.trim()) { + token.raw += line + '\n'; - const flush_buffer = () => { - if (! raw_buffer.length) { - return; + if (current) { + current.raw += line + '\n'; + current.text += '\n'; } - // Grab the second character from the first line to determine the - // token type (should be "#" or "-") - const type = raw_buffer[0][1] === '#' ? 'description_term' : 'description_detail'; - - const sub_token: (DescriptionTermToken | DescriptionDetailToken) = { - type, - raw: raw_buffer.join('\n'), - text: text_buffer.join('\n'), - tokens: [ ], - }; - - raw_buffer.length = 0; - text_buffer.length = 0; - - this.lexer.blockTokens(sub_token.text, sub_token.tokens); - token.items.push(sub_token); - }; - - for (const line of items) { - const rule = /^:([:#-])(?:\s([^\n]*))?(?:\n|$)/; - const match = rule.exec(line); - - if (match) { - if (match[1] !== ':') { - flush_buffer(); - } - - raw_buffer.push(match[0]); - text_buffer.push(match[2]); - } + continue; } - flush_buffer(); - + // If the line starts immediately with a colon, it is a
+ if (line.startsWith(': ')) { + render_current(); + token.raw += line + '\n'; + token.items.push( + current = { + type: 'description_term', + raw: line, + text: line.slice(2), + tokens: [ ], + } + ); + continue; + } + + // If the line starts with a colon after an indent, it is a
+ if (line.startsWith(' : ')) { + render_current(); + token.raw += line + '\n'; + token.items.push( + current = { + type: 'description_detail', + raw: line, + text: line.slice(4), + tokens: [ ], + } + ); + continue; + } + + // If the line starts with (at least) two indents, it is a child + // of the current element + if (line.startsWith(' ')) { + token.raw += line + '\n'; + current.raw += '\n' + line; + current.text += '\n' + line.slice(current.type === 'description_term' ? 2 : 4); + continue; + } + + // If the line starts with one indent, it is a child of the current + //
(but is not allowed after a
) + if (line.startsWith(' ')) { + if (current.type !== 'description_term') { + render_current(); + break; + } + + token.raw += line + '\n'; + current.raw += '\n' + line; + current.text += '\n' + line.slice(2); + continue; + } + + // If the line starts any other way, it is the start of new content + // and we are done parsing + render_current(); + break; + } + + render_current(); + + if (token.items.length) { + console.log(token); return token; } }, renderer(token: DescriptionListToken) { const items = token.items.map((item) => { const tag = item.type === 'description_term' ? 'dt' : 'dd'; - return ` - <${tag}> - ${this.parser.parse(item.tokens)} - - `; + return `<${tag}>${this.parser.parse(item.tokens)}`; }); return `
${items.join('')}
`; diff --git a/src/render.ts b/src/render.ts index 5cb6bc2..47d43d9 100644 --- a/src/render.ts +++ b/src/render.ts @@ -9,7 +9,6 @@ import { katex_block_ext, katex_inline_ext } from './katex'; import { footnote_list_ext, footnote_ref_ext } from './footnotes'; import { description_list_ext } from './description-list'; import { resolve_async_bindings } from './async-steps'; -import { breadcrumb_nav_ext } from './breadcrumb-nav'; import { base_url_walk_tokens } from './base-url'; export interface MarkdownOptions { @@ -65,7 +64,6 @@ function setup_marked(options: MarkdownOptions, marked_options: marked.MarkedOpt description_list_ext(marked_options.renderer, options), section_ext(marked_options.renderer, options), icon_ext(marked_options.renderer, options), - breadcrumb_nav_ext(marked_options.renderer, options), ...(options.extensions || [ ]).map((ext) => { return ext(marked_options.renderer, options); }), diff --git a/src/renderer.ts b/src/renderer.ts index b069569..479e9a7 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -152,6 +152,10 @@ function code(renderer: marked.Renderer, opts: MarkdownOptions) { const binding = bind_data_async(promise); return figure(binding); }; + + case 'yaml:calendar': { + // todo + }; default: return figure(`
${render_prism(code, args[0])}
`);