Backlinks script

Hi there! I was playing around and wrote a little AppleScript that will populate a Bike outline with backlinks (and if you run it again, it will update/replace all the backlinks). Here is the code:

tell front document of application "Bike"
	repeat with currentRow in rows
		set currentId to id of currentRow
		set currentUrl to URL of currentRow
		
		set backlinks to {}
		repeat with theRow in rows
			tell theRow
				set shouldScanTheRow to not (exists (attribute named "meta"))
			end tell
			if shouldScanTheRow then
				repeat with theRun in attribute runs of text content of theRow
					set theRunUrl to link of theRun
					if link of theRun = currentUrl then
						set backlinks to backlinks & (id of theRow)
					end if
				end repeat
			end if
		end repeat
		
		tell currentRow
			if exists row named "Backlinks" then
				delete row named "Backlinks"
			end if
		end tell
		
		if not (backlinks = {}) then
			tell currentRow
				set backlinksRow to make row with properties {name:"Backlinks"}
				tell backlinksRow
					set bold of text content to true
				end tell
			end tell
			
			repeat with theBacklink in backlinks
				set referrerRow to row id theBacklink
				tell backlinksRow
					set linkRow to make row with properties {name:name of referrerRow}
					tell linkRow
						make attribute with properties {name:"meta"}
						set link of text content to URL of referrerRow
					end tell
				end tell
			end repeat
		end if
		
	end repeat
end tell

Here is what a given outline looks like before running the script:

After I run the script, it looks like this:

(The code is really bad because I don’t know how to use applescript. I would like to have a more efficient one where I store the backlinks in a dictionary, but I don’t know how to do that.)

2 Likes

Sorry for messaging again, but I forgot to say: if Bike had some kind of a “palette” for easily selecting a node to link to (I am thinking of something like typing Cmd-Shift-O and then getting a little search box that lets me filter through to choose a row, and then it spits out a link), I would be able to use Bike very easily for some very non-trivial personal organization tasks, in combination with the script that I wrote. For instance, this can be used to handle tagging. Of course, a more builtin approach to tagging and other things (maybe inspired by TaskPaper) would also be very welcome.

Anyway, thanks so much for the care you have put into the Bike outliner. It’s really a joy, and I’m excited to see it grow — which will make it possible for me to use it in more parts of my workflow.

1 Like

Here is a much faster version of the script:

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

property NSMutableDictionary : a reference to current application's NSMutableDictionary
property NSMutableArray : a reference to current application's NSMutableArray

set backlinksDict to NSMutableDictionary's dictionary()


tell front document of application "Bike"
	-- initialize the backlinks dictionary
	repeat with currentRow in rows
		set currentUrl to URL of currentRow
		set arr to NSMutableArray's array()
		(backlinksDict's setValue:arr forKey:currentUrl)
	end repeat
	
	set rowsToDelete to {}
	repeat with currentRow in rows
		set currentId to id of currentRow
		set currentUrl to URL of currentRow
		
		tell currentRow
			set rowIsMeta to exists (attribute named "meta")
		end tell
		
		if rowIsMeta then
			set rowsToDelete to rowsToDelete & {currentId}
		else
			repeat with theRun in attribute runs of text content of currentRow
				set theRunUrl to link of theRun
				
				if exists theRunUrl then
					set backlinks to (backlinksDict's valueForKey:theRunUrl)
					if backlinks is not missing value then
						(backlinks's addObject:currentUrl)
					end if
				end if
			end repeat
		end if
	end repeat
	
	repeat with theRowId in rowsToDelete
		if exists (row id theRowId) then
			delete row id theRowId
		end if
	end repeat
end tell


set allKeys to backlinksDict's allKeys()
repeat with theKey in allKeys
	set links to (backlinksDict's valueForKey:theKey)
	set currentRowId to (theKey's componentsSeparatedByString:"#")'s lastObject() as text
	if not ((count of links) is 0) then
		tell front document of application "Bike"
			set currentRow to row id currentRowId
			tell currentRow
				set headerRow to make row with properties {name:"Backlinks"}
				tell headerRow
					set bold of text content to true
					make attribute with properties {name:"meta"}
				end tell
			end tell
		end tell
	end if
	
	repeat with theLink in links
		set referrerId to (theLink's componentsSeparatedByString:"#")'s lastObject() as text
		tell front document of application "Bike"
			set referrerRow to row id referrerId
			tell headerRow
				set linkRow to make row with properties {name:name of referrerRow}
				tell linkRow
					make attribute with properties {name:"meta"}
					set link of text content to URL of referrerRow
				end tell
			end tell
		end tell
	end repeat
end repeat
2 Likes

You can:

  • get a search box,
  • choose a row by typing substrings,
  • and copy as link

using this:

1 Like

This is great to see, thanks for sharing it!

I’ve moved this script into it’s own topic so that it will be easier to find and link to in the future.

if Bike had some kind of a “palette” for easily selecting a node to link to

I agree this is a feature that Bike needs. Or more generally a pallet for finding rows and it could be used for a lot of things such as copy links, navigation, etc. Definitely on my list.

2 Likes