TextEdit Script to Find/Replace anything that is NOT the given string

I feel like giving back to AppleScript community. I recently wrote this script
to run from in TextEdit.


--Before running this, user should have an open document in TextEdit
--and some of the text should be highlighted.
--This script takes the currently selected text in an opened file in TextEdit
--and replaces any string of characters that is not the string(s) the user specified
--with a user-specified replacement string.
--Example:  User specifies the script replace anything that is not "the"
--inside the text "the people and the dogs" with the replacement "----" .
--The resulting modified text is "the----the----"
--User also has option to specify multiple strings of text by clicking "Add More".
--Example: User specifies the script replace anything that is not "people" and "dogs"
--inside the text "the people and the dogs" with the replacement "----" .  
--The resulting modified text is "----people----dogs"
--User also has option of entering special constants "(alpha)", "(digit)", "(alphadigit)",
--and "(invisibles)" when asked for text to not replace.  You can create more constants inside
--the function replaceAnythingThatIsNot() if you want. Add the value of each constant to 
--the list in specialConstantValues.
--If you want, you can replace "TextEdit" in the code with the name of any other word-processing
--application you might want to use instead.

--MAIN ROUTINE:
tell application "TextEdit" to activate
--Copy the currently selected text:
tell application "System Events"
	tell process "TextEdit"
		click menu item "Copy" of the menu "Edit" of menu bar 1
	end tell
end tell
tell application "TextEdit"
	--Get the currently copied text:
	set theString to (the clipboard)
	set stringOrList to {}
	set theResult to display dialog "Enter specific text to NOT replace:" default answer ¬
		"" with title "TextEdit" buttons {"Cancel", "Add More", "Continue"} default button 3 cancel button 1
	set end of stringOrList to theResult's text returned
	set soFarList to "\"" & my LI(1, stringOrList) & "\""
	repeat while theResult's button returned is "Add More"
		set theResult to display dialog "Items so far:" & return & soFarList & return & ¬
			"Enter specific text to NOT replace:" default answer ¬
			"" with title "TextEdit" buttons {"Cancel", "Add More", "Continue"} ¬
			default button 3 cancel button 1
		set end of stringOrList to theResult's text returned
		set soFarList to (soFarList & "\n" & "\"" & my LI((count of stringOrList), stringOrList)) & "\""
	end repeat
	set theResult to display dialog ¬
		"Enter the new text to replace with:" default answer "" with title ¬
		"TextEdit" buttons {"Cancel", "Go"} default button 2 cancel button 1
	set replaceString to theResult's text returned
	--Modify the copied text:
	set newString to my replaceAnythingThatIsNot(stringOrList, replaceString, theString)
	--Replace the originally copied text with the new text:
	set the clipboard to newString
end tell
--Paste new text into open document:
tell application "System Events"
	tell process "TextEdit"
		click menu item "Paste and Match Style" of the menu "Edit" of menu bar 1
	end tell
end tell
--End of MAIN ROUTINE.


--Required subroutines:

