Smart Rule to Search and Replace Markdown Contents

Hey Gang! :smile:

I’ve started using Obsidian.md alongside DEVONthink 3 for my academic notes.

Obsidian uses their own flavor of markdown syntax for images , ![[image.png]], which unfortunately isn’t readable by other external editors.

Ideally, I’d like to use a Smart Rule to monitor my markdown notes and Search and Replace the contents, changing ![[image.png]] to ![](image.png), possibly with RegEX (\!\[\[)(.*)(\]\]) built into the script.

Is it possible to do this?

Grateful in advance for any help, suggestions or solutions.

If i could, I’d do it in JavaScript and it would be a ten liner or so. Unfortunately, JS doesn’t work in smart rules, only in scripts. You could try to do it in
AppleScript, but reg exes are not really available there

This should do it. Please try with duplicates.

-- Replace Obsidian image links with DEVONthink image links

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

tell application id "DNtp"
	try
		set theRecords to selection of think window 1
		if theRecords = {} then error "Nothing selected."
		
		repeat with thisRecord in theRecords
			set theType to (type of thisRecord) as string
			if theType = "markdown" or theType = "«constant Ctypmkdn»" then
				set theText to plain text of thisRecord as string
				if theText ≠ "" then
					set newText to my replaceImageLinks(theText)
					if newText ≠ theText and newText ≠ "" and newText ≠ "missing value" then set plain text of thisRecord to newText
				end if
			end if
		end repeat
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell

on replaceImageLinks(theText)
	try
		set ca to a reference to current application
		set theString to ca's NSString's stringWithString:theText
		set newString to theString's stringByReplacingOccurrencesOfString:("!\\[\\[(.*?)\\]\\]") withString:("![]($1)") options:(ca's NSRegularExpressionSearch) range:{location:0, |length|:length of theText}
		set newText to newString as string
	on error error_message number error_number
		error number -128
	end try
end replaceImageLinks
2 Likes

Why would you do this?
Are you importing notes into DEVONthink? Otherwise, you’d be breaking the syntax for the other app.

OMG
So one can use Regexes in AppleScript.

Edited to remove stupid remark about backslashes

1 Like

So one can use Regexes in AppleScript.

No. This is the bridged AppleScript Objective-C (ASOC); related but different animal.

That’s only possible with AppleScriptObjC and one reason I already fell in love with it :slight_smile:

Without AppleScriptObjC it’s only possible with scripting additions, e.g. Satimage’s text addition.

Yep, in AppleScript every \ must be escaped.

This is not a good RE, because you’re using a greedy quantifier (.*). That will gobble up everything from the first pair of [ to the last pair of ] on the same logical line. In other words, if you have more then one image one the same line or an image and a link (which might be noted as [[URL]] in your software?), this RE wrecks havoc.

