Nowadays kate has a few things implemented as Python plugins that can be useful to C++ programmers. Some of them (like working w/ comments) can be used wth Python, CMake, Bash or JS code as well. To start playing w/ them one has to enable the following plugins:

Then take a look to Pâté menu:

Few words about working with comments

  • Inline comment means that it is placed on a line w/ code. Just like on the screenshot above. To add it to the current line use Alt+D. The cursor will move to 60th (default) position and // will be added. To transform it into a doxygen comment (///<) just press one / (this part works w/ my C++ not-quite-indenter™ ;-) To configure the default position use the Commentar plugin config page (take a look at the pages available at the first screenshot). Pressing on a selection will add (if possible) inline comments to every selected line.

  • To move inline comment to a line above use Meta+Left and Meta+Right to move it back

  • The next example shows how to Comment Block with #if 0, Toggle #if0/#if1, and Remove #if 0 part

  • Transform Doxygen Comments (between /** ... */ and /// forms) and Shrink/Expand Comment Paragraph

Boost MPL-like parameters fold/unfold

To format template or function parameters in the boost (MPL) style, move cursor inside the parenthesis or angle brackets and use Boost Like Format Params (Meta+F) action from the Pâté menu. Every time invoked this action will expand the nesting level. To unfold parameters use the reverse Unformat (Meta+Shift+F) action.

Using expand plugin

Since long time ago in a galaxy far, far away Pâté had the expand plugin. The (initial) idea behind it was quite simple: a user writes a function which returns a string to be inserted into the document (by means of an Expand action) instead of the corresponding function name under the cursor. Expand functions may have parameters. Running in a context of embedded Python, these functions may access kate’s API. There is however another benefit - this still is a fully functional Python with a lot of third party libraries.

Trivial example

The “Hello World” demo function is as simple as

def hi():
    return 'Hello from expand plugin'

To make this expand available put this function into a file named after a MIME type, where / replaced with _. For example use ~/.kde4/share/apps/kate/pate/expand/text_html.expand to make it available in a HTML documents or text_x-python.expand for Python source code.

Then open a document with a corresponding MIME type, add hi text and press Ctrl+E (default) shortcut – hi will be replaced w/ Hello from expand plugin text.

Lets rewrite the code a little and introduce an optional parameter:

def hi(name=None):
    return 'Hello {}'.format(name if name is not None else 'anonymous')

To pass a parameter type hi(John) + Ctrl+E. Note, that the expand function may have positional and named parameters, just like any other ordinary python function.

Level 2: Templating templates

Kate has a builtin template engine. Snippets and file templates use it. Now, expand can use it as well :) Yeah, the string 'Hello {}' can be considered as a level 1 template – a name parameter will be substituted into it. The level 2 is to allow to edit it after expansion, just like with the snippets plugin (yeah, it is actually a plugin ;-). All you need is to return a string with an editable field, i.e. smth like 'Hello ${name:John}' – here is an editable field name with a “predefined” value John which is an actual parameter to the expand function hi ;-) Also we ought to tell the expand plugin that the result string must be inserted into the document via KTexeEditor::TemplateInterface, so we use a Python decorator postprocess from the module expand:

import expand

@expand.postprocess
def hi(name=None):
    return 'Hello ${{name:{}}}'.format(name if name is not None else 'anonymous')

This way a partly editable piece of code can be inserted! Yeah, right, just like these snippets from a plugin… But wait! “Why the heck we’ve just reinvented the wheel?” – one may ask ;-) The benefit is that we can return any template we want! – i.e. not just a static text with a few editable holes (which is what a “classic” snippet is)! What to return can be (trans)formed according to the expansion function parameters… and this is a real advantage over snippets! :-) Still do not believe? Here is a real world example: lets write an expansion function to insert structure definitions to C++ code (it doesn’t use editable fields for simplicity):

_BRIEF_DOC_TPL = '''\
/**
 * \\brief Struct \\c {0}
 */'''

_TEMPLATE_PARAMS_TPL = '''
template <{1}>'''

_STRUCT_BODY_TPL = '''
struct {0}
{{
}};
'''

def st(name, *templateParams):
    params = [name]
    if len(templateParams):
        template = _BRIEF_DOC_TPL + _TEMPLATE_PARAMS_TPL + _STRUCT_BODY_TPL
        params.append('typename ' + ', typename '.join(templateParams))
    else:
        template = _BRIEF_DOC_TPL + _STRUCT_BODY_TPL
    return template.format(*params)

This pretty simple (and quite short) function can do something, that snippets just can’t!

// st(test) expands to

/**
 * \brief Struct \c test
 */
struct test
{
};


// but st(templated_test, T, U) expands to

/**
 * \brief Struct \c templated_test
 */
template <typename T, typename U>
struct templated_test
{
};

The only mandatory parameter (the first one) is a structure name to insert. The rest are optional, if present they are turned into template parameters… That is how st function looked like before KDE SC 4.13.

But soon after trying to use the built-in Python’s format function with “template” strings intensively, I realized that it is time to use a real template engine ;-)

Level 3: Metatemplates

Template engines are widely used in web programming to split business logic from a representation. One of the famous for Python is jinja. Yep, it is not an all-inclusive-framework… just a template engine ;-) and now we can use it with the expand plugin.

Lets rewrite the structure expansion function using jinja templates:

import expand

@expand.jinja('struct.cpp.tpl')
@expand.postprocess
def st(name=None, *templateParams):
    return {'name': name, 'template_params': templateParams}

Yeah, that’s it! :-) Just return a Python dictionary with arbitrary keys. These keys will be used in a template file later, so how to name them is up to you. The representation (template) file (struct.cpp.tpl), mentioned in the @expand.jinja decorator call, must be placed in a ${KDEDIR}/apps/kate/pate/expand/templates/ dir, “near” the corresponding .expand plugin file. .

/**
 * \brief Struct /*< name | editable >*/
 */
/*% if template_params %*/
template <
    /*% for tp in template_params %*/
        /*% if not loop.first %*/, /*% endif %*/typename /*< tp | editable >*/
    /*% endfor %*/
  >
/*% endif %*/
struct /*< name | editable(active=True) >*/
{
    ${cursor}
};
//# kate: hl C++; remove-trailing-spaces false;

where,

  • /*< and >*/ open/close sequences to substitute a variable – yeah, one of the keys in a dictionary, returned by the expand function
  • /*% and %*/ open/close sequence for jijna control tags (like if, endif, for, endfor)
  • //# is a short or single-line form of a comment inside a template file
  • next, one custom filter is used: editable – to transform a value of a variable (key) into an editable KTE::TemplateInterface field after the snippet gets inserted. Also note that the structure name is present both in the comment and in the struct declaration line, but only the second one will be “editable”, the other “copies” are just mirrors in terms of KTE::TemplateInterface
  • everything else, i.e. a text out of open/close sequences, is just a text to be rendered and inserted into a document
  • both open and close sequences are chosen for a reason: since it is a template for the C++ code, it would be nice to have some highlighting and the chosen sequences are subsets of plain C comments ;-) Surely, it can be configured for other languages as well…

To reduce syntactic noise For demonstration purposes I've removed whitespace control characters from jinja tags.

Here are a couple more decorators defined in the expand module for use by expand functions: @expand.description and @expand.details. Both accept a string to be displayed in a completion popup. The first one is a short description. The second one is the details (or usage) text accessible by pressing an Alt key for the selected completion item. Because most of the completions are really short names, completions will not appear in automatic mode, so in order to see them, one has to bring up this popup manually. There are a few expansion functions available for C++ code out-of-the-box. The most complicated one (but powerful compared to trivial snippets) is cl. It is capable to generate template or non-template classes, possibly with a constructor with zero (default) or more argumentis, maybe with defaulted or deleted copy and/or move constructor and/or assign operator, maybe with a virtual destructor, where the class name and the possible template and/or constructor parameters are (post)editable fields. All parameters, and also the expand function’s name should be really short to reduce the typing effort and be memorizable.

One example is worth more than 1K words… lets generate a class with:

  • template parameter Iter followed by T0, T1, T2, T3
  • default constructor
  • constructor accepting two parameters
  • disabled copy constructor
  • defaulted move constructor
  • and virtual destructor
// cl(some,@c,c2,-cc,@mc,vd,t,Iter,T0..3)
// the result is:

/**
 * \brief Class \c some
 */
template <typename Iter, typename T0, typename T1, typename T2, typename T3>
class some
{
public:
    /// Default constructor
    some() = default;
    /// Make an instance using given parameters
    some(param0, param1)
    {
    }
    /// Copy constructor
    some(const some&) = delete;
    /// Copy assign
    some& operator=(const some&) = delete;
    /// Move constructor
    some(some&&) = default;
    /// Move assign
    some& operator=(some&&) = default;
    /// Destroy an instance of some
    virtual ~some()
    {
    }
};

I will not go into details about other functions – it is easy enough to read the descriptions/sources and play with them. But I have one more final damn cool feature to tell about ;-)

Level 80: God Mode

This feature came into my mind after reminiscing «Mortal Combat» and/or «Doom». The way to use it came from these games: one has to press the exact key sequence like cheat codes in a game ;-) The expand plugin tracks single key press events and if it finds a cheat magic code among the last consequently pressed keys, it will replace it with a result.

Important Usage Notes

  • every magic sequence starts and ends with a semicolon
  • if you type something wrong, you can't "fix" it with cursor movement keys + delete/backspace keys!
  • you have to type «cheat codes» without "errors" from start to end
  • so remove the wrong text and start over again… repeat until you can type them as if it were an unconditioned reflex ;-)

To add your own dynamic expand function one has to use the @expand.dynamic decorator, which accepts a compiled regular expression. If the latter matches the typed key sequence, a magic function will be executed and passed two parameters: a source “magic” sequence (string) and a result of re.search() (which is some match object). Here is an example of that kind of function to expand an enum definition:

@expand.dynamic(re.compile('^e(?P<is_class>c)?(?P<has_type>:)?'))
@expand.jinja('enum.cpp.tpl')
@expand.postprocess
def __dynamic_enum(params, match):
    data = {}
    data['is_class'] = match.group('is_class') is not None
    data['has_type'] = match.group('has_type') is not None
    return data

Yeah, it is quite similar to the “usual” (jijna templated) functions except for a one more decorator ;-)

Below is a list of available dynamic snippets for C++. Every item starts with a simplified graphic image of a regex (for visualization and better remembering ;-) that is used to match that snippet.

;cl; Synopsis

Insert a class declaration. Depending on the options it is capable to add a default, parameterized, copy, move constructors and/or destructor. Also the class can be templated with a desired number of parameters.

Examples:

  • ;clcd; class with default constructor and destructor
  • ;clc2; class with constructor with 2 parameters
  • ;clc1@cc@mc; class with “conversion” constructor (single parameter), defaulted copy and move constructor/assign
  • ;cl@mv-cc; class with defaulted move constructor/assign and delete copy constructor/assign
  • ;clt2vd; class with two template parameters and a virtual destructor

;e; Synopsis

Examples:

  • ;e; insert C++03 enum declaration
  • ;ec; insert C++11 strong typed enum declaration
  • ;ec:; insert C++11 strong typed enum declaration with the type specified

;fn; Synopsis

Insert a function declaration or definition, if a dynamic snippet ends with { character. Function may have runtime and/or template parameters, as well as various modifiers.

Examples:

  • ;fnt1; void function with one template parameter
  • ;fn2s; function with a static modifier and 2 parameters
  • ;fn1vfoc; virtual function with 1 parameter and final, override and const modifiers

;for; Synopsis

Generate a for statement with various flavors. It is capable to expand into a range-based for or more “traditional” iterator-based. The latter can use either C++03 or C++11 semantics – i.e. when begin() is a member or a free function, possibly matched by ADL. The type for the range-based for can be const and/or ref/ref-ref qualified.

Examples:

  • ;fa; range-based for with the auto type and no body
  • ;fa&&{rng; range-based for with auto&& type over some rng and an empty body
  • ;fori3c{var; loop using const_iterators and C++03 syntax over var container

;ns; Synopsis

Generate an anonymous or named (possibly nested) namespace.

Examples:

  • ;ns; insert anonymous namespace
  • ;nskate::details; insert a kate namespace and one nested details

;st; Synopsis

Generate a simple struct possibly with a given number of template parameters.

;sw; Synopsis

Generate a switch statement with a desired number of case statements and possibly with a default one.

;try; Synopsis

Insert a try block with a desired number of catch blocks and possible a catch (...).

Dynamic Snippets Short Demo



blog comments powered by Disqus