Marked: How can I close render '_'

Created on 20 Aug 2019  路  15Comments  路  Source: markedjs/marked

I use KateX in this. The '_' in KateX is a subscript, but marked render it to \ How can I close this '_' render?

question

Most helpful comment

If anyone needs it - another example, KaTeX rendered everywhere except of inside the code ticks.

type something = any

const marked = require('marked')
const katex = require('katex')

const renderer = new marked.Renderer()

let i = 0
const next_id = () => `__special_katext_id_${i++}__`
const math_expressions: { [key: string]: { type: 'block' | 'inline', expression: string } } = {}

function replace_math_with_ids(text: string) {
  // Qllowing newlines inside of `$$...$$`
  text = text.replace(/\$\$([\s\S]+?)\$\$/g, (_match, expression) => {
    const id = next_id()
    math_expressions[id] = { type: 'block', expression }
    return id
  })

  // Not allowing newlines or space inside of `$...$`
  text = text.replace(/\$([^\n\s]+?)\$/g, (_match, expression) => {
    const id = next_id()
    math_expressions[id] = { type: 'inline', expression }
    return id
  })

  return text
}

const original_listitem = renderer.listitem
renderer.listitem = function(text: string, task: boolean, checked: boolean) {
  return original_listitem(replace_math_with_ids(text), task, checked)
}

const original_paragraph = renderer.paragraph
renderer.paragraph = function(text: string) {
  return original_paragraph(replace_math_with_ids(text))
}

const original_tablecell = renderer.tablecell
renderer.tablecell = function(content: string, flags: Object) {
  return original_tablecell(replace_math_with_ids(content), flags)
}

// Inline level, maybe unneded
const original_text = renderer.text
renderer.text = function(text: string) {
  return original_text(replace_math_with_ids(text))
}


// const md = 'some text `$$c=\sqrt{a^2 + b^2}$$` another text'
const md = 'some text $$c=\sqrt{a^2 + b^2}$$ another text'

const rendered_md_only: string = marked(md, { renderer: renderer })
console.log(rendered_md_only)

const rendered = rendered_md_only.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
  const { type, expression } = math_expressions[capture]
  return katex.renderToString(expression, { displayMode: type == 'block' })
})
console.log(rendered)

All 15 comments

Anything that is code should be wrapped in backticks (`), or indented 4 spaces. Code won't be rendered as markdown.

Could you share some markdown that isn't working correctly?

$$x(t)\delta \left( t-{{t}_{0}} \right)=x\left( {{t}_{0}} \right)\delta \left( t-{{t}_{0}} \right)$$got
$${x}'\left( t \right)\delta \left( t-{{t}_{0}} \right)+x\left( t \right){\delta }'\left( t-{{t}_{0}} \right)=x\left( {{t}_{0}} \right){\delta }'\left( t-{{t}_{0}} \right)$$

I got the same question. And this is the demo markdown

My editor support use $$ $$ to write katex, however marked.js does not recognize it.

And these code run good with latest marked.js:
a$$sin(\Omega_{0}n)$$,b$$e^{j\Omega_{0}n}$$ $$\Omega_{0}$$c$$2\pi$$

Anything that is code should be wrapped in backticks (`), or indented 4 spaces. Code won't be rendered as markdown.

@UziTech You are right, thanks.

And I wonder if I could change the InlineLexer.prototype.output method to check em when text not in $$ $$ ? Thanks

I thought I could custom this:

Renderer.prototype.em = function(text) {
  return '<em>' + text + '</em>';
};

To check text if contains $$

If you want to use katex with marked you can modify the renderer to check if code spans are katex.

I pulled this out of marked-katex (it hasn't been updated in a while so I wouldn't use that package).

Here is a working example:

const marked = require('marked');
const katex = require('katex');

const renderer = new marked.Renderer();

function mathsExpression(expr) {
  if (expr.match(/^\$\$[\s\S]*\$\$$/)) {
    expr = expr.substr(2, expr.length - 4);
    return katex.renderToString(expr, { displayMode: true });
  } else if (expr.match(/^\$[\s\S]*\$$/)) {
    expr = expr.substr(1, expr.length - 2);
    return katex.renderToString(expr, { isplayMode: false });
  }
}

const rendererCode = renderer.code;
renderer.code = function(code, lang, escaped) {
  if (!lang) {
    const math = mathsExpression(code);
    if (math) {
      return math;
    }
  }

  return rendererCode(code, lang, escaped);
};

const rendererCodespan = renderer.codespan;
renderer.codespan = function(text) {
  const math = mathsExpression(text);

  if (math) {
    return math;
  }

  return rendererCodespan(text);
};

const md = '`$$c=\sqrt{a^2 + b^2}$$`';

console.log(marked(md, { renderer: renderer }));

