Compare Two Text Files for Differences

I revised the File Compare script to use the swiftDialog app to both get the files that will be compared and to display the results in a markdown table. Please note the swiftDialog display options that alter window size in the showTable handler.

--Compare two text files and display result in a markdown table
--Requires swiftDialog app
--Revised 2024.09.05

use framework "Foundation"
use scripting additions

on main()
	set {fileOne, fileTwo} to showDialog()
	if (fileOne's isEqualToString:"") is true or (fileTwo's isEqualToString:"") is true then display dialog "A file was not selected" buttons {"OK"} cancel button 1 default button 1 with icon stop
	
	set stringOne to current application's NSString's stringWithContentsOfFile:fileOne encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
	if stringOne is (missing value) then display dialog "Text was not found in File One" buttons {"OK"} cancel button 1 default button 1 with icon stop
	set stringOne to cleanString(stringOne)
	set arrayOne to (stringOne's componentsSeparatedByString:linefeed)
	
	set stringTwo to current application's NSString's stringWithContentsOfFile:fileTwo encoding:(current application's NSUTF8StringEncoding) |error|:(missing value)
	if stringTwo is (missing value) then display dialog "Text was not found in File Two" buttons {"OK"} cancel button 1 default button 1 with icon stop
	set stringTwo to cleanString(stringTwo)
	set arrayTwo to (stringTwo's componentsSeparatedByString:linefeed)
	
	set arrayOneUnique to current application's NSMutableArray's new()
	set arrayTwoUnique to arrayTwo's mutableCopy()
	set commonArray to current application's NSMutableArray's new()
	repeat with anItem in arrayOne
		if (arrayTwoUnique's containsObject:anItem) is true then --item in both arrays
			(commonArray's addObject:anItem)
			(arrayTwoUnique's removeObjectAtIndex:(arrayTwoUnique's indexOfObject:anItem))
		else --item in arrayOne only
			(arrayOneUnique's addObject:anItem)
		end if
	end repeat
	
	set arrayCount to {arrayOneUnique's |count|(), arrayTwoUnique's |count|(), commonArray's |count|()}
	set arrayCount to current application's NSArray's arrayWithArray:arrayCount
	set arrayCount to (arrayCount's valueForKeyPath:"@max.self") as integer
	
	set theTable to current application's NSMutableArray's new()
	set emptyString to current application's NSString's stringWithString:""
	repeat with i from 0 to (arrayCount - 1)
		try
			set columnOne to truncateString(commonArray's objectAtIndex:i)
		on error
			set columnOne to emptyString
		end try
		try
			set columnTwo to truncateString(arrayOneUnique's objectAtIndex:i)
		on error
			set columnTwo to emptyString
		end try
		try
			set columnThree to truncateString(arrayTwoUnique's objectAtIndex:i)
		on error
			set columnThree to emptyString
		end try
		set aRow to current application's NSString's stringWithFormat_("| %@ | %@ | %@ |", columnOne, columnTwo, columnThree)
		(theTable's addObject:aRow)
	end repeat
	set theTable to (theTable's componentsJoinedByString:linefeed)
	
	set theHeader to "| Both Files | File One Only | File Two Only |"
	set theFormatter to "| :--- | :--- | :--- |"
	set theLinefeed to linefeed
	set theString to current application's NSString's stringWithFormat_("%@%@%@%@%@", theHeader, theLinefeed, theFormatter, theLinefeed, theTable) as text
	showTable(theString)
end main

on showDialog()
	try
		set userInput to do shell script "/usr/local/bin/dialog --title 'File Compare' --titlefont 'size=15' --message 'Select two text files to compare:' --messagefont 'size=14' --messageposition top --button2 --width 400 --height 230 --hideicon --moveable --textfield 'File One,fileselect' --textfield 'File Two,fileselect'"
	on error
		error number -128
	end try
	set userInput to current application's NSMutableString's stringWithString:userInput
	(userInput's replaceOccurrencesOfString:"(?m)^.* : (.*)$" withString:"$1" options:1024 range:{0, userInput's |length|()}) --option 1024 implements regex
	return (userInput's componentsSeparatedByString:return)
end showDialog

on showTable(theString)
	do shell script "/usr/local/bin/dialog --title none --message " & quoted form of theString & " --messagefont 'size=14' --hideicon --position top --moveable" --consider resizable, and width and height options
end showTable

on cleanString(theString) --disable any of the following
	set theString to theString's stringByReplacingOccurrencesOfString:"([*#$>|])" withString:"\\\\$1" options:1024 range:{0, theString's |length|()} --escape markdown characters
	set theString to theString's stringByReplacingOccurrencesOfString:"(?m)^\\h+|\\h+$" withString:"" options:1024 range:{0, theString's |length|()} --remove leading and trailing whitespace every line
	set theString to theString's stringByReplacingOccurrencesOfString:"^\\s*\\R|\\s*$" withString:"" options:1024 range:{0, theString's |length|()} --remove blank lines at beginning and end of string
	set theString to theString's stringByReplacingOccurrencesOfString:"(\\R)\\s*\\R" withString:"$1" options:1024 range:({0, theString's |length|()}) --remove blank lines except at beginning and end of string
end cleanString

on truncateString(theString) --truncate lines at 100 characters
	set theRange to theString's rangeOfString:".{100}" options:1024
	if theRange's |length| is 0 then return theString
	return (theString's substringWithRange:theRange)'s stringByAppendingString:"..."
end truncateString

main()