(* This searches for instances of stringOrList inside theString,
and if found, replaces any sequence of characters
in between them with the replaceString.	
*)
on replaceAnythingThatIsNot(stringOrList, replaceString, theString)
	--Check for special constants:
	set specialConstantValues to {{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"} & ¬
		{"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}, {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, ¬
		{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"} & ¬
		{"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} & ¬
		{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, {"\r", "\n", " ", "\t"}}
	set specialConstants to {"(alpha)", "(digit)", "(alphadigit)", "(invisibles)"}
	set newList to {}
	repeat with i from 1 to (count of specialConstants)
		if stringOrList contains (LI(i, specialConstants)) then
			set theIxs to item 1 of getIndex(LI(i, specialConstants), stringOrList)
			setLI(theIxs, stringOrList, LI(i, specialConstantValues))
		end if
	end repeat
	---Convert stringOrList into a single list:
	repeat with i from 1 to (count of stringOrList)
		set newList to (newList & (LI(i, stringOrList)))
	end repeat
	set stringOrList to newList
	set {textList, newList, x, z} to {explode(theString, stringOrList), {""}, 0, 1}
	repeat
		if ((count of textList) = 1) then
			if (LI(1, textList) is not "") then set end of newList to ""
			exit repeat
		end if
		set x to (x + (count of (LI(1, textList))) + z)
		if (LI(1, textList) is "") then
			if class of stringOrList is text then
				setLI(-1, newList, (LI(-1, newList) & stringOrList))
			else if class of stringOrList is list then
				set {y, z, appendItem} to findEndCharPosition(theString, x, stringOrList)
				setLI(-1, newList, (LI(-1, newList) & appendItem))
			end if
		else
			if class of stringOrList is text then
				set end of newList to stringOrList
			else if class of stringOrList is list then
				set {y, z, appendItem} to findEndCharPosition(theString, x, stringOrList)
				set end of newList to appendItem
			end if
		end if
		set textList to LI({2, -1}, textList)
	end repeat
	set newString to implode(newList, replaceString)
	return newString
end replaceAnythingThatIsNot



on getIndex(theItem, theList)
	if class of theList is not in {integer, real, text, list} then return false --function stops.
	--If theList is a number then coerce into text:
	if (count of theList) is 0 then set theList to (theList as text)
	if theItem is not in theList then return false -- function stops.
	--Else, theItem must be in theList, so:	
	set indexList to {}
	set itemLength to (count of (theItem as text))
	if (count of theList) is 1 then -- Then theItem IS theList.
		set end of indexList to 1
		return indexList -- function stops.
	end if
	if class of theList is list then
		repeat with i from 1 to count of theList
			if (theItem is (LI(i, theList))) or ((LI(i, theList)) begins with theItem) then
				set end of indexList to i -- Appends number to end of list.
			end if
		end repeat
	else if class of theList is text then -- Then theItem is also text.
		set {theLimit, x, i} to {count of theList, 1, 1}
		set theItem to (theItem as text)
		repeat while theLimit > (itemLength - 1)
			if theItem is (characters i thru (i + itemLength - 1) of theList as text) then
				set end of indexList to i
			end if
			set i to (i + 1)
			set theLimit to (theLimit - 1)
		end repeat
	end if
	if indexList is {} then return false
	--Else:
	return indexList
end getIndex



--This function is just for creating a short-hand way of accessing a list item.
--ItemNum can be a single integer, or a list of two integers for accessing a range of items:
on LI(itemNum, theList)
	if class of itemNum is integer then
		return (item itemNum of theList)
	else if class of itemNum is list then
		return (items (item 1 of itemNum as integer) thru ¬
			(item 2 of itemNum as integer) of theList)
	end if
end LI

--This function is for assigning a value to a list item:
on setLI(itemNum, theList, theValue)
	set item itemNum of theList to theValue
end setLI

-- This function separates pieces of a string into list items, using theDelimit
-- as the separator:
on explode(theString, theDelimit)
	set origDelimit to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimit
	set theResult to every text item of theString
	set AppleScript's text item delimiters to origDelimit
	return theResult
end explode

--This function re-assembles a list of strings into a single string,
--using theDelimit as glue to reconnect each string.
on implode(textList, theDelimit)
	set origDelimit to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimit
	set theString to (textList as string)
	set AppleScript's text item delimiters to origDelimit
	return theString
end implode


--We use this function to find a substring within a string that matches an item in a list.
--It returns 3 items: the position of the last character of the substring as a negative integer,
--and the 2nd item is total length of the substring. The 3rd item is the substring itself.
on findEndCharPosition(theString, beginPos, theList)
	set theList to item 1 of removeDuplicates(theList) --removes any duplicate items.
	set itm to character beginPos of theString
	set z to count of theString
	repeat with i from 1 to count of theList
		if LI(i, theList) begins with itm then
			if itm is LI(i, theList) then return {(beginPos + 1 - 2 - z), (count of itm), itm}
			set y to 1
			set itm to (characters beginPos thru (beginPos + y) of theString) as string
			repeat while LI(i, theList) begins with itm
				if itm is LI(i, theList) then
					set subLength to (count of itm)
					set lastLetterPosition to (beginPos + subLength - 2 - z)
					return {lastLetterPosition, subLength, itm}
				end if
				set y to (y + 1)
				set itm to (characters beginPos thru (beginPos + y) of theString) as string
			end repeat
		end if
	end repeat
end findEndCharPosition

--Returns list of two items: first is modified version of lst with duplicates removed.
--Second item is list of indexes of removed items.
on removeDuplicates(lst)
	local lst, itemRef, res, itm
	try
		if lst's class is not list then error "not a list." number -1704
		script k
			property l : lst
			property res : {}
			property indx : {}
		end script
		repeat with i from 1 to count of k's l
			set itm to (item i of k's l)
			if k's res does not contain {itm} then
				set k's res's end to itm
			else
				set k's indx's end to i
			end if
		end repeat
		return {k's res, k's indx}
	on error eMsg number eNum
		return false
	end try
end removeDuplicates