AppleScript: exporting active text document to a file

714 views
Skip to first unread message

Max Horn

unread,
Feb 3, 2016, 1:01:57 PM2/3/16
to BBEdit Talk
Hi there,

I want to write a special kind of text filter: It acts only on the current selection, but to work, it requires the contents of the whole file. This means that I unfortunately cannot (as far as I know) use a regular text filter. So instead, I am trying to write an AppleScript. The idea is to save the content of the active text document into a temporary location (to make sure it also supports unsaved files), then call the script which performs the actual work, passing the path to the temporary file plus information on the current selection to that "worker script"

To my surprise, the hardest part seems to be to actually store a copy of the contents of the text document into a temporary file... All my attempts so far failed.

I tried using "save text document 1 to PATH", but that is not quite what I want -- I really want something more like "Save copy as..." or "Export to...".

I then tried the "export" command from the BBEdit suite, but that just gives an error, saying that text documents don't support export (BTW, is there *any* serious documentation on the BBEdit AppleScript suite? The BBEdit manual contains more or less zero useful information on this, as far as I can tell).

Then I tried to write the "contents of text document 1" to a file, like this:

on writeToFile(p, d)
    try
        set fd to open for access file p with write permission
        set eof of fd to 0
        write d to fd starting at 0 as «class utf8»
        close access fd
    on error
        close access file p
    end try
end writeToFile

tell application "BBEdit"
    set fileContents to contents of text document 1
    set fileName to name of text document 1
end tell

set tempFile to (path to temporary items as text) & fileName
writeToFile(tempFile, fileContents)



However, the result has LF (mac) line ends, instead of CR (unix) lineends as the original file.


Is there any way to do this without messing up the lineends?

Christopher Stone

unread,
Feb 4, 2016, 5:56:38 AM2/4/16
to BBEdit-Talk
On Feb 03, 2016, at 11:28, Max Horn <m...@quendi.de> wrote:
it requires the contents of the whole file. This means that I unfortunately cannot (as far as I know) use a regular text filter.
______________________________________________________________________

Hey Max,

You probably can use a regular text filter by putting your get-text AppleScript in with `osascript`.

The idea is to save the content of the active text document into a temporary location (to make sure it also supports unsaved files)

That doesn't make obvious sense, unless you're wanting to save so you can read it back from disk.

In the latter case you can do something like this:

-------------------------------------------------------------------------------------------
tell application "BBEdit"
  tell front text document
    set docName to its name
    set _text to its text
    set docSaved to on disk
  end tell
  if not docSaved then
    set tempFilePath to (path to temporary items as text) & docName
    save front document to tempFilePath
  end if
end tell

read file tempFilePath
-------------------------------------------------------------------------------------------

To my surprise, the hardest part seems to be to actually store a copy of the contents of the text document into a temporary file... All my attempts so far failed.

Simple. ( When you know AppleScript and have spent many hours scripting BBEdit. :)

-------------------------------------------------------------------------------------------
tell application "BBEdit"
  tell front text document
    set docName to its name
    set _text to its text
  end tell
  set tempFilePath to (path to temporary items as text) & docName
  set newDoc to make new document with properties {text:_text} initial save location tempFilePath
  save newDoc
end tell
-------------------------------------------------------------------------------------------

* I believe this correctly manages the line endings but have not thoroughly tested (particularly with non-Unix line endings).

Then I tried to write the "contents of text document 1" to a file, like this:

That not too hard either.

-------------------------------------------------------------------------------------------
tell application "BBEdit"
  tell front text document
    set docName to its name
    set _text to its text
  end tell
  # Convert carriage returns to linefeeds.
  set _text to replace "\\x{0D}" using "\\x{0A}" searchingString _text options {search mode:grep, case sensitive:false, starting at top:true}
  set tempFilePath to (path to temporary items as text) & docName
  writeUTF8File(_text, tempFilePath) of me
  open tempFilePath
end tell

on writeUTF8File(_text, _file)
  try
    if _file starts with "~/" then
      set _file to POSIX path of (path to home folder as text) & text 3 thru -1 of _file
    end if
    set fRef to open for access _file with write permission
    set eof of fRef to 0
    write _text to fRef as «class utf8»
    close access fRef
  on error e number n
    try
      close access fRef
    on error e number n
      error "Error in writeUTF8() handler!" & return & return & e
    end try
  end try
end writeUTF8File
-------------------------------------------------------------------------------------------

However, the result has LF (mac) line ends, instead of CR (unix) lineends as the original file.

Yes.  This is an idiosyncrasy of BBEdit that must be quite difficult to change (or Rich would have done so by now).

Within a document window (not the file on-disk) and within text gotten via AppleScript all EOL characters are represented by carriage returns.

Somehow BBEdit correctly translates these on-disk, but even in the Find Dialog \n and \r are synonymous.

I work around that in the script with this line:

