Explorar el Código

Merge pull request #11 from timlrx/feat/code-highlight-style

Feat/code highlight style
Timothy hace 4 años
padre
commit
2693ab970a
Se han modificado 5 ficheros con 86 adiciones y 61 borrados
  1. 30 6
      README.md
  2. 18 19
      index.js
  3. 1 1
      package.json
  4. BIN
      sample-code-block.png
  5. 37 35
      test.js

+ 30 - 6
README.md

@@ -1,5 +1,7 @@
 # rehype-prism-plus
 
+![sample-code-block-output](sample-code-block.png)
+
 [rehype](https://github.com/wooorm/rehype) plugin to highlight code blocks in HTML with [Prism] (via [refractor]) with additional line highlighting and line numbers functionalities.
 
 Inspired by and uses a compatible API as [@mapbox/rehype-prism](https://github.com/mapbox/rehype-prism) with additional support for line-highlighting and line numbers.
@@ -91,34 +93,56 @@ HTML Output:
 
 ## Styling
 
-Here's a sample stylesheet:
+To style the language tokens, you can just copy them from any prismjs compatible ones. Here's a list of [themes](https://github.com/PrismJS/prism-themes).
+
+In addition, the following styles should be added for line highlighting and line numbers to work correctly:
 
 ```css
+pre {
+  overflow-x: auto;
+}
+
+/**
+ * Inspired by gatsby remark prism - https://www.gatsbyjs.com/plugins/gatsby-remark-prismjs/
+ * 1. Make the element just wide enough to fit its content.
+ * 2. Always fill the visible space in .code-highlight.
+ */
+.code-highlight {
+  float: left; /* 1 */
+  min-width: 100%; /* 2 */
+}
+
 .code-line {
+  display: block;
   padding-left: 16px;
+  padding-right: 16px;
   margin-left: -16px;
   margin-right: -16px;
   border-left-width: 4px;
-  border-left-color: rgb(31, 41, 55); \\ Set to code block color
+  border-left-color: rgb(31, 41, 55); /* Set code block color */
 }
 
 .highlight-line {
   margin-left: -16px;
   margin-right: -16px;
-  background-color: rgba(55, 65, 81, 0.5); \\ Highlight color
+  background-color: rgba(55, 65, 81, 0.5); /* Set highlight bg color */
   border-left-width: 4px;
-  border-left-color: rgb(59, 130, 246);
+  border-left-color: rgb(59, 130, 246); /* Set highlight accent border color */
 }
 
 .line-number::before {
   padding-right: 16px;
   margin-left: -8px;
-  color: rgb(156, 163, 175);  \\ Line number color
+  color: rgb(156, 163, 175); /* Line number color */
   content: attr(line);
 }
 ```
 
-For styling of language tokens, consult [refractor] and [Prism].
+Here's the styled output using the prism-night-owl theme:
+
+![sample-code-block-output](sample-code-block.png)
+
+For more information on styling of language tokens, consult [refractor] and [Prism].
 
 ## API
 

+ 18 - 19
index.js

@@ -76,9 +76,9 @@ const splitLine = (text) => {
   return textArray.map((line) => {
     return {
       type: 'element',
-      tagName: 'div',
+      tagName: 'span',
       properties: { className: ['code-line'] },
-      children: [{ type: 'text', value: line === '' ? '\n' : line }],
+      children: [{ type: 'text', value: line }],
     }
   })
 }
@@ -164,12 +164,8 @@ const rehypePrism = (options = {}) => {
     /** @type {string} */
     // @ts-ignore
     let meta = node.data && node.data.meta ? node.data.meta : ''
-
-    if (lang) {
-      parent.properties.className = (parent.properties.className || []).concat('language-' + lang)
-      // Add lang to meta to allow line highlighting even when no lang is specified
-      meta = `${lang} ${meta}`
-    }
+    node.properties.className = node.properties.className || []
+    node.properties.className.push('code-highlight')
 
     let refractorRoot
     let langError = false
@@ -179,17 +175,22 @@ const rehypePrism = (options = {}) => {
       try {
         // @ts-ignore
         refractorRoot = refractor.highlight(toString(node), lang)
-        refractorRoot = getNodePosition(refractorRoot)
-        refractorRoot.children = splitTextByLine(refractorRoot.children)
       } catch (err) {
         if (options.ignoreMissing && /Unknown language/.test(err.message)) {
           langError = true
+          refractorRoot = node.children
         } else {
           throw err
         }
       }
+    } else {
+      refractorRoot = node.children
     }
 
+    // @ts-ignore
+    refractorRoot = getNodePosition(refractorRoot)
+    refractorRoot.children = splitTextByLine(refractorRoot.children)
+
     const shouldHighlightLine = calculateLinesToHighlight(meta)
     // @ts-ignore
     const codeLineArray = splitLine(toString(node))
@@ -198,22 +199,20 @@ const rehypePrism = (options = {}) => {
       // Code lines
       if (meta.toLowerCase().includes('showLineNumbers'.toLowerCase()) || options.showLineNumbers) {
         line.properties.line = [(i + 1).toString()]
-        line.properties.className = [`${line.properties.className} line-number`]
+        line.properties.className.push('line-number')
       }
 
       // Line highlight
       if (shouldHighlightLine(i)) {
-        line.properties.className = [`${line.properties.className} highlight-line`]
+        line.properties.className.push('highlight-line')
       }
 
       // Syntax highlight
-      if (lang && line.children && !langError) {
-        const treeExtract = filter(
-          refractorRoot,
-          (node) => node.position.start.line <= i + 1 && node.position.end.line >= i + 1
-        )
-        line.children = treeExtract.children
-      }
+      const treeExtract = filter(
+        refractorRoot,
+        (node) => node.position.start.line <= i + 1 && node.position.end.line >= i + 1
+      )
+      line.children = treeExtract.children
     }
 
     node.children = codeLineArray

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "rehype-prism-plus",
-  "version": "0.0.6",
+  "version": "0.1.0",
   "description": "rehype plugin to highlight code blocks in HTML with Prism (via refractor) with line highlighting and line numbers",
   "source": "index.js",
   "files": [

BIN
sample-code-block.png


+ 37 - 35
test.js

@@ -28,19 +28,21 @@ const processHtml = (html, options, metastring) => {
     .toString()
 }
 
-test('copies the language- class to pre tag', () => {
+test('adds a code-highlight class to the code tag', () => {
   const result = processHtml(dedent`
     <pre><code class="language-py"></code></pre>
   `)
-  const expected = dedent`<pre class="language-py"><code class="language-py"></code></pre>`
+  const expected = dedent`<pre><code class="language-py code-highlight"></code></pre>`
   assert.is(result, expected)
 })
 
-test('add div with class code line for each line', () => {
-  const result = processHtml(dedent`
+test('add span with class code line for each line', () => {
+  const result = processHtml(
+    dedent`
     <pre><code>x = 6</code></pre>
-  `)
-  const expected = dedent`<pre><code><div class="code-line">x = 6</div></code></pre>`
+  `
+  )
+  const expected = dedent`<pre><code class="code-highlight"><span class="code-line">x = 6</span></code></pre>`
   assert.is(result, expected)
 })
 
@@ -54,7 +56,7 @@ test('finds code and highlights', () => {
   const expected = dedent`
     <div>
       <p>foo</p>
-      <pre class="language-py"><code class="language-py"><div class="code-line">x <span class="token operator">=</span> <span class="token number">6</span></div></code></pre>
+      <pre><code class="language-py code-highlight"><span class="code-line">x <span class="token operator">=</span> <span class="token number">6</span></span></code></pre>
     </div>
     `
   assert.is(result, expected)
@@ -71,10 +73,10 @@ y
 `).trim()
   const expected = dedent`
   <div>
-  <pre class="language-py"><code class="language-py"><div class="code-line">x
-  </div><div class="code-line">
-  </div><div class="code-line">y
-  </div></code></pre>
+  <pre><code class="language-py code-highlight"><span class="code-line">x
+  </span><span class="code-line">
+  </span><span class="code-line">y
+  </span></code></pre>
   </div>
     `
   assert.is(result, expected)
@@ -90,7 +92,7 @@ test('handles uppercase correctly', () => {
   const expected = dedent`
     <div>
       <p>foo</p>
-      <pre class="language-py"><code class="language-PY"><div class="code-line">x <span class="token operator">=</span> <span class="token number">6</span></div></code></pre>
+      <pre><code class="language-PY code-highlight"><span class="code-line">x <span class="token operator">=</span> <span class="token number">6</span></span></code></pre>
     </div>
     `
   assert.is(result, expected)
@@ -101,13 +103,13 @@ test('each line of code should be a separate div', async () => {
     <div>
       <p>foo</p>
       <pre>
-      <code class="language-py">x = 6
+      <code class="language-py code-highlight">x = 6
       y = 7
       </code>
       </pre>
     </div>
     `).trim()
-  const codeLineCount = (result.match(/<div class="code-line">/g) || []).length
+  const codeLineCount = (result.match(/<span class="code-line">/g) || []).length
   assert.is(codeLineCount, 2)
 })
 
@@ -117,8 +119,8 @@ test('should highlight line', async () => {
     dedent`
     <div>
       <pre>
-      <code class="language-py">x = 6
-      y = 7 
+      <code class="language-py code-highlight">x = 6
+      y = 7
       </code>
       </pre>
     </div>
@@ -126,7 +128,7 @@ test('should highlight line', async () => {
     {},
     meta
   ).trim()
-  const codeHighlightCount = (result.match(/<div class="code-line highlight-line">/g) || []).length
+  const codeHighlightCount = (result.match(/<span class="code-line highlight-line">/g) || []).length
   assert.is(codeHighlightCount, 1)
 })
 
@@ -136,8 +138,8 @@ test('should highlight comma separated lines', async () => {
     dedent`
     <div>
       <pre>
-      <code class="language-py">x = 6
-      y = 7 
+      <code class="language-py code-highlight">x = 6
+      y = 7
       z = 10
       </code>
       </pre>
@@ -146,7 +148,7 @@ test('should highlight comma separated lines', async () => {
     {},
     meta
   ).trim()
-  const codeHighlightCount = (result.match(/<div class="code-line highlight-line">/g) || []).length
+  const codeHighlightCount = (result.match(/<span class="code-line highlight-line">/g) || []).length
   assert.is(codeHighlightCount, 2)
 })
 
@@ -156,8 +158,8 @@ test('should should parse ranges with a space in between', async () => {
     dedent`
     <div>
       <pre>
-      <code class="language-py">x = 6
-      y = 7 
+      <code class="language-py code-highlight">x = 6
+      y = 7
       z = 10
       </code>
       </pre>
@@ -166,7 +168,7 @@ test('should should parse ranges with a space in between', async () => {
     {},
     meta
   ).trim()
-  const codeHighlightCount = (result.match(/<div class="code-line highlight-line">/g) || []).length
+  const codeHighlightCount = (result.match(/<span class="code-line highlight-line">/g) || []).length
   assert.is(codeHighlightCount, 2)
 })
 
@@ -176,8 +178,8 @@ test('should highlight range separated lines', async () => {
     dedent`
     <div>
       <pre>
-      <code class="language-py">x = 6
-      y = 7 
+      <code class="language-py code-highlight">x = 6
+      y = 7
       z = 10
       </code>
       </pre>
@@ -186,7 +188,7 @@ test('should highlight range separated lines', async () => {
     {},
     meta
   ).trim()
-  const codeHighlightCount = (result.match(/<div class="code-line highlight-line">/g) || []).length
+  const codeHighlightCount = (result.match(/<span class="code-line highlight-line">/g) || []).length
   assert.is(codeHighlightCount, 3)
 })
 
@@ -195,7 +197,7 @@ test('showLineNumbers option add line numbers', async () => {
     dedent`
     <div>
       <pre>
-      <code class="language-py">x = 6
+      <code class="language-py code-highlight">x = 6
       y = 7
       </code>
       </pre>
@@ -214,7 +216,7 @@ test('showLineNumbers property works in meta field', async () => {
     dedent`
     <div>
       <pre>
-      <code class="language-py">x = 6
+      <code class="language-py code-highlight">x = 6
       y = 7
       </code>
       </pre>
@@ -235,7 +237,7 @@ test('should support both highlighting and add line number', async () => {
     <div>
       <pre>
       <code class="language-py">x = 6
-      y = 7 
+      y = 7
       z = 10
       </code>
       </pre>
@@ -267,7 +269,7 @@ test('with options.ignoreMissing, does nothing to code block with fake language-
   `,
     { ignoreMissing: true }
   )
-  const expected = dedent`<pre class="language-thisisnotalanguage"><code class="language-thisisnotalanguage"><div class="code-line">x = 6</div></code></pre>`
+  const expected = dedent`<pre><code class="language-thisisnotalanguage code-highlight"><span class="code-line">x = 6</span></code></pre>`
   assert.is(result, expected)
 })
 
@@ -282,11 +284,11 @@ test('should work with multiline code / comments', () => {
   `,
     { ignoreMissing: true }
   )
-  const expected = dedent`<pre class="language-js"><code class="language-js"><div class="code-line">
-        </div><div class="code-line"><span class="token doc-comment comment">/**
-        </span></div><div class="code-line"><span class="token doc-comment comment"> * My comment
-        </span></div><div class="code-line"><span class="token doc-comment comment"> */</span>
-        </div></code></pre>`
+  const expected = dedent`<pre><code class="language-js code-highlight"><span class="code-line">
+        </span><span class="code-line"><span class="token doc-comment comment">/**
+        </span></span><span class="code-line"><span class="token doc-comment comment"> * My comment
+        </span></span><span class="code-line"><span class="token doc-comment comment"> */</span>
+        </span></code></pre>`
   assert.is(result, expected)
 })