thoughts.sort()

Enabling line numbering in Gatsby code blocks

April 06, 2020

Tags: gatsby, this site

I want to be able to discuss code snippets in my posts. And when discussing code, I personally find that the ability to refer to line numbers is crucial.

However, sometimes line numbers serve no real purpose and I want to be able to omit them in case of one liners, git diffs and text dumps.

The solution is using PrismJS, and setting the right options.

npm install --save gatsby-transformer-remark gatsby-remark-prismjs prismjs

This results in an updated package.json.

20-04-06|15:29|~/projects/casperlehmann$ git diff package.json  
diff --git a/package.json b/package.json
index dce0a38..aa715bd 100644
--- a/package.json
+++ b/package.json
@@ -20,13 +20,13 @@
     "gatsby-plugin-typography": "^2.3.25",
     "gatsby-remark-copy-linked-files": "^2.1.40",
     "gatsby-remark-images": "^3.1.50",
-    "gatsby-remark-prismjs": "^3.3.36",
+    "gatsby-remark-prismjs": "^3.4.1",
     "gatsby-remark-responsive-iframe": "^2.2.34",
     "gatsby-remark-smartypants": "^2.1.23",
     "gatsby-source-filesystem": "^2.1.56",
-    "gatsby-transformer-remark": "^2.6.59",
+    "gatsby-transformer-remark": "^2.7.1",
     "gatsby-transformer-sharp": "^2.3.19",
-    "prismjs": "^1.19.0",
+    "prismjs": "^1.20.0",
     "react": "^16.13.1",
     "react-dom": "^16.12.0",
     "react-helmet": "^5.2.1",

Well, there you go. It was already installed.

Next, in gatsby-config.js, add to the gatsby-transformer-remark plugin. It is important that showLineNumbers is set to false in order to apply it selectively.

