Scintilla icon SciTE Extension Interface

Purpose.

Some people want to create enhanced versions of the SciTE editor, while still receiving the benefits of new SciTE features. This could be for an editor designed for a particular environment such as developing games, to incorporate a scripting capability within SciTE or to allow SciTE to be controlled by another process through an IPC mechanism.

There are two example extensions. The SciTE Director Interface allows SciTE on Windows to be controlled by an external application such as a project manager. The SciTE Lua Scripting Extension is an integration of the Lua scripting language into SciTE, done using the Extension interface.

Extension Interface.

bool Initialise(ExtensionAPI *host_);
bool Finalise();
bool Clear();
bool Load(const char *filename);
bool InitBuffer(int index);
bool ActivateBuffer(int index);
bool RemoveBuffer(int index);
bool OnOpen(const char *path);
bool OnSwitchFile(const char *path);
bool OnBeforeSave(const char *path);
bool OnSave(const char *path);
bool OnChar(char ch);
bool OnExecute(const char *s);
bool OnSavePointReached();
bool OnSavePointLeft();
bool OnStyle(unsigned int, int, int, Accessor *);
bool OnDoubleClick();
bool OnUpdateUI();
bool OnMarginClick();
bool OnMacro(const char *, const char *);
bool SendProperty(const char *);
bool OnKey(int keyval, int modifiers);
bool OnDwellStart(int pos, const char *word);
bool OnClose(const char *filename);

An extension must implement the Extension interface defined in scite/src/Extender.h Only the first 4 methods must be implemented although an implementation can be as simple as just returning false. The other methods have empty default implementations. Methods added to this interface in the future should have default implementations so existing extensions will continue to compile.

Each method returns a bool indicating whether the method handled all processing that is needed and so no additional processing is required. Normally, false is returned to indicate that further processing may be done.

The extension should use the Initialise and Finalise methods to allocate and deallocate resources. The ExtensionAPI pointer should be saved in the Initialise method so the extension can communicate back to SciTE.

The Clear and Load methods are used to support extensions that need to load a resource such as a script file when a file is opened. When a file is opened in SciTE, first the extension is asked to clear any data associated with the previous file through Clear. Then SciTE checks for a property called "extension" which matches the file name, so for x.cpp, looks for extension.*.cpp. A file with this name is searched for in standard property file locations and if found Load is called with the path as an argument.

The InitBuffer, ActivateBuffer, and RemoveBuffer methods provide the necessary hooks so that extensions have a mechanism to associate data with a specific buffer, similar to the way SciTE itself remembers the monospace setting of each buffer. InitBuffer is called whenever a new document is opened in a given buffer. The buffer might be a newly allocated one, or it might be recycled if the maximum number of buffers has been reached. Once the buffer has been initialized, it will be the active buffer. Thereafter, ActivateBuffer is called whenever the user switches to another loaded buffer. RemoveBuffer is called when an existing buffer is closed. Thereafter, the indexes of the buffers that come after the removed buffer are shifted down by one. After RemoveBuffer, the extension will receive an InitBuffer or ActivateBuffer to establish the new active buffer.

OnExecute is called only when an extension command is executed. These are indicated in properties as subsystem 3.

OnBeforeSave is called before saving the file and an extension may implement file saving itself and return true to prevent the default file save code from executing.

Other methods are called upon events occurring in SciTE allowing an extension to respond to those events.

ExtensionAPI Interface.

enum Pane { paneEditor=1, paneOutput=2, paneFindOutput=3 };
sptr_t Send(Pane p, unsigned int msg, uptr_t wParam=0, sptr_t lParam=0);
char *Range(Pane p, int start, int end);
void Remove(Pane p, int start, int end);
void Insert(Pane p, int pos, const char *s);
void Trace(const char *s);
char *Property(const char *key);
void SetProperty(const char *key, const char *val);
uptr_t GetInstance();
void ShutDown();
void Perform(const char *actions);
void DoMenuCommand(int cmdID);
void UpdateStatusBar(bool bUpdateSlowData);

An extension can call back into SciTE using this interface which is a simplified way to access the functionality of SciTE.

As well as the normal editor pane and output pane, this interface allows for a future feature where a third pane may be used for the output of search commands. This is currently mapped to the output pane.

Send allows sending messages to the Scintilla control contained in each pane.

Range retrieves text from the pane. This must be deleted with delete[]. Remove and Insert are used to remove and insert text in a pane.

Trace displays a string at the end of the output pane.

SciTE's properties can be read and written with Property and SetProperty. The result from Property should be deleted with delete[].

GetInstance is Windows specific and returns the HINSTANCE of the application which is needed when accessing platform facilities.

ShutDown is equivalent to the user choosing the Quit menu item. If there are any unsaved files loaded, then the user is asked whether to save them and may cancel from this dialog. So under some circumstances, the application will continue to run after ShutDown is called.

Perform takes a string containing an action, a ':' character, and an argument. Currently the only known action is open and then the argument is a path. This is used by the Director extension to relay commands from another application. In the future more actions will be possible through this method.

Attaching the extension.

Extensions are currently added explicitly by code in the start up function. On Windows, the DirectorExtension is attached with code similar to this simplified example:

DirectorExtension director;
Extension *extender = &director;
//...
SciTEWin MainWind(extender);

It would be better to move to an implicit attachment mechanism similar to the way lexers are attached to Scintilla, determining which extensions are used by simply linking their object files into SciTE. It would also be good to allow run-time attachment of extensions housed in DLLs or shared object libraries.

Multiplexing.

SciTE supports multiple extensions at a time. A multiplexer extension maintains a list of extensions and calls each in turn for each method. Once an extension returns true indicating processing should stop, the multiplexer returns without traversing any remaining list members. However, for some methods such as Initialise and Finalise, the remaining extensions are traversed regardless of the return value of the previous extension.

Thread safety.

In general, SciTE is a single threaded application. However, on Windows, command tools call OnExecute from a separate worker thread. The SingleThreadExtension adapter class can be used to wrap an extension so that OnExecute calls are marshalled to the main thread. Of course, this is not necessary if your extension is thread safe, or if it does not implement OnExecute, or if it is a GTK-specific extension.