It should be non-greedy like
!\[\[([^[]+\]\]

No need to escape the !, no need to group the brackets either, since they’re not changing. Now the group inside the parenthesis gobbled up everything that is not a closing bracket followed by two closing brackets.

@pete31 had those fixed already in his script. I just wanted to point out the amendments in case they go unnoticed

1 Like

Dr Strangelove :wink:
Though I guess if one hides away this bloated code in a function replaceRegexp, it would bring me closer to AppleScript.

1 Like

Thanks, forgot to mention that.

I’m pretty sure there is now an option in Obsidian that allows “normal” links.

Yep, but it won’t replace existing links. So the script might still be useful.

1 Like

Important point. I index Obsidian folders in DEVONthink but if I have to edit an indexed Obsidian note, I use Obsidian syntax, knowing it might not work in DEVONthink.

@pete31, you are fast becoming my favorite person! Thank you so much for your help with this script and the previous one! Has made my life so much easier. All my images that originally used Obsidian’s syntax have now been updated using this script and are now showing across all external editors, including DEVONthink.

@Bluefrog, regarding the reason, Obsidian can read both syntax formats (conventional and their own), and as @lsievert correctly pointed out, the app does have an option to select [[wiki-links]] or markdown syntax… but it’s a one or the other deal. I use [[wiki-links]] to link notes and make connections, and I use ![](image.png) to insert images. Because of the current limitations of Obsidian (still in beta), I needed a work around. Thankfully DEVONthink and this wonderful community just keeps on giving :grin:

2 Likes

@pete31 Thank you for the script you provided in the post above.

I’ve adapted it to search for a specific keyword in the markdown file’s contents, and replace it with an empty string.

I use the keyword to automatically move notes indexed from TheBrain into specific DevonThink folders, but it is no longer needed once that smart rule has been triggered. So it would be awesome if the keyword could be somehow removed automatically.

Can you perhaps give a hint on how to make this script smart rule compatible? Any input is on this is much appreciated.

It’s not possible to use AppleScriptObjC embedded in Smart Rule scripts.

So you need to move

  • the use statements (found at the top)
  • the handler (found at the bottom)

out of the script into a Script Library, i.e. a new scpt file that will live in/Users/USERNAME/Library/Script Libraries/.

See this post to see how a Script Library is called from another script.

Great, thanks.

I’ve saved the script library to the location specified in your linked post. Then I used the instructions in that post to try to integrate the code from the script above into the general Smart Rule code.

I’m a total beginner in AppleScript, so I think there must be some kind of syntax error or something. In any case, when it is run the script returns the error message «script» doesn’t understand the “replacekeyword” message.

This is the smart rule script I came up with:

on performSmartRule(theRecords)
	tell application id "DNtp"
		try
			repeat with theRecord in theRecords
				set theText to plain text of theRecord as string
				
				if theText ≠ "" then
					set newText to my replacekeyword(theText)
					if newText ≠ theText and newText ≠ "" and newText ≠ "missing value" then set plain text of thisRecord to newText
				end if
			end repeat
			
		on error error_message number error_number
			if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
			
		end try
	end tell
end performSmartRule

And this is the code in the script library, which is named “Script Library - replacekeyword.scpt”

-- Script Library - replacekeyword

use AppleScript version "2.7"
use framework "Foundation"
use scripting additions


on replacekeyword(theText)
	try
		set ca to a reference to current application
		set theString to ca's NSString's stringWithString:theText
		set newString to theString's stringByReplacingOccurrencesOfString:("DT_Inbox") withString:("A") options:(ca's NSRegularExpressionSearch) range:{location:0, |length|:length of theText}
		set newText to newString as string
	on error error_message number error_number
		error number -128
	end try
end replacekeyword

Is there anything obvious here that needs to be fixed?

Yes! You did

  • remove the use statements
  • create a Script Library
  • create a Smart Rule script

That’s good :slight_smile:

But you also need to

  • remove the handler
    (as it can’t be used embedded in a Smart Rule script. That’s the only reason why it’s necessary to use a Script Library: to be able to remove it from the Smart Rule script)

  • call the Script Library from the Smart Rule
    (which means replacing my with script "[name of the Script Library]"'s.
    my is only used to call an embedded handler.)

Ah, I see your edit, so you’ve found the first mistake. Replace my and it should work.


If you use an external Smart Rule script: remember to restart DEVONthink as scripts are cached

(or see Script: Comfortably developing a Smart Rule Script)

Feels like I’m getting close! Really appreciate your guidance here.

Something still isn’t 100% right though. Now there’s the following error message when using the code below: “Can’t make «script “ScriptLibraryReplacekeyword”» into type Unicode text.”

Maybe I misunderstood how to replace the “my” part in some way due to my current ignorance regarding general AppleScript syntax? :innocent:

on performSmartRule(theRecords)
	tell application id "DNtp"
		try
			repeat with theRecord in theRecords
				set theText to plain text of theRecord as string
				
				if theText ≠ "" then
					set newText to script "ScriptLibraryReplacekeyword.scpt"
					if newText ≠ theText and newText ≠ "" and newText ≠ "missing value" then set plain text of theRecord to newText
				end if
			end repeat
			
		on error error_message number error_number
			if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
			
		end try
	end tell
end performSmartRule

Please compare this line to the line that calls a Script Library in the script I‘ve linked.

Do you see the difference?