Skip to content

Latest commit

 

History

History
442 lines (335 loc) · 9.8 KB

File metadata and controls

442 lines (335 loc) · 9.8 KB
name kate-plugin
description Develop C++ plugins for Kate text editor - KTextEditor interface, CMake, Qt widgets, action system
metadata
author version tags
mte90
1.0.0
kate
kde
text-editor
plugin
c++
qt
kde-frameworks

Kate Plugin Development

Develop C++ plugins for Kate text editor using KTextEditor interface.

Overview

Kate (KDE Advanced Text Editor) is a powerful programmable editor. Plugins extend functionality through C++ using the KTextEditor framework.

Note: PyKDE6 does not exist. Kate plugins are developed in C++ using the native KTextEditor API.

Plugin Types

  • KTextEditor Plugins - C++ plugins using KTextEditor interface (primary method)
  • LSP Plugins - Language Server Protocol clients
  • Python Scripts - Limited scripting via console (not full plugins)

Plugin Locations

# System plugins
/usr/share/kate/plugins/

# User plugins
~/.local/share/kate/plugins/
~/.kde/share/kate/plugins/

KTextEditor API

Document Interface

from PyKDE6 import ktexteditor
from PyQt6.QtCore import QObject

class MyPlugin(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.doc = None
    
    def setDocument(self, doc: ktexteditor.Document):
        self.doc = doc
    
    # Document methods
    def insertText(self, text: str):
        self.doc.insertText(text)
    
    def getText(self) -> str:
        return self.doc.text()
    
    def getLine(self, line: int) -> str:
        return self.doc.line(line)
    
    def getCursor(self) -> ktextEditor.Cursor:
        return self.doc.cursorPosition()

View Interface

class MyPlugin(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.view = None
    
    def setView(self, view: ktexteditor.View):
        self.view = view
    
    # View methods
    def getSelection(self) -> str:
        return self.view.selectionText()
    
    def setSelection(self, start: Cursor, end: Cursor):
        self.view.setSelection(start, end)
    
    def gotoCursor(self, line: int, col: int):
        cursor = ktexteditor.Cursor(line, col)
        self.view.setCursorPosition(cursor)
    
    def getActiveLanguage(self) -> str:
        return self.view.activeLanguageMode()

Cursor and Range

from PyKDE6 import ktexteditor

# Create cursor (line, column)
cursor = ktexteditor.Cursor(10, 5)

# Create range
start = ktexteditor.Cursor(10, 0)
end = ktexteditor.Cursor(10, 20)
range_ = ktexteditor.Range(start, end)

# Cursor methods
line = cursor.line()
col = cursor.column()

# Range methods
start = range_.start()
end = range_.end()
text = range_.text(document)  # Get text in range

Plugin Structure

Python Plugin Layout

my-plugin/
├── plugin/
│   ├── __init__.py
│   └── myplugin.py
├── ui/
│   └── mydialog.ui
├── ../../../metadata.json
└── ../../../myplugin.desktop

metadata.json

{
    "KPlugin": {
        "Name": "My Plugin",
        "Comment": "Description of my plugin",
        "Icon": "kate",
        "Authors": [
            {
                "Name": "Your Name",
                "Email": "you@example.com"
            }
        ],
        "Version": "1.0.0",
        "License": "MIT",
        "ServiceTypes": [
            "Kate/Plugin"
        ],
        "Category": "Editor",
        "ApiVersion": "2.0"
    }
}

Desktop File

[Desktop Entry]
Name=My Plugin
Comment=A brief description
Icon=kate
Type=Service
X-KDE-Library=myplugin
X-KDE-PluginKeyword=myplugin
X-KDE-ParentApp=kate
X-KDE-InitFunction=load_myplugin
X-KDE-UnloadFunction=unload_myplugin

Python Plugin Class

from PyKDE6 import ktexteditor
from PyQt6.QtCore import QObject, pyqtSlot

class MyPlugin(ktexteditor.Plugin):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.widgets = []
    
    def addViewWidget(self, mainwindow):
        """Called when plugin is activated"""
        widget = MyPluginWidget(mainwindow)
        mainwindow.addToolView(widget)
        self.widgets.append(widget)
        return widget
    
    def removeViewWidget(self, widget):
        """Called when plugin is deactivated"""
        widget.mainWindow().removeToolView(widget)
        self.widgets.remove(widget)
    
    def readProperties(self, group, file):
        """Load settings"""
        self.enabled = group.readEntry("Enabled", True)
    
    def writeProperties(self, group, file):
        """Save settings"""
        group.writeEntry("Enabled", self.enabled)

JavaScript Plugins

Action Scripting

// In Kate's console or .katescript files

// Basic editing
view.insertText("Hello, World!");
var selected = view.selectionText();
document.setText("New document content");

// Cursor navigation
view.setCursorPosition(10, 5);
var cursor = view.cursorPosition();

// Selection
view.select(cursor, new Cursor(10, 20));

// Search and replace
view.search("find", Cursor(0, 0));
view.replace("find", "replace");

Key Shortcuts

// Define keyboard shortcuts
// Edit > Shortcuts > Custom Shortcuts

Examples

Simple Insert Plugin

from PyKDE6 import ktexteditor
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton

class SimpleInsertPlugin(ktexteditor.Plugin):
    def __init__(self, parent=None):
        super().__init__(parent)
    
    def addViewWidget(self, mainwindow):
        widget = QWidget()
        layout = QVBoxLayout()
        
        btn = QPushButton("Insert Timestamp")
        btn.clicked.connect(lambda: self.insertTimestamp(mainwindow))
        layout.addWidget(btn)
        
        widget.setLayout(layout)
        mainwindow.addToolView(widget)
        return widget
    
    def insertTimestamp(self, mainwindow):
        from datetime import datetime
        view = mainwindow.activeView()
        if view:
            view.insertText(datetime.now().isoformat())

Find and Replace Plugin

class FindReplacePlugin(ktexteditor.Plugin):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.range = None
    
    def findNext(self, view, text):
        doc = view.document()
        cursor = view.cursorPosition()
        
        # Search from cursor
        result = doc.searchText(text, cursor)
        
        if result.isValid():
            view.setSelection(result)
            view.setCursorPosition(result.end())
            return True
        return False
    
    def replace(self, view, find, replace):
        if view.hasSelection():
            selected = view.selectionText()
            if selected == find:
                view.removeSelectionText()
                view.insertText(replace)
                return True
        return False

Configuration

Settings Integration

class MyPlugin(ktexteditor.Plugin):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.config = {}
    
    def createConfigPage(self, parent, alias):
        """Create configuration page"""
        from PyQt6.QtWidgets import QFormLayout, QLineEdit, QWidget
        
        page = QWidget(parent)
        layout = QFormLayout()
        
        self.pathInput = QLineEdit()
        self.pathInput.setText(self.config.get("path", ""))
        layout.addRow("Path:", self.pathInput)
        
        page.setLayout(layout)
        return page
    
    def applyConfig(self):
        """Apply configuration changes"""
        self.config["path"] = self.pathInput.text()

Best Practices

1. Initialize Properly

# Good: Check for valid document
def do_something(self, view):
    if view and view.document():
        doc = view.document()
        # Work with document

2. Handle Errors

# Good: Error handling
try:
    view.insertText(text)
except Exception as e:
    print(f"Error inserting text: {e}")

3. Use Signals

# Connect to document signals
doc.textChanged.connect(self.onTextChanged)
doc.cursorPositionChanged.connect(self.onCursorMoved)

Advanced Topics

KTextEditor Interface

// Document interface - text manipulation
KTextEditor::Document* doc = view->document();

// Insert text at cursor
doc->insertText(cursor, "text");

// Replace range
doc->replaceText(start, end, "new text");

// Search and replace
doc->setSearchText("pattern", KTextEditor::Search::CaseInsensitive);

View Interface

// Cursor management
KTextEditor::Cursor cursor = view->cursorPosition();
view->setCursorPosition(cursor);

// Selection
view->selection();
view->setSelection(range);
view->clearSelection();

// Folding
view->toggleFolding(idx);

Advanced Features

// Variables (user-configurable settings)
KTextEditor::VariableInterface* varIface = qobject_cast<KTextEditor::VariableInterface*>(doc);
QVariant value = varIface->variable("my-var");

// Plugins can register:
# Document: new editing features
# View: visual enhancements  
# Tools: integrated utilities
# Tolls: additional functionality

Best Practices

// ✅ GOOD: Use proper interfaces
KTextEditor::Editor* editor = KTextEditor::Editor::self();

// ✅ GOOD: Check for null
if (view && view->document()) {
    // ...
}

// ✅ GOOD: Use signals for async
connect(doc, &KTextEditor::Document::textChanged, this, &MyPlugin::onTextChanged);

// ❌ BAD: Direct manipulation without interfaces

Common Issues

Issue Solution
Plugin not loading Check JSON metadata, correct category
Crash on load Initialize in init() not constructor
Feature not working CheckKatePart version requirement

References