Scripting KatePart with JavaScript

KatePart gained scripting support in version 2.5 (KDE 3.5) using the common language JavaScript (ECMAScript).

First some basics: A katepart represents a document and one or more views - that’s all. Scripts can access the document and the active view. As JavaScript supports functions and properties every function will be marked with a [function] and every property with a [property:read only].

Global Functions

debug

debug( string ); [function]
  • string: The string to output.

Outputs the string to STDERR using kdDebug(). A dedicated output area is used for the output, which will be prefixed Kate (KJS Scripts):.

The document API

The document provides basic functions to modify itself, like get or set a specific text line. The following list explains all available functions and properties, each one can be accessed by document.function:

attribute

document.attribute( uint line, uint column ); [function]
  • line: The line of the position for which to find the attribute.
  • column: The column of the position for which to find the attribute.

Returns the numeric ID of the attribute for the document position [line,col]. The attribute represents the visual appearance or style of the text, and is also used to calculate the syntax highlight for a specific part of the text in mixed formats like HTML or PHP.

canBreakAt

document.canBreakAt( Char c, uint attribute ); [function]
  • c: The character to test
  • attribute: The attribute at the position of c.

Returns whether it is allowed to break the line at a character c with attribute attribute. The result is decided by querying the highlight owning attribute for which characters allow breaking the line.

canComment

document.canComment( uint start_attribute, uint end_attribute ); [function]
  • start_attribute: The attribute at the start of the range to turn into a comment.
  • end_attribute: The attribute at end of the range to turn into a comment.

Returns whether startattribute and endattribute belongs to the same syntax highlight system. If they do, it is sane to make a range with startattribute at the start and endattribute at the end into a comment.

Example

