remark icon indicating copy to clipboard operation
remark copied to clipboard

`wrapLines` breaks some code highlighting; fix included

Open benjie opened this issue 7 years ago • 5 comments

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 avatar Sep 07 '17 16:09 benjie

(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 avatar Sep 07 '17 16:09 benjie

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

benjie avatar Oct 05 '17 13:10 benjie

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.

bcherny avatar Mar 26 '18 05:03 bcherny

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.

kdorland avatar Aug 12 '20 10:08 kdorland

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.

lefred avatar Mar 09 '22 08:03 lefred