Here is a first draft of a Keyboard Maestro macro which searches for matching notes in any .tbx files in a given list of folders and/or specific file-paths.
This version just gets a search pattern from a user prompt, and searches for matching text (plain strings or regex) in the $Name and $Text values of every note in every specified .tbx file.
(We could use the same XQuery-based approach to define search predicates over other attribute values etc)
Any matching notes are listed, with Markdown links (both tinderbox:// and hook:// URLs) back to those notes.
The listing is copied to the clipboard.
To use the Macro:
Edit the value of its tbxFoldersAndOrFilePaths variable, listing the folders and or specific .tbx files which you wish to include in searches
use AppleScript version "2.4"
use framework "Foundation" -- We need Apple's Foundation framework for its XQuery processor
use scripting additions
on run
-- The XML contents of the .tbx file that interests us,
set strTBX to readFile("~/Desktop/sample-003.tbx")
-- The string value of the Text element of every item at every level of nesting
-- (View in the Messages panel of the Script Editor Results window)
log valuesFromXQuery("for $item in //item return string($item/text)", strTBX)
-- The string value of the attribute named "Name" for every item at every level of nesting
-- (An AppleScript list of $Name attribute values)
valuesFromXQuery("for $item in //item return string($item/attribute[@name='Name'])", strTBX)
end run
-----------------Reusable XQUERY function------------------
-- valuesFromXQuery :: XQuery String -> XML String -> Either String [a]
on valuesFromXQuery(strXQuery, strXML)
-- Just in case: holders for reporting any errors in the XML or XQuery:
set xmlError to reference
set xqueryError to reference
-- XML parsed,
set {docXML, xmlError} to (current application's NSXMLDocument's alloc()'s ¬
initWithXMLString:strXML options:0 |error|:xmlError)
if xmlError is not missing value then
return (localizedDescription of xmlError) as string
end if
-- XQuery applied
set {xs, xqueryError} to (docXML's objectsForXQuery:strXQuery |error|:xqueryError)
if xqueryError is not missing value then
(localizedDescription of xqueryError) as string
else
-- A list of values returned from the XQuery over the XML
xs as list
end if
end valuesFromXQuery
--------------------------GENERIC--------------------------
-- readFile :: FilePath -> IO String
on readFile(strPath)
set ca to current application
set e to reference
set {s, e} to (ca's NSString's ¬
stringWithContentsOfFile:((ca's NSString's ¬
stringWithString:strPath)'s ¬
stringByStandardizingPath) ¬
encoding:(ca's NSUTF8StringEncoding) |error|:(e))
if missing value is e then
s as string
else
(localizedDescription of e) as string
end if
end readFile
For XQueries over multiple .tbx documents, we generate a small virtual XML document, which contains an include tag (with a url) for each document which we want to include in the query.
We can then apply our XQuery to that virtual document, which, for a query over three .tbx files for example, might look something like:
And we save that that small virtual file as collection.xml, we can run an XQuery over it to generate a sorted list of the names of all notes in those three files:
use AppleScript version "2.4"
use framework "Foundation" -- We need Apple's Foundation framework for its XQuery processor
use scripting additions
on run
-- XML with an <include> tag for the URL of each file that interests us:
set strTBX to readFile("~/Desktop/collection.xml")
-- A sorted AppleScript list of $Name values drawn from 3 .tbx files
set strXQuery to unlines({¬
"for $item in //item ", ¬
"let $name := $item/attribute[@name='Name']\n", ¬
"order by $name return string($name)"})
valuesFromXQueryOverCollection(strXQuery, strTBX)
end run
-----------------Reusable XQUERY function------------------
-- valuesFromXQueryOverCollection :: XQuery String -> XML String -> Either String [a]
on valuesFromXQueryOverCollection(strXQuery, strXML)
-- Just in case: holders for reporting any errors in the XML or XQuery:
set xmlError to reference
set xqueryError to reference
-- XML parsed,
set ca to current application
set {docXML, xmlError} to (ca's NSXMLDocument's alloc()'s ¬
initWithXMLString:strXML options:(ca's NSXMLDocumentXInclude) |error|:xmlError)
if xmlError is not missing value then
return (localizedDescription of xmlError) as string
end if
-- XQuery applied
set {xs, xqueryError} to (docXML's objectsForXQuery:strXQuery |error|:xqueryError)
if xqueryError is not missing value then
(localizedDescription of xqueryError) as string
else
-- A list of values returned from the XQuery over the XML
xs as list
end if
end valuesFromXQueryOverCollection
--------------------------GENERIC--------------------------
-- readFile :: FilePath -> IO String
on readFile(strPath)
set ca to current application
set e to reference
set {s, e} to (ca's NSString's ¬
stringWithContentsOfFile:((ca's NSString's ¬
stringWithString:strPath)'s ¬
stringByStandardizingPath) ¬
encoding:(ca's NSUTF8StringEncoding) |error|:(e))
if missing value is e then
s as string
else
(localizedDescription of e) as string
end if
end readFile
-- unlines :: [String] -> String
on unlines(xs)
-- A single string formed by the intercalation
-- of a list of strings with the newline character.
set {dlm, my text item delimiters} to ¬
{my text item delimiters, linefeed}
set str to xs as text
set my text item delimiters to dlm
str
end unlines