How to Add Code Syntax Highlighting to Blogger

On my blog I always wanted to format source code in Python and a couple more languages, but couldn’t find a convenient way. Until I read a tutorial on adding syntax highlighting to Blogger blogs like mine.


The Blogger post composer actually provides the monospace Courier font that may be used for source code, but it works well only for inline text.

If I apply the Courier font to a block of code, the composer renders each line as a separate paragraph. This leaves too much vertical space that makes the code look ugly. A workaround is to switch to the HTML view in the composer and wrap the block within <pre> ... </pre> tags, which insert the correct line spacing. However, the code doesn’t stand out on the page and there’s room for improving its scannability and visual impact.

Fortunately, Blogger is an old dog I can teach new tricks to, like the setup the tutorial I found presents.

Python code snippet with syntax highlighting rendered by highlights.js
A Python code snippet with syntax highlighting rendered by highlights.js in a post of the Moonshots Beyond the Cloud blog.

The setup relies on highlight.js, an open-source JavaScript library for syntax highlighting of source code in a couple hundred languages with dozens of styles. All it takes is adding three lines to the HTML header of the Blogger theme, and wrapping code blocks within <pre><code> ... </code></pre> in the HTML view of the composer.

I’ll explain how I configured my blog for syntax highlighting and how I apply the formatting to code blocks. 


Getting highlight.js

There are several ways of using highlight.js and I went with the most straightforward.

The first, onetime step is to edit the blog’s header to add HTML code to fetch the library when a browser loads a page of the blog. I edited the HTML source of the blog theme and inserted these lines toward the end of the <head> section:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css" integrity="sha512-3xLMEigMNYLDJLAgaGlDSxpGykyb+nQnJBzbkQy2a0gyVKL2ZpNOPIj1rD8IPFaJbwAgId/atho1+LBpWu5DhA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js" integrity="sha512-Pbb8o120v5/hN/a6LjF4N4Lxou+xYZ0QcVF8J6TWhBbHmctQWd8O6xTDmHpE/91OjPzCk4JRoiJsexHYg4SotQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>hljs.highlightAll();</script>

The first two lines fetch the components of highlights.js from the cdnjs CDN. The third line calls the library’s entry point.

This configuration step is covered in the usage instructions page of the highlights.js documentation under “Basic usage” > “In the browser”. The sample code there is generic and needs to be fleshed out with links to the latest version of the library. The third line of the sample code, <script>hljs.highlightAll();</script>, calls the library and I copied it as is to the blog header as the third line of my configuration snippet above.

The simplest way of linking to the latest version, which is what I did, is to fetch the library from a CDN such as cdnjs.

Section “Fetch via CDN” on the same instructions page includes a code snippet with up-to-date links, which shouldn't be used as is. Why? Because the snippet is vulnerable to downloading compromised versions of the library.

Subresource integrity can address this security issue. Here’s how. I clicked the link next to the name of the cdnjs CDN, again on the same instructions page of the highlights.js documentation. The link leads to a cdnjs tool that generates code snippets with the appropriate digests for checking subresource integrity. The entries I needed are those for the files default.min.css and highlight.min.js.

I finally put together the three pieces in my snippet, i.e. the two lines for fetching the library and the third to call it, and added them to the blog header.

I prefer light designs, so the default light theme of highlights.js works well for me, blends nicely with the design of my Blogger theme, and requires no additional configuration. The library comes with dozens of light and dark themes, though.

Once configured, highlights.js requires no other setup, and the blog is ready to format code blocks with syntax highlighting.


Formatting code blocks

To add a block of code to a post, I paste it into the composer at the right spot. Then I switch to the HTML view and wrap the code within <pre><code> ... </code></pre>, taking care of removing any <p> tags. For longer blocks it’s easier to insert a placeholder where the block should be and paste the full code there in the HTML view, thus saving the effort of removing the tags.

Highlights.js auto detects the language and no further action is usually necessary.

Besides ordinary Python, there are a couple more options of the class attribute I can add to the <code> tag, one for profiler results and another for REPL sessions. For example, wrapping a block within <pre><code class="language-python-repl">...</code></pre> formats a block of a Python REPL session.

As for inline code, I still select it in the composer and apply the Courier font. This is good enough for running text, and it matches the way inline code is typically formatted without highlighting on other sites. Besides, highlights.js works only with blocks.


Code samples

To test highlights.js I applied syntax highlighting to the code blocks of an old post about Spacestills, a NASA TV still frame viewer I wrote in Python. The result looked good, so here is a longer Python example, the main function of Spacestills that runs the PySimpleGUI event loop:

def main():
    """Run event loop."""
    window = sg.Window('Spacestills', LAYOUT, finalize=True)
    current_still = refresh(window)

    delta = DELTA
    next_reload_time = datetime.now() + timedelta(seconds=delta)

    while True:
        event, values = window.read(timeout=100)
        if event in (sg.WIN_CLOSED, 'Exit'):
            break
        elif ((event == '-RELOAD-') or
                (values['-AUTORELOAD-'] and timeout_due(next_reload_time))):
            current_still = refresh(window, values['-RESIZE-'])
            if values['-AUTORELOAD-']:
                next_reload_time = next_timeout(delta)
        elif event == '-RESIZE-':
            current_still = change_aspect_ratio(
                window, current_still, current_still.new_size())
        elif event == '-SAVE-':
            filename = sg.popup_get_file(
                'File name', file_types=[('PNG', '*.png')], save_as=True,
                title='Save image', default_extension='.png')
            if filename:
                saved = save(current_still, filename)
                if not saved:
                    sg.popup_ok('Error while saving file:', filename, title='Error')
        elif event == '-UPDATE_DELTA-':
            # The current cycle should complete at the already scheduled time. So
            # don't update next_reload_time yet because it'll be taken care of at the
            # next -AUTORELOAD- or -RELOAD- event.
            delta, valid = validate_delta(values['-DELTA-'])
            if not valid:
                window['-DELTA-'].update(str(DELTA))

    window.close()
    del window

Next, let’s format a short function taken from Suite8080, a suite of Intel 8080 Assembly cross-development tools I’m writing in Python. The function processes the mov mnemonic in the assembler:

# mov: 0x40 + (8-bit first register offset << 3) + (8-bit second register offset)
# mov m, m: 0x76 (hlt)
def mov():
    check_operands(operand1 != '' and operand2 != '')
    # 0x40 = 64
    opcode = 64 + (register_offset8(operand1) << 3) + register_offset8(operand2)
    pass_action(1, opcode.to_bytes(1, byteorder='little'))

Finally, some code in a different language. This is the source of the hello world Intel 8080 Assembly program for CP/M that’s part of Suite8080:

; Hello world for CP/M

            org     100h
bdos        equ     0005h           ; BDOS entry point
wstrf       equ     09h             ; BDOS function: write string

            mvi     c, wstrf
            lxi     d, message
            call    bdos
            ret

message:    db      'Greetings from Suite8080.$'
            end

Highlights.js supports only ARM, AVM, and x86 Assembly but not Intel 8080 (too bad such a bleeding edge chip is missing), so this time it didn’t detect the language. However, the result still looks reasonably good and I’m ready to publish more code.

No comments:

Post a Comment