Experimental toggling between note attributes and rows

Here is a different experimental draft, sketched for Bike Preview 1.14 (152), updated to work with note type rows in Bike, rather than “>” prefixes.

It aims to toggle between:

  1. Omni group _note attributes (in OPML generated by OmniIOutliner, and opened in Bike)
  2. leading note type children.

Not fully tested or recommended for use with real data. Back up any Bike file before you test this script.

See Using Scripts - Bike


Expand disclosure triangle to view AppleScript source
-- Possible approach ?
-- Not sure ...

-- EXPERIMENT ONLY – NOT READY FOR USE WITH REAL DATA

-- (I never like to share anything that uses a 'delete' method)


-- Ver 2.00
-- This cannot really be done in JXA I, I think, 
-- for lack of location specifiers
-- e.g. in `make new row at beginning of rows`

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 
-- ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
-- TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
-- PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
-- SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 
-- ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
-- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 
--  OR OTHER DEALINGS IN THE SOFTWARE.


------- TOGGLE _NOTE ATTRIBUTES ⇄ NOTE TYPE CHILDREN ------
on run
	tell application "Bike"
		set doc to front document
		
		if exists doc then
			set docRows to a reference to rows of doc
			set rowNotePairs to my docRowsWithNotes(docRows)
			
			if 0 < length of rowNotePairs then
				my noteAttribsToNoteChildren(doc, rowNotePairs)
			else
				my noteChildrenToNoteAttribs(docRows)
			end if
		else
			"No document open in Bike"
		end if
	end tell
end run


--------------------- NOTE ATTRIBUTES --------------------

-- docRowsWithNotes :: Bike Doc -> IO [(Row, String)]
on docRowsWithNotes(rowsOfDoc)
	using terms from application "Bike"
		tell rowsOfDoc
			set noteAttribs to a reference to ¬
				(attributes where name is "_note")
		end tell
		
		script go
			on |λ|(xs)
				if {} ≠ xs then
					{item 1 of xs}
				else
					{}
				end if
			end |λ|
		end script
		
		zip(concatMap(go, container row of noteAttribs), ¬
			concatMap(my identity, value of noteAttribs))
	end using terms from
end docRowsWithNotes


-- noteAttribsToNoteChildren :: [(Row, String)] -> IO ()
on noteAttribsToNoteChildren(doc, rowNotePairs)
	using terms from application "Bike"
		repeat with ab in rowNotePairs
			set {attribRow, attribText} to ab
			--                     
			repeat with para in paragraphs of attribText
				tell doc
					make new row at beginning of rows of attribRow ¬
						with properties ¬
						{name:para, type:note}
				end tell
				
				set attrib to (a reference to ¬
					(attribute "_note" of attribRow))
				if exists attrib then delete attrib
			end repeat
		end repeat
	end using terms from
end noteAttribsToNoteChildren


-- noteChildrenToNoteAttribs :: Char -> Rows -> IO ()
on noteChildrenToNoteAttribs(docRows)
	using terms from application "Bike"
		
		set noteRows to {}
		repeat with oRow in docRows
			set isParent to contains rows of oRow
			if isParent then
				set xs to (rows of oRow)
				set lst to {}
				
				repeat with oChild in xs
					set x to oChild
					if not (contains rows of x) then
						if type of oChild is note then
							set end of lst to name of oChild
							set end of noteRows to x
						end if
					end if
				end repeat
				
				if {} ≠ lst then
					tell oRow
						make attribute with properties ¬
							{name:"_note", value:my unlines(lst)}
					end tell
				end if
			end if
		end repeat
		set n to length of noteRows
		repeat with i from n to 1 by -1
			delete item i of noteRows
		end repeat
	end using terms from
end noteChildrenToNoteAttribs


------------------------- GENERIC ------------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
	set lng to length of xs
	set acc to {}
	
	tell mReturn(f)
		repeat with i from 1 to lng
			set acc to acc & (|λ|(item i of xs, i, xs))
		end repeat
	end tell
	acc
end concatMap


-- identity :: a -> a
on identity(x)
	-- The argument unchanged.
	x
end identity


-- min :: Ord a => a -> a -> a
on min(x, y)
	if y < x then
		y
	else
		x
	end if
end min


-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
	-- 2nd class handler function lifted into 1st class script wrapper. 
	if script is class of f then
		f
	else
		script
			property |λ| : f
		end script
	end if
end mReturn


-- drop :: Int -> String -> String
on drop(n, xs)
	if n < length of xs then
		text (1 + n) thru -1 of xs
	else
		""
	end if
end drop


-- 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 s to xs as text
	set my text item delimiters to dlm
	s
end unlines


-- zip :: [a] -> [b] -> [(a, b)]
on zip(xs, ys)
	set n to min(length of xs, length of ys)
	
	set lst to {}
	repeat with i from 1 to n
		set end of lst to {item i of xs, item i of ys}
	end repeat
	lst
end zip

2 Likes