Smart Rule to Search and Replace Markdown Contents

Success! It’s working beautifully now!

Pete, this has been really helpful. Thanks again, also for pushing me a bit to find the solution on my own - that’s how one learns, right? :grinning:

Here are the scripts, in case someone wants to use them.

Working smart rule script:

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"'s replacekeyword(theText, "DT_Inbox")
					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

And this is the Script Library:

-- 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:("") 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

Are you sure the scripts as posted work?

In the script you‘re calling a Script Library‘s handler with 2 parameters:

But the handler in the Script Library expects only 1 parameter, I think:

Hmm, interesting. On my end, the script is 100% confirmed working, and I’ve also created further versions with different keywords that also behave as they’re supposed to.

So would you suggest modifying that line of code, e.g. like this?

set newText to script "ScriptLibraryReplacekeyword"'s 

You’re right, tested it now.

Interesting. I thought it’s necessary to always have the same count of parameters in both, the code that calls the handler and in the handler itself. Seems I’ve never encountered a situation where the calling code got more parameters than the handler expects.

However, it’s not possible the other way around: calling a handler with less parameters that the handler expects throws an error (that’s what I get quite often).

That’s not necessary. Use a variable in the handler (instead of your hard coded keywords), then call the handler with different keywords for this parameter.

Doesn’t make much sense to create a new handler for each keyword. You probably did it this way because the handler that you’ve used as template did not use this common mechanism (no idea why I did it that way).

Better don’t start doing it the wrong way. Best is to make handlers and especially Script Libraries usable for different tasks right from the start. I did some silly things in the past, it’s really no fun to make subsequent changes in a bunch of different versions. It’s not only unbelievable cumbersome but also very very error prone. Did I say that it’s error prone?

Be aware that the handler uses regex which means you have to take care of not accidentally using regex syntax. Otherwise you’ll get interesting results …

Here’s a handler that can be used variably

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

on regexReplace(theText, thePattern, theRepacementText)
	try
		set theString to current application's NSString's stringWithString:theText
		set newString to theString's stringByReplacingOccurrencesOfString:(thePattern) withString:(theRepacementText) options:(current application's NSRegularExpressionSearch) range:{location:0, |length|:length of theText}
		set newText to newString as string
	on error error_message number error_number
		activate
		if the error_number is not -128 then display alert "Error: Handler \"regexReplace\"" message error_message as warning
		error number -128
	end try
end regexReplace

However, if all you need is to find and replace text (i.e. you don’t need a regex pattern) then everything would be far easier. You could find a lot of find and replace handlers on the forum (if not I could post one). They all can be used directly in a Smart Rule, no need to use a Script Library.

PS You might want to read this post.

Best is to make handlers and especially Script Libraries usable for different tasks right from the start. I did some silly things in the past, it’s really no fun to make subsequent changes in a bunch of different versions. It’s not only unbelievable cumbersome but also very very error prone.

That makes a lot of sense. As I said, for now I’m still a total beginner in AppleScript, so the goal was a “quick-and-dirty” version of the script that addresses my specific use case. And I’m really glad that this has worked out with your help (and based on the scripts you came up with).

There may be further use cases (for myself and others), where RegEx is needed so I would like to stick to this more complicated version and get it right.

I’ve tried to tinker with the SmartRule script to get it to work with the universal script library you provided, but couldn’t make it work. Would you be so kind to provide an example for e.g. the keyword DT_Inbox (or maybe a Regex pattern)?

Thanks for the link to your post on how to learn AppleScript. I will follow those general recommendations.

That’s true for many programming languages: the subroutine/function expects a certain number of parameter to be passed and stops looking when it received them. So passing more then the required number is mostly ok.
On the other hand, passing less then the required number very often causes havoc: the function/subroutine accesses an uninitialized area of the stack and crashes (which is good) or it finds some bogus value and works with that. This is really bad.
Some programming languages even allow for subroutines/functions with a variable number of parameters, eg C.

After using the script for a while without any regular expressions to replace text strings in MD contents, I am currently experimenting with regular expressions and running into an issue:

Whenever I use a \n character in the embedded smart rule script and then close the script editing window, a new line has been inserted while the “\n” characters have disappeared.

I thought it might have something to do with how DevonThink parses apple script, but the same actually happens in Script Editor.

Does anyone have an idea what might be going on and how to fix this?

In what context do you use the \n sequence? And did it help to write \\n instead?

Using two forward slashes instead of one seems to have solved the issue. Thanks, I never would have guessed.

I’m using RegEx to remove the following from certain markdown documents, as well as the new line following it:

[//]: # [TheBrain Thought Link](brain://api.thebrain.com/hUZzP6CPhkWLdyC_w1559g/QsGIrs42GlebTIulXYs30A/ThisIsTheTitle)

And this is the expression that is now working as expected:

.*\\)\\n

Hopefully, they were back slashes :slight_smile:

The RE should work just fine without the new line at the end. If you want to rejoice all occurrences of these links, that is. If you really only want to delete those at the end of a line, the \n is necessary.

As to the „why do I need two \ here“: as you’ve already noticed, \n is a special sequence in (probably) NSString that’s converted to a newline before the regex method even sees it. \\ is converted to a single \ which then in combination with the n reaches the RE method. Or something like that.

Of course, using JavaScript and its built in RE support would cause less hassle.

1 Like

Yes, indeed they were :innocent:

In my testing, adding the \n expression at the end and then leaving the replacement term empty not only deletes the link but also the line following the link in all occurrences (there is always a new line afterwards). This is the desired behavior in my use case.

Really interesting - I get it now.

1 Like