Getting the Selection from TextEdit with UIScripting

Hello

This is a script that returns the selection of the front Text window of TextEdit, whether it is saved or not.

An insertion point for a text document are usually implemented as a list/array with a startValue, and an endValue. If there is no selection, then the endValue is one less than the startValue, if endValue is equal or greater than startValue then something is selected.

I have tried to return a selection object, that contains an insertion point as described above. I have also looked to how a selectionObject is implemented elsewhere, and added a property of my own. hasSel, to simplify the usage somewhat.

To be on the safe side, you’ll have to check for the return value of null when using this, because the library handler will return null if the Preferences window of TextEdit is in front.

I have also implemented it as an AS 2.3 library, if you don’t know how to use this, then, save it under the name “TextEdit” in the folder “Script Libraries” of your “Library” folder. You’ll have to make that folder if it doesn’t exist.

If it doesn’t work, then TextEdit must be enabled for assistive devices.

The driver for the library script at the bottom, should explain the most of it.

Try it with:

  • Some text selected
  • No text selected
  • The preferences window of TextEdit as its frontmost window.

Enjoy


use AppleScript version "2.3"
use scripting additions

script selectionObject
	property pos : {} -- consists of a start, end pair, that correlates with the characters of the text 
	property theText : ""
	property hasSel : false
end script


to getSelection()
	set commence to false
	set o to my selectionObject
	set o's pos to {}
	set o's theText to ""
	set o's hasSel to false
	
	tell application id "com.apple.textedit"
		try
			if document of window 1 is not missing value then set commence to true
		end try
	end tell
	
	if commence then
		tell application id "sevs"
			tell application process "TextEdit"
				tell attribute "AXSelectedTextRange" of text area 1 of scroll area 1 of window 1 ¬
					to set o's pos to its value
				if item 2 of o's pos ≥ item 1 of o's pos then
					tell attribute "AXSelectedText" of text area 1 of scroll area 1 of window 1 ¬
						to set o's theText to its value
					set o's hasSel to true
				else
					set o's theText to ""
				end if
			end tell
		end tell
		return o
	else
		return null
	end if
end getSelection


use AppleScript version "2.3"
use scripting additions
use ttxt : script "TextEdit"

-- Test run with some Text Selected 
set selObj to ttxt's getSelection()
if selObj is not null then -- MANDATORY!
	log selObj's pos
	-- (*234, 491*)
	log selObj's theText
	(*
(*ou "makeCodestring for aSentence" first, 
    then test against all of your other code strings that you have in a list or something, which okCodeStrings kind of demonstrates, this is more laborious for your part, as I under
    if there is a "collision" then*)
*)
	log selObj's hasSel
	(*true*)
	
	-- Test run with no text selected
	
	set selObj to ttxt's getSelection()
	
	log selObj's pos
	(*303, 302*)
	log selObj's theText
	(**)
	
	log selObj's hasSel
	(*false*)
else
	log "this time with the preferences window was in front"
end if

The code above does not work anymore. The application’s UI elements often change the object model, so to get selection in any application, which doesn’t have selection property is far better to use a different, persistent solution:


set the clipboard to ""

tell application id "com.apple.textedit" -- any app id
	set appName to its name
	activate
	tell application "System Events" to tell process appName
		set visible to true
		keystroke "c" using command down
	end tell
end tell

set {aSelection, maxTimeOut} to {"", 0}

repeat while aSelection = ""
	set maxTimeOut to maxTimeOut + 0.1
	if maxTimeOut > 1 then return display notification "NOTHING SELECTED." with title "Do selection and run the script again" sound name "Frog"
	set aSelection to (the clipboard) as text
end repeat

Hi KniazidisR.

McUsrII’s script still works on my Mojave system, but there were some rogue characters in his post owing to MacScripter’s current forum software using a different text encoding from that in use four-and-three-quarters years ago. I’ve now corrected these above.

McUsrII’s script returns a script object whose properties are set to the front TextEdit document’s selection range (pos), selected text (theText), and whether or not anything is actually selected (hasSel).

The selection range is synonymous with the insertion range and is a two-integer list. These integers can be thought of as character positions in the text, but actually represent the insertion points before and after these characters. Since the insertion cursor is always somewhere in the text, there’s always a selection range, even when no actual characters are selected. A zero-width selection between two adjacent characters is indicated by the second range integer being one less than the first — eg. {397, 396}, indicating that the selection range stretches from the insertion point before character 397 to the insertion point after character 396, or that it both begins and ends between these characters. McUsrII takes this situation to mean there’s no selection.

One disadvantage of his method (which also appears in a script I wrote for my own use) is that when there are multiple selections in the document, only the details of the first are returned.

Your script only returns the selected text, changes the contents of the clipboard (not necessarily a bad thing, but some scripters like to store the existing contents first and restore them afterwards), and doesn’t always catch the new contents before the timeout. But when it works, the returned string does include the text of every selection in the document, each assigned to a new paragraph. For some reason, I can’t get the “Frog” sound with the alert, but I think I may have silenced notifications on my system. :slight_smile:

I get slightly more reliable results with this version of your script:


set the clipboard to ""

tell application id "com.apple.textedit" -- any app id
	set appName to its name
	activate
end tell

tell application "System Events"
	tell application process appName to set frontmost to true
	keystroke "c" using {command down}
end tell

set {aSelection, maxTimeOut} to {"", 0}

repeat while aSelection = "" and maxTimeOut ≤ 1
	delay 0.2
	set maxTimeOut to maxTimeOut + 0.2
	set aSelection to (the clipboard as text)
end repeat
if maxTimeOut > 1 then display notification "NOTHING SELECTED." with title "Do selection and run the script again" sound name "Frog"
return aSelection