20-04-06|15:32|~/projects/casperlehmann$ git diff gatsby-config.js
diff --git a/gatsby-config.js b/gatsby-config.js
index 0bf1fc9..e8b86c8 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -45,6 +45,71 @@ module.exports = {
  plugins: [
    [...]
    [...]
    [...]
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
           `gatsby-remark-prismjs`,
           `gatsby-remark-copy-linked-files`,
           `gatsby-remark-smartypants`,
+          {
+            resolve: `gatsby-remark-prismjs`,
+            options: {
+              // Class prefix for <pre> tags containing syntax highlighting;
+              // defaults to 'language-' (e.g. <pre class="language-js">).
+              // If your site loads Prism into the browser at runtime,
+              // (e.g. for use with libraries like react-live),
+              // you may use this to prevent Prism from re-processing syntax.
+              // This is an uncommon use-case though;
+              // If you're unsure, it's best to use the default value.
+              classPrefix: "language-",
+              // This is used to allow setting a language for inline code
+              // (i.e. single backticks) by creating a separator.
+              // This separator is a string and will do no white-space
+              // stripping.
+              // A suggested value for English speakers is the non-ascii
+              // character '›'.
+              inlineCodeMarker: null,
+              // This lets you set up language aliases.  For example,
+              // setting this to '{ sh: "bash" }' will let you use
+              // the language "sh" which will highlight using the
+              // bash highlighter.
+              aliases: {},
+              // This toggles the display of line numbers globally alongside the code.
+              // To use it, add the following line in gatsby-browser.js
+              // right after importing the prism color scheme:
+              //  require("prismjs/plugins/line-numbers/prism-line-numbers.css")
+              // Defaults to false.
+              // If you wish to only show line numbers on certain code blocks,
+              // leave false and use the {numberLines: true} syntax below
+              showLineNumbers: false,
+              // If setting this to true, the parser won't handle and highlight inline
+              // code used in markdown i.e. single backtick code like `this`.
+              noInlineHighlight: false,
+              // This adds a new language definition to Prism or extend an already
+              // existing language definition. More details on this option can be
+              // found under the header "Add new language definition or extend an
+              // existing language" below.
+              languageExtensions: [
+                {
+                  language: "superscript",
+                  extend: "javascript",
+                  definition: {
+                    superscript_types: /(SuperType)/,
+                  },
+                  insertBefore: {
+                    function: {
+                      superscript_keywords: /(superif|superelse)/,
+                    },
+                  },
+                },
+              ],
+              // Customize the prompt used in shell output
+              // Values below are default
+              prompt: {
+                user: "root",
+                host: "localhost",
+                global: false,
+              },
+              // By default the HTML entities <>&'" are escaped.
+              // Add additional HTML escapes by providing a mapping
+              // of HTML entities and their escape value IE: { '}': '&#123;' }
+              escapeEntities: {},
+            },
+          },

Adding recommended Gatsby styles to a style sheet. Start by creating src/styles/global.css. It is important to add the highlights sectioning due to a bug in Gatsby that otherwise results in line numbers being out of line <— That’s a pun.

20-04-06|15:40|~/projects/casperlehmann$ git diff --untracked src
20-04-06|15:40|~/projects/casperlehmann$ git diff src
diff --git a/src/styles/global.css b/src/styles/global.css
new file mode 100644
index 0000000..3b70f33
--- /dev/null
+++ b/src/styles/global.css
@@ -0,0 +1,97 @@
+.gatsby-highlight-code-line {
+    background-color: #feb;
+    display: block;
+    margin-right: -1em;
+    margin-left: -1em;
+    padding-right: 1em;
+    padding-left: 0.75em;
+    border-left: 0.25em solid #f99;
+  }
+
+
+  /**
+ * Add back the container background-color, border-radius, padding, margin
+ * and overflow that we removed from <pre>.
+ */
+.gatsby-highlight {
+    background-color: #fdf6e3;
+    border-radius: 0.3em;
+    margin: 0.5em 0;
+    padding: 1em;
+    overflow: auto;
+  }
+  
+  /**
+   * Remove the default PrismJS theme background-color, border-radius, margin,
+   * padding and overflow.
+   * 1. Make the element just wide enough to fit its content.
+   * 2. Always fill the visible space in .gatsby-highlight.
+   * 3. Adjust the position of the line numbers
+   */
+  .gatsby-highlight pre[class*="language-"] {
+    background-color: transparent;
+    margin: 0;
+    padding: 0;
+    overflow: initial;
+    float: left; /* 1 */
+    min-width: 100%; /* 2 */
+  }
+
+  /**
+ * If you already use line highlighting
+ */
+
+/* Adjust the position of the line numbers */
+.gatsby-highlight pre[class*="language-"].line-numbers {
+    padding-left: 2.8em;
+  }
+  
+  /**
+   * If you only want to use line numbering
+   */
+  
+  .gatsby-highlight {
+    background-color: #fdf6e3;
+    border-radius: 0.3em;
+    margin: 0.5em 0;
+    padding: 1em;
+    overflow: auto;
+  }
+  
+  .gatsby-highlight pre[class*="language-"].line-numbers {
+    padding: 0;
+    padding-left: 2.8em;
+    overflow: initial;
+  }
+
+  /**
+ * Add back the container background-color, border-radius, padding, margin
+ * and overflow that we removed from <pre>.
+ */
+.gatsby-highlight {
+    background-color: #fdf6e3;
+    border-radius: 0.3em;
+    margin: 0.5em 0;
+    padding: 1em;
+    overflow: auto;
+  }
+  
+  /**
+   * Remove the default PrismJS theme background-color, border-radius, margin,
+   * padding and overflow.
+   * 1. Make the element just wide enough to fit its content.
+   * 2. Always fill the visible space in .gatsby-highlight.
+   * 3. Adjust the position of the line numbers
+   */
+  .gatsby-highlight pre[class*="language-"] {
+    background-color: transparent;
+    margin: 0;
+    padding: 0;
+    overflow: initial;
+    float: left; /* 1 */
+    min-width: 100%; /* 2 */
+  }
+  
+  .gatsby-highlight pre[class*="language-"].line-numbers {
+    padding-left: 2.8em; /* 3 */
+  }

In gatsby-browser.js import this global css along with the supplied styles for Prism.

20-04-06|15:37|~/projects/casperlehmann$ git diff gatsby-browser.js
diff --git a/gatsby-browser.js b/gatsby-browser.js
index 3eae868..31b4030 100644
--- a/gatsby-browser.js
+++ b/gatsby-browser.js
@@ -3,3 +3,10 @@ import "typeface-montserrat"
 import "typeface-merriweather"

 import "prismjs/themes/prism.css"
+
+import "./src/styles/global.css"
+
+require("prismjs/themes/prism-solarizedlight.css")
+require("prismjs/plugins/line-numbers/prism-line-numbers.css")

Now, we can write code like this to render it in Markdown.

```javascript{numberLines: 104}
// Omit this: ^^^^^^^^^^^^^^^^ part when line numbers are not required.
// Change the number to change the value for the start of the range.
plugins: [
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      plugins: [
        `gatsby-remark-prismjs`,
      ]
    }
  }
]
```

NB for escaping the entire code blog, another back-tick is added for a total of four back-ticks.

The result looks like this.

plugins: [
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      plugins: [
        `gatsby-remark-prismjs`,
      ]
    }
  }
]

And that is how we add line numbers. We can even change numberLines parameter to a negative number to get relative line numbering. Neat.

plugins: [
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      plugins: [
        `gatsby-remark-prismjs`,
      ]
    }
  }
]

What’s more, it even works with DAX.

Sum of Sales Amount (DK) = CALCULATE(
    SUM('Sales'[Sales Amount]),
    FILTER('Customer', 'Customer'[Country] = "DK")
)

Honestly, not much hassle. Definitely more straight forward than I imagined, and I’d say it works really well. Add, commit, push.

20-04-06|15:52|~/projects/casperlehmann$ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   content/blog/enable-line-numbering-in-gatsby-code-blocks/index.md
        modified:   gatsby-browser.js
        modified:   gatsby-config.js
        modified:   package.json
        new file:   src/styles/global.css

Sources: