Search and Replace in Script Debugger

That’s right. You could still use the same code to do the actual work.

I revised the script in post 16 to remove an unnecessary error handler and to cleanup the code a bit. I did not alter db123’s dialog handler, which works great as written.

-- Revised 2022.02.28

on main()
	tell application "Script Debugger" to tell document 1
		set selectedText to selection
		set {c1, c2} to character range of selection
	end tell
	
	if c2 = 0 then -- select entire script if no selection
		display dialog "A text selection was not found" buttons {"Cancel", "Select All"} cancel button 1 default button 1 with title "Search and Replace" with icon caution -- disable if desired
		tell application "Script Debugger" to tell document 1
			set selectedText to source text
			set {c1, c2} to {1, (count selectedText)}
			set selection to {c1, c2}
		end tell
	end if
	
	set dialogResult to my showSearchReplaceDialog()
	if dialogResult is missing value then error number -128
	set {searchString, replaceString} to {searchString, replaceString} of dialogResult
	
	set text item delimiters to searchString
	set modifiedText to text items of selectedText
	set searchStringMatches to ((count modifiedText) - 1)
	set text item delimiters to replaceString
	set modifiedText to modifiedText as text
	set text item delimiters to {""}
	
	if searchStringMatches = 0 then
		display dialog "The search string " & quote & searchString & quote & " was not found in the selected text" buttons {"OK"} cancel button 1 default button 1 with title "Search and Replace" with icon stop
	else if searchStringMatches = 1 then
		display dialog "Replace 1 instance of " & quote & searchString & quote & " with " & quote & replaceString & quote with title "Search and Replace" with icon note
	else
		display dialog "Replace " & searchStringMatches & " instances of " & quote & searchString & quote & " with " & quote & replaceString & quote with title "Search and Replace" with icon note
	end if
	
	set c2 to c2 + (((count replaceString) - (count searchString)) * searchStringMatches)
	tell application "Script Debugger" to tell document 1
		set selection to modifiedText
		set selection to {c1, c2}
		-- compile without showing errors -- enable if desired
	end tell
end main

on showSearchReplaceDialog()
	script theScript
		use scripting additions
		use framework "Foundation"
		use framework "AppKit"
		use framework "Carbon"
		
		property ca : current application
		property dialogWindow : missing value
		property searchTextField : missing value
		property replaceTextField : missing value
		property cancelButton : missing value
		property replaceButton : missing value
		property searchString : missing value
		property replaceString : missing value
		property replaceClicked : false
		
		on showSearchReplaceDialog()
			if current application's AEInteractWithUser(-1, missing value, missing value) ≠ 0 then
				return missing value
			end if
			
			if ca's NSThread's isMainThread() then
				my performSearchReplaceDialog:(missing value)
			else
				its performSelectorOnMainThread:"performSearchReplaceDialog:" withObject:(missing value) waitUntilDone:true
			end if
			
			if my replaceClicked then
				return {searchString:my searchString as text, replaceString:my replaceString as text}
			end if
			return missing value
		end showSearchReplaceDialog
		
		on performSearchReplaceDialog:args
			set findLabel to ca's NSTextField's labelWithString:"Search:"
			findLabel's setFrame:(ca's NSMakeRect(20, 85, 70, 20))
			
			set my searchTextField to ca's NSTextField's textFieldWithString:""
			searchTextField's setFrame:(ca's NSMakeRect(87, 85, 245, 20))
			searchTextField's setEditable:true
			searchTextField's setBordered:true
			searchTextField's setPlaceholderString:"Search for …"
			searchTextField's setDelegate:me
			
			set replaceLabel to ca's NSTextField's labelWithString:"Replace:"
			replaceLabel's setFrame:(ca's NSMakeRect(20, 55, 70, 20))
			
			set my replaceTextField to ca's NSTextField's textFieldWithString:""
			replaceTextField's setFrame:(ca's NSMakeRect(87, 55, 245, 20))
			replaceTextField's setEditable:true
			replaceTextField's setBordered:true
			replaceTextField's setPlaceholderString:"Replace with …"
			
			set my cancelButton to ca's NSButton's buttonWithTitle:"Cancel" target:me action:"buttonAction:"
			cancelButton's setFrameSize:{94, 32}
			cancelButton's setFrameOrigin:{150, 10}
			cancelButton's setKeyEquivalent:(character id 27)
			
			set my replaceButton to ca's NSButton's buttonWithTitle:"Replace" target:me action:"buttonAction:"
			replaceButton's setFrameSize:{94, 32}
			replaceButton's setFrameOrigin:{245, 10}
			replaceButton's setKeyEquivalent:return
			replaceButton's setEnabled:false
			
			set windowSize to ca's NSMakeRect(0, 0, 355, 125)
			set winStyle to (ca's NSWindowStyleMaskTitled as integer) + (ca's NSWindowStyleMaskClosable as integer)
			set my dialogWindow to ca's NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:(ca's NSBackingStoreBuffered) defer:true
			
			dialogWindow's contentView()'s addSubview:findLabel
			dialogWindow's contentView()'s addSubview:searchTextField
			dialogWindow's contentView()'s addSubview:replaceLabel
			dialogWindow's contentView()'s addSubview:replaceTextField
			dialogWindow's contentView()'s addSubview:cancelButton
			dialogWindow's contentView()'s addSubview:replaceButton
			
			dialogWindow's setTitle:"Search and Replace"
			dialogWindow's setLevel:(ca's NSModalPanelWindowLevel)
			dialogWindow's setDelegate:me
			dialogWindow's orderFront:me
			dialogWindow's |center|()
			
			ca's NSApp's activateIgnoringOtherApps:true
			ca's NSApp's runModalForWindow:dialogWindow
		end performSearchReplaceDialog:
		
		on buttonAction:sender
			if sender is my replaceButton then
				set my searchString to searchTextField's stringValue()
				set my replaceString to replaceTextField's stringValue()
				set my replaceClicked to true
			end if
			my dialogWindow's |close|()
		end buttonAction:
		
		on controlTextDidChange:aNotification
			set sender to aNotification's object()
			if sender is my searchTextField then
				if sender's stringValue() as text ≠ "" then
					my (replaceButton's setEnabled:true)
				else
					my (replaceButton's setEnabled:false)
				end if
			end if
		end controlTextDidChange:
		
		on windowWillClose:aNotification
			ca's NSApp's stopModal()
		end windowWillClose:
	end script
	return theScript's showSearchReplaceDialog()
end showSearchReplaceDialog

main()


This script works with Script Editor but is otherwise identical to that above.

-- Revised 2022.03.05

on main()
	tell application "Script Editor" to tell document 1
		set selectedText to contents of selection
		set {c1, c2} to character range of selection
	end tell
	
	if c1 = c2 then -- select entire script if no selection
		display dialog "A text selection was not found" buttons {"Cancel", "Select All"} cancel button 1 default button 1 with title "Search and Replace" with icon caution -- disable if desired
		tell application "Script Editor" to tell document 1
			set selectedText to contents
			set {c1, c2} to {1, (count selectedText)}
			set selection to {0, c2}
		end tell
	end if
	
	set dialogResult to my showSearchReplaceDialog()
	if dialogResult is missing value then error number -128
	set {searchString, replaceString} to {searchString, replaceString} of dialogResult
	
	set text item delimiters to searchString
	set modifiedText to text items of selectedText
	set searchStringMatches to ((count modifiedText) - 1)
	set text item delimiters to replaceString
	set modifiedText to modifiedText as text
	set text item delimiters to {""}
	
	if searchStringMatches = 0 then
		display dialog "The search string " & quote & searchString & quote & " was not found in the selected text" buttons {"OK"} cancel button 1 default button 1 with title "Search and Replace" with icon stop
	else if searchStringMatches = 1 then
		display dialog "Replace 1 instance of " & quote & searchString & quote & " with " & quote & replaceString & quote with title "Search and Replace" with icon note
	else
		display dialog "Replace " & searchStringMatches & " instances of " & quote & searchString & quote & " with " & quote & replaceString & quote with title "Search and Replace" with icon note
	end if
	
	set c2 to c2 + (((count replaceString) - (count searchString)) * searchStringMatches)
	tell application "Script Editor" to tell document 1
		set contents of selection to modifiedText
		set selection to {(c1 - 1), c2}
	end tell
end main

on showSearchReplaceDialog()
	script theScript
		use scripting additions
		use framework "Foundation"
		use framework "AppKit"
		use framework "Carbon"
		
		property ca : current application
		property dialogWindow : missing value
		property searchTextField : missing value
		property replaceTextField : missing value
		property cancelButton : missing value
		property replaceButton : missing value
		property searchString : missing value
		property replaceString : missing value
		property replaceClicked : false
		
		on showSearchReplaceDialog()
			if current application's AEInteractWithUser(-1, missing value, missing value) ≠ 0 then
				return missing value
			end if
			
			if ca's NSThread's isMainThread() then
				my performSearchReplaceDialog:(missing value)
			else
				its performSelectorOnMainThread:"performSearchReplaceDialog:" withObject:(missing value) waitUntilDone:true
			end if
			
			if my replaceClicked then
				return {searchString:my searchString as text, replaceString:my replaceString as text}
			end if
			return missing value
		end showSearchReplaceDialog
		
		on performSearchReplaceDialog:args
			set findLabel to ca's NSTextField's labelWithString:"Search:"
			findLabel's setFrame:(ca's NSMakeRect(20, 85, 70, 20))
			
			set my searchTextField to ca's NSTextField's textFieldWithString:""
			searchTextField's setFrame:(ca's NSMakeRect(87, 85, 245, 20))
			searchTextField's setEditable:true
			searchTextField's setBordered:true
			searchTextField's setPlaceholderString:"Search for …"
			searchTextField's setDelegate:me
			
			set replaceLabel to ca's NSTextField's labelWithString:"Replace:"
			replaceLabel's setFrame:(ca's NSMakeRect(20, 55, 70, 20))
			
			set my replaceTextField to ca's NSTextField's textFieldWithString:""
			replaceTextField's setFrame:(ca's NSMakeRect(87, 55, 245, 20))
			replaceTextField's setEditable:true
			replaceTextField's setBordered:true
			replaceTextField's setPlaceholderString:"Replace with …"
			
			set my cancelButton to ca's NSButton's buttonWithTitle:"Cancel" target:me action:"buttonAction:"
			cancelButton's setFrameSize:{94, 32}
			cancelButton's setFrameOrigin:{150, 10}
			cancelButton's setKeyEquivalent:(character id 27)
			
			set my replaceButton to ca's NSButton's buttonWithTitle:"Replace" target:me action:"buttonAction:"
			replaceButton's setFrameSize:{94, 32}
			replaceButton's setFrameOrigin:{245, 10}
			replaceButton's setKeyEquivalent:return
			replaceButton's setEnabled:false
			
			set windowSize to ca's NSMakeRect(0, 0, 355, 125)
			set winStyle to (ca's NSWindowStyleMaskTitled as integer) + (ca's NSWindowStyleMaskClosable as integer)
			set my dialogWindow to ca's NSWindow's alloc()'s initWithContentRect:windowSize styleMask:winStyle backing:(ca's NSBackingStoreBuffered) defer:true
			
			dialogWindow's contentView()'s addSubview:findLabel
			dialogWindow's contentView()'s addSubview:searchTextField
			dialogWindow's contentView()'s addSubview:replaceLabel
			dialogWindow's contentView()'s addSubview:replaceTextField
			dialogWindow's contentView()'s addSubview:cancelButton
			dialogWindow's contentView()'s addSubview:replaceButton
			
			dialogWindow's setTitle:"Search and Replace"
			dialogWindow's setLevel:(ca's NSModalPanelWindowLevel)
			dialogWindow's setDelegate:me
			dialogWindow's orderFront:me
			dialogWindow's |center|()
			
			ca's NSApp's activateIgnoringOtherApps:true
			ca's NSApp's runModalForWindow:dialogWindow
		end performSearchReplaceDialog:
		
		on buttonAction:sender
			if sender is my replaceButton then
				set my searchString to searchTextField's stringValue()
				set my replaceString to replaceTextField's stringValue()
				set my replaceClicked to true
			end if
			my dialogWindow's |close|()
		end buttonAction:
		
		on controlTextDidChange:aNotification
			set sender to aNotification's object()
			if sender is my searchTextField then
				if sender's stringValue() as text ≠ "" then
					my (replaceButton's setEnabled:true)
				else
					my (replaceButton's setEnabled:false)
				end if
			end if
		end controlTextDidChange:
		
		on windowWillClose:aNotification
			ca's NSApp's stopModal()
		end windowWillClose:
	end script
	return theScript's showSearchReplaceDialog()
end showSearchReplaceDialog

main()

The scripts included below were written for use with Script Debugger and Script Editor, and both utilize Shane’s Dialog Toolkit Plus script library. These scripts include search-all, match-word, and consider-case options. I tested these scripts on Monterey; they can be tested on earlier versions of macOS by removing the first line of the scripts.

Dialog Toolkit Plus can be downloaded from:

https://latenightsw.com/freeware/The Script Debugger script:

The Script Debugger script is:

-- revised 2022.07.31

use AppleScript version "2.8" -- remove to test with earlier versions of macOS
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1.0"
use scripting additions

on main()
	set {searchString, replaceString, searchOption, wordOption, caseOption} to displayDialog()
	if searchString = "" then errorDialog("A search string was not entered")
	searchAndReplace(searchString, replaceString, searchOption, wordOption, caseOption)
end main

on displayDialog()
	set dialogWidth to 330
	set verticalSpace to 10
	set {theButtons, minWidth} to create buttons {"Cancel", "OK"} cancel button 1 default button 2
	set {searchCheckbox, unusedTop, searchWidth} to create checkbox "Search All" left inset 0 bottom 0 max width dialogWidth
	set {wordCheckbox, unusedTop, wordWidth} to create checkbox "Match Words" left inset (searchWidth + 8) bottom 0 max width dialogWidth
	set {caseCheckbox, theTop, caseWidth} to create checkbox "Consider Case" left inset (searchWidth + wordWidth + 16) bottom 0 max width dialogWidth
	set {replaceField, replaceLabel, theTop, fieldLeft} to create side labeled field "" placeholder text "replace with..." left inset 0 bottom (theTop + verticalSpace) total width dialogWidth label text "Replace:" field left 0
	set {searchField, searchLabel, theTop, fieldLeft} to create side labeled field "" placeholder text "search for..." left inset 0 bottom (theTop + verticalSpace) total width dialogWidth label text "Search:" field left 0
	set allControls to {searchCheckbox, wordCheckbox, caseCheckbox, replaceField, replaceLabel, searchField, searchLabel}
	set {buttonName, controlsResults} to display enhanced window "Search and Replace" buttons theButtons acc view width dialogWidth acc view height theTop acc view controls allControls initial position {} without align cancel button
	return {item 6, item 4, item 1, item 2, item 3} of controlsResults
end displayDialog

on searchAndReplace(searchString, replaceString, searchOption, wordOption, caseOption)
	tell application id "com.latenightsw.ScriptDebugger8" to tell document 1
		if searchOption is false then
			set selectedText to selection
			set {c1, c2} to character range of selection
		else
			set selectedText to source text
			set selection to {1, (count selectedText)}
		end if
	end tell
	if selectedText = "" then errorDialog("A text selection was not found")
	set selectedText to current application's NSMutableString's stringWithString:selectedText
	
	if wordOption is true and caseOption is true then
		set searchPattern to "\\b" & searchString & "\\b"
		set optionText to "The match-words and consider-case search options were enabled."
	else if caseOption is true then
		set searchPattern to searchString
		set optionText to "The consider-case search option was enabled."
	else if wordOption is true then
		set searchPattern to "(?i)" & "\\b" & searchString & "\\b"
		set optionText to "The match-words search option was enabled."
	else
		set searchPattern to "(?i)" & searchString
		set optionText to "No search options were enabled."
	end if
	
	set searchStringMatches to (selectedText's replaceOccurrencesOfString:searchPattern withString:replaceString options:(current application's NSRegularExpressionSearch) range:{0, selectedText's |length|()})
	if searchStringMatches = 0 then errorDialog("The search string " & quote & searchString & quote & " was not found in the selected text. " & optionText)
	set selectedText to selectedText as text
	
	if replaceString = "" then
		if searchStringMatches = 1 then
			set dialogText to "Delete 1 instance of " & quote & searchString & quote
		else
			set dialogText to "Delete " & searchStringMatches & " instances of " & quote & searchString & quote
		end if
	else
		if searchStringMatches = 1 then
			set dialogText to "Replace 1 instance of " & quote & searchString & quote & " with " & quote & replaceString & quote
		else
			set dialogText to "Replace " & searchStringMatches & " instances of " & quote & searchString & quote & " with " & quote & replaceString & quote
		end if
	end if
	display dialog dialogText with title "Search and Replace" with icon note
	
	tell application id "com.latenightsw.ScriptDebugger8" to tell document 1
		set selection to selectedText
		if searchOption is false then
			set c2 to c2 + (((count replaceString) - (count searchString)) * searchStringMatches)
			set selection to {c1, c2}
		else
			set selection to {1, 0}
			set selection to {1, (count selectedText)}
		end if
	end tell
end searchAndReplace

on errorDialog(theText)
	display dialog theText buttons {"OK"} cancel button 1 default button 1 with title "Search and Replace" with icon stop
end errorDialog

main()

The Script Editor script is:

-- revised 2022.07.31

use AppleScript version "2.8" -- remove to test with earlier versions of macOS
use framework "Foundation"
use script "Dialog Toolkit Plus" version "1.1.0"
use scripting additions

on main()
	set {searchString, replaceString, searchOption, wordOption, caseOption} to displayDialog()
	if searchString = "" then errorDialog("A search string was not entered")
	searchAndReplace(searchString, replaceString, searchOption, wordOption, caseOption)
end main

on displayDialog()
	set dialogWidth to 330
	set verticalSpace to 10
	set {theButtons, minWidth} to create buttons {"Cancel", "OK"} cancel button 1 default button 2
	set {searchCheckbox, unusedTop, searchWidth} to create checkbox "Search All" left inset 0 bottom 0 max width dialogWidth
	set {wordCheckbox, unusedTop, wordWidth} to create checkbox "Match Words" left inset (searchWidth + 8) bottom 0 max width dialogWidth
	set {caseCheckbox, theTop, caseWidth} to create checkbox "Consider Case" left inset (searchWidth + wordWidth + 16) bottom 0 max width dialogWidth
	set {replaceField, replaceLabel, theTop, fieldLeft} to create side labeled field "" placeholder text "replace with..." left inset 0 bottom (theTop + verticalSpace) total width dialogWidth label text "Replace:" field left 0
	set {searchField, searchLabel, theTop, fieldLeft} to create side labeled field "" placeholder text "search for..." left inset 0 bottom (theTop + verticalSpace) total width dialogWidth label text "Search:" field left 0
	set allControls to {searchCheckbox, wordCheckbox, caseCheckbox, replaceField, replaceLabel, searchField, searchLabel}
	set {buttonName, controlsResults} to display enhanced window "Search and Replace" buttons theButtons acc view width dialogWidth acc view height theTop acc view controls allControls initial position {} without align cancel button
	return {item 6, item 4, item 1, item 2, item 3} of controlsResults
end displayDialog

on searchAndReplace(searchString, replaceString, searchOption, wordOption, caseOption)
	tell application "Script Editor" to tell document 1
		if searchOption is false then
			set selectedText to contents of selection
			set {c1, c2} to character range of selection
		else
			set selectedText to contents
			set {c1, c2} to {1, (count selectedText)}
			set selection to {0, c2}
		end if
	end tell
	if selectedText = "" then errorDialog("A text selection was not found")
	set selectedText to current application's NSMutableString's stringWithString:selectedText
	
	if wordOption is true and caseOption is true then
		set searchPattern to "\\b" & searchString & "\\b"
		set optionText to "The match-words and consider-case search options were enabled."
	else if caseOption is true then
		set searchPattern to searchString
		set optionText to "The consider-case search option was enabled."
	else if wordOption is true then
		set searchPattern to "(?i)" & "\\b" & searchString & "\\b"
		set optionText to "The match-words search option was enabled."
	else
		set searchPattern to "(?i)" & searchString
		set optionText to "No search options were enabled."
	end if
	
	set searchStringMatches to (selectedText's replaceOccurrencesOfString:searchPattern withString:replaceString options:(current application's NSRegularExpressionSearch) range:{0, selectedText's |length|()})
	if searchStringMatches = 0 then errorDialog("The search string " & quote & searchString & quote & " was not found in the selected text. " & optionText)
	set selectedText to selectedText as text
	
	if replaceString = "" then
		if searchStringMatches = 1 then
			set dialogText to "Delete 1 instance of " & quote & searchString & quote
		else
			set dialogText to "Delete " & searchStringMatches & " instances of " & quote & searchString & quote
		end if
	else
		if searchStringMatches = 1 then
			set dialogText to "Replace 1 instance of " & quote & searchString & quote & " with " & quote & replaceString & quote
		else
			set dialogText to "Replace " & searchStringMatches & " instances of " & quote & searchString & quote & " with " & quote & replaceString & quote
		end if
	end if
	display dialog dialogText with title "Search and Replace" with icon note
	
	set c2 to c2 + (((count replaceString) - (count searchString)) * searchStringMatches)
	tell application "Script Editor" to tell document 1
		set contents of selection to selectedText
		set selection to {(c1 - 1), c2}
	end tell
end searchAndReplace

on errorDialog(theText)
	display dialog theText buttons {"OK"} cancel button 1 default button 1 with title "Search and Replace" with icon stop
end errorDialog

main()

Very nice. You should share this on the ScriptDebugger forums. Find and change is one of the few things I don’t like about SD.

Ed. Thanks for looking at my script and for the kind words.