Refreshing a document's window using applescript

I want to save the document, run a unix script on it and refresh the document view.

The following applescript does this successfully, however, unless I close the document the open command does nothing and with the close command the window disappears only to reappear a short time later. I would expect this behaviour.

tell application id "OOut"
   	set doc_file to file of front document
   	set filename to quoted form POSIX path of file doc_file
   	set scriptpath to quoted form of POSIX path of ((path to me as text) & "::" & "update_oo.rb")		
   	set thedoc to front document
   	save thedoc
   	close thedoc
   	do shell script "/bin/bash -c \" ruby " & scriptpath & " " & filename & "\""
   	open doc_file
   on error errStr number errorNumber
   	display dialog errStr
   end try
end tell

Is there anyway to edit the document’s file in the background and force the window to update without getting the distracting flashing as the window closes and re opens?



1 Like

This kind of thing is fairly straightforward with plain text outliners, but not easy, I think with apps like this.

You don’t want to do the transformations inside the document using [Apple|Java]Script ? Perhaps including a .doShellScript() component if external tools are needed ?

If you use OS X’s file coordination APIs to coordinate access to the file, any well-behaved OS X app (including OmniOutliner, of course!) should automatically notice the changes you’ve made and refresh any open copies of the document. (This is how Time Machine and iCloud Drive and OmniPresence work.)

For those times when it isn’t convenient to modify the tool to call those APIs directly, I’ve written this little UNIX tool which can coordinate access to another file while running another UNIX command.

// Copyright 2016 Omni Development, Inc. All rights reserved.
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// <>.
// $Header: svn+ssh:// 253856 2016-01-28 15:01:15Z kc $
// Usage: ./file-coord path -- command [arguments]
// Example usage: ./file-coord /tmp/test-coord.oo3 -- /bin/zsh -c 'sed "s/Hello.*</Hello, world</" < /tmp/test-coord.oo3/contents.xml > /tmp/new-contents.xml; mv /tmp/new-contents.xml /tmp/test-coord.oo3/contents.xml'
// Build with: cc -o file-coord -Wall -fobjc-arc file-coord.m -framework Foundation

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
    __block int taskTerminationStatus = 0;

    @autoreleasepool {
        NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
        NSMutableArray *fileURLs = [NSMutableArray array];
        NSMutableArray *toolArguments = [NSMutableArray array];
        BOOL scanningToolArguments;
        for (int argIndex = 1; argIndex < argc; argIndex++) {
            NSString *argument = [NSString stringWithUTF8String:argv[argIndex]];
            if (scanningToolArguments) {
                [toolArguments addObject:argument];
            } else if ([argument isEqualToString:@"--"]) {
                scanningToolArguments = YES;
            } else {
                [fileURLs addObject:[NSURL fileURLWithPath:argument]];

        if ([fileURLs count] != 1 || [toolArguments count] < 1) {
            fprintf(stderr, "Usage: %s path -- command [arguments]\n", argv[0]);
            NSLog(@"fileURLs=%@, toolArguments=%@", fileURLs, toolArguments);

        NSURL *fileURL = [fileURLs objectAtIndex:0]; // We've already tested to make sure there's one and only one

        NSError *fileCoordinatorError = nil;
        [fileCoordinator coordinateReadingItemAtURL:fileURL options:0 writingItemAtURL:fileURL options:0 error:&fileCoordinatorError byAccessor:^(NSURL *newReadingURL, NSURL *newWritingURL) {
            if (![newReadingURL isEqual:fileURL]) {
                NSLog(@"Aborting: file moved to %@", newReadingURL);
            if (![newWritingURL isEqual:fileURL]) {
                NSLog(@"Aborting: file moved to %@", newReadingURL);

            // Launch our UNIX command
            NSString *launchPath = [toolArguments objectAtIndex:0];
            [toolArguments removeObjectAtIndex:0];
            NSLog(@"Launching %@...", launchPath);
            NSTask *task = [NSTask launchedTaskWithLaunchPath:launchPath arguments:toolArguments];
            [task waitUntilExit];
            taskTerminationStatus = [task terminationStatus];
            if (taskTerminationStatus == 0) {
                NSLog(@"    ... done");
            } else {
                NSLog(@"    ... exited with status %d", taskTerminationStatus);

        if (fileCoordinatorError != nil) {
            NSLog(@"File coordination error: %@: %@", [fileCoordinatorError localizedDescription], [fileCoordinatorError localizedFailureReason]);
    return taskTerminationStatus;

Here’s an example of how I might use this tool to edit an open OmniOutliner document using some UNIX commands:

./file-coord /tmp/test-coord.oo3 -- /bin/zsh -c 'sed "s/Hello.*</Hello, world</" < /tmp/test-coord.oo3/contents.xml > /tmp/new-contents.xml; mv /tmp/new-contents.xml /tmp/test-coord.oo3/contents.xml'

Of course, you can also use it to edit other things, like open TextEdit documents. For example, this command will grab the current contents of an open TextEdit /tmp/foo.txt window (saving its contents if the user has unsaved edits) and replace them with “hello world”:

./file-coord /tmp/foo.txt -- /bin/zsh -c 'cat /tmp/foo.txt; echo hello world > /tmp/foo.txt'

Hope this helps!


Perfect, does the job nicely, thanks, but a few comments:

There is a delay before the window is updated and it feels longer than the time to go via the close/open route. I understand file system events are not instantaneous.

Visually it is less distracting, but there is no feedback that the script is running. Maybe when a script is invoked one of the spiny progress wheels should be shown, or the script icon on the toolbar changed in some way.

Also having an applescript reload command seems like a trivial addition and make this type of behind-the-scenes updates easier.

Thanks again for your effort in providing this code.


This topic was automatically closed after 9 hours. New replies are no longer allowed.