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