#### Knowledge Continuum

Programmer. (Many Interests: Philosophy & Mathematics)

Home

# Extending jekyll markdown syntax using Liquid

## Introduction

If you've ever used tools like Mediawiki or Tiddlywiki or Gollum you probably are familiar with the nifty little markup syntax to specify links i.e., of the form [​[​Link to some file]​]​, but it can be a pain in the backside to accomplish the same with the markdown syntax without making changes to the markdown parser that you are using. Although I am pretty comfortable writing in markdown now after having adopted jekyll as my go-to SSG for my personal website, I was a little troubled with having to type out the much verbose alternative i.e. [Link to some file](Url of the file) offered by markdown. And since I have no idea how to change the kramdown engine—the markdown parser that I am currently using for parsing my markdown files in jekyll—I put out this question in search for a plugin, and luckily got a neat solution by Joost van der Schee using just a hundred odd lines of liquid code (including comments).

The code below is a little different from what Joost suggested, but most of the change is just due to addition of support for external links that I wanted.

## Usage:

Note: You can still use the old syntax alongside the new one as the new syntax is written with liquid and doesn't interfere with kramdown's markdown parsing.

General SyntaxOriginal Syntax

Examples

Example of an internal link that points to a valid post or page, that is, a page with the title (not url) mentioned in the brackets.

• Example of a good internal link: [​[​The Value of Philosophy]]
• Rendered Text:

Example of an internal link that do not point to a valid post or page, that is, a page with the title (not url) mentioned in the brackets.

Note: The yellow highlight shows that there does not exist a page with that title in the project folders.

Example of an external link that points to an url.

Note: Since handling broken external links will require either manipulating headers using JS or using server-side code for link monitoring, we will not be handling external broken links

## Code

The code has been heavily commented to make it more accessible, but here is a gist of what it does just in case:

We split the entire file by using double square brackets and put the split tokens into an array we call the content_array.

...
...
{% assign link_open_delimiter = '[​[' %}
{% assign content_array = page.content | split:link_open_delimiter %}
...
...


Example: If a page has "lorem ipsum varum [​[dorum]] borum [​[morum]] gerum" as its content, the resultant content_array will contain the following: ["lorem ipsum varum", "dorum]] borum", "morum]] gerum"]

We loop through the content array and do the same thing for the closing tag so as to be able to get the title separately

...
...
{% assign link_close_delimiter = ']]' %}
{% for item in content_array %}
{% assign itemparts = item | split:link_close_delimiter %}
...
...


Now itemparts[0] has the part inside the square brackets, that is, the title in the case of internal link and both title as well as the url in the case of external link, since the entire external link i.e., of the form [​[Title::URL]] is contained within the brackets.

Once we have the title of the internal links, all that remains in the parsing section is to look through all the files and get the data corresponding to the pages with those titles. Which is done using the Where filter

...
...
{% assign result_posts = site.posts | where: 'title',itemparts[0] %}
...
...


The above code snippet stores all the data pertaining to a particular page in result_posts like its yaml matters in index 0 and its contents in index 1. So, now we can easily fetch the url of the page by using result_post[0].url.

We are pretty much done with our quest to getting the wiki-like syntax in jekyll, Hurray! Come on, already Celebrate!

The only thing that is left now is to be able to create anchor tags with proper title and proper url when generating html. For that we need to store all our urls and links in a separate array so that we can easily loop through them and just create a string with anchor tag and append the corresponding url and title to it.

...
...

{% assign replaced_content = page.content %}

{% for title in internal_link_array %}
{% assign url = internal_url_array[forloop.index0] %}

{% if url == nil %}
{% assign link_text = '<a style="background-color:#ffffc4;" href="' | append: 'javascript:void(0)' | append: '">' | append: title | append: '</a>' %}
{% elsif url == empty %}
{% assign link_text = '<a style="background-color:#ffffc4;" href="' | append: 'javascript:void(0)' | append: '">' | append: title | append: '</a>' %}
{% else %}
{% assign link_text = '<a  href="' | append: url | append: '">' | append: title | append: '</a>' %}
{% endif %}

{% endfor %}
...
...
Repeat the same for external links
...
...



Here we maintain four arrays two for links/titles and two for urls; and once we have them all, we just loop though the corresponding title/link array and append the url to the anchor string that we have created. And finally to display the resultant anchor tag in our generated html, we have to replace the [​[Title::URL]]/[​[Some Random Link]] in markdown with the newly created anchor tag.

That is what is done in this step:

...
...
{% assign link_text = '<a  href="' | append: url | append: '">' | append: title | append: '</a>' %}

