4

I have a .txt file saved on my computer. I want my Automator application to open the .txt file and then move the location of the cursor to the first blank line in the file.

For example, I have a .txt file entitled My Fruit Log.txt saved on my computer. This is what the contents of the .txt file may look like:

So, in the above instance, I want the file to be opened, and then the cursor moved to the second line of the file.

Note: It is not necessarily the second line from the top where I want the cursor to be sent to. Depending on what text has already been written to the file, I might want the cursor on a lower line. For example:

In the above case, I would want the cursor to be moved to the sixth line.

The location where I want the cursor will always be at least the second line from the top, but it could be as far down as the seventh line from the top.

So, the most reliable way to articulate where I want the cursor to be placed is "on the first instance of a blank line in the file."

I already have the code to open the file in TextEdit (borrowed from the top answer of this Stack Overflow question):

set Target_Filepath to POSIX file "/Users/Me/Desktop/My Fruit Log.txt"

tell application "Finder" to open Target_Filepath

Now I need the code to move the cursor. The default location of the cursor is the first line of the file.

  • I think it can't be done with TextEdit because it misses a find function in the AppleScript Library. BBEdit and TextWrangler should work though because they both provide one. – klanomath Nov 24 '16 at 2:12
4

I don't know if there's an easy or pretty way to do what you're asking. In other words, you can't just simply say something like open target file and move cursor to first empty line, however the code below will do that.

set filePathName to POSIX path of (path to desktop as string) & "My Fruit Log.txt"
set firstEmptyLineNumber to (do shell script "awk '$1 == \"\" {print NR;exit;}' \"" & filePathName & "\"")
do shell script "open -e " & quoted form of filePathName
tell application "TextEdit" to activate
tell application "System Events" to tell process "TextEdit"
    repeat (firstEmptyLineNumber - 1) times
        key code 125 # Down Arrow
    end repeat
end tell

The code above is coded to open the text file in TextEdit, which is what open -e in the second
do shell script command is doing and it's coded this way because System Events needs to know where to sent the down arrow keystrokes to. If you want a different text editor then remove
the -e and the open command will open it in whatever app the .txt file extension is registered to open with. Then you'll also need to change:

tell application "System Events" to tell process "TextEdit"

To:

tell application "System Events" to tell front process

And replace:

tell application "TextEdit" to activate

With: delay 1

In the first do shell script command, awk is getting the line number of the first empty line and exiting and this is what's used to calculate how many down arrow keystrokes to repeat.


I modified my original answer slightly to get rid of the delay commmand but wanted to add my take on adc's answer while eliminating all the menu_click stuff.

set filePathName to POSIX path of (path to desktop as string) & "My Fruit Log.txt"
set firstEmptyLineNumber to (do shell script "awk '$1 == \"\" {print NR;exit;}' \"" & filePathName & "\"")
if firstEmptyLineNumber = "" then set firstEmptyLineNumber to 1 as string
do shell script "open -e " & quoted form of filePathName
tell application "TextEdit" to activate
tell application "System Events" to tell process "TextEdit"
    key code 37 using command down # ⌘L
    keystroke firstEmptyLineNumber
    keystroke return
    key code 123 # Left Arrow - So the line is not highlighted.
end tell

Update:

The code below has been modified from the code above at the top of my answer, not my take on arc's answer although it's applicable there too, to address the issue you're having with TextEdit writing two carriage returns instead of the expected and normal two line feeds for an empty line after a line containing content or two empty lines in a row.

set filePathName to POSIX path of (path to desktop as string) & "My Fruit Log.txt"
set firstEmptyLineNumber to (do shell script "awk '$1 == \"\" {print NR;exit;}' \"" & filePathName & "\"")
if firstEmptyLineNumber is equal to "" then  
    set firstEmptyLineNumber to (do shell script "awk '/\r\r/{print NR+1;exit;}' \"" & filePathName & "\"")
end if
do shell script "open -e " & quoted form of filePathName
tell application "TextEdit" to activate
tell application "System Events" to tell process "TextEdit"
    repeat (firstEmptyLineNumber - 1) times
        key code 125 # Down Arrow
    end repeat
end tell