if ( document.canComment( document.attribute(1,0), document.attribute(5,0) ) ) { // 1,0 and 5,0 belongs to the same syntax highlighting system }

clear

document.clear(); [function]

Clears the document.

commentStart

document.commentStart( uint attribute ); [function]
  • attribute: The attribute of the text for which to get the commentStart string

Returns the string required to start a multiline comment for a text with attribute, or an empty string if multiline comments are not supported for that text.

commentMarker

document.commentMarker( uint attribute ); [function]
  • attribute: The attribute of the text for which to get the commentMarker string

Returns the string used to mark the rest of the line as a comment for a text with attribute or an empty string if single line comments are not supported for that text.

commentEnd

document.commentEnd( uint attribute ); [function]
  • attribute: The attribute of the text for which to get the commentEnd string

Returns the string required to end a multiline comment for a text with attribute, or an empty string if multiline comments are not supported for that text.

editBegin

document.editBegin(); [function]

Start an editing group. All actions done until the call of editEnd() will be grouped as one undo-action.

editEnd

document.editEnd(); [function]

Finish an editing group.

highlightMode

document.highlightMode; [property:read only]

The name of the document’s highlight mode, such as JavaScript or C++. If no syntax highlight mode is set for the document, the value is None. Notice that you need to use the English name in cases where it differs from the translated one.

indentMode

document.indentMode; [property:read only]

The name of the document indent mode, such as <code>normal</code>' orcstyle’. Remember that if no indent mode is set, the value is `none’.

indentWidth

document.indentWidth; [property:read only]

The indentation width set for the document. This is used if space indenting is enabled.

insertLine

document.insertLine( uint line, string text ); [function]
  • line: document line
  • text: text

Inserts the text at the line.

insertText

document.insertText( uint line, uint column, string text ); [function]
  • line: the line number
  • column: the column
  • text: the text which is to be inserted

Inserts the text in line and column.

length

document.length(); [function]

Returns the document’s size in bytes.

lineLength

document.lineLength( uint line ); [function]
  • line: the line

Returns the number of characters in line.

lines

document.lines(); [function]

Returns the number of lines in the document.

mixedIndent

document.mixedIndent; [property:read only]

A boolean telling whether the mixed-indent setting is enabled for the document. If so, indentation is optimized to contain a mix of tab characters and spaces like used by the Emacs editor.

removeLine

document.removeLine( uint line ); [function]
  • line: line number

Removes the document line.

removeText

document.removeText( uint startLine, uint startColumn,
                  uint endLine, uint endColumn ); [function]
  • startLine: specifies the beginning line
  • startColumn: specifies the beginning column
  • endLine: specifies the ending line
  • endColumn: specifies the ending column

Removes the text range from line startLine and column startColumn up to line endLine and column endColumn.

setText

document.setText( string text ); [function]
  • text: document text

Sets the entire document content.

spaceIndent

document.spaceIndent; [property:read only]

A boolean telling whether space-indent is enabled for the document. If so, the document is indented with indentWidth spaces per level, otherwise indentation is one tab character per level.

textFull

document.textFull(); [function]

Returns the full document text. If the text spans over multiple lines the linefeed character is ‘\n’.

textLine

document.textLine( uint line ); [function]
  • line: the line

Returns the text of line.

textRange

document.textRange( uint startLine, uint startColumn,
                    uint endLine, uint endColumn ); [function]
  • startLine: specifies the beginning line
  • startColumn: specifies the beginning column
  • endLine: specifies the ending line
  • endColumn: specifies the ending column

Returns the specified text range. If the range spans over multiple lines the linefeed character is ‘\n’.

The view API

A document has one or more views. The object view represents the active view and exports the following functions and properties:

clearSelection

view.clearSelection(); [function]

Deselects all text.

cursorColumn

view.cursorColumn(); [function]

Returns the current cursor column.

cursorColumnReal

view.cursorColumnReal(); [function]

Returns the current real cursor column.

cursorLine

view.cursorLine(); [function]

Returns the current cursor line.

hasSelection

view.hasSelection(); [function]

Returns true if the view contains selected text, otherwise false.

removeSelectedText

view.removeSelectedText(); [function]

Removes the selected text, if the view has a selection.

selectAll

view.selectAll(); [function]

Selects all text.

selection

view.selection(); [function]

Returns the selected text. If the selection spans over multiple lines the linefeed character is ‘\n’.

selectionEndColumn

view.selectionEndColumn; [property:read only]

Returns the ending column of the selection.

selectionEndLine

view.selectionEndLine; [property:read only]

Returns the ending line of the selection.

selectionStartColumn

view.selectionStartColumn; [property:read only]

Returns the starting column of the selection.

selectionStartLine

view.selectionStartLine; [property:read only]

Returns the starting line of the selection.

setCursorPosition

view.setCursorPosition( uint line, uint column ); [function]
  • line: Specifies the line for the cursor.
  • column: Specifies the column for the cursor.

Sets the input cursor position in the view to [line,col]. This sets the cursor position by visual means, that is the a TAB character counts up to <tabwidth> depending on the position inside the line. The cursor position is made visible. Both line and column are zero-based.

setCursorPositionReal

view.setCursorPositionReal( uint line, uint column ); [function]
  • line: Specifies the line for the cursor.
  • column: Specifies the column for the cursor.

Sets the input cursor position to [line,col]. This sets the string position, that is a TAB character counts for 1. The cursor position is made visible. Both line and column are zero-based.

setSelection

view.setSelection( uint startLine, uint startColumn,
                   uint endLine, uint endColumn ); [function]
  • startLine: specifies the beginning line
  • startColumn: specifies the beginning column
  • endLine: specifies the ending line
  • endColumn: specifies the ending column

Sets a selection from line startLine and column startColumn up to line endLine and column endColumn.

Example

As an example we will create a small script that uppercases the selection. It is obvious that we first need to check whether a selection exists, if so we get the text, change the case and then replace it with the new one.

An implementation could look like this:

 if ( view.hasSelection() )
 {
   // uppercase selection
   column = view.selectionStartColumn;
   line = view.selectionStartLine;

   selection = view.selection().toUpperCase();

   document.editBegin();
   view.removeSelectedText();
   document.insertText( line, column, selection );
   document.editEnd();
 }

To group this action together we encapsulate the lines view.removeSelectedText() and document.insertText() with a document.editBegin() and document.editEnd().

Now save the file as for example uppercase.js and put it into the scripts folder (see next chapter).

Register scripts in katepart

The katepart searches the folders $KDEDIR/share/apps/katepart/scripts/ and ~/.kde/share/apps/katepart/scripts/ for .js files. For every file it checks whether there is a corresponding .desktop file, like for uppercase.js it would look for uppercase.desktop.

If a .desktop file can not be found the script will be registered in katepart’s command line with the filename without the ending .js, so in our example this would be uppercase. If the command-name is fine and you don’t need the extra features a .desktop file provides you do not need a .desktop file at all.

If a .desktop file exists katepart will read the name under which the script will be registered from the .desktop-entry X-Kate-Command, for example X-Kate-Command=uppercase-selection.

Additional Features using .desktop files

As already explained, with X-Kate-Command you can change the command line name. However, you can provide much more information, available settings are:

# Example of a .desktop file
[Desktop Entry]
Encoding=UTF-8
Name=Kate Part JavaScript Uppercase
Comment=Script to uppercase the selection
X-Kate-Command=uppercase-selection
X-Kate-Help=&lt;p&gt;Usage: &lt;code&gt;uppercase-selection&lt;/code&gt;&lt;/p&gt;

So you can define the Encoding, set a Name, a Comment, a help text using X-Kate-Help and the command line name via X-Kate-Command.

The entries Name, Comment and X-Kate-Help are automatically translated into other languages, if the files are in KDE’s SVN repository.

Comments

Base64 encode/decode

I have preserved a comment block written by the author of the encode64/decode64 functions. My contributions are also public domain.

base64.js

// This code was written by Tyler Akins and has been placed in the
// public domain.  It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com

var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function encode64(input)
{
  var output = "", output2 = "";
  var chr1, chr2, chr3;
  var enc1, enc2, enc3, enc4;
  var i = 0;

  do
  {
    chr1 = input.charCodeAt(i++);
    chr2 = input.charCodeAt(i++);
    chr3 = input.charCodeAt(i++);

    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;

    if      (isNaN(chr2)) enc3 = enc4 = 64;
    else if (isNaN(chr3)) enc4 = 64;

    output += keyStr.charAt(enc1) +
              keyStr.charAt(enc2) +
              keyStr.charAt(enc3) +
              keyStr.charAt(enc4);
  }
  while (i < input.length);

  i = 0;

  while (i < output.length)
  {
    if ((i > 0) && (i % 76 == 0)) output2 += '\n';
    output2 += output[i];
    i++;
  }

  if (output2[output2.length -1] != '\n')
    output2 += '\n';

  return output2;
}


if (!view.hasSelection()) view.selectAll();

c = view.selectionStartColumn;
l = view.selectionStartLine;
t = encode64(view.selection());
document.editBegin();
view.removeSelectedText();
document.insertText(l, c, t);
document.editEnd();

unbase64.js

// This code was written by Tyler Akins and has been placed in the
// public domain.  It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com

var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function decode64(input) {
   var output = "";
   var chr1, chr2, chr3;
   var enc1, enc2, enc3, enc4;
   var i = 0;

   // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
   input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

   do {
      enc1 = keyStr.indexOf(input.charAt(i++));
      enc2 = keyStr.indexOf(input.charAt(i++));
      enc3 = keyStr.indexOf(input.charAt(i++));
      enc4 = keyStr.indexOf(input.charAt(i++));

      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;

      output = output + String.fromCharCode(chr1);

      if (enc3 != 64) {
         output = output + String.fromCharCode(chr2);
      }
      if (enc4 != 64) {
         output = output + String.fromCharCode(chr3);
      }
   } while (i < input.length);

   return output;
}

if (!view.hasSelection()) view.selectAll();

c = view.selectionStartColumn;
l = view.selectionStartLine;
t = decode64(view.selection());
document.editBegin();
view.removeSelectedText();
document.insertText(l, c, t);
document.editEnd();

desktop file

What is the effect of the desktop file from above? Is it possible to add a menu item by such a desktop file?

bind the script to a shortcut

Same question here: Is there a way I could bind the script to a shortcut, and/or let it appear in a menu?

serlis http://blog.lsmnetworks.com/

Can you call external shell command from the script?

I would like to reformat the entire document so that the line width is, say, 60 characters. If I have file.txt, the following shell command will do:

fmt -w 60 file.txt

or alternatively using pipe

cat file.txt | fmt -w 60

How to do this in Kate? Ideally, I would like to be able to select text, pipe it into fmt, and replace the selection with the output of fmt.

Invoking the script

I can invoke the script by pressing F7 (the default key binding), writing its name, and pressing Enter.

This is rather lengthy. Is there a way I could bind the script to a shortcut, and/or let it appear in a menu (in Kate, for example)?

Invoking the script

It would be really simple to extend kate with scripts, although it is useless too, cause some simple scripts like deleting/copying/inserting a line aren’t useful without keybindings. I would really appreciate the option for keybindings to my own scripts.

Post new comment

  • 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.