ShellActions.sugar enables you to add custom text and file actions to Espresso written in any language that can run as a shell script (bash, sh, ruby, python, etc.). Note that ShellActions.sugar does not add any new actions when you install it; it merely enables you to create custom actions in your language of choice. Some third-party Sugars (known examples listed below) may require it to run.
Requires Espresso 2.1
- Download ShellActions.sugar
- Unzip the downloaded file (if your browser doesn't do it for you)
- Double click the ShellActions.sugar file to install it
- HTMLBundle.sugar
- Kaleidoscope.sugar
- ConTeXT.sugar
- Validatorian.sugar
- BOM-B-Gone.sugar
- Get-Info.sugar
There are two parts to writing a custom shell script to add functionality to Espresso:
- The XML action definition
- The shell script itself
You can, of course, use any of the elements available in Action XML definitions, but these are the ones that are specific to shell actions:
<action name="action.id" category="category.id">
<!--REQUIRED-->
<class>OCShellAction</class>
<setup>
<!--REQUIRED-->
<script>my_script.py</script>
<!--Available for all types of actions; default values shown-->
<!--These say if your script can handle these scenarios or not-->
<multiple-selections>true</multiple-selections>
<single-selection>true</single-selection>
<empty-selection>true</empty-selection>
<!--Only set this to `false` if you want to be slapped in the face with errors-->
<suppress-errors>true</suppress-errors>
<!--If suppress-errors is `true`, you can redirect errors where you wish-->
<error-output>log</error-output>
<!--Optional plist dictionary for passing config variables to shell scripts (see below)-->
<config>
<dict>
<key>custom-key</key>
<string>custom value</string>
</dict>
</config>
<!--Applicable mainly for TextActions; default values shown-->
<input>selection</input>
<alternate>document</alternate>
<output>input</output>
<output-format>text</output-format>
</setup>
</action>
<script>
: your script's filename. Scripts must be stored in your Sugar's root-level Scripts folder- selection elements: these all default to true. Set one to
false
to disable this action in that scenario. For instance, if you include<empty-selection>false</empty-selection>
your action will be disabled unless there is at least one selection. <input>
: what will be passed to STDIN. Accepts:- selection (default)
- document
- nothing (important: if your script does not read from STDIN, you must choose this to prevent Espresso from locking up if the user runs your script on a very large document or selection)
<alternate>
: if your input isselection
, this will be used as the fallback if there is no selection. Accepts:- document
- line (the line around the cursor)
- word (the word around the cursor)
- character (the character immediately to the left of the cursor)
<output>
: what your script will output. Accepts:- input (default): STDOUT will replace the input
- document: STDOUT will replace the document
- range: STDOUT will represent one or more ranges to select. Formatting for ranges is
index,length
. So if you wanted to select the first ten characters of the document you would output0,10
. You can select multiple ranges by separating them with a linebreak or&
character:0,10&12,5
- tooltip: STDOUT will be displayed in a tooltip anchored to the selection (maximum 250 characters)
- log: STDOUT will be output straight to Console.app
- html: STDOUT will be rendered as HTML in a new window. Any relative URLs will resolve using EDITOR_SUGAR_PATH as the base URL (so you can store shared CSS or images in your Sugar). Any links clicked will open in the user's default browser, although unadorned anchor links will work to navigate within the page (for instance,
<a href="#top">To top</a>
will not open a browser). - console: STDOUT will be displayed as plain text in a new window
- nothing: STDOUT will be ignored
<output-format>
: only necessary if usinginput
ordocument
for your output. Specifies the format you are outputting:- text (default): contents of STDOUT will be inserted as plain text
- snippet: contents of STDOUT will be inserte as a CETextSnippet. Note: if the user has multiple selections, your output will be automatically aggregated into a single snippet and overwrite the whole range (leaving text between the existing selections alone). Make good use of the EDITOR_SELECTIONS_TOTAL and EDITOR_SELECTION_NUMBER environment variables to manage your tab stops!
<suppress-errors>
: if you set this asfalse
, any errors that occur will open a dialog in Espresso. Only useful for debugging, typically.<error-output>
: where STDERR should be output if<suppress-errors>
istrue
. Accepts:- log (default): STDOUT will be output to Console.app
- console: STDERR will be displayed as plain text in a new window
- html: STDERR will be rendered as HTML in a new window (same behavior as
<output>
) - sheet: STDERR will be output in a sheet attached to the current window
<config>
: using plist dictionary formatting, you can specify custom configuration variables to pass to your shell scripts using this element. These variables will be available as environment variables with the prefix "CONFIG_"
Note that FileActions ignore <input>
, <alternate>
, and <output-format>
, and they only accept "nothing" (default), "log", "html", or "console" for <output>
.
You may write your shell script in whatever language you prefer. Regardless of language:
- You must use a shebang to specify to the system how to execute the file
- STDIN will be whatever you requested as input
- STDOUT will be whatever your script needs to output
- Anything written to STDERR will result in an error (by default just logged to Console.app, but you can modify this behavior)
- TextActions will be executed once for every selection
- FileActions will be executed a single time, and receive a linebreak-delimited list of selected files via STDIN
- Neither STDIN nor any environment variable is ever escaped for use on the shell! So be careful if you are working with bash/sh/etc. as anything in STDIN could potentially have a space, quotation mark, or other character with special meaning
- Environment variables that for whatever reason do not have any contents for this particular action will be empty strings, but they will still exist
- If your Sugar contains a
ScriptLibraries
folder, it will automatically be included in the $PATH, and Python or Ruby scripts should be able to lookup modules there usingimport
andrequire
, respectively
The following environment variables are available to all scripts:
- EDITOR_SUGAR_PATH: the path to the root of the action's Sugar
- EDITOR_DIRECTORY_PATH: the path to the most specific possible context directory
- EDITOR_PROJECT_PATH: the path to the root project folder
- EDITOR_PATH: the path to the active file (only available in FileActions if there is only a single file)
- EDITOR_FILENAME: the filename of the active file (only available if EDITOR_PATH is set)
Path variables pointing to directories will not include a trailing slash.
The following variables are only available in TextActions:
- EDITOR_CURRENT_WORD: the word around the cursor (or first index of the selection)
- EDITOR_CURRENT_LINE: the line around the cursor (or first index of the selection)
- EDITOR_LINE_INDEX: the zero-based index where the cursor falls in the line (or first index of the selection)
- EDITOR_LINE_NUMBER: the number of the line around the cursor (or first index of the selection)
- EDITOR_TAB_STRING: the string inserted when the user hits tab
- EDITOR_LINE_ENDING_STRING: the string inserted when the user hits enter
- EDITOR_ROOT_ZONE: textual ID of the root syntax zone
- EDITOR_ACTIVE_ZONE: textual ID of the active syntax zone
- EDITOR_SELECTIONS_TOTAL: the total number of selections in the document
- EDITOR_SELECTION_NUMBER: the number of the selection currently being processed
- EDITOR_SELECTION_RANGE: the range of the selected text in the document; uses the same formatting as the "range" output (index,length). So if the first ten characters are selected, this will be "0,10" (without the quotes, of course)
If your XML includes a <config>
element, then its keys will be available as environment variables with the prefix "CONFIG_". For instance, for the example code above you would be able to access an environment variable named CONFIG_custom-key with the value "custom value".
How to access environment variables and standard input/output will vary depending on the language you are using. Here's two crowd favorites:
Python
#!/usr/bin/env python
import sys, os
sugarPath = os.environ['EDITOR_SUGAR_PATH']
input = sys.stdin.read()
# Could alternately use print, but it appends a linebreak
sys.stdout.write('custom output')
Ruby
#!/usr/bin/env ruby
sugarPath = ENV['EDITOR_SUGAR_PATH']
input = STDIN.read
print 'custom output'
Have fun!
1.2
- Support for displaying errors to the user using STDERR and
<error-output>
- Support for passing configuration variables to shell scripts from XML
1.1:
- New experimental tooltip output option when working with TextActions
- EDITOR_LINE_INDEX now properly reports the correct index (was previously off by one)
- Many thanks to Matt Gemmel and Jerry Krinock for their excellent open source code!
1.0:
- Initial release
- OCShellAction class supports both TextActions and FileActions
- Output text or snippets, select text, log output directly to the Console, or output rendered HTML or plain text
- Process anything from the selection up to the whole document via STDIN
- Access extra information in special environment variables
MAAttachedWindow (c) Matt Gemmel under a custom license: http://mattgemmell.com/license/ NS(Attributed)String+Geometrics (c) Jerry Krinock, released as open source
Copyright (c) 2012-2014 Ian Beck
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.