Save Folder Hierarchy as RTF (using AsObjC)


-- saves names of folders as blue, names of files - as green
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use framework "AppKit"

property |NSURL| : a reference to current application's |NSURL|
property NSFileManager : a reference to current application's NSFileManager
property NSFont : a reference to current application's NSFont
property NSAttributedString : a reference to current application's NSAttributedString
property NSMutableAttributedString : a reference to current application's NSMutableAttributedString
property NSColor : a reference to current application's NSColor
property NSDictionary : a reference to current application's NSDictionary
property NSString : a reference to current application's NSString

-- user settings
set rootFolderPath to POSIX path of (choose folder with prompt "Choose the root folder")
set LF to (NSString's stringWithString:linefeed)
set theFont to NSFont's fontWithName:"Geneva" |size|:14.0
set blue to NSColor's colorWithCalibratedRed:0.0 green:0.0 blue:0.6 alpha:1.0
set blueText to NSDictionary's dictionaryWithObject:blue forKey:"NSColor"
set green to NSColor's colorWithCalibratedRed:0.0 green:0.4 blue:0.0 alpha:1.0
set greenText to NSDictionary's dictionaryWithObject:green forKey:"NSColor"
set rootAndContainerRegex to "^" & rootFolderPath & "|[^/]++/"
set theFileManager to NSFileManager's defaultManager()

-- get entire contents of root folder
set rootURL to |NSURL|'s fileURLWithPath:rootFolderPath
set logFilePath to POSIX path of ((path to desktop as text) & "Folder Hierarchy.rtf")
set logFileURL to |NSURL|'s fileURLWithPath:logFilePath
set theEnumerator to theFileManager's enumeratorAtURL:rootURL ¬
	includingPropertiesForKeys:{"NSURLIsDirectoryKey", "NSURLIsPackageKey"} ¬
	options:6 errorHandler:(missing value)
set entireContents to theEnumerator's allObjects()

-- Initialise an NSMutableAttributedString with the title line and two linefeeds.
set styledText to NSMutableAttributedString's alloc()'s ¬
	initWithString:("Start " & (current date) & linefeed & linefeed)

-- Start a "run" of folder names with the name of the root folder.
set currentRun to rootURL's lastPathComponent()'s mutableCopy()
set isFileRun to false

repeat with thisItem in entireContents
	set thisPath to thisItem's |path|()
	-- Replace the root path and any other container components in this path with spaces, leaving just an indented name.
	set indentedName to (thisPath's stringByReplacingOccurrencesOfString:rootAndContainerRegex withString:("    ") options:1024 range:({location:0, |length|:thisPath's |length|()}))
	-- Append to a linefeed, ready for appending to the preceding text.
	set indentedNameLine to (LF's stringByAppendingString:indentedName)
	
	-- Get this item's directory and package flags. If they're equal, the item's either a file or a package. Otherwise it's a folder.
	set dpDictionary to (thisItem's resourceValuesForKeys:{"NSURLIsDirectoryKey", "NSURLIsPackageKey"} |error|:(missing value))
	set treatingAsFile to ((dpDictionary's objectForKey:"NSURLIsDirectoryKey")'s isEqual:(dpDictionary's objectForKey:"NSURLIsPackageKey")) as boolean
	-- Is the item of the same type as for the current run of names?
	if treatingAsFile = isFileRun then
		-- If so, simply append its indented name to the run text.
		tell currentRun to appendString:indentedNameLine
	else
		-- Otherwise, make an NSAttributedString with the current run text, coloured or not as appropriate.
		if isFileRun then
			set thisColourBlock to (NSAttributedString's alloc()'s initWithString:currentRun attributes:greenText)
		else
			set thisColourBlock to (NSAttributedString's alloc()'s initWithString:currentRun attributes:blueText)
		end if
		-- Append this to the main styled text.
		tell styledText to appendAttributedString:thisColourBlock
		-- Start a new run text with the current name line and set the run-type flag accordingly.
		set currentRun to indentedNameLine's mutableCopy()
		set isFileRun to treatingAsFile
	end if
end repeat

-- Append the final run to the styled text at the end. 
if (isFileRun) then
	set thisColourBlock to (NSAttributedString's alloc()'s initWithString:currentRun attributes:greenText)
else
	set thisColourBlock to (NSAttributedString's alloc()'s initWithString:currentRun attributes:blueText)
end if
tell styledText to appendAttributedString:thisColourBlock
tell styledText to addAttribute:"NSFont" value:theFont range:{location:0, |length|:its |length|()}

-- Extract RTF data from the styled text and save using a URL derived from the given log file path.
set theRTFData to styledText's RTFFromRange:{location:0, |length|:styledText's |length|()} documentAttributes:(missing value)
tell theRTFData to writeToURL:logFileURL atomically:true

display notification "Folder hierarchy RTF" & linefeed & "created on the desktop." with title "Folder hierarchy.script"