API wtyczek — przegląd¶
Wszystkie typy PluginApi żyją w namespace MTE::plugin. Publiczne nagłówki nie
mogą dołączać żadnego nagłówka Qt, Scintilli ani wewnętrznego nagłówka MTE — tylko
bibliotekę standardową. Jedynym celowym wyjątkiem jest opcjonalny folder
PluginApi/Qt/ (PluginQtGlue.h, ISettingsPage.h, ThemeFormat.h) dla wtyczek,
które dostarczają własne widżety Qt.
Ten sam kontrakt realizują dwa backendy: natywny loader Qt
(biblioteki współdzielone) oraz działający poza procesem backend Pythona
(Wtyczki w Pythonie). Ta sekcja dokumentuje
powierzchnię C++; każda opisana tu usługa niezależna od backendu ma swój
odpowiednik w Pythonie na ctx.
Cykl życia wtyczki¶
Wtyczka natywna implementuje jedną klasę:
class IPlugin {
public:
virtual PluginInfo info() const = 0; // id, name, version, apiVersion
virtual bool initialize(IPluginContext& ctx) = 0; // register everything here
virtual void shutdown() noexcept = 0; // release every token
};
info()musi zwrócić te sameidiapiVersioncoplugin.json; host weryfikuje zgodność i przy rozbieżności odmawia załadowania.initialize()uruchamiane jest raz przy starcie (po wykryciu wszystkich wtyczek). Tutaj rejestruj polecenia, menu, providery i panele; zwróćfalsetylko przy faktycznym błędzie (host wyładuje wtyczkę i to zaloguje).shutdown()uruchamiane jest przy wyjściu (lub gdy wtyczka jest wyładowywana). Nie może rzucać wyjątków; wykonajreset()na każdym tokenie i wyzeruj przechowywane wskaźniki do usług.
Tokeny RAII¶
Każda rejestracja zwraca token (CommandToken, MenuToken, DockToken,
SettingsPageToken, Subscription, …) — przenoszalny (move-only) BasicToken,
który jest właścicielem rejestracji: zniszczenie go lub wywołanie reset()
cofa rejestrację polecenia / pozycji menu / panelu / subskrypcji. Trzymaj każdy
token jako pole składowe i zwalniaj go w shutdown(). Istotne są dwa akcesory:
id()— nieniszczący widokTokenId, dla API, które adresują element bez przejmowania go (np.IMenuRegistry::setItemChecked).release()— odłącza token i zwraca id; token nie cofa już żadnej rejestracji. Rzadko tego chcesz.
Lokator usług¶
initialize() otrzymuje IPluginContext — lokator usług, którego właścicielem
jest host. Nie przechowuj kontekstu po initialize(); przechowuj referencje
do poszczególnych potrzebnych usług (każda usługa żyje dłużej niż każda wtyczka).
| Akcesor | Interfejs | Zastosowanie |
|---|---|---|
ctx.editor() |
IEditorService |
odczyt/edycja bufora, zaznaczenie, karetka, pliki, wyszukiwanie, zakładki, wcięcia, grupy widoku podzielonego |
ctx.commands() |
ICommandRegistry |
rejestrowanie / wywoływanie akcji użytkownika (ze skrótami) |
ctx.menus() |
IMenuRegistry |
pozycje menu, separatory, przełączniki z zaznaczeniem |
ctx.docks() |
IDockRegistry |
dokowane panele boczne |
ctx.documents() |
IDocumentTypeRegistry |
pełnoprawne typy kart MainWindow (diff, widok logów, …) |
ctx.compare() |
ICompareService |
otwieranie dwupanelowego widoku porównania |
ctx.diagnostics() |
IDiagnosticsSink |
publikowanie podkreśleń + glifów na marginesie |
ctx.events() |
IEventBus |
subskrypcja / publikacja zdarzeń edytora |
ctx.completions() |
ICompletionRegistry |
dostarczanie kandydatów auto-uzupełniania |
ctx.contextMenu() |
IContextMenuRegistry |
dynamiczne pozycje w menu prawego przycisku edytora |
ctx.snippetSessions() |
ISnippetSessionService |
dostarczanie snippetów rozwijanych Tabem (host prowadzi sesję pól) |
ctx.hover() |
IHoverRegistry |
dymki podpowiedzi (hover) |
ctx.signatureHelp() |
ISignatureHelpRegistry |
podpowiedzi wywołań przy ( / , |
ctx.symbols() |
ISymbolRegistry |
symbole dokumentu / przestrzeni roboczej (go-to-symbol) |
ctx.statusBar() |
IStatusBar |
komunikaty tymczasowe + trwałe segmenty |
ctx.settings() |
ISettings |
trwała konfiguracja klucz/wartość per wtyczka |
ctx.appSettings() |
const ISettings& |
odczyt ustawień globalnych aplikacji (AppSettingsKeys.h) |
ctx.documentSettings() |
IDocumentSettings |
warstwowa konfiguracja per dokument z .editorconfig |
ctx.settingsRegistry() |
ISettingsRegistry |
dodanie strony do okna Preferencji |
ctx.themePalette() |
IThemePalette |
nazwane kolory motywu, integracja z edytorem motywów |
ctx.log() |
ILogger |
trace / debug / info / warning / error |
ctx.pluginDataDir() |
std::filesystem::path |
Twój prywatny katalog z prawem zapisu |
ctx.diagnosticsDir() |
std::filesystem::path |
wspólny katalog artefaktów awarii + logów (dla wtyczek tylko do odczytu) |
Późniejsze rozszerzanie API oznacza dodanie nowego interfejsu usługi i akcesora; istniejące wtyczki pozostają zgodne źródłowo i na poziomie ABI.
IEditorService — model dokumentu¶
Edytor widziany oczami wtyczek — bez Scintilli, bez Qt. Dokumenty adresowane są
nieprzezroczystym DocumentId (std::uint64_t, 0 = nieprawidłowy). Offsety to
offsety bajtowe w tekście UTF-8; MatchRange to przedział półotwarty
{start, end}, gdzie noMatch() oznacza „nie znaleziono"; EditorPosition to
liczony od 0 {line, column}.
// Documents
DocumentId activeDocument() const;
std::vector<DocumentId> openDocuments() const;
void setActiveDocument(DocumentId);
std::optional<std::filesystem::path> filePath(DocumentId) const;
DocumentId openFile(std::filesystem::path);
bool saveDocument(DocumentId);
bool isModified(DocumentId) const;
// Text, selection, caret
std::string text(DocumentId) const; // whole buffer
std::string textRange(DocumentId, start, end) const;
void setText(DocumentId, std::string_view);
std::int64_t length(DocumentId) const;
std::string selectedText(DocumentId) const;
void replaceSelection(DocumentId, std::string_view);
MatchRange selectionRange(DocumentId) const;
void setSelection(DocumentId, MatchRange);
EditorPosition caret(DocumentId) const;
void setCaret(DocumentId, EditorPosition);
// Search & replace (SearchOptions: case, whole-word, regex, wrap)
MatchRange findInRange(DocumentId, needle, range, SearchOptions) const;
void replaceTarget(DocumentId, MatchRange, std::string_view);
// Bookmarks, marks, URL highlighting
void toggleBookmark(DocumentId, line); std::vector<std::int64_t> bookmarkedLines(DocumentId) const;
void markRange(DocumentId, MatchRange); void clearMarks(DocumentId);
// Language & indentation
std::string colorizerId(DocumentId) const; // "cpp", "" if none
IndentationStyle indentation(DocumentId) const; // {useTabs, width}
void setIndentation(DocumentId, bool useTabs, int width);
// Semantic classification (from the active colorizer's style rules;
// cheap -- no re-lex). CategorySpan = {start, length, SyntaxCategory}.
// The spell checker keeps to Comment/Documentation/String spans this way.
std::vector<CategorySpan> textCategories(DocumentId, start, end) const;
// Clipboard
std::string clipboardText() const;
Udostępnia też układ grup kart widoku podzielonego w terminach niezależnych od
backendu (groupCount(), groupOf(), groupWeights(), moveDocumentToGroup(),
setGroupWeights()), więc wtyczka może zapisać i odtworzyć rozmieszczenie paneli,
nie widząc splittera Qt. Pełna lista w PluginApi/IEditorService.h — nagłówek
jest referencją rozstrzygającą.
Wątki
Wszystkie wywołania kontekstu i usług odbywają się w wątku UI hosta. Nie wywołuj usług z uruchomionego przez siebie wątku w tle; najpierw wróć do wątku UI.
Wersjonowanie¶
MTE_PLUGIN_API_VERSION (obecnie 1) zdefiniowane jest w PluginApi/PluginInfo.h.
Wtyczka musi deklarować tę samą wartość zarówno w plugin.json, jak i w
IPlugin::info().apiVersion, inaczej host odmówi jej załadowania.
Polityka przedpremierowa
Przed pierwszym publicznym wydaniem kontrakt może się jeszcze zmieniać bez
podbijania MTE_PLUGIN_API_VERSION. Po wydaniu każda niekompatybilna wstecz
zmiana istniejącego interfejsu podbija wersję.