OptimusCrime.net

Computers & Programming

Output Filters For Code Highlighting

Posted: 05.09.2016 12:11

My blog contains some code, and most of us programmers are used to read code with some sort of highlighting, either in our IDE, or in the manager interface via the Ace editor or something similar.

I wanted to have a simple syntax that I could write in the content field that would result in highlighted code. I had earlier attempted to paste code and set the format as pre, however, this turned out to be difficult when I was editing the code back and forth. Instead I decided to use an output modifier and some simple regex to do what I wanted.

highlight.js

Of course I did not write the highlighter myself. What I did was process the content from the content field and add the classes and markup that an existing library expected. I did some research, and the most promessing alternative (by far) was highlight.js. I suggest you check out their website, it is really a nice piece of code! highlight.js is very simple. It just requires one line of code, like so:

hljs.initHighlightingOnLoad();

The markup highlight.js expected was also self explanatory. It looks like this:

<pre><code class="html">...</code></pre>

Where html can be any of the 166 languages it has highlight support for.

Output modifiers + regex = true

In my template I have the output from the content field. It looks like this:

[[*content:parse_content]]

The parse_content code looks like this:

// For highlight.js
return preg_replace_callback("/<p>```(?P<type>[a-zA-Z]+)?(?P<content>.*)```<\\/p>/m", function($match) {
    // Here we check if the optional type was supplied
    $code = '<pre>';
    if (isset($match['type']) and strlen($match['type']) > 0) {
       $code .= '<code class="' . $match['type'] . '">';
    }
    else {
       $code .= '<code>';
    }
    
    // Replace first <br> with nothing to avoid that the first line break
    // creates a blank line at the top of our code block
    $match['content'] = preg_replace("/^<br\W*?\/>/", "", $match['content']);
    
    // Replace <br> with newlines for <pre>
    $code .= preg_replace("/<br\W*?\/>/", "\n", $match['content']);
    
    // Close the wrapper
    $code .= '</code></pre>';
    
    // Return the final markup
    return $code;
    }, $input);

To create a code block in TinyMCE, my regex pattern recognizes the pattern:

`‌``language
code
goes
here`‌`‌`

For example:

`‌`‌`php
<?php
echo "Hello world";
?>`‌`‌`

Would produce:

<?php
echo "Hello world";
?>

The important thing is that the content can not be separated by a full line break. The newlines needs to be <br />. You get this by holding down Shift while pressing Enter. "Regular" newlines you get when you just press Enter actually creates a new paragraph block within TinyMCE, and this breaks my regex expression. I guess I could adjust it to support this, but simply having the entire code as a single paragraph block worked very well.

You can see just how the end result looks by looking at this page and inspecting the source code.

Inline code

The code blocks explained so far is designed for multilevel code that is separated from the rest of the text. I also wanted support for some sort of highlighting that I could have for inline text. I knew StackOverflow had support for this, and I shamelessly copied the CSS they use for their inline code:

main code {
    color: #333333;
    background-color: #EEEEEE;
    font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;
    padding: 1px 5px;
}

I also expanded the code in parse_content with the following line:

$input = str_replace(['{‌{', '}‌}'], ['<code>', '</code>'], $input);

This code simply replaces curly brackets with code tags that are allowed to be inlined. The result looks like this and that. It works great for making code or keywords stand out in the middle of a sentence.

Easy as that

I just wanted to share these few couple of lines because I was very satisfied with the outcome myself. I originally tried using pre tags in my markup directly in TinyMCE, but I found it too clumsy to work with. My solution does not need any toggling of formats in the editor. The only thing you need to remember is to not create newlines while expanding or pasting the code.

Easy as that!

Feel free to comment or share.