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)
References
- KDE Documentation: https://docs.kde.org/
- Kate API: https://api.kde.org/
- KTextEditor: https://docs.kde.org/stable/en/kate/kate-part/
- KDE Developer: https://developer.kde.org/