Språkintelligens (diagnostik, pekhjälp, signaturhjälp, symboler)¶
En liten, backend-neutral kontraktsfamilj för språkintelligens — de data en
leverantör matar in till redigerarens ytor för kodintelligens. Den följer projektets
regel «insticksmoduler bidrar med data, kärnan ritar»: våglinjerna,
pekhjälpsfönstret och markörhoppen bor i redigerarvyn och exponeras aldrig för
insticksmoduler; en insticksmodul levererar bara POD- och std::string-data, så att
samma kontrakt senare kan uppfyllas av en Python-backend.
Den första förbrukaren är LSP-insticksmodulen, men kanalerna är allmänna — en statisk analysator eller en stavningskontroll kan publicera diagnostik på samma sätt.
Vad som inte finns här
Gå till definitionen, sök referenser, byt namn och formatering behöver inget
eget API — de bara navigerar eller redigerar text, vilket IEditorService redan
uttrycker (caret, openFile, setCaret, setText), så de förblir kommandon
inuti insticksmodulen. Komplettering återanvänder befintliga
ICompletionRegistry. Ett nytt kontrakt läggs till bara när det finns en
rit- eller väljaryta i kärnan och potentiellt flera leverantörer —
vilket är fallet för diagnostik, pekhjälp, signaturhjälp och symboler.
Diagnostik — push¶
En leverantör skjuter ut diagnostik så snart den har den (den avgör när
resultaten är klara); redigeraren ritar dem som vågiga understrykningar. Nå mottagaren
via IPluginContext::diagnostics(). Värdetyperna är enkla 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;
};
sourceär en ersättningsnyckel per(doc, source). Att publicera för en källa ersätter den källans tidigare diagnostik i dokumentet, så att flera producenter (LSP, stavningskontroll, byggogg) samexisterar utan att skriva över varandra.- Publicera en tom vektor för en källa för att dra tillbaka dess diagnostik.
- Allvarlighetsgrader ritas med distinkta färger (fel/varning/information/tips); redigeraren vidgar dessutom mycket korta intervall så att en markör på ett enda tecken förblir synlig.
ctx.diagnostics().publishDiagnostics(doc, "org.mycompany.linter", {
{ byteRange, DiagnosticSeverity::Warning, "unused variable", "W001" },
});
Värden tillhandahåller alltid en giltig mottagare (en nulloperation när redigeraren
inte har någon diagnostikyta), så du behöver aldrig nollkontrollera
diagnostics().
Pekhjälp — asynkron hämtning¶
Pekhjälpen drivs av redigeraren (musen som vilar över en symbol). En
insticksmodul registrerar en leverantör som svarar asynkront; nå registret via
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,
std::int32_t priority) = 0;
};
namespace hover_priority {
inline constexpr std::int32_t kFallback = 0;
inline constexpr std::int32_t kLanguage = 100;
inline constexpr std::int32_t kActiveSession = 1000;
}
registerProviderreturnerar enHoverToken(RAII); förstör den ishutdown()för att avregistrera.- Varje leverantör registrerar sig med en prioritet. Välj det band som
beskriver vad du svarar, inte hur viktigt det känns:
kFallbackför information på ord- eller textnivå (en ordboksdefinition, målet för en URL),kLanguageför språkinformation om symbolen (en språkservers hover, en linters regel),kActiveSessionför information som måste vinna medan den är aktiv (värdet under pekaren i en felsökare). Värden mellan banden (kLanguage + 10) är tillåtna när du måste ordna dig i förhållande till en känd granne. - När pekaren vilar frågar värden alla leverantörer samtidigt och visar det icke-tomma svaret från leverantören med högst prioritet; vid lika prioritet vinner den leverantör som registrerades först. Ett svar med lägre prioritet som kommer först hålls kvar tills varje högre leverantör har svarat tomt. När alla svarar tomt visas ingenting.
- En långsam leverantör kan inte hålla tillbaka verktygstipset hur länge som
helst: 300 ms efter förfrågan visas det bästa svaret som har kommit dit
och senare svar på den förfrågan kastas. En
kActiveSession-leverantör måste därför svara tomt genast när den inte har något att säga (ingen session) — annars fördröjer den varje hover i redigeraren med upp till tidsgränsen. - Svar som kommer efter att användaren gått vidare kastas.
- Svaret är vanlig uppmärkningstext och återges som Markdown i ett popup-fönster, så att rubriker, fetstil och kodblock fungerar — håll kontraktet sträng in / sträng ut.
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, hover_priority::kLanguage);
Signaturhjälp — asynkron hämtning¶
Som pekhjälpen, men driven av att ett anropets ( eller ett , skrivs. En
leverantör svarar med den aktiva signaturen; värden ritar den som ett anropstips
med den aktiva parametern färgmarkerad. Nå registret via
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; };
Värden visar det första icke-tomma svaret; [activeStart, activeEnd) är den
aktiva parameterns byteintervall inuti signature, använt för färgmarkeringen.
Symboler — asynkron hämtning (gå till symbol)¶
En leverantör räknar upp ett dokuments symboler (och söker i arbetsytan); värden
ritar väljaren (kommandopalettens symbollägen Ctrl+Shift+O / Ctrl+T) och varje
insticksmodul kan hämta de samlade symbolerna — dockan Funktionslista gör det,
vilket gör detta till ett verkligt kontrakt med flera förbrukare.
Nå det via 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;
};
- Platser uttrycks som värden navigerar: en
pathi filsystemet (tom betyder begärans dokument) plus enlineräknad från 0 och encolumni byte i den — ingen ytterligare konvertering behövs på anropsplatsen. - Värden levererar den första icke-tomma symbollistan, eller en tom lista när varje leverantör svarat tomt (så att en väntande väljare alltid får veta något).
- Förbrukarsidan:
requestDocumentSymbols/requestWorkspaceSymbolspå registret är hämtningsmotsvarigheten tillregisterProvider— en insticksmodul anropar dem för att läsa vad andra insticksmoduler (till exempel en språkserver) bidrar med. Svaret utlöses en gång, på gränssnittstråden, möjligen senare (asynkront). Dockan Funktionslista använder detta mönster med fråga och falla tillbaka: den visar sin egen översikt baserad på reguljära uttryck omedelbart och, om ett icke-tomt svar kommer, uppgraderar till det; ett tomt svar rensar aldrig grunden. Skydda ett sent svar mot nedrivning och mot en nyare begäran (dokument-id + generation).
Språk — räkna upp, byta, bidra¶
Syntaxfärgning är en funktion i kärnan: värden äger en colorizer-katalog (de
medföljande filerna colorizers/*.json plus användarens egna), identifierar ett
dokuments språk från dess sökväg och första rad, och färgar vyn utan någon
insticksmodul. ctx.languages() (ILanguageRegistry) är hur en insticksmodul är
med:
// Enumeration -- e.g. to fill a per-language settings dropdown.
std::vector<LanguageInfo> langs = ctx.languages().availableLanguages(); // {id, name}, by name
// Per-document state. languageFor() reports the same id as
// IEditorService::colorizerId(); setLanguageFor() takes exactly the path
// the Language menu takes ("" clears colouring; false = unknown id / doc).
std::string id = ctx.languages().languageFor(doc);
bool ok = ctx.languages().setLanguageFor(doc, "json");
bool redone = ctx.languages().autoDetectFor(doc); // undo a manual override
// Contribution: a whole language definition, in the same shape as a
// shipped colorizer file -- backend lexer NAME, style NAMES -> category,
// keyword groups by the lexer's own slot description. Never engine ids.
LanguageDefinition def;
def.id = "mylang"; def.name = "My Language"; def.lexer = "cpp";
def.extensions = {"mylang"};
def.keywords = {{"Primary keywords and identifiers", "if else while"}};
def.styles = {{"SCE_C_COMMENT", SyntaxCategory::Comment},
{"SCE_C_STRING", SyntaxCategory::String}};
m_lang = ctx.languages().registerLanguage(def); // LanguageToken (RAII)
Ett bidraget språk beter sig precis som ett medföljande: värden löser namnen mot
backendlexerns metadata och det aktiva temat levererar färgerna. Att registrera ett
id som redan finns ersätter det så länge symbolen lever; när symbolen dör kommer
den undanträngda definitionen tillbaka och öppna dokument som använder id:t färgas
om — så att en insticksmodul kan åsidosätta ett medföljande språk utan att skada
katalogen permanent. Bidrag överlever också den omläsning av katalogen som ett
Inställningar ▸ Verkställ eller ett temabyte utlöser. Värden kan inte få nya
backendlexrar under körning — lexer måste namnge en som finns. Alltid giltigt:
en värd utan färgningslager tillhandahåller en nulloperation som inte räknar upp
något och avvisar varje ändring.
Status¶
Alla fyra kontrakten (diagnostik, pekhjälp, signaturhjälp, symboler) lades till före
utgåvan utan att MTE_PLUGIN_API_VERSION höjdes; språkytan (ILanguageRegistry)
följde på samma sätt. Deras första förbrukare är
LSP-insticksmodulen; symbolkontraktet är med avsikt gjort
för flera förbrukare (kommandopaletten i dag, dockan Funktionslista sedan).