gnab / remark

A simple, in-browser, markdown-driven slideshow tool.
http://remarkjs.com
MIT License
12.69k stars 856 forks source link

`wrapLines` breaks some code highlighting; fix included #461

Open benjie opened 7 years ago

benjie commented 7 years ago

Where wrapLines splits the code content on \n and wraps it in <div>s if the highlighter opens a <span> on one line and doesn't close it until the next line the browser will auto-close the span in the first div and the second line won't inherit the styles. This causes code formatting to appear broken - I thought it was an issue with highlight.js so switched out for prism as seen in #444 but it persisted.

https://github.com/gnab/remark/blob/868781c84457bd1f0e0c1ee657423136fdbe3c75/src/remark/views/slideView.js#L281-L292

I've managed to work around the issue by pre-empting this in my highlightBlock override and closing spans out at the end of a line and re-opening them at the beginning of the next line. ES6 code to achieve this is here:

function parseLanguage(lang) {
  return {
    js: 'jsx',
  }[lang] || lang;
}
remark.highlighter.engine.highlightBlock = block => {
  const language = parseLanguage(block.className.split(" ")[0]);
  const prismLang = Prism.languages[language];
  if (prismLang) {
    block.parentNode.className = `${block.parentNode.className} language-${language}`;
    const html = Prism.highlight(block.textContent, prismLang);

    const lines = html.split(`\n`);
    let currentSpan = null;
    for (var i = 0; i < lines.length; i++) {
      let line = lines[i];
      if (currentSpan) {
        line = currentSpan + line;
        currentSpan = null;
      }
      const openTags = [];
      const re = /(<span[^>]*>|<\/span>)/gi;
      let matches;
      while ((matches = re.exec(line)) != null) {
        const tag = matches[1];
        if (tag[1] === '/') {
          openTags.pop();
        } else {
          openTags.push(tag);
        }
      }
      currentSpan = openTags.join('');
      line = line + ('</' + 'span>').repeat(openTags.length);
      lines[i] = line;
    }
    block.innerHTML = lines.join('\n');

  } else {
    console.warn(`Language '${language}' not supported?`)
  }
};

Perhaps you'd consider incorporating the part from const lines = html.split... to lines.join('\n') into the wrapLines function directly?

benjie commented 7 years ago

(I absolutely love this presentation framework by the way, have been using it for years and extoll the virtues of it everywhere I present ❤️ - I've even got an awesome setup now with Browsersync where when you save your markdown file the browser automatically replaces all the slides instantly and jumps to the first edited slide which has increased my productivity significantly!)

benjie commented 6 years ago

For those interested in my workflow, check out this script:

https://github.com/GraphQLTraining/lightweight-graphql-react/blob/gh-pages/browsersync

And the corresponding main function in the index.html file:

https://github.com/GraphQLTraining/lightweight-graphql-react/blob/gh-pages/index.html

bcherny commented 6 years ago

Thank you so much for sharing your setup @benjie - I am literally 2x more productive with it. @gnab How do you feel about adding this to remark as a default dev experience? It's sort of magical.

kdorland commented 4 years ago

Oh, this is great, @benjie! I wish I didn't have to hack remark to make it highlight 'jsx' properly. I would settle for an option to disable "highlight.js" and the remark code blocks altogether so I could simply link in my preferred highlighter (prism.js) for my <pre> <code> tags. Or something like that.

lefred commented 2 years ago

I'm using your changes to (remark.prism.js) to have the syntax highlight and it works fine. Thank you. Do you know if it will be possible to have some plugins like line-numbers working too ? Thank you.