Note: Although the modified code works with your testfile.txt file from the link in your comment, nonetheless I personally do not subscribe to this workaround and would instead find out the root cause of the issue and fix it and your files!

  • Clever solution. But, for some reason, your code does not work for me. It opens the .txt file in TextEdit, but the cursor does not move. I tried changing delay 1 to delay 10 to be safe; still, it did not move the cursor. Then I inserted display dialog firstEmptyLineNumber after the second line of your code. When I ran the code, a completely empty dialog box appeared. This is the .txt file that I am using to test your code. When you use this .txt file, does the code work properly on your computer? – rubik's sphere Nov 24 '16 at 3:06
  • The "testfile.txt" file was created on my Mac. Here's how it was made (and I don't know of another way to create a .txt file on a Mac): I created a New Document in TextEdit, typed the text, clicked the Format menu and then clicked Plain Text, and saved the file as "testfile.txt" with the default encoding option of "Unicode (UTF-8)". I see what you are seeing in the hex editor; there is a 0D0D between the fourth and fifth lines of the file. Is it possible to edit your code so that it can recognize both 0D0D and 0A0D as new blank lines? – rubik's sphere Nov 24 '16 at 4:06
  • @rubik's sphere, Please delete all but your first comment under my answer up to this point as they are not really needed now that we know that the issue is TextEdit on your system is writing 0D instead of 0A as is the norm on a Mac. I have tried reproducing your issue in TextEdit under OS X 10.11.6 and macOS 10.12, doing as you say you do, and cannot reproduce it. All of my line endings are 0A as they should be and thus the empty line shows as 0A0A in the Hex Editor, two line feeds in a row as it is supposed to by default on a Mac, not two carriage returns. – user3439894 Nov 24 '16 at 16:06
  • @rubik's sphere, I have updated my answer to handle your issue and tested it on your testfile.txt file from the link in your comment and it works on it. FWIW – user3439894 Nov 24 '16 at 16:42
  • Wow, the new code really works. Thanks very much user3439894. I will still heed your advice about diagnosing the root issue, but this will hold me over in the meantime. On a separate note, what made you decide to entirely remove the delay command from your code? I thought you had originally included the delay command to ensure that the down-arrow presses would not occur prematurely. (Not that I am having an issue with that; I am simply curious.) – rubik's sphere Nov 25 '16 at 0:56
0

Here is an AppleScript for TextWrangler to jump to the end of the first paragraph of a text file:

try
    tell application "TextWrangler"
        find "(?=\\r^\\r|\\A)" searching in text of front text window options {search mode:grep, wrap around:false, backwards:false, case sensitive:false} with selecting match
    end tell
end try

By adding a second expression directly after (?=\\r^\\r|\\A) but within the quotes one should be able to jump to the next line. Since I don't know what (?=\\r^\\r|\\A) really means I can't help you further.

  • The issue I see is that TextWrangle remembers the cursor position in the document when last saved in TextWrangler and the cursor may not be at the top of the file like it will be with TextEdit when it's opened. So as coded it can fail in more then one way, it does on my system. In other words if I saved the file at the end of the first paragraph it go to the end of the second paragraph when I run the code. Or if I save at the end of the second paragraph it does noting whenI run the code as at this point there are only three paragraphs in the file. – user3439894 Nov 24 '16 at 2:58
0

I would probably approach this using something like Ruby and TextMate— a Ruby script could determine which line to choose (probably by a simple regular expression) and even add the date for the next day if appropriate. TextMate can be invoked on the command line to move the cursor to a specific line by mate -l 7 for example.

That said, using only AppleScript in TextEdit and awk from user3439894's answer a somewhat more robust way of navigating to a specific line uses System Events and TextEdit's "Select Line" (Command + L) menu item.

I also used a function from MacOSXHints because menu scripting in AppleScript is a huge pain. Disposing of the menu_click function is an exercise for the reader.

set filePathName to POSIX path of (path to desktop as string) & "My Fruit Log.txt"
set firstEmptyLineNumber to (do shell script "awk '$1 == \"\" {print NR;exit;}' \"" & filePathName & "\"")
if firstEmptyLineNumber = "" then set firstEmptyLineNumber to 1

do shell script "open -e " & quoted form of filePathName

menu_click({"TextEdit", "Edit", "Find", "Select Line..."})


tell application "System Events"
    keystroke firstEmptyLineNumber
    keystroke return
end tell




-- `menu_click`, by Jacob Rus, September 2006
-- 
-- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
-- Execute the specified menu item.  In this case, assuming the Finder 
-- is the active application, arranging the frontmost folder by date.

on menu_click(mList)
    local appName, topMenu, r

    -- Validate our input
    if mList's length < 3 then error "Menu list is not long enough"

    -- Set these variables for clarity and brevity later on
    set {appName, topMenu} to (items 1 through 2 of mList)
    set r to (items 3 through (mList's length) of mList)

    -- This overly-long line calls the menu_recurse function with
    -- two arguments: r, and a reference to the top-level menu
    tell application "System Events" to my menu_click_recurse(r, ((process appName)'s ¬
        (menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
end menu_click

on menu_click_recurse(mList, parentObject)
    local f, r

    -- `f` = first item, `r` = rest of items
    set f to item 1 of mList
    if mList's length > 1 then set r to (items 2 through (mList's length) of mList)

    -- either actually click the menu item, or recurse again
    tell application "System Events"
        if mList's length is 1 then
            click parentObject's menu item f
        else
            my menu_click_recurse(r, (parentObject's (menu item f)'s (menu f)))
        end if
    end tell
end menu_click_recurse
  • You could replace all that menu_click stuff (which is interesting BTW) with two lines of code. Place tell application "TextEdit" to activate just before the tell application "System Events" block, and add key code 37 using command down (which is ⌘L the shortcut key for "Select Line...") or keystroke "l" using command down above keystroke firstEmptyLineNumber in the tell application "System Events" block. I'd also add key code 123 (right arrow) after keystroke return so the line is not highlighted. – user3439894 Nov 24 '16 at 5:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .