# Macro API

## The Komodo Macro API

Macros in Komodo can be written in JavaScript or Python. The Macro API for JavaScript is a subset of the Komodo JavaScript API. Python macros have access to the XPCOM interface. The `komodo.` namespace for macro functions is deprecated, but currently still available for supporting older macros and providing functions in Python for which there are not direct equivalents in XPCOM.

The interfaces for the two macro languages are described separately:

- JavaScript Macros
- Python Macros

### Warning

The macro system is a powerful mechanism by which Komodo users can execute arbitrary code inside the Komodo process. It is easy for novices to write macros that can significantly disrupt Komodo's behavior, leading to instability and data loss. Avoid experimenting with macros while working with important files.

Of particular note:

- Macros that never terminate (for example, due to infinite loops) can hang Komodo.
- Macros that modify the buffer should never be run in the background, as multiple threads accessing the editor object could cause crashes.
- Macros that modify the scimoz object should be written with care, to avoid data loss.

### Feedback

The Komodo JavaScript API is under active development. If you have questions or feedback about the API, please post to the Komodo Macros and Extensions forum.

The API described below may change in future releases (we will of course try to minimize backwards-incompatible changes).

## JavaScript Macros

For macros, the most important parts of the Komodo JavaScript API are:

- The `ko.views.manager.currentView` object which contains:
  - The `scimoz` object for manipulation of code buffers.
  - The `koDoc` object for manipulation of documents in memory.
  - The `koDoc.file` object, corresponding to files on disk.
- The `ko.commands.doCommand(...)` function to execute Komodo commands.
- The `ko.open.URI(...)` function to open a local or remote file using a URI.
- The `ko.projects.findPart(...)` function to find other components (snippets, run commands, other macros, etc).
- The `ko.interpolate` object which contains:
  - The `interpolateString(...)` function for evaluation of interpolation codes
  - The `getWordUnderCursor()` function to retrieve the word under the editing cursor.
- The `ko.run.runEncodedCommand(...)` function to open a local or remote file using a URI.

### ko.views.manager.currentView.scimoz

The `ko.views.manager.currentView.scimoz` object corresponds to the main text editing widget that contains and manipulates files in the Editor Pane. It is a thin wrapper around the Scintilla widget, an open-source component written by Neil Hodgson (www.scintilla.org).

The Scintilla API is large, complex and subject to change. This document only contains the calls most relevant to Komodo, and notes some common patterns of use relevant to changing the editor widget.

#### scimoz Attributes

int currentPos
  The location (in character units) of the caret.
int anchor
  The location (in character units) of the selection anchor.
string text
  The contents of the buffer.
string selText
  The contents of the selection (readonly).
long scrollWidth
  The width of the scroll area (in pixels).
long xOffset
  The horizontal scroll position (in pixels) of the start of the text view.
boolean viewEOL
  Whether to show end-of-line markers or not.
long viewWS
  Whether to show whitespace characters (0: no, 1: yes).
long eOLMode
  The characters that are inserted when the user presses 'Enter': either 'CRLF' (0 - the default on Windows), 'CR' (1) or 'LF' (2 - the default on Linux).
long tabWidth
  The size of a tab as a multiple of the size of a space character.
long indent
  The size of indentation in terms of the width of a space.
boolean useTabs
  Whether indentation should be created out of a mixture of tabs and spaces (1) or be based purely on spaces (0).
boolean indentationGuides
  Whether to show indentation guides or not.
readonly long firstVisibleLine
  The line number of the first visible line in the text view.
long lineCount
  The number of lines in the text view.
long textLength
  The length of the current buffer in characters.
long targetStart
  The start of the target region; see replaceTarget.
long targetEnd
  The end of the target region; see replaceTarget.
long linesOnScreen
  The number of complete lines visible on the screen.

#### scimoz Methods

void emptyUndoBuffer()
  Empty the undo buffer.
void undo()
  Undo the last action.
void cut()
  Cut current selection (`ko.commands.doCommand('cmdCut')` is the preferred method).
void copy()
  Copy current current selection.
void paste()
  Replace current selection with the clipboard contents.
void clear()
  Clear current selection.
long replaceTarget(in long length, in string text)
  Replace the target text with the argument text. Text is counted so it can contain NULs. Returns the length of the replacement text..
string getTextRange(in long min, in long max)
  Return a range of characters from the current buffer.
void insertText(in long pos, in string text)
  Insert text at a specified position.
void colourise(in long start, in long end)
  Force the re-coloring of the specified range.
wchar getWCharAt(in long pos)
  Get the (Unicode) character at the specified position.
void addText(in long length, in string text)
  Add text to the end of the current buffer.
void selectAll()
  Select the entire buffer.
void gotoLine(in long line)
  Jump to the specified line.
void gotoPos(in long pos)
  Jump to the specified position in the buffer.
void deleteBack()
  Delete the character to the left of the cursor.
void newLine()
  Add a newline (note: this is a less 'smart' newline than can be obtained using `ko.commands.doCommand('cmd_newlineExtra')`.
void redo()
  Redo the last action.
boolean canRedo()
  There is an action that can be redone.
void beginUndoAction()
  Begin an undo block (see note).
void endUndoAction()
  End an undo block (see note).
long getColumn(in long pos)
  Get the column (0-based) of the specified position.
long getLineEndPosition(in long line)
  Get the position corresponding to the last character on the specified line (not including EOL characters).
void setSel(in long start, in long end)
  Make selection start at `start` and end at `end`.
long lineFromPosition(in long pos)
  Get the line number (0-indexed) from character position `pos`.
long positionFromLine(in long line)
  Get character position which begins the specified line.
void lineScroll(in long columns, in long lines)
  This will attempt to scroll the display by the number of columns and lines that you specify. Positive line values increase the line number at the top of the screen (i.e. they move the text upwards as far as the user is concerned). Negative line values do the reverse.
void scrollCaret()
  If the current position (this is the caret if there is no selection) is not visible, the view is scrolled to make it visible.
long lineLength(in long line)
  Return the length of the current line.
void replaceSel(string)
  Replace current selection with the text in the string.
void lineDown()
  Move cursor down one line.
void lineDownExtend()
  Extend selection down one line.
void lineUp()
  Move cursor up one line.
void lineUpExtend()
  Extend selection up one line.
void charLeft()
  Move cursor one character to the left.
void charLeftExtend()
  Extend selection one character to the left.
void charRight()
  Move cursor one character to the right.
void charRightExtend()
  Extend selection one character to the right.
void wordLeft()
  Move cursor one word to the left.
void wordLeftExtend()
  Extend selection one word to the left.
void wordRight()
  Move cursor one word to the right.
void wordRightExtend()
  Extend selection one word to the right.
void home()
  Move cursor to the Home position.
void homeExtend()
  Extend selection to the Home position.
void lineEnd()
  Move cursor to the end of the line.
void lineEndExtend()
  Extend selection to the end of the line.
void documentStart()
  Move cursor to the start of the document.
void documentStartExtend()
  Extend selection to the start of the document.
void documentEnd()
  Move cursor to the end of the document.
void documentEndExtend()
  Extend selection to the end of the document.
void pageUp()
  Page up.
void pageUpExtend()
  Extend selection one page up.
void pageDown()
  Page down.
void pageDownExtend()
  Extend selection one page down.
void editToggleOvertype()
  Toggle overtype mode.
void vCHome()
  Move cursor to the first non-whitespace character on a line or, if none, the beginning of a line.
void vCHomeExtend()
  Extend the selection to the first non-whitespace character on a line or, if none, the beginning of a line.
void zoomIn()
  Increase font size.
void zoomOut()
  Decrease font size.
void delWordLeft()
  Delete word to the left of the cursor.
void delWordRight()
  Delete word to the right of the cursor.
void lineCopy()
  Copy line at the cursor.
void lineCut()
  Cut line at the cursor.
void lineDelete()
  Delete line at the cursor.
void lineTranspose()
  Transpose current line and previous line.
void lineDuplicate()
  Duplicate current line.
void lowerCase()
  Convert selection to lower case.
void upperCase()
  Convert selection to upper case.
void lineScrollDown()
  Scroll display down one line.
void lineScrollUp()
  Scroll display up one line.
void deleteBackNotLine()
  Delete last character except if at beginning of line.
void homeDisplay()
  Move cursor to Home position for the current display line (as opposed to the buffer line when word wrap is enabled).
void homeDisplayExtend()
  Extend selection to the Home position for the current display line (as opposed to the buffer line when word wrap is enabled).
void lineEndDisplay()
  Move cursor to end of the current display line (as opposed to the buffer line when word wrap is enabled).
void lineEndDisplayExtend()
  Extend selection to the end of the current display line (as opposed to the buffer line when word wrap is enabled).
void wordPartLeft()
  Move cursor a word segment to the left. Word segments are marked by capitalization (aCamelCaseIdentifier) or underscores (an_under_bar_ident).
void wordPartLeftExtend()
  Extend selection a word segment (as described in void wordPartLeft()) to the left.
void wordPartRight()
  Move cursor a word segment (as described in void wordPartLeft()) to the right.
void wordPartRightExtend()
  Extend selection a word segment (as described in void wordPartLeft()) to the right.
void delLineLeft()
  Delete to beginning of line.
void delLineRight()
  Delete to end of line.
void paraDown()
  Move cursor one paragraph down.
void paraDownExtend()
  Extend selection one paragraph down.
void paraUp()
  Move cursor one paragraph up.
void paraUpExtend()
  Extend selection one paragraph up.

#### scimoz Notes

Invalid Parameters: The Scintilla API assumes that users of the API do their own error-checking. Passing arguments that are out of bounds or otherwise erroneous can result in Komodo crashing.

The Undo Stack: Scintilla manages the "undo" stack. To treat a sequence of operations as a single operation for the sake of Undo/Redo, wrap these operations in a `beginUndoAction` / `endUndoAction` pair. The `endUndoAction` must be called even in the case of an exception in the code. Otherwise, the undo stack will be corrupted and might lose data.

For example, for JavaScript:

    ko.views.manager.currentView.scimoz.beginUndoAction()
    try {
        ...  // do your sequence here
    } finally {
        ko.views.manager.currentView.scimoz.endUndoAction()
    }

### ko.views.manager.currentView.koDoc

The `ko.views.manager.currentView.koDoc` object refers to the current document; the contents of the file being edited. These contents will be different than those of the file on disk if the file is unsaved or "dirty".

`koDoc` replaces the `document` object present in Komodo 5 and previous versions. If you need to write macros compatible with earlier versions, use capability-detection style code such as:

JavaScript

    var koDoc = (komodo.koDoc === undefined ? komodo.document : komodo.koDoc);

Python

    koDoc = (komodo.koDoc if hasattr(komodo, "koDoc") else komodo.document)

#### koDoc Attributes

string baseName
  The basename of the document (e.g. `"myfile.txt"`).
string displayPath
  The display path of the document (e.g. `"C:\Code\myfile.txt"`).
file
  The koDoc.file object corresponding to the document (null if the document is unsaved).
string buffer
  The contents of the document (Unicode string).
boolean isDirty
  Whether there are unsaved changes to the document.
boolean isUntitled
  Whether the document has never been saved.
string language
  The language that this document is viewed as (`"Python"`, `"Perl"`, etc.

### ko.views.manager.currentView.koDoc.file

The `file` object is an attribute of `koDoc` objects, and corresponds to a wrapper object around the file object.

#### `document.file` attributes

string URI
  The URI to the file (e.g. `"file:///C:/Code/myfile.txt"`).
string displayPath
  The display path of the file (e.g. `"C:\Code\myfile.txt"`), or the URI if the URI is not of the `file://` scheme.
string baseName
  The base name of the file (e.g. `"myfile.txt"`).
string dirName
  The directory of the file (e.g. `"C:\Code"`).

### ko.commands.doCommand()

Signature:

    ko.commands.doCommand(commandId)

Execute the internal Komodo command specified by `commandId`.

Command IDs and their corresponding functions are available in the Command ID reference.

Most editor-related commands require that the Editor Pane be in focus. To ensure focus before invoking `doCommand`, set the focus explicitly as follows:

    ko.views.currentView.setFocus()

### ko.open.URI()

Signature:

    ko.open.URI(uri_string#line_number)

Open the file specified by the URI string and move the cursor to the line number specified after '#' (if any).

All file access protocols used by Komodo are valid. For example:

    ko.open.URI("file:///home/user/example.txt#33");
    ko.open.URI("ftp://example.org/pub/example.xml");
    ko.open.URI("scp:///host.example.org/home/user/file.txt#11");

### ko.projects.findPart()

Signature:

    ko.projects.findPart(type, name, where) -> part

Find a "part" (the internal name for a component such as a snippet, another macro, a run command, etc) in the Toolbox or a project.

- `type`: The type of component to search for. It can be one of:
- - `"snippet"`
  - `"command"`
  - `"macro"`
  - `"file"`
  - `"folder"`
  - `"dialog"`
  - `"URL"`
  - `"template"`
  - `"DirectoryShortcut"`
- `name`: The component's name.
- `where`: A string corresponding to the component container that should be searched. Supported values are:
  `"toolbox"`
    search in the Toolbox
  `"shared toolbox"`
    search in the Shared Toolbox (if enabled)
  `"toolboxes"`
    search in both the Toolbox and the Shared Toolbox
  `"container"`
    search the project or Toolbox that contains the current macro
  `"*"`
    search all of the above

### ko.interpolate.interpolateString()

Signature:

    ko.interpolate.interpolateString(s[, bracketed], queryTitle]]) -> string

Evaluate interpolation shortcuts in the given string.

- `s`: The string to interpolate.
- `bracketed`: An optional boolean value indicating whether plain (e.g. `%F`) or bracketed (e.g. `[[%F]]`) syntax is being used. If not specified, plain interpolation is used (i.e. `bracketed=false`)</.

### ko.interpolate.getWordUnderCursor()

Signature:

    ko.interpolate.getWordUnderCursor() -> string

This function returns the word under the cursor in the current buffer.

### ko.run.runEncodedCommand()

Signature:

    ko.run.runEncodedCommand(window, command[, callback]) -> string

Runs a shell command and optional callback function.

- `window`: Required (literal) argument indicating Komodo's top-level window object. This allows the method to be called from dialog boxes or other sub-windows.
- `command`: The shell command to run. Can include arguments in Python `dict` format.
- `callback`: An optional callback function.

For example:

    function did_it() {
      alert("Done.");
    }
    ko.run.runEncodedCommand(window, 'sleep 5', did_it);

Command options and environment variables can be added in curly braces "{...}" after the command in Python `dict` format. For example:

    ko.run.runEncodedCommand(window,
      'svn up "%F" {"cwd": "%D", "env": "SVN_SSH=plink.exe"}')

The following JavaScript macro uses Komodo's `cmd_refreshStatus` function as the callback in `ko.run.runEncodedCommand()`. This updates the SCC status information after running '`svn up`' on the file in the current editor tab:

    if (komodo.view) {
      komodo.view.setFocus();

      ko.run.runEncodedCommand(window,
          'svn up "%F"',
          (function (view) {
              return function () {
                  view.setFocus();
                  komodo.doCommand('cmd_refreshStatus');
              }
          })(komodo.view)
      );
    };

## Python Macros

Macros written in Python can use the XPCOM interface. Though it is deprecated, `komodo.` namespace is still available for those functions (`interpolate()` and `getWordUnderCursor()`) which cannot be done easily with XPCOM.

To use the XPCOM components described below, add the following to your macro:

    from xpcom import components

### Creating a view object

There is no global object in Python macros to get the current "view" (i.e. the main active Komodo tab) because this can change during macro execution. Instead, a view object can be created in the macro at the point where it will be used. For example:

    from xpcom import components
    viewSvc = components.classes["@activestate.com/koViewService;1"]\
        .getService(components.interfaces.koIViewService)
    view = viewSvc.currentView.queryInterface(components.interfaces.koIScintillaView)

The `view` object will have the same properties as `ko.views.manager.currentView`, exposing:

- The `scimoz` object for manipulation of code buffers.
- The `koDoc` object for manipulation of documents in memory.
- The `koDoc.file` object, corresponding to files on disk.

### Accessing the editor

Get the view and then utilize the Scintilla (scimoz) instance:

    from xpcom import components
    viewSvc = components.classes["@activestate.com/koViewService;1"]\
        .getService(components.interfaces.koIViewService)
    view = viewSvc.currentView
    view = view.queryInterface(components.interfaces.koIScintillaView)
    sm = view.scimoz
    sm.currentPos   # current position in the editor
    sm.text         # editor text
    sm.selText      # the selected text
    scimoz.replaceSel("new text") # replace the current selection

### Opening a URI

To open a URI:

    from xpcom import components
    obsvc = components.classes["@mozilla.org/observer-service;1"].\
        getService(components.interfaces.nsIObserverService);
    obsvc.notifyObservers(None, 'open-url', uri);

### Interpolate a string

There is currently no equivalent in XPCOM for evaluating interpolation codes. This can be done using the old `komodo` namespace as follows:

    import komodo
    komodo.interpolate()

Some interpolation shortcuts cannot be used within Python macros. These include `%P` and `%ask`, and the `:orask` modifier on other shortcuts. A `ValueError` is raised if they are used.

### Get word under cursor

There is currently no equivalent in XPCOM to retrieve the word under the editing cursor. This can be done using the old `komodo` namespace as follows:

    import komodo
    komodo.getWordUnderCursor()

### Running a Komodo command

To execute Komodo command:

    from xpcom import components
    def doCommand(commandId):
        observerSvc = components.classes["@mozilla.org/observer-service;1"]\
            .getService(components.interfaces.nsIObserverService)
        observerSvc.notifyObservers(None, 'command-docommand', commandId);

### Find a part

To find other components (snippets, run commands, other macros, etc):

    from xpcom import components
    partSvc = components.classes["@activestate.com/koPartService;1"]\
                .getService(components.interfaces.koIPartService);
    partSvc.findPartForRunningMacro(type, name, where);