A script to find markdown documents containing open to-dos

The markdown syntax - [ ] for to-dos is supported by a number of writing and note-taking apps, including DEVONthink. Some of them have built-in mechanisms to list all currently open (not completed) to-dos. In DT, although such a built-in workflow does not exist, good automation support makes it possible to devise your own.

DEVONthink does not index non-alphanumeric characters (with only a few exceptions), so it’s not possible to use a search or smart rule for our purpose, as has been noted in a previous post. Instead, the following script looks directly into the plain text of markdown files, thereby avoiding the constraint of DT’s index.

Use case:

  1. You can manually run this script on selected documents in DT. Documents that contain an open to-do are given a specific tag (open tasks by default). Documents that don’t, on the other hand, are checked to make sure they don’t have that tag.
  2. Depending on your exact use case, you can have a smart rule to run this script After Saving a markdown document.
-- Detects whether markdown documents contain a syntax for open tasks (i.e. to-dos).
-- Yes → add a tag.
-- No → remove the tag.

property taskSyntaxes : {"- [ ]"} -- a list of task syntaxes you use
property conditionTag : "open tasks" -- name of a DEVONthink tag, which will be applied to documents with at least 1 open task

on run
	tell application id "DNtp"
		try
			set theRecords to selected records
			repeat with theRecord in theRecords
				my DetectOpenTasks(theRecord)
			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 run

on performSmartRule(theRecords)
	tell application id "DNtp"
		repeat with theRecord in theRecords
			my DetectOpenTasks(theRecord)
		end repeat
	end tell
end performSmartRule

on DetectOpenTasks(thisrecord)
	tell application id "DNtp"
		if (type of thisrecord) is not markdown then return
		set theContent to plain text of thisrecord
		set taskDetection to false
		repeat with taskSyntax in taskSyntaxes
			if theContent contains taskSyntax then
				set taskDetection to true
				exit repeat
			end if
		end repeat
		my TagOpenTask(thisrecord, taskDetection)
	end tell
end DetectOpenTasks

on TagOpenTask(aRecord, haveOpenTask)
	tell application id "DNtp"
		set theTags to (tags of aRecord)
		if (haveOpenTask is true) then
			if theTags does not contain (conditionTag as text) then
				set theTags to theTags & conditionTag
				set (tags of aRecord) to theTags
			end if
		else -- if (haveOpenTask is false)
			if theTags contains (conditionTag as text) then
				set newTags to {}
				repeat with theTag in theTags
					if (theTag as text) is not (conditionTag as text) then
						set newTags to newTags & theTag
					end if
				end repeat
				set tags of aRecord to newTags
			end if
		end if
	end tell
end TagOpenTask

Script has been tested to perform as expected on a batch of 30 documents.

1 Like

If I understand your use case; any document can contain an open to-do
Do you have criteria to limit the documents requiring the search?

The range is either selected records (when run manually) or determined by the smart rule criteria. That can be modified to suit different needs.