Help with automating TextEdit and Tex-Edit Plus

Actually I think Chris misspoke there. His example is for the "documents folder" rather than the "home folder".

Here are some examples:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set homeFolder to path to home folder
-->alias "Macintosh HD:Users:YourUserName:"

set docFolder to path to documents folder
-->alias "Macintosh HD:Users:YourUserName:Documents:"

set testFolder to alias ((path to documents folder as text) & "Test")
-->alias "Macintosh HD:Users:YourUserName:Documents:Test:"

set testFolderPath to POSIX path of testFolder
-->"/Users/YourUserName/Documents/Test/"

Using data from the StandardAdditions.osax provided by Script Debugger 6, I produced this PDF which lists all of the folders that can be accessed using the "path to" command:

AppleScript AFDR Constants Used in path to Command.pdf.zip (53.7 KB)

1 Like

Short answer: Just use the same notation as in the snippet (i.e. replace the “/” with “:”):

"Documents:_test2:test doc.rtf"

Whups, that's true.

Nevertheless AppleScript's Path-To system provides anchors to many specific folders on a system and improves upon Unix's $HOME-based notation.

I have a script that creates what I call Relative-Aliases from the selected items in the Finder, so I don't have to fiddle with creating them manually.

So this:

alias ((path to home folder as text) & "test_directory:KM_TEST:test_file_001.txt")

Is 1-keystroke away.

-Chris

Hey John,

Good idea. It's hard to advise without seeing good sample data.

-Chris

@ComplexPoint Rob -

Can you figure out how to do, in JXA, any of the things mentioned in this post (above)? Or any of the posts following that one?

I’d love to log directly from JXA to Tex-Edit Plus. But I can’t figure out how to make a new document, or do anything else for that matter, in JXA.

Any help would be awesome!

This kind of thing ?


(function () {
    'use strict';

    var te = Application('TextEdit'),
        doc = te.Document({
            name: 'tabulaRasa',
            text: 'Next to nothing'
        });


    te.activate();
    te.documents.push(doc);

    doc.text = doc.text() + '\n\nSome additional content'


    return doc.properties();
})();

OK, now you’re just ticking me off. I SWEAR I tried the same exact thing with TextEdit - dozens of times, and the line

te.documents.push(doc);

always generated an error. I can’t for the life of me see what’s different, but of course I didn’t keep any of that code, because it never worked.

In all seriousness, thanks.

My original question was about “Tex-Edit Plus”, and this doesn’t work for TE+. However, since this works for TextEdit, it just might be enough for me.

Thanks again!

1 Like

I guess the other approach to logging might be to write Markdown (a bit lighter and quicker ?), and then convert it to HTML | RTF when required …

(with textutil, pandoc or Marked.app etc)

That’s a good idea, although for this purpose, speed doesn’t really matter. I’m just trying to get some real-time feedback of what’s going on. Basically, console.log() for when it’s being run from KM.

Hopefully I won’t need it much.

Dan, I'm still struggling myself, but I got this much to work, so maybe will be of some, if little, help:

I was able, after many, many false starts, to create a new document.
But, I'm stumped from there.

Have tried many things to get a word, or set its style, like Chris did in AppleScript, but just can't get there.

I assume you've looked at the SDEF for Tex-Edit Plus in Script Editor.
Lots of info, but almost no help on how to use. :frowning2:

Oh, and very disappointing, the Safari Debugger is of NO help, because it does NOT show the properties of any of the Tex-Edit Plus app, or object/elements created from it:

'use strict';

myTest();

function myTest() {
var app = Application.currentApplication()
app.includeStandardAdditions = true

var tepApp = Application("Tex-Edit Plus")

var docList = tepApp.documents()
var doc1 = docList[0]

// --- CREATE A NEW DOCUMENT (unsaved) ---
var newDoc = tepApp.Document({
  name: "My Test Doc.rtf",
  contents: "Now is the time"
  }).make()

//var newDoc = docList[0];
// ### I can't figure out how to get the text of a specific word or line

var word1 = newDoc.text.line[0].word[0]
return newDoc.name()    // but this works
//-->"My Test Doc.rtf"

}
1 Like

Compare that with the Script Debugger Inspector:

@DanThomas, since this thread has now included Tex-Edit Plus, I have added that to the Topic title, rather than create a new Topic.

###About Tex-Edit Plus

I'm not sure about the compatibility of the current version (4.10.3), but I thought all should be aware of a beta that specifically addresses El Capitan 10.11:

Tex-Edit Plus Beta for OS X

2 Likes

PS you have probably spotted this since, but just in case - I notice that somewhere above you were trying to reference a document by name using the pattern .documents(strName)

I guess you were probably looking for the array-like syntactic sugar version (square brackets) :

.documents[strName]

The underlying function is .byName(), and putting aside reference by id for the moment, we have essentially four routes to referencing a member of an object collection using the Automation library. (Snippet below)

(function () {
    'use strict';

    var te = Application('TextEdit'),
        docs = te.documents,
        names = docs.name(),
        strName = "LogFile.rtf",

        iIndex = names.indexOf(strName);


    if (iIndex !== -1) {

        // by Index using .at()

        var docByIndexAt = docs.at(iIndex),

            // by Index using array-like syntactic sugar

            docBySugaredIndex = docs[iIndex],

            // by Name using .byName()

            docByName = docs.byName(strName),

            // by Name using array-like syntactic sugar

            docBySugaredName = docs[strName];

        return [docByIndexAt, docBySugaredIndex, docByName,
                    docBySugaredName]
                .map(function (x) {
            return x.properties();
        })
    }
})();
1 Like

Here’s a few other things I figured out:

var tep = Application("Tex-Edit Plus");
tep.activate();

function createDocument(name, options) {
    options = options || {};
    options.name = name;
    return tep.Document(options).make();
}

function getDocumentByName(name) {
    var result = tep.documents.whose({ name: name });
    if (result && result.length > 0)
        return result[0];
    return null;
}

function execute() {
    var doc = createDocument("Test", {
        contents: "Hello World",
        bounds: {
            x: 2500,
            y: 50,
            width: 700,
            height: 500
        }
    });
    console.log(doc.name());
    var doc1 = getDocumentByName("Test");
    console.log(doc1.name());
}

Here’s how I figured out how to specify “bounds”:

console.log(JSON.stringify(doc.bounds()));

Try it and see what you get. This can help us figure out a lot of things.

Make it 5:

var results = tep.documents.whose({ name: name });

Which, by the way, can return more than one result.

1 Like

Yes, the (.whose | .where) function is probably the best part of the Automation interface, and always the fastest route to gathering values from a range of objects.

Not quite what I would call a reference to a member of an object collection, but a very good way to generate a shorter collection :slight_smile:

(i.e. once you have filtered down a collection to only those members matching the given properties, you still have to use one of the four basic routes to referencing the member you want, even if only one match is found)

You have probably noticed that (.whose | .where) filtering of a collection can also be done in sugared or desugared forms. The impact on performance either way seems very slight.

(function () {
    'use strict';

    var te = Application('TextEdit'),
        docs = te.documents,
        names = docs.name(),
        strName = "LogFile.rtf";


    // Direct .at(index) reference to object in collection
    function route1() {
        var iIndex = names.indexOf(strName);

        return iIndex !== -1 ? docs.at(iIndex)
            .path() : undefined;
    }

    // sugared .whose query to shorten collection
    function route2() {
        var refShort = docs.whose({
            name: strName
        });

        return (refShort.length > 0 ? refShort[0].path() : undefined);
    }


    // desugared .whose query to shorten collection
    function route3() {
        var refShort = docs.whose({
            _match: [ObjectSpecifier()
                .name, strName]
        });

        return (refShort.length > 0 ? refShort.at(0).path() : undefined);
    }


    var tmStart, tmFinish, t = 1000,
        varResult; // ADD ZEROS HERE...

    tmStart = new Date()
        .getTime();
    while (t--) {

        //varResult = route1();
        varResult = route3();
        //varResult = route3();

    }
    tmFinish = new Date()
        .getTime();
    return [tmFinish - tmStart, varResult];


})();

Thanks, Dan. That's very helpful.

FYI, I had to add execute() near the top to run:

var tep = Application("Tex-Edit Plus");
tep.activate();

execute()  //## Had to add this to get results

function createDocument(name, options) {
    options = options || {};
    options.name = name;
    return tep.Document(options).make();
}

// snip here

Also, when running on Yosemite 10.10.5, I got an error here:
(works fine on macOS 10.11.4)

function getDocumentByName(name) {
    var result = tep.documents.whose({ name: name });
    
    //-->Error -1700: Can't convert types. (next line)
    if (result && result.length > 0)
        return result[0];
    return null;
}

Have you been able to set the text style of anything, like Chris did in his AppleScript?


[quote="DanThomas, post:34, topic:4653"]
Here's how I figured out how to specify "bounds":

console.log(JSON.stringify(doc.bounds()));

Try it and see what you get. This can help us figure out a lot of things.
[/quote]

Thanks. great tip!

Well, this works:

doc.paragraphs[0].words[0].color = "red";

so I think it should be reasonably easy to go from there.

I believe if you want to read a property, you end it with parens because it's a function call. So:

doc.paragraphs[0].words[0].color();

And if you want to set it, you don't use parens, like I showed above.

So have at it!