Scribe Scripting
Scripting Scribe
Scribe as of v1.89-test11 has a basic scripting language for tools and filters. The syntax of the language is loosely based on Javascript, but will likely have areas that aren't implemented, and also areas that extend the language. Over time things will likely move more towards Javascript than away. The language has been implemented entirely by myself in C++ and because I'm not a compiler engineer it's probably going to be highly inefficient. However the implementation is open source as part of LGI so if you feel the need to "fix" it then, well... use the source!

So on to the actual details of the language. The benifit of having a built in language as opposed to an addon like Python or Ruby, is that it intergrates more closely with the existing object model as defined by Scribe and Lgi. The core variable format in the scripting language is the GVariant class (from LGI) that can contain any common C type (string, int, double, void*) or some custom types, in particular a GDom* which is basically a base class for all the complex objects in Scribe. Things like, the application, an email, a contact etc. You can get a feel for Scribe's objects by having a look at it's DOM (Document Object Model). It lists all the main objects and their fields. The language allows you to access an object's properties via the dot operator, e.g. if you are given an email object in a variable called "m", then you can access the subject of the email using the syntax:

m.Subject
Variables don't need to by defined, they get created when needed. For instance if you do this:
k = 10;
Then the language creates a variable called "k" to store the value "10" in. So assuming you understand the general syntax of the Javascript language you could start putting together some simple scripts. Also at this point there is no scope yet, just one global variable scope. This is likely to change later however.

Filters

There are 2 places that you can start scripting with test11. The first is inside a filter, if you open or create a filter and click on the script tab, you have a text editor where you can enter script commands. The script is executed before any of the filters conditions have been evaluated, and there are some predefined variables that you can use: And there are also a number of methods that are specific to Scribe: An important thing to use inside a filter script is the TestConditions field. The filter object that gets passed to the script in the predefined variable "Filter" is the filter object currently being evaluated, and it has a field called "TestConditions" that evaluates the current email against the conditions defined in the conditions tab. This allows you to use the graphical conditions UI and also script the actions that get executed if the conditions match the filter. It also means that you don't have to use the conditions if you don't want to. You can make up your own conditions if you want, or just execute the script against every email by having no conditions.

A simple example filter script looks like this:

if (Filter.TestConditions)
{
    Fd = GetFolder("/Inbox");
    if (Fd)
    {
        Print("Got folder '" + Fd.Name + "'\n");
        
        Child = CreateSubFolder(Fd, Mail.Subject, "Mail");
        if (Child)
        {
            MoveItem(Child, Mail);
        }
    }
}
This moves email matching the filter's conditions into a sub-folder under the inbox, named after the subject of the email. Creating new sub-folders as new email subjects arrive. The "Print" command outputs to the scripting console, which you can view via Tools -> Debug -> Scripting Console.

Tools

The second place that scriting can be used (so far) in Scribe is the tools menu. If you create a sub-folder under the place where Scribe.exe is installed called "Scripts" and save some scripts in that with the extension ".script" then when you restart Scribe the scripts will appear under the Tools menu in the main window. These are like little add-on programs that can do some function, usually on the contents of the current folder. An example of what you can do is provided in my blog post about the feature. Tool scripts are a little different to filter scripts in that you have to define a "Main" function for anything to be executed. e.g:
function Main(App) { }
With the body of the script inside the main function. You may define other helper functions in the same file. And in future builds you'll be able to include other code via #include as well (but not yet). So the application object is passed in via the "App" parameter. You can then retreive a folder object and start scanning it to operate on objects. The tools scripts are only executed when you select the menu item corrasponding to the script.

You'll need to keep the DOM handy and print out your variables on the way to help with debugging. There is no debugger (other than "Print") yet but maybe one day I'll add one. It wouldn't be particularly difficult.

Future Expansion

In the future there will be a special "Events.script" that implements a number of event hooks to add behaviour to Scribe. So don't use that as a filename while implementing tool scripts.

Also I'm currently working on converting the C++ tools in the Tools menu to scripts. This requires that I expand the types of variables you can use in the scripting language to include lists and hashtables.

Built In Methods

There are some functions that come built in, like a C lib:
int Strchr(string, char[, string_length])
Returns the position of 'char' in 'string' or -1 if 'string' doesn't contain 'char'. You may optionally supply a max number of chars to search.
j = Strchr("Some String", "t");
Where 'j' would end up being '6'.
int Strcmp(string1, string2[, case_insensitive])
Returns a comparision of 'string1' and 'string2', where if the strings are the same, the return value is '0'. You can optionally make it case insensitive by seting the 'case_insensitive' flag.
int Substr(string, start[, length])
Returns a sub-segment of 'string', starting with the 'start'th character and running till the end of the string or for 'length' characters.
HashTable NewHashTable()
Creates an empty hashtable. You should access elements in the hash table via array indexing:
t = NewHashTable();
t["foo"] = 56;
k = t["foo"];
To add or change an element, just assign something to a key. To delete something use the method 'DeleteElement'.
List NewList()
Creates an empty list. You should access elements in the hash table via array indexing:
t = NewList();
t[0] = 56;
k = t[0];
Also use the method 'DeleteElement' to remove a value. Assigning something to an index that doesn't exist has the effect of adding that value to the end.
t[-1] = 234;
Appends the value '234' to the list.
void DeleteElement(container, element)
Removes an element from a container. Works on both hash tables and lists. For a hash table pass the element's key (a string) and for a list pass the elements index (an integer).