Skip to content
This repository has been archived by the owner on Jan 31, 2023. It is now read-only.

Add Prism code highlighting #1

Open
jletey opened this issue Apr 2, 2019 · 6 comments
Open

Add Prism code highlighting #1

jletey opened this issue Apr 2, 2019 · 6 comments

Comments

@jletey
Copy link
Contributor

jletey commented Apr 2, 2019

Kind of like Kent C. Dodds does in his website, I'd love for Prism code highlighting (and specific code lines highlighted too)

Reference: Kent C. Dodds' src/components/mdx/code.js

@jxnblk
Copy link
Owner

jxnblk commented Apr 2, 2019

Ah! Think I missed this in the readme, but you can shadow src/components.js to add a custom code component for syntax highlighting etc – can add that to the docs, but maybe that helps you move forward for now

@jletey
Copy link
Contributor Author

jletey commented Apr 3, 2019

but you can shadow src/components.js to add a custom code component for syntax highlighting etc

@jxnblk Not seeing how I can do this ... could you maybe give an example?

can add that to the docs

For sure ... let me just understand what is going on first and how to use it properly

@jletey
Copy link
Contributor Author

jletey commented Apr 3, 2019

I have the following code in src/@jxnblk/gatsby-theme-mdx-blog/components.js:

import components from "@jxnblk/gatsby-theme-mdx-blog/src/components.js";
import Code from "../../components/mdx/code";

export default {
  ...components,
  code: Code
};

Where ../../components/mdx/code is Kent C. Dodds' src/components/mdx/code.js

But I get this error:

TypeError: undefined is not an object (evaluating 'token.type')
normalizeTokens
node_modules/prism-react-renderer/es/utils/normalizeTokens.js:42
  39 |   types = stackIndex > 0 ? types : ["plain"];
  40 |   content = token;
  41 | } else {
> 42 |   types = types[0] === token.type ? types : types.concat(token.type);
  43 |   content = token.content;
  44 | } // If token.content is an array, increase the stack depth and repeat this while-loop
  45 | 

@jxnblk
Copy link
Owner

jxnblk commented Apr 3, 2019

Looks like you have the part in src/@jxnblk/gatsby-theme-mdx-blog/components.js correct, but that error seems to be coming from somewhere else... maybe the Code component?

@jletey
Copy link
Contributor Author

jletey commented Apr 3, 2019

maybe the Code component?

@jxnblk I'll take a look ... thanks for the help. I'll keep you posted

@jletey
Copy link
Contributor Author

jletey commented Apr 5, 2019

@jxnblk Still not working ... I'm not getting any error (like before), it just doesn't colour the code.

src/@jxnblk/gatsby-theme-mdx-blog/components.js:

import components from "@jxnblk/gatsby-theme-mdx-blog/src/components.js";
import mdxComponents from "../../components/mdx";

export default {
  ...components,
  ...mdxComponents
};

Note that the following code was taken from Kent C Dodds website

src/components/mdx/index.js:

import React from "react";

import Code from "./code";

export default {
  pre: preProps => {
    const props = preToCodeBlock(preProps);
    // if there's a codeString and some props, we passed the test
    if (props) {
      return <Code {...props} />;
    } else {
      // it's possible to have a pre without a code in it
      return <pre {...preProps} />;
    }
  }
};

function preToCodeBlock(preProps) {
  if (
    // children is MDXTag
    preProps.children &&
    // MDXTag props
    preProps.children.props &&
    // if MDXTag is going to render a <code>
    preProps.children.props.name === "code"
  ) {
    // we have a <pre><code> situation
    const {
      children: codeString,
      props: { className, ...props }
    } = preProps.children.props;

    return {
      codeString: codeString.trim(),
      language: className && className.split("-")[1],
      ...props
    };
  }
}

src/components/mdx/code.js:

import React from "react";
import { css } from "@emotion/core";
import theme from "prism-react-renderer/themes/oceanicNext";
import Highlight, { defaultProps } from "prism-react-renderer";

const RE = /{([\d,-]+)}/;

const wrapperStyles = css`
  overflow: auto;
`;

const preStyles = css`
  float: left;
  min-width: 100%;
  overflow: initial;
`;

function calculateLinesToHighlight(meta) {
  if (RE.test(meta)) {
    const lineNumbers = RE.exec(meta)[1]
      .split(",")
      .map(v => v.split("-").map(y => parseInt(y, 10)));
    return index => {
      const lineNumber = index + 1;
      const inRange = lineNumbers.some(([start, end]) =>
        end ? lineNumber >= start && lineNumber <= end : lineNumber === start
      );
      return inRange;
    };
  } else {
    return () => false;
  }
}

function Code({ codeString, language, metastring }) {
  const shouldHighlightLine = calculateLinesToHighlight(metastring);
  return (
    <Highlight
      {...defaultProps}
      code={codeString}
      language={language}
      theme={theme}
    >
      {({ className, style, tokens, getLineProps, getTokenProps }) => (
        <div css={wrapperStyles}>
          <pre className={className} style={style} css={preStyles}>
            {tokens.map((line, i) => (
              <div
                key={i}
                {...getLineProps({
                  line,
                  key: i,
                  className: shouldHighlightLine(i) ? "highlight-line" : ""
                })}
              >
                <span
                  css={css`
                    display: inline-block;
                    width: 2em;
                    user-select: none;
                    opacity: 0.3;
                  `}
                >
                  {i + 1}
                </span>
                {line.map((token, key) => (
                  <span key={key} {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        </div>
      )}
    </Highlight>
  );
}

export default Code;

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

No branches or pull requests

2 participants