All posts by Dominik

Dominik is a PhD student at the Control Theory and Robotics Lab, TU Darmstadt, as part of the Research Training Group GKMM (GRK1362). My research focuses on state estimation in distributed systems. As hobby, I contribute to the KDE project and work on the Kate application and editor component.

Kate Highlighting Power

Kate’s highlighting capabilities are amazing. If you want you can highlight really complex syntax, without having to hardcode rules in C++. As an example, we’ll take a look at how Lua comments can be realized:

  • –[=[ starts a multiline comment (the ‘=’ chars are optional)
  • ]=] ends the multiline comment
  • the number of ‘=’ chars in ]=] must match the number of –[=[

That means: When the highlighting processor matches the end of a multiline comment, it has to know how many ‘=’ chars started the comment. Thanks to the concept of dynamic rules and contexts Kate is able to do that. The highlighting file looks like this. First comes the header

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "language.dtd" >
<language name="Test" version="1.0" kateversion="2.4" section="Markup" extensions="" mimetype="">
  <highlighting>

Then the body with the contexts. We start in the first context called “Normal Text”. When the regular expression –[(=*)[ matches it switches to the context Comment.

    <contexts>
      <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
        <RegExpr attribute="Comment" context="Comment" String="--\[(=*)\[" dynamic="true"/>
      </context>

The part (=*) is now available as %1 in the rule below:

      <context name="Comment" attribute="Comment" lineEndContext="#stay" dynamic="true" >
        <RegExpr attribute="Comment" context="#pop" String="\]%1\]" dynamic="true" />
      </context>
    </contexts>

The last part is the footer:

    <itemDatas>
      <itemData name="Normal Text" defStyleNum="dsNormal" />
      <itemData name="Comment"     defStyleNum="dsComment" />
    </itemDatas>
  </highlighting>
</language>

If you want to know more about Kate’s highlighting, have a look at the documentation :) There are also lots of bug reports, so if you want to contribute you can fix them!

PS: As I don’t know much about Lua, comments might work differently. That does not really matter, as the example still shows what you can do :)

Extending Kate by Scripts

We have seen how scripting basically works for indentation. It’s also possible to register commandline functions (The command line is bound to F7 by default, or invoke View > Switch to Command Line). We will consider a small example again: sort the selected text.

/* kate-script
 * name: unused
 * author: foo bar
 * license: LGPL
 * version: 1
 * kate-version: 3.0
 * functions: sorter
 */

function sorter ()
{
    if (view.hasSelection()) {
        var start = view.startOfSelection().line;
        var end = view.endOfSelection().line;

        var text = document.textRange(start, 0, end, document.lineLength(end));

        var lines = text.split(“\n”);
        lines.sort();
        text = lines.join(“\n”);

        view.clearSelection();

        document.editBegin();
        document.removeText(start, 0, end, document.lineLength(end));
        document.insertText(start, 0, text);
        document.editEnd();
    }
}

The header line functions: sorter makes Kate Part aware of the function in the script. A list of functions is supported, separated by white spaces. You can use the function by typing ‘sorter’ in the commandline.
Some todo items:

  • provide better JavaScript API. For example: document.textRange() takes 4 parameters. It would be more elegant to take one range or two cursors, just like we do in the KTextEditor interfaces in kdelibs/interfaces/ktexteditor
  • make is possible to bind scripts to shortcuts. This could be done by e.g. binding commandline functions to shortcuts or implementing a vim-like command-mode in Kate’s commandline. How to configure the shortcuts is unclear, though.
  • then, think about replacing the C++ implementations of ‘uppercase’, ‘lowercase’, ‘capitalize’ etc. with scripts
  • things I forgot…

If you are interested subscribe to kwrite-devel@kde.org and contribute :) We also need indentation scripts, of course!

Kate: More on Indentation Scripting

My last blog was about the theory of how indentation works by using javascripts. Now we will look at a concrete example: a LISP-style indenter. (LISP indentation is easy, that’s why it’s a good example).
The rules:

  • comments starting with ;;; always have indentation 0
  • comments starting with ;; should be aligned with the next line
  • comments starting with ; should only appear behind code, so they are simply ignored
  • every ‘(‘ indents and every ‘)’ unindents

lisp.js looks like this:

/** kate-script
* name: LISP
* license: LGPL
* author: foo bar
* version: 1
* kate-version: 3.0
* type: indentation
*/
// indent should be called when ; is pressed
triggerCharacters = “;”;
function indent(line, indentWidth, ch)
{
// special rules: ;;; -> indent 0
//                ;;  -> align with next line, if possible
//                ;   -> usually on the same line as code -> ignore
textLine = document.line(line);
if (textLine.search(/^\s*;;;/) != -1) {
return 0;
} else if (textLine.search(/^\s*;;/) != -1) {
// try to align with the next line
nextLine = document.nextNonEmptyLine(line + 1);
if (nextLine != -1) {
return document.firstVirtualColumn(nextLine);
}
}

cursor = document.anchor(line, 0, ‘(‘);
if (cursor) {
return document.toVirtualColumn(cursor.line, cursor.column) + indentWidth;
} else {
return 0;
}
}

The result:

;;; fib : number -> number
(define (fib n)
(if (< n 2)
1
(+ (fib (- n 1)) (fib (- n 2)))))

As this indenter is scripted, everybody can adapt the style to the own needs.

Kate Scripting: Indentation

Kate Part in KDE4 supports the ECMAScript (JavaScript) language by using kjs. In KDE3 we had several hard-coded indenters in C++, the idea is to let scripts do all the indentation in KDE4.
How does it work? It is similar to vim: You simply create a script in the directory $KDEDIR/share/apps/katepart/jscript. An indentation script has to follow several rules:

  1. it must have a valid script header (the first line must include the string kate-script and indentation scripts must have the type: indentation)
  2. it must define some variables and functions

Whenever the user types a character, the flow in Kate Part works like this

  1. check the indentation script’s trigger characters, i.e. whether the script wants to indent code for the typed character
  2. if yes, call the indentation function
  3. the return value of the indentation function is an integer value representing the new indentation depth in spaces.

In the 3rd step there are 2 special cases for the return value:

  1. return value = -1: Kate keeps the indentation, that is it searches for the last non-empty line and uses its indentation for the current line
  2. return value < -1 tells Kate to do nothing

So how does a script look like exactly?
The name does not really matter, so let’s call it foobar.js:

/* kate-script
 * name: MyLanguage Indenter
 * license: LGPL
 * author: Foo Bar
 * version: 1
 * kate-version: 3.0
 * type: indentation
 *
 * optional bla bla here
 */

// specifies the characters which should trigger indent() beside the default '\n'
triggerCharacters = "{}";

// called for the triggerCharacters {} and
function indent(line, indentWidth, typedChar)
{
  // do calculations here
  // if typedChar is an empty string, the user hit enter/return

  // todo: Implement your indentation algorithms here.
  return -1; // keep indentation
}

More details on the header:

  • name [required]: the name will appear in Kate’s menus
  • license [optional]: not visible in gui, but should be specified in js-files. it is always better to have a defined license
  • author [optional]: name
  • version [optional]: recommended. an integer
  • kate-version [required]: the minimum required kate-version (e.g. for api changes)
  • type [required]: must be set to indentation

The only missing part is the API which Kate Part exports to access the document and the view. Right now, there is no API documentation, so you have to look at the code:

You will find, that the current document exports functions like

  • document.fileName()
  • document.isModified()
  • document.charAt(line, column)
  • etc…

The view exports functions like

  • view.cursorPosition()
  • view.hasSelection()
  • view.clearSelection()

That’s the boring part of this blog. The interesting one is unfortunately shorter: we are looking for contributors who want to write scripts or help in the C++ implementation :)