Scintilla icon SciTE Lua Scripting Extension

Lua Scripting Extension Notes

The SciTE Lua Scripting Extension uses a copy of Lua 5.1 as its scripting engine. Currently, all of the standard libraries are included, although this list may be trimmed in a future revision.

Lua is Copyright (C) 1994-2007 Lua.org, PUC-Rio. The complete Lua license is included in luaCOPYRIGHT in the SciTE installation directory. To find more information about Lua, including documentation for the language itself, visit www.lua.org.

For more ideas about what Lua can do, you may also want to check out the community portal, lua-users.org, an introduction to using Lua with SciTE, and some example scripts.

SciTE Properties and Lua Event / Command Handlers

The properties ext.lua.startup.script and extension.filepattern can be used to define commands and event handlers that will be called by the SciTE. Other properties beginning with ext.lua may also influence how Lua behaves. See the SciTE Documentation for more details on this.

By defining functions in the startup script or the active extension script, you can tailor SciTE to your needs, adding new behavior and functionality that is tightly integrated.

To begin, you can handle any many of the events exposed by the SciTE Extension Interface. You do this simply by defining functions with the same name as the event. Currently, OnOpen, OnClose, OnSwitchFile, OnSave, OnBeforeSave, OnChar, OnKey, OnSavePointReached, OnSavePointLeft, OnDwellStart, OnDoubleClick, OnMarginClick, OnUpdateUI, and OnUserListSelection are supported.

For some of these events, SciTE will pass one or more arguments to the event handler function: OnOpen, OnClose, OnSwitchFile, OnSave, and OnBeforeSave will receive the filename of the affected buffer as their first argument. An OnChar handler should expect a single-character string argument. An OnKey handler should expect an integer keycode and boolean shift, control, and alt arguments. The keycode is currently a platform specific value but this may change in future. OnDwellStart will receive the position of the mouse and the word under the mouse as arguments and the word will be empty when the mouse starts moving. OnUserListSelection receives two arguments: a number indicating the list type, and a string indicating the selected item text. The other event handlers will not be passed any arguments.

Event handlers return a boolean value to indicate whether SciTE should continue processing the event. Return a true value to indicate that the event has been fully handled, and that no further handlers should be called. Return a false value to give other extensions a chance to process the same event. In many but not all cases, a well behaved event handler will return false. Remember that, in Lua, the only non-true values are false and nil. Unlike in C++, Python and many other languages, 0 evaluates to true.

There is one additional event handler, OnClear, that is not expressly defined in the Extension interface, but is exposed to Lua. Whenever SciTE re-reads the properties (which occurs every time you switch buffers or open a new file, but can also occur at other times), the Lua Extension removes any globals that were created since the last time properties were read, and restores any globals that were overwritten. Then, if the startup script defines a function OnClear, that function will be called so that scripts have a chance to clean up other changes they might have made outside of the Lua global scope (e.g. dynamic properties modified through the props object; see below) and/or to tailor the Lua environment according to local properties for the current buffer.

After this, SciTE reads the properties and ultimately loads the extension script, if one is defined. However, at the time when the OnClear event fires, the extension script is not yet loaded. Thus, OnClear can only be defined in the startup script, not in an extension script.

In addition to event handlers, you can also use define new commands that are available through the Tools menu or through keyboard shortcuts. To specify that a command that will be handled by Lua, specify subsystem 3 for the command. Then, to implement the command using Lua, just define a global function. The command name is the function name.

You can also use predefined functions like dofile and dostring as tool commands.

Anything specified after the command name is passed to the Lua function as a single string argument. An example of a command, using the built-in dofile command, is shown below.

command.name.1.*=Run My Script
command.subsystem.1.*=3
command.1.*=dofile $(SciteDefaultHome)/My Script.lua

Note that the command line is "not" evaluated directly as a Lua script.

If there is no function matching the command name, no error will be displayed. This is because Lua assumes in this case that the command is meant for some other extension, such as the SciTE Director Extension. However, if the command function is found, but fails to execute, an error is reported.

Multiple handlers

Scite Ext Man can help in more complex applications where you have multiple scripts needing to handle an event.


Predefined Lua Functions and Objects:

Within Lua scripts you can use the following functions / objects:

  trace(s) - writes s to the output pane (no prefix, no newlines)
  dostring(s) - executes s as a Lua string, like Lua 4's dostring
  editor - the editor pane
  output - the output pane
  props - a pseudo-table representing the SciTE properties
  buffer - a table associated with the current buffer or document
  scite - a namespace for functions which control SciTE.

In addition, all constants defined in Scintilla.iface are exposed as Lua globals variables. Function names are exposed as their block capital equivalents, with the SCI_ prefix.

All functions and objects defined in the Lua standard library are also available. Although dostring was deprecated in Lua 5, it is restored since some have said it would be useful in tool commands.

A function _ALERT() is also defined to be an alias for the built-in print(), which prints the alert message (plus a newline) to the window. This provides a reasonable way for Lua to present error messages to the user. You are free to override _ALERT with a different definition if you prefer.

The props pseudo-table allows you to read or write properties by name using normal Lua table-access semantics, e.g. props["property.name"]. As with Lua tables, you can also un-set a property by assigning nil to its key.

When you assign a value to a property from Lua, this overrides any values specified in the configuration files for that setting. The underlying file properties are not changed. If you later assign nil to the same property from Lua, this removes the run-time setting, allowing any file-based property setting to show through once again.

The editor and output panes support the following properties and methods:

  textrange(startPos, endPos) - gets the text in the specified range

  findtext(text, [flags], [startPos, [endPos]])
    - returns the start and end of the first match, or nil if no match
    - flags can be 0 (the default), or a combination of SCFIND constants
      such as SCFIND_WHOLEWORD, SCFIND_MATCHCASE, and SCFIND_REGEXP

  match(text, [flags], [startPos])
    - returns a generator that allows you to loop over the matches
      i.e. for m in editor:match(text, flags) do ... end
    - the match object (i.e. the loop counter m in the above
      example) supports read-only properties pos, len, and text;
      and also supports a function replace(replaceText) to
      support search and replace.
    - while looping through matches, if the document is modified
      by any method other than the loop counter's replace method,
      this may cause the match generator to lose its place.
    - also, do not attempt to store the match object for later
      access outside the loop; it will not be useable.

  append(text) - appends text to the end of the document
  insert(pos, text) - inserts text at the specified position
  remove(startPos, endPos) - removes the text in the range

Most of the functions defined in Scintilla.iface are also be exposed as pane methods. Those functions having simple parameters (string, boolean, and numeric types) are fully supported. For example, editor:InsertText(pos, text) does practically the same thing as editor:insert(pos, text). Functions having a stringresult parameter will include a string in the return value. For both strings and stringresults, if the function is documented as expecting a length as its first parameter, you do not pass the length from Lua. Instead, it is inferred from the context.

The keymod parameter type has partial support. When an iface function is declared as taking a keymod, the Lua equivalent expects two numbers: first the key code (e.g. SCK_LEFT or string.byte("'"), and second the modifiers (e.g. SCMOD_CTRL).

Functions that have more complex parameters are not supported.

Functions that are declared to return a numeric type have the result added to their return value. If the function also has a stringresult, that comes first, followed by the numeric return value.

Some functions are declared as 'get' or 'set' rather than 'fun' in the iface file. These are generally exposed to Lua as properties, e.g. editor.TabSize = 8. Some of the getters and setters also have a parameter. Where possible, these are exposed to Lua as indexed properties, e.g. editor.StyleBold[SCE_PROPS_DEFAULT] = true. However, if an iface function is declared as get / set but cannot be mapped to a Lua property, it is exposed as a Lua function instead.

The possible Scintilla calls are listed as the Pane API and in API file format. The Scintilla API is described in ScintillaDoc.

The scite namespace includes the following functions:

  scite.Open(filename)
    - opens a file in a new buffer
    - activates the file's buffer if it is already opened.

  scite.SendEditor(SCI_constant, ...)
    - sends a message to the editor pane
    - equivalent to the corresponding iface function or property

  scite.SendOutput(SCI_constant, ...)
    - sends a message to the output pane

  scite.ConstantName(number[, prefix])
    - returns the symbolic name of a Scintilla / SciTE constant
    - optional prefix to find a specific constant name

  scite.MenuCommand(IDM_constant)
    - equivalent to the corresponding IDM_ command defined in SciTE.h

Open requires special care. When the buffer changes in SciTE, the Lua global namespace is reset to its initial state, and any extension script associated with the new buffer is loaded. Thus, when you call Open, this may change the environment in which your current script is running. When possible, you can avoid confusion by simply returning after scite.Open, but when that is not possible, just bear in mind that there are side effects. Local variables, unlike globals, will be retained after the buffer change until your script returns.

The SendEditor and SendOuput functions duplicate the functionality of the editor and output objects, providing access to these through an interface that is more familiar to Scintilla C++ developers. This may be useful for prototyping C++ code using Lua. Internally, SendEditor and SendOutput are translated to the corresponding iface function or property, so their arguments and return types are identical. (Although the calling convention for properties is obviously different.)

The ConstantName function may be useful when generating debug messages, or if extending the SciTE LuaExtension to support macro recording.

The MenuCommand function enables usage of SciTE's menu commands as defined in SciTE.h.

Scripting user interfaces with strips

Simple user interfaces may be defined from Lua as strips similar to the find and replace strips. An example looks like

Strips are shown with StripShow which takes a string describing the user interface then builds and displays it as a strip at the bottom of the application window. There are 5 supported elements: labels, editable text, combo boxes, buttons and default buttons. These are surrounded by different indicator strings: ' for labels; [] for editable text; {} for combo boxes; () for buttons; and (()) for default buttons. There can also be a newline to start a new line and a ! to show a close box on Windows only.

A default button looks like ((OK)) and can be triggered by pressing the Enter key. Buttons may include accelerator keys prefixed with &. On Windows and GTK but not OS X, labels may also define accelerator keys which cause focus to move to the next element which accepts focus. Literal ampersands are defined with &&. For example, the code

       scite.StripShow("!'Explanation:'{}(&Search)\n'Name:'[Name](OK)(Cancel)")
shows the strip displayed in the picture. The strip can be closed by passing an empty string.

On GTK a table is used for layout and the approach was copied to the other platforms. It is not yet as capable on the other platforms yet as on GTK - columns containing editable text and combo boxes can expand and other columns are fixed to the their natural width of their widest element.

Events from the user are communicated back to the script through the OnStrip function which takes an element number (starting at 0 and including static text elements) and a change type (clicked=1, change=2, focusIn=3, focusOut=4). 'clicked' is for button presses, 'change' for changes to editable text or the editable text part of a combo boxes and 'focusIn' and 'focusOut' are when the user moves focus between elements. The value of editable text or combo boxes can be retrieved with StripValue(element).

There are some bugs and limitations with these events currently. Focus events may not occur or may occur only when text is edited. Selecting an item from the list in a combo box may not send a 'change' event.

The editable part of combo boxes and editable text can be set with StripSet(element, value) and the list part of combo boxes can be set with StripSetList(element, value) where the items in 'value' are separated with new lines.

Lua 5.1

Despite some of the big changes in Lua 5.1 dealing with changes
in the language, most of the compatibility options have been
turned on.
Compatibilities:
- table.getn still works, but the '#' operator should be used
- Lua 5.0's varargs are still available
- Lua 5.0's math.mod is still available, as well as 5.1's
math.fmod
- Lua 5.0's string.gfind is still available, as well as 5.1's
string.gmatch
- [C API] Lua 5.0's luaL_openlib behavior is still available
Changes:
- table.setn was deprecated
- loadlib was moved into the package table (package.loadlib)
- Lua 5.0's long string nesting throws an error

Disabling Lua

Lua is currently loaded just-in-time, before it is first used. The ways that Lua can become are through the ext.lua.startup.script property, by naming a lua file named in the extension.filepattern property, or by using the extension mechanism to define tool commands (i.e. subsystem 3). If you do not do any of these things, the Lua scripting engine is not loaded, and for all practical purposes, SciTE should behave as it did before Lua was added.

Nevertheless, it is still possible to build SciTE without the Lua support. To do this, simply define the variable NO_LUA when you build it, e.g. for MSVC, nmake -f scite.mak -DNO_LUA; or with GNU tools, make NO_LUA=1.