TextEdit script - Find and replace all with clipboard content
Often I need to insert a particular phrase (such as an address) 15-20 times in one Text Edit document, and pasting it in continuously interrupts the flow of my typing. This script allows one to type a phrase once, then a short keyword for each following instance. (I have used “zzz” for the keyword as you’re probably not going to run into any unwanted replacements with that. Of course, you can use whatever you will.)
Here’s how it works:
You highlight the portion you want to be the replacement text in a TextEdit document, then run the script.
Script will copy the highlighted portion to clipboard.
Script opens Find and Replace, enters keyword into “find” and clipboard content into “replace with.”
Script clicks “Replace All” button.
Script then closes F&R window.
My Applescripting experience is limited; I’m sure it could be streamlined, but the syntax escapes me! Many thanks to all the generous scripters here, from which I’ve found inspiration and borrowed snippets.
activate application "TextEdit"
tell application "System Events"
tell process "TextEdit"
keystroke "c" using {command down}
end tell
end tell
tell application "System Events"
tell process "TextEdit"
keystroke "f" using {command down}
end tell
end tell
tell application "System Events"
tell process "textedit"
keystroke "zzz"
keystroke tab
end tell
tell process "textedit"
keystroke "v" using {command down}
end tell
tell process "TextEdit"
tell window "Find"
click button "Replace All"
tell window "Find"
keystroke "w" using {command down}
end tell
end tell
end tell
end tell
Another way might be like the script below.
Which just replaces the whole keywords with the selected text, rather than doing a all those key strokes.
The only issue with this method, is you can not undo. So at beginning the script copies all of the text and at the end of the script puts it into the clipboard. Incase you are not happy with the results.
You could actually write out the old version to a temp file first, which would be better…
activate application "TextEdit"
tell application "System Events"
tell process "TextEdit"
keystroke "c" using {command down}
end tell
end tell
tell application "TextEdit"
set doc1 to document 1
set theOldText to text of doc1
display dialog "Enter Word to replace" default answer "zzz" buttons {"Cancel", "OK"} default button 1
copy the result as list to {keyword, button_pressed}
if button_pressed is "OK" then
set thepaste to the clipboard
set words of doc1 whose it is keyword to thepaste
end if
set the clipboard to theOldText
end tell
I am busy to replace keywords with their real value using a dialog.
So, my code looks similar but I walks into a trap when I use << >>.
So I did a search here at MScripter and found this topic.
Old topic but almost same question. Is this solvable?
btw, it is for in text edit but also in mail.
tell application "TextEdit"
activate
set doc to document 1
set keyword to "<<NAME>>" -- as string
--set keyword to "NAME" --<-- works but let the <<>> there
display dialog "Enter a Name to replace <<NAME>>" default answer "<<NAME>>" buttons {"Cancel", "OK"} default button 2
copy the result as list to {button_res, value_res}
if button_res is "OK" then
set keyword_repl to value_res
set words of doc whose it is keyword to keyword_repl
end if
end tell
btw, If I use
...
if button_res is "OK" then
set keyword_repl to value_res as Unicode text
set _body to the text of doc
set _body to my ReplaceText(_body, keyword, keyword_repl)
set the text of doc to _body
end if
end tell
(* found here at MS to test with *)
to ReplaceText(|SourceText|, |SearchText|, |ReplacemetText|)
set {DTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, |SearchText|}
try
set {TextItems, AppleScript's text item delimiters} to {every text item of |SourceText|, |ReplacemetText|}
set {|SourceText|, AppleScript's text item delimiters} to {TextItems as text, DTID}
on error errorMessage number errorNumber -- oops
set AppleScript's text item delimiters to DTID
error errorMessage number errorNumber -- pass it on
end try
return |SourceText|
end ReplaceText
You’re getting the text from TextEdit as a whole but you’ll also get the text as plain text not an marked up text. Then change the text and set the contents back as plain text. There is no way TextEdit, or any kind of text editor, that can change magically plain text into text with markup.
When working inside text editor you’re working with attributes strings (attribute runs) which is an array of strings but each attribute run contains also a font, font size and color. So to maintain overlap in runs for search and replace it’s just easier to work with text, character, word or paragraph inside the document so the search and replace is done correctly.
Klopt. eh. Right, did want to show that I did try several things.
I know when you copy text from textedit you got several clipboards type’s back and depending on the ‘paste’ ( target ) you get the plain or rtf info.
In the case of formatted text e.t.c I should use
set words of doc whose it is keyword to keyword_repl
, right?
Like in my first sample?
Why isn’t that working when there are <<>> around the word?
With other words it is working.
What do I miss?
Aha, ik begrijp het probleem nu (Now I see the problem).
The problem is that “«class»” is not a word, when you do:
word of "«class»"
AppleScript will return just “class”. This means that you will have an invalid objects specifier. So I thought you can work on character (text) level but unfortunately it seems that TextEdit doesn’t support this. So I came up with this, it looks cumbersome but I’ll explain below why I have written it down like this:
set findString to "«class»"
set replaceString to "«replace»"
tell application "TextEdit"
tell document 1
set offsets to my getOffsets(text of it, findString)
set offsets to reverse of offsets
repeat with o in offsets
delete characters (o + 1) thru (o + (count of findString) - 1) of it
set character o of it to replaceString
end repeat
end tell
end tell
on getOffsets(theString, subString)
set offsets to {}
set totalLen to 0
set sLen to count of subString
set {oldTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, subString}
set textItems to every text item of theString
set AppleScript's text item delimiters to oldTID
if (count of textItems) = 1 then return offsets
repeat with i from 1 to (count textItems) - 1
set iLen to count of item i of textItems
set end of offsets to totalLen + iLen + 1
set totalLen to totalLen + iLen + sLen
end repeat
return offsets
end getOffsets
The first and most important reason was that we search and replace inside TextEdit to make sure that we keep our markup.
When search and replace, the markup of the replace string must equal the markup of the first character of the search string. Therefore I delete the second to last character of the search string and keep the first character in the text. Then I replace the remaining character, the first character of the search string, with the replace string.
I get all the offsets of a substring before starting to replace them because when the replace string contains the search string we would end in an infinite loop
We reverse the offsets the search and replace works backwards, this way the string before the replacement is always untouched resulting that the offsets of the substring to proceed won’t shift.
In the getOffsets handler I’m using text item delimiters to determine the offset rather than using the offset command because of 2 reasons. 1) this way our code is scripting addition free (read: we don’t need the use scripting addition statement in Mavericks) 2) offset command is slower on some machines than text item delimiters