Okay, I found a solution for you, and I hope you use it because it was a lot of work 
There is no fold
function, and honestly, there is no easy way to do what you are asking with Liquid. I did find what I would call a workaround for the solution, and I think it should generally work. Maybe you can play around with the logic, but I found it to work with <= 74 characters and >74 characters.
Liquid limitations
Letâs list out what you cannot do first:
- Add a new line (carriage return) for every n number of characters
- Add spaces for every n number of characters
The approach
What you can do is use the slice
filter, which will, in your case, will grab 74 characters and put them into a new variable. With that variable, you can add extra spaces to the beginning.
We do not know how many rows the event description will create, so we should store each new line as a row in an array. But that causes a problem because you canât technically create an array and start stuffing items into it. Instead, you need to get all your text and use the split
filter to convert your string into rows within an array. You need some character to key off, so I selected the pipe (|
) character. You can choose something else. The trick is to make sure you never (ever) use that character in the event description.
What we want
Grab every instance of 74 characters, add two spaces to the beginning, and put it into an array. Do not add spaces to the first line.
What we will do
- Grab every instance of 74 characters into an array
- Prepend two spaces to the start of each line (we will strip out the first two spaces later)
- Append each line with a separator character
- Remove the leading spaces on the first line where the description is located using the liquid
lstrip
filter
- Remove the last separator character from the end of the string using the
slice
filter
We will end up with a string that looks something like this:
DESCRIPTION:Lorem ipsum dolor sit amet consectetur adipisicing elit. Quam | laudantium sapiente blanditiis provident praesentium amet ad unde consecte| tur placeat voluptates? Officiis ipsa praesentium esse ipsam quibusdam nob
With that code in place, we can use the Liquid split
filter to break up the text into rows in an array based on our separator. We can then use that array to build the iCal event details.
The code
You will note that I output a bunch of these variables to the screen so you can verify the numbers first, so feel free to comment or remove them when you are okay with it. First, I will provide all the code, and then I will describe what is happening:
ical.md
---
layout: default
---
{% comment %} Create a string that represents the description for an iCal event {% endcomment %}
{% comment %} >74 characters {% endcomment %}
{% assign eventOrig = 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quam laudantium sapiente blanditiis provident praesentium amet ad unde consectetur placeat voluptates? Officiis ipsa praesentium esse ipsam quibusdam nobis, aliquam voluptate eveniet laborum possimus similique aperiam quo iste repudiandae repellendus accusantium blanditiis nam, inventore atque facere natus a. Ratione et ea minus quis pariatur quisquam corrupti eum nulla officia cumque earum temporibus deserunt' %}
{% comment %} uncomment to test with up to 74 characters {% endcomment %}
{% comment %} {% assign eventOrig = 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quams' %} {% endcomment %}
{% comment %} Put the word "DESCRIPTION:" in front of the first line {% endcomment %}
{% assign eventOrig = eventOrig | prepend: 'DESCRIPTION:' %}
{% comment %} Determine the number of rows, assuming 74 characters {% endcomment %}
{% assign eventChars = eventOrig.size %}
{% assign stringsToGet = 74.00 %}
{% assign getiCalRows = eventChars | divided_by: stringsToGet | round %}
{% assign iCalRows = getiCalRows | round %}
eventChars: {{eventChars}}
iCalRows: {{iCalRows}}
{% comment %} Define string path elements {% endcomment %}
{% assign start = 0 %}
{% assign adder = 74 %}
{% comment %} Define the number of spaces to add before each new line {% endcomment %}
{% assign spaces = ' ' %}
{% comment %} Define the separator character that will be used to turn the event into an array {% endcomment %}
{% assign appender = '|' %}
{% comment %} Empty string that will later contain the new event {% endcomment %}
{%- assign iCalEvent = '' -%}
{% comment %} Represents a single row for the event {% endcomment %}
{% assign iCalEventRow = '' %}
{% for i in (1..iCalRows) %}
{% comment %} If the event is only one line, do nothing {% endcomment %}
{% if eventChars <= stringsToGet %}
{% assign iCalEvent = eventOrig %}
{% break %}
{% else %}
{% assign iCalEventRow = eventOrig | slice: start, adder | prepend: spaces | append: appender %}
{% assign iCalEvent = iCalEvent | append: iCalEventRow %}
{% assign start = start | plus: adder %}
{% endif %}
{% endfor %}
{% comment %} With the string in place, convert each line to an array {% endcomment %}
{% if eventChars >= stringsToGet %}
{% assign iCalEvent = iCalEvent | lstrip %}
{% assign lastChar = iCalEvent.size | minus: 1 %}
{% assign iCalEvent = iCalEvent | slice:0,lastChar %}
{% assign iCalDescriptions = iCalEvent | split: appender %}
{% endif %}
iCalDescriptions (size: {{iCalDescriptions.size}})
{% for desc in iCalDescriptions %}
{{desc}}
{% endfor %}
Original event (size: {{eventOrig.size}}):
{{eventOrig}}
Code walkthrough
eventOrig
is the original content you use for the event. In this case, make sure it does not contain the |
separator. I prepend that same string with the DESCRIPTION:
text to make life easier.
Determining rows
{% assign eventChars = eventOrig.size %}
{% assign stringsToGet = 74.00 %}
{% assign getiCalRows = eventChars | divided_by: stringsToGet | round %}
{% assign iCalRows = getiCalRows | round %}
I assume every line must be 74 characters, but it is really 76 because you add two spaces to the beginning (except for the first DESCRIPTION line).
To figure out how many rows you have, I divide the total number of characters in the event by the float number of 74.00
. That float number is essential because had I just used 74
, Liquid would have rounded down to 6 lines when you really have 6.5 lines. You always need to round up, and the round
filter will not work without changing 74
to 74.00
.
String path elements
We will walk through the string, collecting blocks of 74 characters along the way.
{% comment %} Define string path elements {% endcomment %}
{% assign start = 0 %}
{% assign adder = 74 %}
Later, we will introduce the use of the Liquid slice filter. We will tell the code where to start, which is position zero, and then how many characters to get after that, which in this case is 74.
Using my sample text, there will be 7 lines for the description. During the for
loop, the new start
variable will look like this:
Loop 1: 0
Loop 2: 74
Loop 3: 148
Loop 4: 222
Loop 5: 296
Loop 6: 370
Loop 7: 444
The adder
will be used for two different purposes. First, it will add 74
to the start
integer, as I show above. Second, it will tell the slice
filter how many characters to get.
Separator variables
{% assign spaces = ' ' %}
{% assign appender = '|' %}
The spaces
variable determines how many spaces to add for each line of the DESCRIPTION
. I put two spaces in there, so you can increase to however many you need.
As mentioned earlier in this post, our string containing the event will be converted into an array using the Liquid split filter. I selected the pipe character (|
), but you can choose something else. Never use that character in the description for one of your events or the code breaks.
Placeholder strings
{%- assign iCalEvent = '' -%}
{% assign iCalEventRow = '' %}
As you loop through the descriptions, you will create a temporary iCalEventRow
to get the text for a description row, prepend it with some spaces, and append it with the appender
character.
When complete, our string that contains all the spaces and appenders required, you will end up with an iCalEvent
. Later, we will populate the iCalDescriptions
array with the contents of iCalEvent
.
The for
loop
{% for i in (1..iCalRows) %}
{% comment %} If the event is only one line, do nothing {% endcomment %}
{% if eventChars <= stringsToGet %}
{% assign iCalEvent = eventOrig %}
{% break %}
{% else %}
{% assign iCalEventRow = eventOrig | slice: start, adder | prepend: spaces | append: appender %}
{% assign iCalEvent = iCalEvent | append: iCalEventRow %}
{% assign start = start | plus: adder %}
{% endif %}
{% endfor %}
Do nothing if the description line (which also includes the text DESCRIPTION:
is <= 74 characters.
If there are more than 74 characters, begging collecting each ârowâ of data as determined by the iCalRows
integer.
- Slice out the appropriate 74 characters of text based on the current
for
loop number. Also, prepend the two spaces and append the |
appender.
- Build the
iCalEvent
string to include the 74 characters + 2 spaces + |
- Increment the
start
value, so the slicer
gets the next group of 74 characters.
Cleanup and array creation
The iCalEvent
string now has all the text, with added spaces and a | separator. However, we do have a little cleanup to do.
{% comment %} With the string in place, convert each line to an array {% endcomment %}
{% if eventChars >= stringsToGet %}
{% assign iCalEvent = iCalEvent | lstrip %}
{% assign lastChar = iCalEvent.size | minus: 1 %}
{% assign iCalEvent = iCalEvent | slice:0,lastChar %}
{% assign iCalDescriptions = iCalEvent | split: appender %}
{% endif %}
- Use the lstrip filter to remove any leading space at the front of the string
- Use the
slice
filter once more to remove the last character in the string, which will be the | character (the appender
string)
- Now that we have the string cleaned up, use the Liquid
split
filter to look for all the text separated by the appender
| character and create an array with the name iCalDescriptions
Use the iCalDescriptions array
The following code shows how you can loop through each line of the description. In this case, it displays the text on the website page. You can, of course, add this to the variable you build for the rest of the event.
iCalDescriptions (size: {{iCalDescriptions.size}})
{% for desc in iCalDescriptions %}
{{desc}}
{% endfor %}
Potential bugs
The following might be problems with my code, so please do some testing:
- When I did the round-up math to calculate how many rows, I used a number of characters that ended up being 6.51 rows, so it rounded up to 7. I do not know if I had fewer characters if it would still round up. That could result in a bug where there are characters that do not show up in the final iCal description.
- I do not know if there are any special characters iCal does not support, so you might want to strip those out of the string before using the Liquid replace filter.
- Speaking of special characters, you may want to use that same
replace
filter to remove the special appender
character. That way, you do not have to worry about someone typing something that you will be using as part of your code.
Okay, I hope that helps!