Append Tags to Selection with Hashtag

Hey Gang,

I’m working with indexed markdown notes, and add tags within DEVONthink. However, when working in an external editor, the tags don’t carry over.

I see @cgrunenberg created a script to append tags and comments to a selection of notes, which is great, but I have been unable to format the output so each tag is appended with a hashtag. I also don’t need the comments appended, so adapted to remove this feature.

This is what I am trying to achieve with a script:

Here is @cgrunenberg script for reference.

Appreciate the help in advance!

-- Append Tags to selection
-- Created by John Mickey on 2017-02-23
-- Based on a script created by Christian Grunenberg on Aug Tue 16 2011.

tell application id "DNtp"
	try
		set theSelection to the selection
		set theWindow to missing value
		set {od, text item delimiters of AppleScript} to {text item delimiters of AppleScript, ", "}
		
		repeat with theRecord in theSelection
			if type of theRecord is rtf or type of theRecord is rtfd then
				set theTab to open tab for record theRecord in theWindow
				set theWindow to think window of theTab
				
				set theTags to tags of theRecord as string -- tags
				
				set theTab to open tab for record theRecord in theWindow
				set current tab of theWindow to theTab -- Rich text scripting needs a visible document
				tell text of theTab to make new paragraph with data (return & return & "Tags: " & theTags & return) at end
				save theTab
			end if
		end repeat
		
		set text item delimiters of AppleScript to od
		if theWindow is not missing value then close theWindow
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink Pro" message error_message as warning
	end try
end tell

This script appends tags to markdown text.

It also updates tags if you removed them in DEVONthink.

-- Append to/update tags in markdown text

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
				if theText ≠ "" then
					set theText_cleaned to my trimEnd(theText)
					set theParagraphs to paragraphs of theText_cleaned
					set lastLine to (item -1 of theParagraphs) as string
					
					set theTags to tags of thisRecord
					
					if theTags ≠ {} then
						set theTags_sorted to my sort_list(theTags)
						set theTextTags to {}
						repeat with thisTag in theTags_sorted
							set end of theTextTags to "#" & thisTag as string
						end repeat
						set newLastLine to my tid(theTextTags, space & "," & space)
						if lastLine starts with "#" then
							set newText_list to (items 1 thru -2 in theParagraphs)
						else
							set newText_list to theParagraphs
						end if
						set newText to my tid(newText_list, linefeed)
						set newText_cleaned to my trimEnd(newText)
						set plain text of thisRecord to (newText_cleaned & linefeed & linefeed & newLastLine) as string
						
					else if theTags = {} and lastLine starts with "#" then
						set newText_list to items 1 thru -2 in theParagraphs
						set newText to my tid(newText_list, linefeed)
						set plain text of thisRecord to (newText & linefeed) as string
					end if
				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
		return
	end try
end tell

on trimEnd(str)
	local str, whiteSpace
	try
		set str to str as string
		set whiteSpace to {character id 10, return, space, tab}
		try
			repeat while str's last character is in whiteSpace
				set str to str's text 1 thru -2
			end repeat
			return str
		on error number -1728
			return ""
		end try
	on error eMsg number eNum
		error "Can't trimEnd: " & eMsg number eNum
	end try
end trimEnd

on tid(theList, theDelimiter)
	set d to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	set theString to theList as text
	set AppleScript's text item delimiters to d
	return theString
end tid

on sort_list(theList)
	considering numeric strings
		set theIndexList to {}
		set theSortedList to {}
		repeat (length of theList) times
			set theLowItem to ""
			repeat with a from 1 to (length of theList)
				if a is not in theIndexList then
					set theCurrentItem to item a of theList as text
					if theLowItem is "" then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					else if theCurrentItem comes before theLowItem then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					end if
				end if
			end repeat
			set end of theSortedList to theLowItem
			set end of theIndexList to theLowItemIndex
		end repeat
	end considering
	return theSortedList
end sort_list

Hey @pete31, thank you for the script! It works great! And the updating feature an awesome and unexpected bonus! Thank you so much!! :grinning:

1 Like

I’ve also adapted it so it can be used as a smart rule :grinning:

-- Append to/update tags in markdown text
-- pete31 @ https://discourse.devontechnologies.com/t/append-tags-to-selection-with-hashtag/58812

on performSmartRule(theRecords)
	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
					if theText ≠ "" then
						set theText_cleaned to my trimEnd(theText)
						set theParagraphs to paragraphs of theText_cleaned
						set lastLine to (item -1 of theParagraphs) as string
						
						set theTags to tags of thisRecord
						
						if theTags ≠ {} then
							set theTags_sorted to my sort_list(theTags)
							set theTextTags to {}
							repeat with thisTag in theTags_sorted
								set end of theTextTags to "#" & thisTag as string
							end repeat
							set newLastLine to my tid(theTextTags, space & "," & space)
							if lastLine starts with "#" then
								set newText_list to (items 1 thru -2 in theParagraphs)
							else
								set newText_list to theParagraphs
							end if
							set newText to my tid(newText_list, linefeed)
							set newText_cleaned to my trimEnd(newText)
							set plain text of thisRecord to (newText_cleaned & linefeed & linefeed & newLastLine) as string
							
						else if theTags = {} and lastLine starts with "#" then
							set newText_list to items 1 thru -2 in theParagraphs
							set newText to my tid(newText_list, linefeed)
							set plain text of thisRecord to (newText & linefeed) as string
						end if
					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
			return
		end try
	end tell
end performSmartRule

on trimEnd(str)
	local str, whiteSpace
	try
		set str to str as string
		set whiteSpace to {character id 10, return, space, tab}
		try
			repeat while str's last character is in whiteSpace
				set str to str's text 1 thru -2
			end repeat
			return str
		on error number -1728
			return ""
		end try
	on error eMsg number eNum
		error "Can't trimEnd: " & eMsg number eNum
	end try
end trimEnd

on tid(theList, theDelimiter)
	set d to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	set theString to theList as text
	set AppleScript's text item delimiters to d
	return theString
end tid

on sort_list(theList)
	considering numeric strings
		set theIndexList to {}
		set theSortedList to {}
		repeat (length of theList) times
			set theLowItem to ""
			repeat with a from 1 to (length of theList)
				if a is not in theIndexList then
					set theCurrentItem to item a of theList as text
					if theLowItem is "" then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					else if theCurrentItem comes before theLowItem then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					end if
				end if
			end repeat
			set end of theSortedList to theLowItem
			set end of theIndexList to theLowItemIndex
		end repeat
	end considering
	return theSortedList
end sort_list

This won’t work. You need to remove these lines

set theRecords to selection of think window 1
if theRecords = {} then error "Nothing selected."

as Smart Rules don’t act on a selection.

And you can remove

set theType to (type of thisRecord) as string
if theType = "markdown" or theType = "«constant Ctypmkdn»" then

and the

end if

before “end repeat” and check for the type in the Smart Rule conditions.

And hey, that’s my name in the Smart Rule. Cool! :grinning:

Credit where credit is due :smiley: :+1:

I’ve applied the changes, thank you! I was wondering why I was being prompted for a selection. You’ve been a great help!!