Code highlighting in Markdown documents

Have DevonThink recognise ```swift

My goals is to have as a bare minimum have ```swift being recognised and being color coded.

I have the following simple example document:

```swift
// Example 1
let x: Int = 3 
```
[---] 

```language-swift
// Example 2
let x: Int = 3
```

[---]
 
```
// Example 3 
let x: Int = 3
```

Making example 1 work is the goal. I prefer example 1 over example 2 because it is 1) less typing 2) I have many existing documents that use the example 1 format.

Try 1: Enable Prism

Enable prism, select no Stylesheets and no Javascript in the preferences.

Result: Example 2 now works. This is not my goal.

Try 2: Use Stylesheet & JavaScript

For the Stylesheet and JavaScript I am using highlight.js.

For the CSS I choose: http://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/base16/one-light.min.css. For the Javascript I choose http://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js.

Disable prism.

Result: None of the examples works

Try 3: Add init code to the document

Keep the settings identical to “Use Stylesheet & Javascript”. Add the following line to the top of the document:

<script>hljs.highlightAll();</script> 

Result: All of the examples work

Try 4: Add init code to the document and enable Prism

Keep the settings identical to “Add init code to the document”. Additionally also enable Prism in the preferences.

Result: All of the examples work however example 1 and example 3 have lost their background. Apparently Prism interacts with the nightlight.js code. Beside the background, other effects may be present.

Question/Feature request

So this leads to the question, how is it that we can add JavaScript to a document using the JavaScript preference but there is no way that we can initialise the code?

Although the extra line at the top of the document seems minor, it is not. If I now open this document in another editor (Obsidian, Typora and others) this line is without context. It has no meaning without the ingestion of the stylesheet and JavaScript. Sometimes, they just print this as text.

Should there be an option where the loaded JavaScript can be initialised? Does anyone know a better way?

Finally ideally, DevonThink should support highlight.js out of the box. So just add Hightlight.js support and add a dropdown for the known stylesheets. This should include a style ‘none’ so that it can be overridden by the Stylesheet option.

It’s a known shortcoming (design decision?) in DT’s markdown support that you have to use the language- prefix. So that’s basically what you have to accept right now.

Why would you want two competing highlighting products enabled?

Not necessarily. Both frameworks will have to set classes on the code and pre elements in HTML. And they use CSS to set the attributes of these classes. This is most probably not about the code (in the sense of JavaScript), but about the styling. Which again begs the question why you’d want to use two highlighting techniques at the same time.

First, there is a way (you mentioned that yourself). Second, there’s another way, namely providing your own JS file that loads the highlighting product and initialises it.

Well, in the worst case that line produces a JavaScript runtime error. Which you won’t see in anything but a browser’s web developer console. But it does not influence the rendering at all (except that you won’t have any highlighting unless you included the highlight.js code, too).

That would be quite normal inside the markdown view (aka “in the editor”). In a processed MD file (which could be anything, but I assume that you’re referring to HTML), this line must not be visible. If it is, the processor or the HTML view is broken. HTML elements are perfectly valid in MD, and is an element that does never render in HTML.

BTW, these products will not display highlighted code sections either unless they’re providing support for Prism.js or hightlight.js or whatever. So including adding the initialisation code is neither here nor there: Either highlight.js is somehow™ installed in these other products and then the line will do what it is supposed to. Or hightlight.js is not installed, but then there’s no highlighting anyway.

How many JS frameworks should DT “ideally” support? How many checkboxes should the preferences pane include?

MD is a text format. Nothing else. There are processors that convert it to something else, like PDF, HTML, RTF, possibly Word. For this, one needs to parametrize the conversion (font, color, line width, all that). But all that is beyond the reach of MD. It is in the domain of the processor. Imagine a browser where the execution of JS is turned off – neither highlight.js nor prism.js would produce any highlighting.

If you need others to see your processed MD file (and I see very few reasons for highlighted code otherwise), make sure that the environment for that Does The Right Thing™. But overloading a document management system with arbitrary JS frameworks is not the way to go, I think.

Thank you for your reply. Let me clarify a few things:

  1. I have no intention of mixing the Prism & highlight.js. It was just that I wanted to be thorough and complete. It just happened that I had enabled Prism without realizing it would interact with stylesheets I loaded using other mechanisms. Yes, that was perhaps a bit ignorant but it lead me to do this testing.
  2. About adding highlight.js support. I agree with you that there are simply too many to support them all. It is just that I like highlight.js better than Prism :slight_smile:
  3. Finally you mention that there is other way being “providing our own JS file that loads the highlighting product and initialises it”. Can you give me the syntax on what that looks like? Is this simply adding the init line to the JavaScript? I appreciate a pointer here.

Explanation first. There are several ways to do that but in my opinion the simplest would be to add the relevant script and style elements to the top of the HTML after the DOM is loaded. So your external JS file consists basically of a listener for the DOMContentLoaded event that does two things:

  • inject a script and a style element into the DOM
  • call the initialisation code for hightlight.js as soon as the script has finished loading.

Untested (!) code now. I suggest to test it in a basic HTML document in your browser’s web developer tools first.

Edit Fixed typo in code according to @fredap’s post below.

document.addEventListener('DOMContentLoaded', () => {
  const script = document.createElement('script');
  script.type = 'text/javascript';
  /* Initialize Highlight code as soon as the script is loaded */
  script.addEventListener('load', () =>  hljs.highlightAll() );
  script.src = 'Script URL';
  const style = document.createElement('link');
  style.rel = 'stylesheet';
  style.type =  'text/css';
  style.href = 'Style Sheet URL';
  document.head.append(script, style);
})

There’s some more information on MDN

An alternative solution would be to use Ajax in the DOMContentLoaded listener that loads the JS file and executes it. I find that a bit clumsy and since it requires the use of eval a security burden.

Thank you for your support. This works like a charm.

The above script has a small error. The code style.setAttribute.href should be style.href.

So to get this working do the following:

  1. Create a text file in DevonThink and give it the following contents:

    document.addEventListener('DOMContentLoaded', () => {
     const script = document.createElement('script');
     script.type = 'text/javascript';
     /* Initialize Highlight code as soon as the script is loaded */
     script.addEventListener('load', () =>  hljs.highlightAll() );
     script.src = 'http://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js';
     const style = document.createElement('link');
     style.rel = 'stylesheet';
     style.type =  'text/css';
     style.href = 'http://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/base16/one-light.min.css';
     document.head.append(script, style);
    })
    
  2. Copy the link to this file using “Copy Item Link”

  3. Go to the preferences and:

    1. Clear out the stylesheet line
    2. Copy the link from step two into the JavaScript line
    3. Uncheck Prism support

Please note that highlight.js has many other styles that you can choose from.

You are welcome. I fixed the typo in the original code as well, thanks for pointing that out.

The next release will support this syntax too.

2 Likes

That is good to hear.