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

How can I close render '_' #1538

Closed
sandy-shi opened this issue Aug 20, 2019 · 19 comments
Closed

How can I close render '_' #1538

sandy-shi opened this issue Aug 20, 2019 · 19 comments
Labels

Comments

@sandy-shi
Copy link

sandy-shi commented Aug 20, 2019

I use KateX in this. The '_' in KateX is a subscript, but marked render it to <em>, so the KateX is failed.
How can I close this '_' render?

@sandy-shi sandy-shi changed the title How can I close render two '_' How can I close render '_' Aug 20, 2019
@UziTech
Copy link
Member

UziTech commented Aug 20, 2019

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?

@wufeng87
Copy link

$$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

@wufeng87
Copy link

wufeng87 commented Aug 29, 2019

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

@wufeng87
Copy link

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$$

@wufeng87
Copy link

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.

@wufeng87
Copy link

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

@wufeng87
Copy link

I thought I could custom this:

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

To check text if contains $$

@UziTech
Copy link
Member

UziTech commented Aug 29, 2019

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 }));

@wufeng87
Copy link

wufeng87 commented Sep 5, 2019

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

@sandy-shi
Copy link
Author

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 <em>,

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

@sandy-shi
Copy link
Author

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

@wufeng87
Copy link

wufeng87 commented Sep 5, 2019

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>

@UziTech
Copy link
Member

UziTech commented Sep 5, 2019

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.

@al6x
Copy link

al6x commented Jan 17, 2020

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)

@agustingabiola
Copy link

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)
}

@usstq
Copy link

usstq commented Jul 23, 2021

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)
}

Not exactly right about the root cause, the rendererCodespan member function is still there and not changed by assignment to renderer.codespan (because codespan is not render's own property, it's inherited from prototype) it's just that your way of calling it:

   ...
   return rendererCodespan(text);

rendererCodespan is called in function context rather than class context here (because it's not a valid name in the prototype chain), if there is no "this" keyword inside that function, it's perfectly fine to be called like that, but if there is "this" keyword and it's called in function context, "this" would referencing to some global object rather than the renderer object. to fix it, you can force call it as a member function like this:

   ...
   return rendererCodespan.call(this, text);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

@qwqcode
Copy link

qwqcode commented Apr 19, 2022

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)

In cases of white spaces in a latex expresion, the no-newlines regex /\$([^\n\s]+?)\$/g may be wrong, it should be /\$([^\n]+?)\$/g

not match:

image

it works:

image

@rockie-yang
Copy link

Here is another one based @qwqcode 's solution.

import marked from "marked";
import katex from "katex";
import "katex/dist/katex.css";

const renderer = new marked.Renderer();

const replacer = (((blockRegex, inlineRegex) => (text) => {
    text = text.replace(blockRegex, (match, expression) => {
        return katex.renderToString(expression, {displayMode: true});
    });

    text = text.replace(inlineRegex, (match, expression) => {
        return katex.renderToString(expression, {displayMode: false});
    });

    return text;
})(/\$\$([\s\S]+?)\$\$/g, /\$([^\n\s]+?)\$/g));

const replaceTypes = ["listitems", "paragraph", "tablecell", "text"];
replaceTypes.forEach(type => {
    const original = renderer[type];
    renderer[type] = (...args) => {
        args[0] = replacer(args[0]);
        return original(...args);
    };
});

export default (md) => {
    return marked(md, {renderer: renderer});
}

@DDKK64
Copy link

DDKK64 commented Apr 23, 2024

If anyone is trying to manually render Markdown and Latex formulae into HTML, rendering LaTex before markdown works for me.

The following example uses MathJax. But I assume KaTex will work in similar way:

fetch("articles/calculus.md")
  .then((res) => res.text())
  .then((text) => {
      // Dynamicly load the content. This is unnecessary if you use static content.
      document.getElementById('article').innerHTML = text;
      // Render LaTex
      return MathJax.typesetPromise()
  }).then(() => {
      // Then render Markdown
      text = document.getElementById('article').innerHTML;
      document.getElementById('article').innerHTML = marked.parse(text);
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants