From 020c1e77489b772f854bb3288b9c8d2818a6bf9d Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Fri, 18 Apr 2025 02:17:12 -0300 Subject: git mv src/content/* src/content/en/ --- src/content/en/tils/2020/08/13/code-jekyll.adoc | 159 ++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/content/en/tils/2020/08/13/code-jekyll.adoc (limited to 'src/content/en/tils/2020/08/13/code-jekyll.adoc') diff --git a/src/content/en/tils/2020/08/13/code-jekyll.adoc b/src/content/en/tils/2020/08/13/code-jekyll.adoc new file mode 100644 index 0000000..6bd90b0 --- /dev/null +++ b/src/content/en/tils/2020/08/13/code-jekyll.adoc @@ -0,0 +1,159 @@ += Anchor headers and code lines in Jekyll + +:empty: +:jekyll: https://jekyllrb.com/ +:kramdown: https://kramdown.gettalong.org/ +:rouge: https://rouge.jneen.net/ +:jekyll-hook: https://jekyllrb.com/docs/plugins/hooks/ + +The default Jekyll toolbox ({jekyll}[Jekyll], {kramdown}[kramdown] and +{rouge}[rouge]) doesn't provide with a configuration option to add anchors to +headers and code blocks. + +The best way I found of doing this is by creating a simple Jekyll plugin, more +specifically, a {jekyll-hook}[Jekyll hook]. These allow you to jump in to the +Jekyll build and add a processing stage before of after Jekyll performs +something. + +All you have to do is add the code to `_plugins/my-jekyll-plugin-code.rb`, and +Jekyll knows to pick it up and call your code on the appropriate time. + +== Anchor on headers + +:jemoji: https://github.com/jekyll/jemoji +:jekyll-mentions: https://github.com/jekyll/jekyll-mentions +:html-regex: https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 + +Since I wanted to add anchors to headers in all documents, this Jekyll hook +works on `:documents` after they have been transformed into HTML, the +`:post_render` phase: + +[source,ruby] +---- +Jekyll::Hooks.register :documents, :post_render do |doc| + if doc.output_ext == ".html" + doc.output = + doc.output.gsub( + /(.*?)<\/h[1-6]>/, + '\5' + ) + end +end +---- + +I've derived my implementations from two +"official"{empty}footnote:official[ + I don't know how official they are, I just assumed it because they live in the + same organization inside GitHub that Jekyll does. +] hooks, {jemoji}[jemoji] and {jekyll-mentions}[jekyll-mentions]. + +All I did was to wrap the header tag inside an ``, and set the `href` of that +`` to the existing id of the header. Before the hook the HTML looks like: + +[source,html] +---- +...some unmodified text... +

+ My header +

+...more unmodified text... +---- + +And after the hook should turn that into: + +[source,html] +---- +...some unmodified text... +
+

+ My header +

+
+...more unmodified text... +---- + +The used regexp tries to match only h1-h6 tags, and keep the rest of the HTML +attributes untouched, since this isn't a general HTML parser, but the generated +HTML is somewhat under your control. Use at your own risk because +{html-regex}[you shouldn't parse HTML with regexps]. Also I used this strategy +in my environment, where no other plugins are installed. I haven't considered +how this approach may conflict with other Jekyll plugins. + +In the new anchor tag you can add your custom CSS class to style it as you wish. + +== Anchor on code blocks + +Adding anchors to code blocks needs a little bit of extra work, because line +numbers themselves don't have preexisting ids, so we need to generate them +without duplications between multiple code blocks in the same page. + +Similarly, this Jekyll hook also works on `:documents` in the `:post_render` +phase: + +[source,ruby] +---- +PREFIX = '
'
+POSTFIX = '
' +Jekyll::Hooks.register :documents, :post_render do |doc| + if doc.output_ext == ".html" + code_block_counter = 1 + doc.output = doc.output.gsub(/
[\n0-9]+<\/pre>/) do |match|
+      line_numbers = match
+                      .gsub(/
([\n0-9]+)<\/pre>/, '\1')
+                      .split("\n")
+
+      anchored_line_numbers_array = line_numbers.map do |n|
+        id = "B#{code_block_counter}-L#{n}"
+        "#{n}"
+      end
+      code_block_counter += 1
+
+      PREFIX + anchored_line_numbers_array.join("\n") + POSTFIX
+    end
+  end
+end
+----
+
+This solution assumes the default Jekyll toolbox with code line numbers turned
+on in `_config.yml`:
+
+[source,yaml]
+----
+kramdown:
+  syntax_highlighter_opts:
+    span:
+      line_numbers: false
+    block:
+      line_numbers: true
+----
+
+The anchors go from B1-L1 to BN-LN, using the `code_block_counter` to track
+which code block we're in and don't duplicate anchor ids.  Before the hook the
+HTML looks like:
+
+[source,html]
+----
+...some unmodified text...
+
1
+2
+3
+4
+5
+
+...more unmodified text... +---- + +And after the hook should turn that into: + +[source,html] +---- +...some unmodified text... +
1
+2
+3
+4
+5
+...more unmodified text... +---- + +Happy writing :) -- cgit v1.2.3