Inteligencja językowa (diagnostyka, hover, signature help, symbole)¶
Mała, niezależna od backendu rodzina kontraktów dla inteligencji językowej —
danych, które provider dostarcza do powierzchni code intelligence edytora.
Realizuje projektową zasadę „wtyczki dostarczają dane, rdzeń renderuje":
podkreślenia, popup hovera i skoki karetki żyją w widoku edytora i nigdy nie są
udostępniane wtyczkom; wtyczka dostarcza wyłącznie dane POD/std::string, więc
ten sam kontrakt może później realizować backend Pythona.
Pierwszym konsumentem jest wtyczka LSP, ale kanały są generyczne — linter czy sprawdzanie pisowni mogą publikować diagnostykę w ten sam sposób.
Czego tu nie ma
Go-to-definition, find-references, zmiana nazwy i formatowanie nie
potrzebują żadnego własnego API — tylko nawigują lub edytują tekst, co
IEditorService już wyraża (caret, openFile, setCaret, setText),
więc pozostają poleceniami wewnątrz wtyczki. Uzupełnianie wykorzystuje
istniejący ICompletionRegistry. Nowy kontrakt dodawany jest tylko wtedy,
gdy istnieje rdzenna powierzchnia renderowania/wyboru i potencjalnie
kilku providerów — a tak jest w przypadku diagnostyki, hovera,
signature help i symboli.
Diagnostyka — push¶
Provider pcha (push) diagnostykę, kiedy tylko ją ma (sam decyduje, kiedy
wyniki są gotowe); edytor renderuje ją jako faliste podkreślenia. Dostęp do
ujścia przez IPluginContext::diagnostics(). Typy wartości to zwykłe POD
(PluginApi/DiagnosticTypes.h):
enum class DiagnosticSeverity { Error, Warning, Information, Hint };
struct Diagnostic {
MatchRange range; // UTF-8 byte range in the document
DiagnosticSeverity severity = DiagnosticSeverity::Error;
std::string message;
std::string code;
};
struct IDiagnosticsSink { // PluginApi/IDiagnosticsSink.h
virtual void publishDiagnostics(DocumentId doc, std::string_view source,
const std::vector<Diagnostic>& diags) = 0;
virtual void clearDiagnostics(DocumentId doc) = 0;
};
sourceto klucz podmiany per(doc, source). Publikacja dla danego źródła zastępuje jego poprzednią diagnostykę na dokumencie, więc wielu producentów (LSP, sprawdzanie pisowni, log builda) współistnieje bez nadpisywania się nawzajem.- Opublikuj pusty wektor dla źródła, aby wycofać jego diagnostykę.
- Poziomy ważności renderowane są odrębnymi kolorami (error/warning/info/hint); edytor poszerza też bardzo krótkie zakresy, aby znacznik jednoznakowy pozostał widoczny.
ctx.diagnostics().publishDiagnostics(doc, "org.mycompany.linter", {
{ byteRange, DiagnosticSeverity::Warning, "unused variable", "W001" },
});
Host zawsze dostarcza prawidłowe ujście (no-op, gdy edytor nie ma powierzchni
diagnostyki), więc nigdy nie musisz sprawdzać diagnostics() pod kątem null.
Hover — asynchroniczny pull¶
Hover jest sterowany przez edytor (zatrzymanie myszy nad symbolem). Wtyczka
rejestruje providera, który odpowiada asynchronicznie; dostęp do rejestru
przez IPluginContext::hover() (PluginApi/IHoverRegistry.h):
struct HoverRequest { DocumentId doc; std::int64_t position; }; // byte offset
using HoverReply = std::function<void(std::string markup)>; // empty = nothing
struct IHoverProvider {
virtual void requestHover(const HoverRequest&, HoverReply reply) = 0;
};
struct IHoverRegistry {
virtual HoverToken registerProvider(IHoverProvider& provider) = 0;
};
registerProviderzwracaHoverToken(RAII); zniszcz go wshutdown(), aby się wyrejestrować.- Przy zatrzymaniu kursora host odpytuje każdego zarejestrowanego providera i pokazuje pierwszą niepustą odpowiedź; odpowiedzi przychodzące po tym, jak użytkownik przeszedł dalej, są odrzucane.
- Odpowiedź to zwykły tekst z formatowaniem, renderowany w popupie jako Markdown, więc nagłówki, pogrubienia i bloki kodu działają — kontrakt pozostaje string na wejściu / string na wyjściu.
class MyHover final : public IHoverProvider {
public:
void requestHover(const HoverRequest& r, HoverReply reply) override {
// ...look up info for r.doc at byte offset r.position, possibly async...
reply("**foo** — a thing\n\n```cpp\nint foo();\n```");
}
};
// in initialize(): m_token = ctx.hover().registerProvider(m_hover);
Signature help — asynchroniczny pull¶
Jak hover, ale wyzwalany wpisaniem ( wywołania lub ,. Provider odpowiada
aktywną sygnaturą; host renderuje ją jako call tip z podświetlonym aktywnym
parametrem. Dostęp do rejestru przez IPluginContext::signatureHelp()
(PluginApi/ISignatureHelpRegistry.h):
struct SignatureHelpRequest { DocumentId doc; std::int64_t position; std::string languageId; };
struct SignatureHelp { std::string signature; std::int32_t activeStart, activeEnd; }; // byte range in `signature`
using SignatureHelpReply = std::function<void(SignatureHelp)>; // empty signature = nothing
struct ISignatureHelpProvider {
virtual void requestSignatureHelp(const SignatureHelpRequest&, SignatureHelpReply) = 0;
};
struct ISignatureHelpRegistry { virtual SignatureHelpToken registerProvider(ISignatureHelpProvider&) = 0; };
Host pokazuje pierwszą niepustą odpowiedź; [activeStart, activeEnd) to
bajtowy zakres aktywnego parametru wewnątrz signature, używany do
podświetlenia.
Symbole — asynchroniczny pull (go-to-symbol)¶
Provider wylicza symbole dokumentu (i przeszukuje przestrzeń roboczą); host
renderuje picker (tryby Ctrl+Shift+O / Ctrl+T palety poleceń), a każda
wtyczka może pobrać (pull) zagregowane symbole — robi to dok Function List,
co czyni ten kontrakt autentycznie wielokonsumenckim.
Dostęp przez IPluginContext::symbols() (PluginApi/ISymbolRegistry.h):
struct SymbolLocation { std::string path; std::int32_t line, column; }; // path empty = request doc; column = byte offset in line
struct SymbolInfo { std::string name; std::int32_t kind; std::string containerName; SymbolLocation location; }; // kind = LSP SymbolKind (1..26)
using SymbolReply = std::function<void(std::vector<SymbolInfo>)>;
struct ISymbolProvider {
virtual void requestDocumentSymbols(const DocumentSymbolRequest&, SymbolReply) = 0; // { DocumentId doc; std::string languageId; }
virtual void requestWorkspaceSymbols(const WorkspaceSymbolRequest&, SymbolReply) = 0; // { std::string query; }
};
struct ISymbolRegistry {
virtual SymbolToken registerProvider(ISymbolProvider&) = 0; // producer side
// consumer side (pull) — used by the Command Palette and the Function List dock:
virtual void requestDocumentSymbols(const DocumentSymbolRequest&, SymbolReply) const = 0;
virtual void requestWorkspaceSymbols(const WorkspaceSymbolRequest&, SymbolReply) const = 0;
};
- Lokalizacje wyrażane są tak, jak nawiguje host:
pathw systemie plików (puste oznacza dokument z żądania) plus liczony od 0linei bajtowycolumnw jego obrębie — w miejscu wywołania nie trzeba już nic konwertować. - Host dostarcza pierwszą niepustą listę symboli, albo pustą listę, gdy wszyscy providerzy odpowiedzieli pusto (więc czekający picker zawsze dostaje jakąś odpowiedź).
- Strona konsumenta:
requestDocumentSymbols/requestWorkspaceSymbolsna rejestrze to pull-owy odpowiednikregisterProvider— wtyczka wywołuje je, aby odczytać to, co dostarczają inne wtyczki (np. serwer języka). Odpowiedź przychodzi raz, w wątku UI, być może później (asynchronicznie). Dok Function List używa tego w trybie zapytaj-i-wróć-do-bazy: natychmiast pokazuje własny zarys oparty na regexach, a jeśli przyjdzie niepusta odpowiedź, przechodzi na nią; pusta odpowiedź nigdy nie czyści linii bazowej. Zabezpiecz spóźnioną odpowiedź przed sprzątaniem obiektów i przed nowszym żądaniem (id dokumentu + numer generacji).
Status¶
Wszystkie cztery kontrakty (diagnostyka, hover, signature help, symbole) zostały
dodane przed wydaniem bez podbijania MTE_PLUGIN_API_VERSION. Ich pierwszym
konsumentem jest wtyczka LSP; kontrakt symboli jest
celowo wielokonsumencki (dziś paleta poleceń, następny w kolejce dok Function
List).