...
...



Note: The complete code is shown below and also has comments in the appropriate places to make the code more accesible. Please refer to it. (Also scroll below to see how to use/troubleshoot the code once you've copied it your project).

### Complete Code

{% comment %} Liquid Code to Parse Wiki-like link Syntax {% endcomment %}

{% comment %}
Line1: Get content from the current page into the variable content_array by splitting it using '[​[' as a delimiter, If a page has "lorem ipsum varum [​[dorum]] borum [​[morum]] gerum" as its content, the resultant content_array will contain the following: ["lorem ipsum varum", "dorum]] borum", "morum]] gerum"]
{% endcomment %}

{% assign link_open_delimiter = '[​[' %}
{% assign link_close_delimiter = ']]' %}
{% assign content_array = page.content | split:link_open_delimiter %}
{% assign external_link_delimiter = '::' %}

{% comment %}
The use of this weird looking symbol i.e., "$@" is to ensure that we do not fall victim to parsing error due to use of common delimiters like commas(,) in user-code. {% endcomment %} {% assign link_joiner_delimiter = '$@' %}
{% for item in content_array %}
{% if forloop.index > 1 %}

{% comment %}
Same as the first comment, but for closing brackets
{% endcomment %}

{% assign itemparts = item | split:link_close_delimiter %}

{% comment %}
{% endcomment %}

{% comment %}
external_link[1] will be zero only when it is an internal link as we are splitting itemparts by :: in the previous line
{% endcomment %}

{% if external_link[1] == nil %}
{% comment %}
result_collection will contain the yaml matter of the page with title present in itemparts[0] i.e., title mentioned in double brackets in the current page as a link.
Note: you must replace the collection_name here with your own collection name.
{% endcomment %}

{% assign result_collection_name = site.collection_name | where: 'title',itemparts[0] %}
{% assign result_posts = site.posts | where: 'title',itemparts[0] %}
{% else %}
{% assign external_urls = external_urls | append: link_joiner_delimiter | append: external_link[1] %}
{% endif %}
{% endif %}
{% endfor %}

{% comment %}
Store all links and urls in separate arrays
{% endcomment %}

{% assign external_url_array = external_urls | split:link_joiner_delimiter %}

{% comment %}
Store the content to changed in the replaced_content variable
{% endcomment %}

{% assign replaced_content = page.content %}

{% for title in internal_link_array %}
{% assign url = internal_url_array[forloop.index0] %}

{% if url == nil %}
{% assign link_text = '<a style="background-color:#ffffc4;" href="' | append: 'javascript:void(0)' | append: '">' | append: title | append: '</a>' %}
{% elsif url == empty %}
{% assign link_text = '<a style="background-color:#ffffc4;" href="' | append: 'javascript:void(0)' | append: '">' | append: title | append: '</a>' %}
{% else %}
{% assign link_text = '<a  href="' | append: url | append: '">' | append: title | append: '</a>' %}
{% endif %}

{% endfor %}

{% for title in external_link_array %}
{% assign url = external_url_array[forloop.index0] %}

{% assign link_text = '<a href="' | append: url | append: '">' | append: title | append: '</a>' %}

{% comment %}
Since the external link is of the form [​[Title::URL]], while replacing we have to append the "::URL" part to the title, which is done here using "append: external_link_delimiter"
{% endcomment %}

{% endfor %}
{{ replaced_content | markdownify }}


### How to use the code in your project

All you have to do to be able to use the code above in your own project is just copy the snippet as it is and paste it in the place where you usually display your content — {​{​content}}, that is, instead of {​{​content}} now it will be that liquid code snippet that you just copied, which will replace your usual content will {​{replaced_​content}} as can be seen in the last line of the code above.

Note: If the code does not work out-of-the-box, do not be worried. It works, I use it all over my site, even this page and all the examples in the page are powered by that code.

Here are some troubleshooting steps if it does not work out of the box.

1. You might not have changed the site.collection_name in the above code.
1. If you do not use collections, just remove all the statements that use collection_name.
2. If you do use collections, just change the collection_name to your custom collection name.
2. There might be stray characters introduced due to copy-paste.
1. Try to delete all the occurences of "[​[", "]]", "::", "\$@" in the above code once you have copied them and just re-type them.

### A Trivia for people who are interested:

If you observed keenly, you'd have noticed that this page makes extensive use of "[​[" and "]]" for expository purposes without getting converted to a link. All thanks to zero-width space. Also yes, you will have to deal with this issue too if you wished to use "[​[" in your makdown files. 😅

P.S. please do let me know if you still have any questions or issues with getting it to work in the comments section below: