How to modify text selection in applescript in an automator service

applescriptautomatorservicesterminal

I wrote an Automator Service (intended for use on any selected text in any application) that modifies the length of the selection given a number/length that the user specifies. Here's a snippet of relevant code that excludes all the other features/niceties of the service:

--Figure out whether the selection must be lengthened or shrunk
set lengthen to false
set mod_length to 0
if character_count is equal to 0 or n is greater than character_count then
    set lengthen to true
    set mod_length to n - character_count
else
    set mod_length to character_count - n
    --This is a trick to make sure arrowing affects the right side of the selection
    key code 124 using {shift down}
    key code 123 using {shift down}
end if

repeat mod_length times
    if lengthen is true then
        --arrow to the right
        key code 124 using {shift down}
    else
        --arrow to the left
        key code 123 using {shift down}
    end if
end repeat

The service works great in most applications, except Terminal.app (because the arrow keys are used to move the cursor on the command line and not the size of the currently active selection). Ideally, this would actually work in Terminal, because that's usually where I need to use this service.

So is there a way to modify the length of selected text on any line in the history in a Terminal.app window (assuming the first selected character is position 1) using an automator service (and applescript inside that service) in a way that doesn't use the arrow keys? E.g. I select the output of a command and run the service to adjust the length of the selection to a length I've supplied.

Background:

The reason I'm doing this is because my work involves DNA manipulation/modification and as a part of the debug process for these scripts, I sometimes need to briefly observe what has happened at a specific position in a DNA string output on the command line – quickly and dirtily – just to confirm an intended change had the intended result. So this is just a quick way to visually find a coordinate in a long DNA string. I eventually intend to modify this service in such a way that only certain characters are counted in the determination of the length of the selection (e.g. don't count numbers, spaces, newlines, etc).

Best Answer

So as I've learned, there are/were only 3 ways to manipulate the selection in Terminal by entering/using a length value (or via a series of keyboard presses).

  1. Accessibility Inspector shows that it should be possible to programmatically manipulate the selection length, because you can edit an existing selection length using the len field in the "Selected Text Range" in the "Advanced" section by clicking edit and entering a new number, as seen in the screen-cap: https://www.dropbox.com/s/bn2z6ewmdj4mfai/Accessibility_Inspector-selected_text_len.png?dl=0

The "Selected Text Range" Property/Attribute(?) does not appear to be accessible in AppleScript, though if it was accessible, it would be here: (text area of (scroll area 1 of (splitter group 1 of (front window)))) if you have enabled UI elements in your Applescript. It might be possible to access these values, as @CJK indicated in the comments via Objective-C. Applescript however, even if it could access the variable, likely wouldn't have the ability to alter it.

One useful feature to see what applescript can manipulate or has access to is to simply return attributes or return properties. For example:

tell application "System Events"
    if UI elements enabled is false then
        set UI elements enabled to true
        if UI elements enabled is false then error "Can't do it"
    end if
    tell application process "Terminal"
        tell window 1
            tell splitter group 1
                tell scroll area 1
                    --return properties
                    return attributes
                end tell
            end tell
        end tell
    end tell
end tell
  1. This method, as of OS X 10.8 no longer works, but making a selection in Terminal using the keyboard used to be possible, as described here. All you had to do was hit Command-Option-Return, navigate to the region you want to select with the arrow keys, hit return, select from the cursor position using the arrow keys, and hit return. I searched for the ability to re-enable this feature, but came up dry.

  2. There is a way to select text in the Terminal using the keyboard, but it's very limited. You can select text between "marks" using Command-Shift-A (not including the lines that the marks are on). Marks are added by default on every command prompt line. However, you can add marks by right-clicking a selection and typing Command-U. Marks are only per-line however, not specific to the selection start/stop on a line. If you right-click any unselected text that is soft-wrapped (i.e. multi-line string) and add a mark, a separate mark is added for every soft-wrapped line. You cannot select text between multiple marks, nor is it possible to add marks via applescript, thus it's not possible to use this to adjust a selection length programmatically. More info on marks can be found here.

I had also explored what the arrow keys are supposed to be able to do, since shift-{left,right}-arrow for me enters the strings ";2C" and ";2D" with a beep on the command line for me. So I removed those entries from the table in Terminal -> Preferences -> Profiles -> Keyboard to prevent that. I had hoped it would allow me to select text (at least on the current line prompt) using shift-arrow but all it did was move the cursor without selecting.

However, given the goal of this endeavor (to see where a coordinate is in a selected string), you can accomplish this via a dialog box from the Automator service using an Applescript. All you have to do is present the selection in a dialog box and if the target length is longer than the selection, you have to fill the remaining characters with a dummy character ('N' for DNA).

You can do this strategy conditionally, only when Terminal is the frontmost application. Note, this is not the entire script - just a snippet, but you can see the entire script here, which contains lots of bells & whistles unrelated to this question.

--Check the length of the selected text passed in
set character_count to count characters of ((input as string) as string)

set lengthen to false
set mod_length to character_count - n
--Figure out whether the selection must be lengthened or shrunk
if character_count is equal to 0 or n is greater than character_count then
    set lengthen to true
    set mod_length to n - character_count
end if

--See if we're in Terminal
set isTerminal to ((name of first process where it is frontmost) as string) is equal to "Terminal"

if isTerminal is true then
    if lengthen is true then
        set substr to (input as string)
        repeat mod_length times
            set substr to substr & "N"
        end repeat
    else
        set substr to text 1 thru n of (input as string)
        set substr to substr & (text (n + 1) thru character_count of (input as string))
    end if
    ignoring application responses
        display dialog "Length " & n & " is selected below:" default answer substr buttons {"OK"} default button 1 with title "Selected Character Position"
    end ignoring
end if

--Trick to make sure arrow presses affect the right side of the selection instead of the left
if not (character_count is equal to 0 or n is greater than character_count) then
    key code 124 using {shift down}
    key code 123 using {shift down}
    key code 124 using {shift down}
end if

repeat mod_length times
    if lengthen is true then
        key code 124 using {shift down}
    else
        key code 123 using {shift down}
    end if
end repeat
delay 1

That will effectively accomplish the end-goal. Unfortunately, the position has to be marked with a text marker in a variable-width type face since dialog boxes don't allow formatting or automatic selection of text.