tl,dr: can I {% include %} a file and increment all the heading levels?
If I have a file chapter1.md:
# chapter 1
blah blah
## subheading 1
blah blah
When I render it with Jekyll, I get 'Chapter 1' as an h1 and 'subheading 1' as an h2, as planned.
Now I have a different page, book.md, and I want to include my Chapter 1 into it like so:
# Book
Here is my book.
{% capture inc %}{% include chapter1.md %}{% endcapture %}
{{ inc | markdownify }}
{% comment %} ditto for remaining chapters {% endcomment %}
Where the (silly) capture/endcapture is the usual trick for including snippet that you need to be rendered with markdown (without plugins).
Here, "chapter 1" is an h1 and "subheading 1" is an h2 (again, as expected - following the original markdown file).
My question is: How can I cause all the headings in chapter1.md to be incremented by one level upon inclusion so that "chapter 1" becomes an h2 and "subheading 1" becomes an h3?
This allows me to use my chapter snippet both as a standalone file and included into a master document.
Is there a {{ inc | markdownify: 'header-increment', '1'}} or something like that?
Can I {% include %} a file and increment all the heading levels?
Not really. Neither Jekyll nor Liquid have a built-in way to indent header levels as described. You could ostensibly write a plugin to make the appropriate transformation, but it's a nontrivial task (that is to say, naively gsubing # for ##, for example, would break any use of "#" in code blocks or paragraphs).
It'd be much easier to simply maintain a convention that chapter content start at h2. Alternatively, you could nest chapters in an article or section tag, and differentiate using CSS without running afoul of markup semantics.
Related
I'd like to create something like a shortcode for a blockquote in Jekyll. It should include the quote source in a nicely formatted way.
The shortcode could look like this:
{% quote author="My Author" %}
This is the quoted content
spanning multiple lines
And paragraphs
{% endquote %}
What's the best way to achieve this in Jekyll? Can it be that there is no way to provide multiple arguments to a Jekyll tag plugin?
I have found a blog post that provides multiple attributes using string concatenation or JSON.
My Research
I have found two systems in Jekyll that can be used similar to shortcodes:
HTML Includes
Custom Tags
To summarize, both methods only provide a single attribute to the Ruby code, the content. Below, you will find the limitations of both solutions.
HTML Includes limiations
https://jekyllrb.com/docs/includes/
An include in use looks like this:
{% include note.html content=download_note %}
It is possible to use content from captures for parameters, so we could create the following include file:
<blockquote>
{{ include.quote | markdownify }}
<p><em>{{ include.author }}</em></p>
</blockquote>
And use it in a blog post like this:
{% capture quote %}
This is the quote content
spanning multiple lines
And paragraphs
{% endcapture %}
{% include quote.html quote=quote author="My Author" %}
It works, but in my opinion, it's not really a nice approach to use when writing blog posts.
Custom Tags limiations
https://jekyllrb.com/docs/plugins/tags/
Sounds promising, but the documentation only shows two ways to use them:
{% render_time page rendered at: %}
and
{% render_time %}
page rendered at:
{% endrender_time %}
If I understand correctly Jekyll takes the first paragraph as an excerpt unless you use one of the various methods mark or specify one manually.
In my case, I want to be able to distinguish in the templates whether there was no excerpt or not so I can effectively do this
{% if post.excerpt %}
{{ post.excerpt }}
{% else %}
{{ post.content }}
{% endif %}
Effectively if there was no excerpt use the entire post. As it is, since Jekyll auto generates excerpts the test will always fail.
I suppose one solution so to go to every post that has no excerpt and add <!-- more --> at the very bottom of the post but that's very error prone as in if I forget I'll get the wrong result. I'd prefer to make the default be if I didn't manually mark an excerpt then the entire post appears on the home page.
To put it another way I'm trying to port from Wordpress to Jekyll. Wordpress's behavior is that no excerpt = insert entire post.
Is that possible in Jekyll? Is there some flag or variable I can check in the templates on whether or not an excerpt was manually specified vs auto generated?
There is an alternative solution with Liquid. You need to check, if the excerpt separator is present in the post:
{% if post.content contains site.excerpt_separator %}
{{ post.excerpt }}
<p>Read more</p>
{% else %}
{{ post.content }}
{% endif %}
I don't know any method to tell if an excerpt is manual or generated. Maybe writing a plugin to analyze the raw file's front-matter can be an option (but that would not work on Github Pages for example).
But I may have a solution for this:
I'd prefer to make the default be if I didn't manually mark an excerpt then the entire post appears on the home page.
According to the documentation, you can set excerpt_separator for every page (you can also set it at once in defaults).
Try setting a value which you know will never appear in your posts. If Jekyll doesn't find the separator, it won't separate, so the generated excerpt will be the entire post.
Example:
---
title: Some title
excerpt_separator: "CANTFINDME!"
---
Post line 1
Post line 2
The generated excerpt will be the entire post:
<p>Post line 1</p>
<p>Post line 2</p>
I wonder if anyone can illuminate this puzzle. I am using a new installation of vanilla jekyll on a Mac. Everything seems to work fine, but I discovered that some text being shown in my page footer was rendering differently on posts and all other pages. On most pages the text would render as HTML, but in posts it was rendering as Markdown. I found a workaround, but it left me with even more questions.
Context
I have defined footer_sections as a collection to hold portions of the footer. In my _config.yml this looks like:
collections:
footer_sections:
output: false
A footer section is then defined in a Markdown file such as _footer_sections/address.md as:
---
title: Address
order: 1
---
**My Name**
123 My Street
My Town, ST 12345
123-555-5555
In my default.html I had a footer section in my HTML something like this:
<div id="footer">
{{ site.footer_sections | where: "title", "Address" }}
</div>
And my posts are set up like this example:
---
title: Silly new post
date: 2017-02-27T12:33:53+00:00
author: Eric Celeste
layout: post
---
Silly post.
And finally, the post layout is connected to the default layout like this:
---
layout: default
---
<h1>{{ page.title }}</h1>
<p class="meta">{{ page.date | date_to_string }}</p>
<div class="post">
{{ content }}
</div>
The Problem
Notice that the address.md file is defined in Markdown and then its content is shown in the footer by the inclusion of the section in default.html. On all regular pages this would render as HTML (a bold name, a plain address), but on posts like the silly post above, it would render as Markdown (a name surrounded by stars and an address without like breaks).
I thought maybe it had to do with different procedural steps between posts and pages, maybe the Markdown rendering is happening "later" on pages but has already happened "earlier" in posts. I am only two days old on Jekyll, so I really don't know how it works.
In order to test that theory, I tried forcing the Markdown rendering with the markdownify filter. I changed the liquid tags in default.html so that they read:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.content | markdownify }}
Oddly, this produced a worse result everywhere. Now no text of any sort appeared in the footer of regular or post pages.
On the theory that maybe the where filter is actually different from looping through members of an array with foreach I tried another approach:
{% for section in site.footer_sections %}
{% if section.title == "Address" %}
{{ section.content | markdownify }}
{% endif %}
{% endfor %}
That worked! Now the content of the footer sections rendered as HTML on both regular pages and posts.
My Questions
Why didn't the initial approach work? What is the difference between rendering of posts and other pages in Jekyll?
While I found workaround, I don't understand why it works. In what ways does pulling out an item from an array with a where filter differ from using a member from a foreach loop? How does this affect the results of the markdownify filter?
Is there a cleaner, simpler way to grab the HTML-rendered content from my sections than looping through them each time I want to use one of them?
Thanks for any insights you may have!
site.footer_sections is an array and the output of the 'where' filter is still an array (but only containing the values that match your condition).
In your case, you are getting a single-element array but it's still an array object.
To see this for yourself use the inspect filter:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.content | inspect }}
On the other hand, when you loop through the elements with a for loop, at each iteration you get the individual elements of the array. Try using inspect inside your loop to see how the two types of your section variable differ.
For the 'where' method to work you need to get the actual element from the array either with first or [0]:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.first.content | markdownify }}
OR
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section[0].content | markdownify }}
links:
array documentation
first documentation
where documentation
Blank lines within and especially at the top of an HTML source file look untidy to me.
A common template code (in this case, Jinja2) may look like this:
{% block header %}
{% include "templates/partials/something_header.html" %}
{% endblock header %}
{% block body %}
{% include "templates/partials/something_body.html" %}
{% endblock body %}
{% block footer %}
{% include "templates/partials/something_footer.html" %}
{% endblock footer %}
Now, without even adding indentation issues to make the above more presentable, it already has the adverse effect of generating 2 empty lines due to the 2 carriage returns within the templating code:
.
.
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv=....
Whilst I can utilize a minifier/post-processor in this particular case, I'm wondering what others do to keep their template code easy on the eyes whilst preventing unnecessary blank lines?
EDIT: To eliminate the blank lines at the head of the generated source code, the above example of template code would appear as below (much less readable):
{% block header %}{% include "templates/partials/grid_header.html" %}{% endblock header %}
{% block body %}
...
My original question was a bit of an "I know this can be done, but is there another way?".
Thanks to the feedback from David and Lajos, confirming that post-processing of the generated HTML from the template engine is the most common way to alleviate unwanted blank lines and spacing.
Lajos also suggested a concept of maintaining both a "clean" and "correct" version of each template itself, whereby the developer may work unperturbed with clean template code and upon any modification, another version of the file will be ghost-written, but reformatted so that generating HTML from it would in fact produce clean HTML without any unwanted artifacts due to templating.
Whilst I tend towards the common post-processing method and chain any such cleaning up of HTML along with combining and minifying CSS/JS, etc, there would be scenarios that Lajos's implementation definitely would be beneficial (ie, if you don't/should't have control outside of the templating stage).
How can I number the code lines which are highlighted using pygments in Jekyll?
According to the Liquid Extensions wiki page of the Jekyll documentation, the highlight Liquid tag has an optional second parameter, which may have the value linenos to turn on line numbering:
{% highlight language linenos %}
your code here
{% endhighlight %}
Use it with caution. With linenos the line numbers are actually inserted in the code's text, so will be impossible to copy the code block without them. (This could be solved by letting the visitor to $('.lineno').toggle() the line numbers' visibility. Works in Firefox, not sure if is portable.)
Update: Better use linenos=table:
{% highlight language linenos=table %}
your code here
{% endhighlight %}
That will place the code in a table with two cells: first td all the line numbers, second td the code itself. This makes possible to select only the code, without the line numbers.