Markdown syntax highlighting discussion... again

In this thread, BLUEFROG gave a way to highlight your code in markdown by using Prism.js:

<link href="styles/prism.css" rel="stylesheet">
<script src="styles/prism.js"></script>

It does work, however, a script tag and a css tag hanging at the top of EVERY markdown file is not an elegant solution after all, for mainly three reasons:

  1. When importing markdown from other apps, it will not automatically insert CSS and JS html tag into the markdown file.
  2. When exporting or copying markdown plain text to other apps or publish it to website, those two lines unnecessarily remain and may cause unexpected consequence.
  3. It is just weird to have those two annoying and unrelated lines at the top of each document while editing… And you have to be careful not to accidentally delete those lines.

I wrote a script trying to solve part of the problems, but it would be great if we have an option to attach a JavaScript file to every markdown file automatically, just like the “Style sheet” option in web preference panel:

And here is my JXA script to insert codes at the top of every markdown file in database.

let dtp = Application("DEVONthink Pro");

function insertJavaScript(markdownRecord) {
    let originalText = markdownRecord.plainText();
	let originalModification = markdownRecord.modificationDate();
    let scriptSource = '<link href="styles/prism.css" rel="stylesheet">\n<script src="styles/prism.js"></script>'
    if (!originalText.trimStart().startsWith(scriptSource)) {
        markdownRecord.plainText = scriptSource + '\n\n' + originalText;
		markdownRecord.modificationDate = originalModification;
    }
	
}
dtp.search("~[^`] OR ~`").filter( // Not sure if it is the best way to flatten all records in the database
        (record) => record.type() == "markdown"
    ).map(
        (record) => insertJavaScript(record)
)
1 Like

Welcome @thekoc

at the top of every markdown file in database.

I think this is a bit heavy-handed. You may want to offer a more focused version, ie. working on a selection.

It is just weird to have those two annoying and unrelated lines at the top of each document while editing

I see nothing weird or inelegant here. It’s an easy solution to the question. It’s also not an uncommon thing to have boilerplate elements in Markdown or HTML. They allow for flexibility in the same way a stylesheet often defines elements you may not use in every Markdown or HTML file.

I see nothing weird or inelegant here. It’s an easy solution to the question.

I think I AM little fussy about this kind of things… Just felt uncomfortable thinking of those two lines in my markdown file :joy:

Maybe it is my problem, but still I would be rather happy if an option is offered to attach js to markdown file internally. Since the “Style Sheet” option is offered to apply CSS to all web records automatically.

And for anyone who might need it, this JXA will only modify selected records.

let dtp = Application("DEVONthink Pro");

function insertJavaScript(markdownRecord) {
    let originalText = markdownRecord.plainText();
	let originalModification = markdownRecord.modificationDate();
    let scriptSource = '<link href="styles/prism.css" rel="stylesheet">\n<script src="styles/prism.js"></script>'
    if (!originalText.trimStart().startsWith(scriptSource)) {
        markdownRecord.plainText = scriptSource + '\n\n' + originalText;
		markdownRecord.modificationDate = originalModification;
    }
	
}
dtp.selection().filter(
        (record) => record.type() == "markdown"
    ).map(
        (record) => insertJavaScript(record)
)

And for a simple AppleScript approach…

set header to ("<link href=\"styles/prism.css\" rel=\"stylesheet\">" & return & "<script src=\"styles/prism.js\"></script>")

tell application id "DNtp"
	repeat with thisRecord in (selection as list)
		if type of thisRecord = markdown then
			tell thisRecord
				if plain text does not contain header then
					set cachedModDate to modification date
					set plain text to (header & return & plain text)
					set modification date to cachedModDate
				end if
			end tell
		end if
	end repeat
end tell

:slight_smile:

1 Like

Hello, did toi correct that in DT3 ? It is the only reason I didn’t still migrate all my knowledge base , which is written in markdown. If I cannot define a global css files instead of inserting lines at the top of each file, I will have to continue using Notebooks app…
Regards,
Alban

If I cannot define a global css files instead of inserting lines at the top of each file, I will have to…

Technically, you don’t have to… :stuck_out_tongue:

DEVONthink 3 has an option for a global Markdown stylesheet.

PS: I don’t know what “toi” is.

Great ! Sorry for “toi” it is my keyboard. I was meaning “you” .

No worries. Cheers! :slight_smile:

I’ve extended your script to replace language tags like python in the first line of code blocks with prism style language-* tags. This helps me keep syntax highlighting when I import notes from the bear app.

set replaceCommand to "awk '{for(i=1; i<=NF; i++) if(match($i,\"```\")) if(++count%2==1) $i=\"```language-\" substr($1, RSTART+RLENGTH)}1'"
# NOTE: Replace x-devonthink-item links with your own prism.js and prism.css asset links
set header to ("<link href=\"x-devonthink-item://AF2949C4-1A13-4F45-9CAF-FB44A3A6F6D5\" rel=\"stylesheet\">" & return & "<script src=\"x-devonthink-item://605F491F-FFE1-407E-9956-4DBF69D22A7F\"></script>") & return

tell application id "DNtp"
	repeat with thisRecord in (selection as list)
		if type of thisRecord = markdown then
			tell thisRecord
				if plain text does not contain header then
					set cachedModDate to modification date
					set plainText to plain text
					
					# Writing temporary file
					set posixtmpfile to (system attribute "TMPDIR") & "DEVONTMP_" & texts 3 thru -1 of ((random number) as string)
					try
						set fhandle to open for access posixtmpfile with write permission
						write plainText to fhandle as «class utf8»
						close access fhandle
					on error
						try
							close access posixtmpfile
						end try
					end try
					
					set replaced to do shell script (replaceCommand & " " & posixtmpfile)
					set plain text to (header & return & replaced)
					set modification date to cachedModDate
					
				end if
			end tell
		end if
	end repeat
end tell

P.S. I’m using awk and a temp file to do this because replacing every nth occurrence of something in AppleScript is surprisingly harder than it needs to be :sweat_smile: