Writing Indenters

Kate Part itself already ships with several indenters, however it may be that users are not content or need another indenter to indent their code.

Kate Part’s indenters

  • None does no indentation
  • Normal keeps the current indentation when processing a newline
  • C Style is optimised for C/C++ code and works well for Java, PHP and C# and the likes
  • S&S C Style is similar to C Style but sticks to S&S indentation rules
  • XML Style indents XML based languages
  • Python Style is optimised for Python
  • Variable based Indenter indents if regular expressions match

As Kate Part does not have a plugin architecture for indentation you have to hard code every indenter directly into the Kate Part sources. The code can be found in the KDE module kdelibs/kate/part/ — this HOWTO assumes you already have the code around.

Step I: Derive Class

The important files are kateautoindent.h and kateautoindent.cpp. In the file kateautoindent.h there are basically two important classes:

  • KateAutoIndent is actually a dummy class and contains many virtual functions for sub classing, all other indenters are derived from this class and overwrite the virtual functions. The indenter None actually is an instance of this class.
  • KateNormalIndent is derived from KateAutoIndent and itself implements only very basic indentation features like “Keep indentation” or such. However this is a key class, because it extends the KateAutoIndent class with many configuration flags like tabWidth or indentWidth. Look into the source code to see a full list of available flags.

All other indenters are directly derived from KateNormalIndent - so it is a good start to do the same: Derive a class KateMyIndent from KateNormalIndent, the real implementation is up to you, so let’s look first to register the new indenter in the Kate Part (see next section).

Step II: Register the Indenter

To make Kate Part aware of the new indenter you have to modify the base class KateAutoIndent.

Open the file kateconfig.h and add an entry to the enumeration IndentationMode (let’s call it imMyIndent). Then overwrite the function modeNumber() in your indenter, like:

virtual uint modeNumber() const { return KateDocumentConfig::imMyIndent; }

In the file kateautoindent.cpp add imMyIndent to the list in the function createIndenter, like for example

KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode) {
// ...
else if ( mode == KateDocumentConfig::imMyIndent )
  return new KateMyIndent ( doc );
// ...
}

Do the same in the functions

QStringList KateAutoIndent::listModes () { ... }
QString KateAutoIndent::modeName (uint mode) { ... }
QString KateAutoIndent::modeDescription (uint mode) { ... }
uint KateAutoIndent::modeNumber (const QString &name) { ... }

Now the indenter should already appear in Settings > Configure Kate > Indentation in the combo box Indentation Mode.

Step III: Real Implemenation

Now still missing is the implementation of the new indenter.

The class declaration: class KateMyIndent : public KateNormalIndent { public: KateMyIndent (KateDocument *doc); ~KateMyIndent ();

If your indenter supports indentation when the users hits enter or return, canProcessNewLine() must return true.

virtual bool canProcessNewLine () const { return true; }

The same applies to line indentation. If the user indents a line and your indenter supports this sort of indentation canProcessLine must return true.

 virtual bool canProcessLine() const { return true; }

processNewline() is called everytime a user inserts a newline-character.

 virtual void processNewline (KateDocCursor &begin, bool needContinue);

processChar() is called everytime a user types a character. You’d need this for deindenting the character ‘}’ in realtime for example.

    virtual void processChar (QChar c);

processLine() and processSection() are called if the user indends a line or multiple lines.

    virtual void processLine (KateDocCursor &line);
    virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);

    virtual uint modeNumber () const { return KateDocumentConfig::imMyIndent; };
// other stuff maybe...
};

You can insert fillers (like tabs or spaces) with doc->insertText(int line, int column, const QString& text). Look into the code to see what the API provides. Indentation is tricky, to make your indenter work in all cases is hard to achieve. So happy testing.

Step IV: Indenter specific Config Page

Since KDE 3.5 you can add an additional config page. As there are already many global options available you usually would not need this, however it may be that some very indenter specific settings should be available (like insert * for doxygen comments automatically). In such situations it’s wiser to add a special config page to not clutter the available config pages even more.

To propagate your indenter has an own config page return true in kateautoindent.cpp in

bool KateAutoIndent::hasConfigPage (uint mode) { ... }

Second you have to derive a class MyIndentConfigPage from IndenterConfigPage, the base looks like this:

class MyIndentConfigPage : public IndenterConfigPage
{
    Q_OBJECT

  public:
    MyIndentConfigPage ( QWidget *parent=0, const char *name=0 );
    virtual ~MyIndentConfigPage ();

  public slots:
    /** Apply changes here. */
    virtual void apply ();
};

As you see, you have to override the abstract virtual function apply(), which will be called if the user clicks OK (instead of Cancel). So here you can save the changes made. Finally return an instance of your config page in

IndenterConfigPage* KateAutoIndent::configPage(QWidget *parent, uint mode) { ... }

As there is not necessarily an instance of your indenter you cannot save settings directly in the indenter. The way to go right now is to get the application’s global KConfig* and save the settings into an own group like [Kate Indenter MyIndent]. The Kate prefix is important to not clash with other config groups the application embedding the Kate Part is using. Get the config like

KConfig* config = kapp->config();

Step V: Fine-tuning - Kate Variables

The Kate Part has a lot of options which can change during the editing session, important is that the indenter always uses the most recent settings. For example, if you use tabs to indent and change this to use spaces, the indenter should recognize this change and use spaces instead of tabs. An option can change because the user changes it

  • in the settings dialog (GUI)
  • by using a Command Line command or
  • by save/reload the current document (the document’s Modelines might have changed).

The class Kate::Document provides the signal

void Document::variableChanged( const QString& variable, const QString& value );

You can connect this signal to a slot (in the indenter’s constructor). The variable provides the changed option, and the value its new value.

Note: It is also possible to ask for variables by using the function

QString Document::variable(const QString& variable) const;

This way you can also access variables/values that are not (officially) known by KatePart.

Reply

  • You can use Markdown syntax to format and style the text.
  • You may link to images on this site using a special syntax
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.