# Convert carriage returns to linefeeds.
set _text to replace "\\x{0D}" using "\\x{0A}" searchingString _text options {search mode:grepcase 

So.  That should give you some ideas

BTW, is there *any* serious documentation on the BBEdit AppleScript suite?

No.

But if you search the group for me you'll find just a few examples.

--
Best Regards,
Chris

Max Horn

unread,
Feb 5, 2016, 10:38:12 AM2/5/16
to BBEdit Talk, listm...@suddenlink.net

Hi Christopher,

thank you for your help.

Am Donnerstag, 4. Februar 2016 11:56:38 UTC+1 schrieb Christopher Stone:
On Feb 03, 2016, at 11:28, Max Horn <m...@quendi.de> wrote:
it requires the contents of the whole file. This means that I unfortunately cannot (as far as I know) use a regular text filter.
______________________________________________________________________

Hey Max,

You probably can use a regular text filter by putting your get-text AppleScript in with `osascript`.

Note sure what you mean with that. Perhaps this is a misunderstanding? To clarify, I would prefer *not* to use AppleScript, almost any other language is preferable to me.

The reason I am not using a regular text filter is that to my understanding, a (non-AppleScript) text filter receives *only* the content of the current selection (if there is any), or else receives the whole text file, in either case on stdin. That doesn't work for me, though: While the text transformation I have implented is transforms only the currently selected text, to work correctly it *needs* to know the complete content of the file to work correctly.

If doing that is possible with a text filter in a way that does not require me to write any AppleScript, I'd love to know that.


The idea is to save the content of the active text document into a temporary location (to make sure it also supports unsaved files)

That doesn't make obvious sense, unless you're wanting to save so you can read it back from disk.

I want to store the content to a temporary file, so that my actual filter tool (written in Python, and invoking various command line tools) can process the full content of the file -- I can pipe the content of the temp file into the tool, and pass the selection start and length as command line parameters to the tool (and if there is no selection, I signal that by not passing in any command line parameters).

So, of course I want to read the file back from disk, I just don't want to do it from AppleScript, nor from BBEdit. I only use AppleScript as that seems to be the only chance of doing what I need -- but again, I'd love to learn of alternatives, if there are any.

[...]
 
To my surprise, the hardest part seems to be to actually store a copy of the contents of the text document into a temporary file... All my attempts so far failed.

Simple. ( When you know AppleScript and have spent many hours scripting BBEdit. :)

-------------------------------------------------------------------------------------------
tell application "BBEdit"
  tell front text document
    set docName to its name
    set _text to its text
  end tell
  set tempFilePath to (path to temporary items as text) & docName
  set newDoc to make new document with properties {text:_text} initial save location tempFilePath
  save newDoc
end tell
-------------------------------------------------------------------------------------------

That's educational, thank you -- the content of the temp file it generates is perfect, alas even adding a "close newDoc" in, one briefly sees a new window flash up in BBEdit, which is undesirable. Still, good to learn about this approach.
 
 [...]
 
I work around that in the script with this line:

# Convert carriage returns to linefeeds.
set _text to replace "\\x{0D}" using "\\x{0A}" searchingString _text options {search mode:grepcase 

So.  That should give you some ideas

Yes, thank you.

However, the key result seem to be this: "It is not really possible to do this, but you can workaround the line ends problem by converting them manually" -- which of course I already could do by adding a conversion step to my python script. I just thought it was natural for the scripting interface of a text editor to allow a verbatim export of the content of a text document to a temp file, and that I must be missing something for not finding it. Seems I didn't really miss anything :)

Anyway, so knowing that, I'll just accept that I have to do a line ends conversion, no biggie.


Cheers,
Max

Christopher Stone

unread,
Feb 5, 2016, 9:35:01 PM2/5/16
to BBEdit-Talk
On Feb 05, 2016, at 09:24, Max Horn <m...@quendi.de> wrote:
You probably can use a regular text filter by putting your get-text AppleScript in with `osascript`.

Note sure what you mean with that. Perhaps this is a misunderstanding? To clarify, I would prefer *not* to use AppleScript, almost any other language is preferable to me.
______________________________________________________________________

Hey Max,

If you want to get the full text of an unsaved document you have no choice but to use AppleScript.

If your document was always saved you can read if from disk using the environment variable BB_DOC_PATH.

(Search for “runtime environment variables” in the user manual.)

… While the text transformation I have implented is transforms only the currently selected text, to work correctly it *needs* to know the complete content of the file to work correctly.

You can do this in your Python script by shelling out to `osascript`:

-------------------------------------------------------------------------------------------
#! /usr/bin/env bash

read -r -d '' theAppleScript <<'EOF'
    tell application "BBEdit" to tell text document 1 to return its contents
EOF

osascript -e "$theAppleScript";
-------------------------------------------------------------------------------------------

You can boil that down to this:

-------------------------------------------------------------------------------------------
#! /usr/bin/env bash
osascript -e 'tell application "BBEdit" to tell text document 1 to return its contents';
-------------------------------------------------------------------------------------------

I prefer to use the first form, so I don't have to monkey with quoting.

It looks like there's a module for Python to run AppleScript:


I want to store the content to a temporary file, so that my actual filter tool (written in Python, and invoking various command line tools) can process the full content of the file …

I'd read the contents into a variable and operate on it sans file, unless the file was quite large – or there were other advantages to having a temp file.

So.  You can do what you want by including 1 line of AppleScript in your script.

--
Best Regards,
Chris

Reply all
Reply to author
Forward
0 new messages