Panic Inc.

Coda 2

Creating Coda Plug-ins

If you've ever wished Coda could do even more than it does already, you're at the right place.

Coda 1.6 and later support Coda Plug-ins, which allow you to further extend its text editing functionality — even if you can't program Cocoa.

Plug-ins have access to the text of the current document and can perform modifications on it as desired. For example, you could write a plug-in to insert the current date and time into your document, change the case of selected text, run code through a custom validator, or even wrap code with a special tag.

Plug-ins appear in Coda's menu when installed, and can be bound to specific keyboard shortcuts.

There are two ways to write a plug-in. The first way is to write a script in any common scripting language, then use the separate Coda Plug-In Creator application to bundle your script into a Coda plug-in. The second (more complex) way is to write directly to the Cocoa Plug-In API.

Read on for full details!

Coda 2 Plug-In Creator.zip
3.1 MB
Requires Mac OS X 10.6 or later.
Samples & Source Code
565 KB

Using the Coda Plug-in Creator

plugincreator

For plug-in authors who are not familiar with Cocoa, but wish to write a simple text-manipulation plug-in for Coda in a high-level scripting language, we provide the Coda Plug-in Creator. This tool takes your high-level script code and wraps it within a native Cocoa bundle that Coda will accept.

If you're familiar with "commands" in TextMate bundles, you'll feel right at home with Coda script plug-ins.

Your script can be written in any language for which you have installed a command line interpreter, including Perl, Ruby, shell scripting languages, among others.

Commands and submenus

A single plug-in can provide one or more commands, each with its own script. Additionally, commands can be grouped into submenus. Use the "Add Command" and "Add Submenu" toolbar items to customize the way you'd like your plug-in to appear in Coda's Plug-ins menu.

Choosing a script file

To embed an existing script into the plug-in, you can drag it from the Finder to the Script field. Alternatively, click Create to start a new script.

Choosing an interpreter

Your script should provide a "bang path" to its interpreter on its first line, such as:

#!/usr/bin/perl

Performing actions before and after your script runs

You can optionally request that Coda save either the current document, or all unsaved documents before your script is run, or on successful completion of your script by selecting the appropriate items from the "On Run" and "On Success" popups.

Input types

Your script-based plug-in can request to receive different parts of the document as input. Use the STDIN popup to choose which type of input you'd like your plug-in to receive. The input will be delivered to the script on standard input (stdin). If you choose "Selection" as your input type, you should also provide a fallback input type using the "If empty" popup that will be sent instead if there is no selection when your plug-in is invoked.

None
Your plug-in doesn't require any input.
Selection
The script will receive the currently selected text. If there is no selection, the "If empty" input type will be sent instead.
Line
The script will receive the entire line that the insertion point is on.
Character
The script will receive the character immediately to the right of the insertion point.
Word
The script will receive the word that the insertion point is within.
Document
The entire document will be sent to the script.

Output types

Your plug-in may generate some sort of output that you want the user to see. Use the STDOUT popup to indicate what should be done with the output that your plug-in generates. The output from your plug-in should be placed on standard output (stdout). Choose from one of the following output types:

After Selection
Your plug-in's output will be inserted into the editor immediately after the current selection. If there is no selection, the text will be inserted at the insertion point.
Discard
Coda will ignore any output your plug-in generates.
New Document (Coda 1.6.1 and later)
Your plug-in's output will be displayed in a new editor tab within Coda.
Replace Document (Coda 1.6.1 and later)
Your plug-in's output will replace the entire text of the current editor.
Replace Selection
The selected text in the editor will be replaced by your plug-in's output. If there is no selection, the text will be inserted at the insertion point.
Show as HTML
Your plug-in's output will be displayed as rendered HTML in a new Preview tab within Coda.

If you choose "Replace Selection", "After Selection", "Replace Document", or "New Document", you have the option of positioning the editor's insertion point within the text that your plug-in generates. If your plug-in's output contains the string $$IP$$ and you check "Set insertion point to $$IP$$", then Coda will place the insertion point at that position after inserting the rest of your output text.

Support files

If your script requires additional external files, you can add them to the "Support Files" list, and they will be included in the final plug-in bundle when you save it.

Environment variables passed to your script

Your script will receive the following information as part of its execution environment. For the convenience of script authors, the same environment variables are available with either a CODA_ or TM_ prefix.

CODA_BUNDLE_PATH
The path to your plug-in bundle.
CODA_BUNDLE_SUPPORT
The path to the Support directory within your plug-in bundle. (if you choose to create one).
CODA_FILEPATH
The full path to the file being edited, including the filename. May be empty for unsaved documents.
CODA_LINE_ENDING
The line ending character used in the source document. This will be a string containing one of: CR, LF, CR+LF, U+2028, or U+2029. The string contains the literal line ending -- for example, an actual LF character; not the letters "L" and "F".
CODA_LINE_INDEX
The character index of the selection, relative to the beginning of the line.
CODA_LINE_NUMBER
The current line number.
CODA_SELECTED_TEXT
The document's selected text, if any.
CODA_SITE_LOCAL_PATH
The full local path for the Site associated with the document. May be empty if no Site is active, or the Site's local path hasn't been set.
HOME
The path to the current user's home directory.

Importing TextMate Commands

The Coda Plug-In Creator can also import commands from a TextMate bundle. Choose File > Import Commmands... then select the TextMate bundle you'd like to import commands from. We've tried hard to provide compatibility for most TextMate commands so they'll run without modification after import, but it's possible you may have to make slight modifications to the resulting script before it will work with Coda.

Setting a keyboard shortcut

To set a keyboard shortcut for your command, double-click in the shortcut column next to the command. When the edit field appears, press the key combination you'd like to assign to the command. If the shortcut you choose is already used elsewhere in your plug-in (or in Coda proper) a small warning triangle icon will appear.

Building your plug-in

Once you have configured your settings, the Coda Plug-in Creator will save a .codaplugin bundle that you can double-click to install. If your plug-in fails to load, or does not work as expected, check the Console application for possible error messages.

Using the Cocoa plug-in API

If you can program Cocoa, you can write plug-ins that work directly and natively within Coda. They execute a bit faster, and can offer additional functionality such as opening windows or presenting a user interface.

Coda plug-ins are Cocoa bundles with a .codaplugin filename extension.

Coda looks for plug-ins recursively in the Home > Library > Application Support > Coda > Plug-ins folder. Any plug-ins found will be loaded, and one instance of the plug-in's principal class created.

If your plug-in's Info.plist contains the CodaPlugInSupportedAPIVersion key with a value of 6 or higher, your instance will be sent the following message:

- (id)initWithPlugInController:(CodaPlugInsController*)aController plugInBundle:(NSObject <CodaPlugInBundle>*)plugInBundle;

Note: this may be called on a secondary thread. Please ensure this method is thread-safe.

This allows Coda to load your plug-in more quickly on versions 2.0.1 and higher.

Otherwise, it will be sent the following message (which is deprecated as of API version 6):

- (id)initWithPlugInController:(CodaPlugInsController*)inController bundle:(NSBundle*)yourBundle;

CodaPlugInsController provides the means for the plug-in to communicate with Coda itself. You must also implement the following method in your plug-in:

- (NSString*)name;

This method should simply return the desired display name of your plug-in. Keep it short, because this may be used to generate user interface elements, such as menu items.

The CodaPlugInsController class

CodaPlugInsController will respond to the following messages from your plug-in:

- (int)apiVersion;
apiVersion returns one of the following API versions:
Coda VersionAPI Version
Coda 1.62
Coda 1.6.13
Coda 1.6.34
Coda 1.6.85
Coda 2.05
Coda 2.0.16
- (NSString*)codaVersion:(id)sender;
codaVersion returns the version of Coda that is hosting the plug-in as a string, such as "1.6.1". It may be easier to use the apiVersion method to determine API availability, as it returns a numerical value.
- (void)displayHTMLString:(NSString*)html;
displayHTMLString creates a new preview tab in Coda and displays the rendered version of the passed HTML. This is useful for displaying plug-in output for plug-ins that do not modify the editor text.
- (void)displayHTMLString:(NSString*)html baseURL:(NSURL*)baseURL;
(Coda 2.0 and above only)
displayHTMLString:baseURL: creates a new preview tab in Coda and displays the rendered version of the passed HTML, relative to the specified base URL. This is useful for displaying plug-in output for plug-ins that do not modify the editor text.
- (CodaTextView*)focusedTextView:(id)sender;
focusedTextView returns to the plug-in an abstract object representing the editor text view in Coda that currently has focus.
The text view returned is auto-released, so the caller does not need to explicitly release it.
- (CodaTextView*)makeUntitledDocument;
makeUntitledDocument creates a new document in the frontmost Coda window and returns the CodaTextView associated with it.
The text view returned is auto-released, so the caller does not need to explicitly release it.
- (CodaTextView*)openFileAtPath:(NSString*)path error:(NSError**)error;
(Coda 2.0 and above only) openFileAtPath:error: opens the text file at path, returning a CodaTextView if successful.
- (void)registerActionWithTitle:(NSString*)title target:(id)target selector:(SEL)selector;
registerActionWithTitle:target:selector: exposes to the user a plug-in action (a menu item) with the given title, that will perform the given selector on the target.
- (void)registerActionWithTitle:(NSString*)title underSubmenuWithTitle:(NSString*)submenuTitle target:(id)target selector:(SEL)selector representedObject:(id)repOb keyEquivalent:(NSString*)keyEquivalent;
This is equivalent to the previous registerActionWithTitle: method but accepts additional arguments to allow creation of submenu items in a simple two-level hierarchy. It also allows you to set the representedObject and keyEquivalent of the created menu item.
keyEquivalent is a string that uses shorthand for qualifier keys: $ represents the shift key, ~ represents the option key, @ represents the command (Apple) key, and ^ represents the control key. So, ^@C for example represents control-command-C.
- (void)saveAll;
saveAll causes the frontmost Coda window to save all modified documents.

The CodaTextView class

Send the focusedTextView message to CodaPlugInsController to get a CodaTextView object representing the user's current text editor. Note that this object is only a proxy for the real thing — it is not a subclass of NSTextView.

The following methods are available in API version 2 (Coda 1.6) or later, except where noted:

- (void)beginUndoGrouping;
If you would like to perform a series of text manipulation operations that should all be considered one "undo" action, send beginUndoGrouping, perform the actions, then send endUndoGrouping.
- (NSString*)currentLine;
currentLine returns a string containing the entire content of the line that the insertion point is on.
- (unsigned int)currentLineNumber;
currentLineNumber returns the line number that the insertion point is on.
- (NSRange)currentWordRange;
currentWordRange returns the range of the word that contains the insertion point. Requires API version 3.
- (void)deleteSelection;
deleteSelection deletes the currently selected text range.
- (NSStringEncoding)encoding;
Returns the string encoding of the document. Requires API version 5.
- (void)endUndoGrouping;
If you would like to perform a series of text manipulation operations that should all be considered one "undo" action, send beginUndoGrouping, perform the actions, then send endUndoGrouping.
- (void)goToLine:(NSInteger)line column:(NSInteger)column;
(Coda 2.0 and later only)
Moves insertion point to specified line and column, scrolling them to be visible if needed.
- (void)insertText:(NSString*)inText;
insertText: inserts the given string at the insertion point.
- (NSString*)lineEnding;
lineEnding returns the line ending character(s) used in the document.
- (NSString*)path;
path returns the filesystem path to the document displayed in the text view. Returns nil for unsaved documents.
- (NSRange)previousWordRange;
previousWordRange returns the character range of the word immediately before the insertion point.
- (NSRange)rangeOfCurrentLine;
rangeOfCurrentLine returns the character range of the entire line the insertion point is on.
- (NSString*)remoteURL;
(Coda 2.0 and later only)
Returns the Remote URL for the file given the site configuration.
- (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString*)aString;
replaceCharactersInRange:withString: replaces characters in the given range with the given string.
- (void)save;
save saves any changes to the document.
- (BOOL)saveToPath:(NSString*)aPath;
saveToPath: saves the document to the specified local filesystem path, returning YES if successful.
- (NSRange)selectedRange;
selectedRange returns the range of currently selected characters. This can be an empty range.
- (NSString*)selectedText;
selectedText returns the currently selected text, or nil if none.
- (void)setSelectedRange:(NSRange)range;
setSelectedRange: selects the given character range.
- (NSString*)siteLocalPath;
siteLocalPath returns the local root path of the active site if it has one, nil if unspecified or if no site is active.
- (NSString*)siteLocalURL;
siteLocalURL returns the local URL of the currently active site (if specified). Nil otherwise, or if there is no site active. Requires API version 4.
- (NSString*)siteNickname;
siteNickname returns the nickname of the currently active site. Nil if there is no site active. Requires API version 4.
- (NSString*)siteRemotePath;
siteRemotePath returns the remote root path of the currently active site. Nil otherwise, or if there is no site active. Requires API version 4.
- (NSString*)siteURL;
siteURL returns the remote URL of the currently active site. Nil otherwise, or if there is no site active. Requires API version 4.
- (unsigned int)startOfLine;
startOfLine returns the character index (relative to the beginning of the document) of the start of the line the insertion point is on.
- (NSString*)string;
string returns the entire document as a plain string.
- (NSString*)stringWithRange:(NSRange)range;
stringWithRange: returns the specified substring of the entire document.
- (int)tabWidth;
tabWidth returns the width of tabs as a number of spaces.
- (void)textViewDidFocus:(CodaTextView*)textView;
(Coda 2.0 and later only)
Called when a text view is focused/opened. NOTE: This method is time sensitive and should return quickly.
- (void)textViewWillSave:(CodaTextView*)textView;
(Coda 2.0 and later only)
Called before the text view will be saved to disk.
- (BOOL)usesTabs;
usesTabs returns YES if the document uses tab characters for indentation, NO if it uses spaces.
- (NSString*)willPublishFileAtPath:(NSString*)inputPath;
(Coda 2.0 and later only)
WARNING: this method will be called on a non-main thread.
This delegate method gives the plugin a chance to modify files just before publishing. Return the path to the file to publish.
- (NSWindow*)window;
window returns a pointer to the window containing this text view. We strongly recommend only using this to display sheets or other basic UI, and only if your plug-in absolutely requires it.

Info.plist values for Coda plug-ins

To specify that your plug-in requires a certain minimum version of the Coda plug-in API, add the CodaPlugInMinimumAPIVersion key to your plug-in's Info.plist. The value of this key is an integer corresponding to the values returned by the -apiVersion method.

If you do not specify a minimum version, Coda will assume your plug-in is compliant with the API in the running version of Coda. If you call API that doesn't exist in the running version of Coda, an exception will be thrown.

This key is supported by Coda 1.6.1 and later.

The CodaPlugInSupportedAPIVersion key tells Coda which version of the API your plug-in is aware of. This affects which messages your plug-in will receive.

This key is recognized by Coda 2.0.1 and later. If set to 6 or higher, and you implement the API 6 init method, your plug-in will be loaded more quickly by Coda 2.0.1 and later.