Could I add some code or config to make the $$ $$ part not to be marked? Thanks.

Anything that is code should be wrapped in backticks (`), or indented 4 spaces. Code won't be rendered as markdown.

Could you share some markdown that isn't working correctly?

Firstly, use (`)can solve the problem.

some markdown that isn't working correctly
1.${x}_{2}$,${x}_{2}$,
this code has two '_', marked cant render it correctly, render the '_' to \

2.$x_2$,$x_2$,or ${x}_2$,${x}_2$
but this code, marked render it successfully, do not render '_' to \

Anything that is code should be wrapped in backticks (`), or indented 4 spaces. Code won't be rendered as markdown.

Could you share some markdown that isn't working correctly?

@UziTech

console.log(marked('$$(\lambda_{1})^{n}u(n)*(\lambda_{2})^{n}u(n)$$', { renderer: renderer }));
VM423:1 <p>$$(lambda<em>{1})^{n}u(n)*(lambda</em>{2})^{n}u(n)$$</p>

I'm going to close this because it sounds like using backticks solved your problem. If that is not the case, feel free to reopen it.

If anyone needs it - another example, KaTeX rendered everywhere except of inside the code ticks.

type something = any

const marked = require('marked')
const katex = require('katex')

const renderer = new marked.Renderer()

let i = 0
const next_id = () => `__special_katext_id_${i++}__`
const math_expressions: { [key: string]: { type: 'block' | 'inline', expression: string } } = {}

function replace_math_with_ids(text: string) {
  // Qllowing newlines inside of `$$...$$`
  text = text.replace(/\$\$([\s\S]+?)\$\$/g, (_match, expression) => {
    const id = next_id()
    math_expressions[id] = { type: 'block', expression }
    return id
  })

  // Not allowing newlines or space inside of `$...$`
  text = text.replace(/\$([^\n\s]+?)\$/g, (_match, expression) => {
    const id = next_id()
    math_expressions[id] = { type: 'inline', expression }
    return id
  })

  return text
}

const original_listitem = renderer.listitem
renderer.listitem = function(text: string, task: boolean, checked: boolean) {
  return original_listitem(replace_math_with_ids(text), task, checked)
}

const original_paragraph = renderer.paragraph
renderer.paragraph = function(text: string) {
  return original_paragraph(replace_math_with_ids(text))
}

const original_tablecell = renderer.tablecell
renderer.tablecell = function(content: string, flags: Object) {
  return original_tablecell(replace_math_with_ids(content), flags)
}

// Inline level, maybe unneded
const original_text = renderer.text
renderer.text = function(text: string) {
  return original_text(replace_math_with_ids(text))
}


// const md = 'some text `$$c=\sqrt{a^2 + b^2}$$` another text'
const md = 'some text $$c=\sqrt{a^2 + b^2}$$ another text'

const rendered_md_only: string = marked(md, { renderer: renderer })
console.log(rendered_md_only)

const rendered = rendered_md_only.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
  const { type, expression } = math_expressions[capture]
  return katex.renderToString(expression, { displayMode: type == 'block' })
})
console.log(rendered)

If you want to use katex with marked you can modify the renderer to check if code spans are katex.

I pulled this out of marked-katex (it hasn't been updated in a while so I wouldn't use that package).

Here is a working example:

const marked = require('marked');
const katex = require('katex');

const renderer = new marked.Renderer();

function mathsExpression(expr) {
  if (expr.match(/^\$\$[\s\S]*\$\$$/)) {
    expr = expr.substr(2, expr.length - 4);
    return katex.renderToString(expr, { displayMode: true });
  } else if (expr.match(/^\$[\s\S]*\$$/)) {
    expr = expr.substr(1, expr.length - 2);
    return katex.renderToString(expr, { isplayMode: false });
  }
}

const rendererCode = renderer.code;
renderer.code = function(code, lang, escaped) {
  if (!lang) {
    const math = mathsExpression(code);
    if (math) {
      return math;
    }
  }

  return rendererCode(code, lang, escaped);
};

const rendererCodespan = renderer.codespan;
renderer.codespan = function(text) {
  const math = mathsExpression(text);

  if (math) {
    return math;
  }

  return rendererCodespan(text);
};

const md = '`$$c=\sqrt{a^2 + b^2}$$`';

console.log(marked(md, { renderer: renderer }));

renderer.codespan doing like this you are actually changing the reference. You will need to create a new renderer and reference that inside the one you are changing:

const unchanged = new Renderer()
const myRenderer = new Renderer()

myRenderer.codespan = (params) => {
  ...
  return unchanged.codespan(params)
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

toc
zoe-cjf picture zoe-cjf  路  3Comments

james4388 picture james4388  路  3Comments

bennycode picture bennycode  路  4Comments

learykara picture learykara  路  3Comments

samit4me picture samit4me  路  3Comments