ctx — insticksmodulens kontext¶
register(ctx) tar emot ett argument: ett PluginContext. Det exponerar
tjänsterna nedan (hela ytan av IPluginContext som passar modellen utanför
processen). Var och en är en liten Python-klass som arbetaren bygger ovanpå
RPC-kanalen; källkoden finns i Python/mte/.
def register(ctx):
ctx.commands # register/invoke commands
ctx.menus # add menu items & separators
ctx.events # subscribe to editor events
ctx.log # write to the host's diagnostics stream
ctx.status # post transient status-bar messages
ctx.editor # buffer / caret / selection / files / search / bookmarks / groups
ctx.languages # list languages, read / switch a document's language, contribute one
ctx.settings # per-plugin key/value store
ctx.app_settings # editor-wide read-only settings view (keys: mte.keys)
ctx.document_settings # .editorconfig-layered per-document reads
ctx.theme # themeable colour categories for your own UI
ctx.diagnostics # publish squiggles + margin glyphs
ctx.compare # open the side-by-side compare view
ctx.completions # register auto-completion providers
ctx.docks # declarative dock panels (+ set_panel_visible, is_panel_visible)
ctx.line_markers # margin glyphs / line tints that follow edits; the margin-click claim
ctx.settings_page # declarative Preferences pages
ctx.ui # runtime get/set for panel controls
Två delar av det inbyggda kontraktet har ännu ingen motsvarighet i Python: hover-leverantörer (så hoverns prioritetsband gäller inte Python-moduler) och leverantörer för redigerarens snabbmeny (så inte heller snabbmenyns område text / marginal). Båda besvaras medan användaren väntar, och det kräver samma deadlinebehandling som kompletteringsleverantörerna har innan en rundtur till arbetaren är säker.
Spara inte ctx i en global variabel i tron att du ska använda den senare — även
om attributen är stabila så länge insticksmodulen lever, behandla ctx som knuten
till en enda registrering. Behåll referenser till de enskilda tjänsterna om du
behöver dem (till exempel log = ctx.log).
commands¶
Dekorator som registrerar ett kommando och binder det inkapslade anropbara som dess hanterare.
id(str, obligatorisk): unik identifierare i omvänd DNS-stil (till exempel"wordcount.count"). Samma regler som iplugin.json— måste vara unik globalt.title(str, obligatorisk): läsbar etikett som visas överallt där kommandot dyker upp (menyer, kommandopalett).shortcut(str, valfri): portabel standardsträng för kortkommando, till exempel"Ctrl+Alt+W".Ctrlkopplas automatiskt till plattformens primära modifierare (Cmdpå macOS). Lämna tomt för inget. Användaren kan tilldela om eller lossa det i Inställningar ▸ Kortkommandon; för menybundna kommandon tillämpar värden omtilldelningen åt dig.category(str, valfri): fri grupperingsetikett — visas som kommandots kategori i tilldelningen av kortkommandon.scope(str, valfri):""/"application"(standard — värden binder kortkommandot till kommandots menyåtgärd) eller"pluginWindow"för kombinationer som bor i ett gränssnitt insticksmodulen äger: värden listar bara kommandot i tilldelningen; läsctx.commands.effective_shortcut(id)när du bygger dina widgetar och tillämpa på nytt vid händelsen"command.shortcutChanged".
Det kortkommando som gäller just nu efter användarens omtilldelning och
konfliktlösning ("" = lossat eller okänt). Säkert att anropa från hanterare.
Den inkapslade funktionen tar inga argument och returnerar inget. Undantag i den fångas av arbetaren och noteras till stderr; de kraschar inte redigeraren.
@ctx.commands.command(id="wordcount.count",
title="Count Words",
shortcut="Ctrl+Alt+W",
category="Text")
def _count():
ctx.status.show("counted")
Under huven: Python-sidan tilldelar lokalt ett handlerId, registrerar återanropet
på RPC-anslutningen, och avfyrar commands.registerCommand mot värden. När
användaren aktiverar kommandot (menyklick, kortkommando, palett) skickar värden ett
RPC-anrop callback och din funktion körs.
Kommando-id:n gäller för hela processen, inte per insticksmodul. Förse dina
id:n med din omvända DNS-namnrymd (wordcount.…) för att undvika krockar.
menus¶
Menysökvägar använder / som avskiljare. Det vänstraste segmentet är en
toppnivåmeny (Plugins, Tools, …); mellanliggande segment skapar undermenyer på
begäran; det sista segmentet är postens etikett.
ctx.menus.add_item(path, command_id)¶
path(str):"Top/Sub/…/Label". Mellanliggande undermenyer skapas om de inte finns.command_id(str): id för ett tidigare registrerat kommando. Att klicka på posten anropar kommandot.
Returnerar en ogenomskinlig sträng handlerId. Behåll den om du senare behöver
unsubscribe posten (sällsynt i fas 1).
Det vänstraste segmentet ("Plugins" ovan) måste stämma med title för en av dina
poster i menus[] i plugin.json så att placeringen i menyraden plockas upp.
ctx.menus.add_separator(path)¶
Infogar en avskiljare i en befintlig undermeny.
ctx.menus.set_item_checked(handler_id, checked)¶
Gör den tidigare tillagda posten till en kryssbar menypost och sätter dess kryssade tillstånd — standardmönstret för en bestående på/av-växel som visar ett synligt kryss.
item = ctx.menus.add_item("View/Auto-open Preview",
"myplugin.toggleAutoOpen")
# Seed the check mark from the persisted setting so it matches
# reality before the user opens the menu.
ctx.menus.set_item_checked(item, ctx.settings.get("autoOpen", True))
@ctx.commands.command(id="myplugin.toggleAutoOpen",
title="Toggle Auto-Open")
def _toggle():
on = not ctx.settings.get("autoOpen", True)
ctx.settings.set("autoOpen", on)
ctx.menus.set_item_checked(item, on)
Det första anropet gör posten kryssbar; senare anrop vänder bara krysset. Att
skicka ett handler_id som värden inte känner är en tyst nulloperation (säkert att
anropa efter att din symbol släppts, till exempel från ett fördröjt återanrop som
utlöses under avslutet).
ctx.menus.set_item_enabled(handler_id, enabled)¶
Aktiverar eller gråtonar en tidigare tillagd post. Använd den när kommandot behöver ett villkor — något att stänga, något att återställa — så att menyn visar att den inte är tillgänglig i stället för att erbjuda en post som tyst inte gör något.
item = ctx.menus.add_item("File/Close Thing", "myplugin.closeThing")
# Nothing to close yet:
ctx.menus.set_item_enabled(item, False)
# ... and from every place that changes the state the command needs:
ctx.menus.set_item_enabled(item, thing_is_open)
En inaktiverad post förblir synlig men kan inte utlösas, varken med ett klick eller
med sitt kortkommando. Anropet är idempotent, så du behöver aldrig minnas det
tidigare tillståndet, och ett okänt handler_id är en tyst nulloperation som ovan.
Poster börjar aktiverade: ett enda anrop direkt efter add_item ställer i ordning en
post som inte är tillgänglig vid start.
events¶
Prenumerera på redigerarhändelser. Två likvärdiga API:er — sockerdekoratorerna
rekommenderas; den råa subscribe() finns om du vill beräkna ämnet under körning.
Sockerdekoratorer¶
@ctx.events.on_document_saved
def _saved(ev):
ctx.log.info(f"saved doc {ev.document}")
@ctx.events.on_document_opened
def _opened(ev):
ctx.log.info(f"opened {ev.path}")
Den inkapslade funktionen tar ett argument: en typad dataklass som motsvarar ämnet. Se Händelsereferensen för hela tabellen ämne ↔ dataklass.
Rått API¶
handler_id = ctx.events.subscribe("document.saved", _saved)
# later:
ctx.events.unsubscribe(handler_id)
Tillgängliga sockeregenskaper (alla dekoratorer):
| Egenskap | Utlöses vid |
|---|---|
on_document_opened |
Dokument öppnat i en flik |
on_document_closed |
Dokument stängt |
on_document_saved |
Dokument skrivet till disken |
on_selection_changed |
Markör / markering flyttad i det aktiva dokumentet |
on_active_document_changed |
Förgrundsfliken ändrad |
on_document_modified_changed |
Flaggan ren/smutsig vände (modified: bool) |
on_text_changed |
Varje ändring (position, added_bytes, removed_bytes) — håll det trivialt |
on_settings_changed |
Inställningar verkställda (scope: "" hela redigeraren, "plugins/<id>") |
on_theme_changed |
Tema bytt eller redigerat (variant) — läs om färgerna i ctx.theme |
on_workspace_root_changed |
Arbetsytemapp öppnad / stängd (root) |
on_workspace_files_changed |
Arbetsytans filmängd ändrad (files) |
on_host_window_shown |
En gång per körning, efter den första bilden — schemalägg långsamt startarbete här |
on_app_about_to_quit |
Redigeraren stängs — sista chansen att göra något |
on_command_shortcut_changed |
Ett kommandos effektiva kortkommando ändrades (omtilldelning av användaren / import av tangentuppsättning / konfliktlösning); innehåll: command_id, sequence ("" = lossat) |
Okända ämnesnamn kastar ValueError vid prenumerationen. Prenumeranter kan köras
efter motsvarande inhemska hanterare; behandla händelseleveransen som
informativ, inte auktoritativ.
Publicera¶
ctx.events.publish(topic, payload) sänder lika till inhemska och
Python-prenumeranter — godtas bara för arbetsytans två ämnen
(workspace.rootChanged, workspace.filesChanged); allt annat kastar
ValueError. Se Händelsereferensen.
log¶
ctx.log.trace ("very-fine-grained")
ctx.log.debug ("dev-only detail")
ctx.log.info ("routine event")
ctx.log.warning("recoverable weirdness")
ctx.log.error ("something is wrong")
Varje nivå motsvarar värdens ILogger. Rader föregås av
[python:<plugin.id>], så att din utdata går att hitta i redigerarens
diagnostiklogg:
Använd aldrig print() från en insticksmodul — arbetarens stdout är RPC-kanalen,
och att skriva till den förstör protokollet. ctx.log.* är det enda tillåtna sättet
att skicka ut diagnostisk text.
status¶
Lägg en flyktig avisering på statusraden.
message(str): vad som ska visas. Bara korta strängar — statusraden har en fast bredd.timeout_ms(int, standard 0): hur länge den behålls på skärmen;0låter värden välja ett förnuftigt värde (några sekunder).level(str, standard"info"):"info"/"warning"/"error". Högre nivåer kan hålla kvar minimitiden på skärmen lite längre så att ett fel inte genast sopas bort av ett rutinmässigt informationsmeddelande.
ctx.status.show är avfyra-och-glöm. Har värden ingen synlig statusrad (ett bygge
utan skärm eller ett minimalt bygge) kastas anropet tyst. ctx.status.clear() tömmer
vilken flyktig avisering som än står på skärmen.
Bestående segment¶
För levande tillstånd (en ordräkning, en servers status) använder du ett segment som du äger och uppdaterar — det skriver aldrig över en avisering, och tvärtom:
seg = ctx.status.add_segment("wordcount.total", priority=100)
seg.set_text("2,431 words")
seg.set_visible(False) # hide without giving it up
seg.remove() # take it off the bar (idempotent)
priority ordnar insticksmodulernas segment från vänster till höger (lägre = längre
till vänster; vid lika behålls registreringsordningen). Att en insticksmodul avslutas
tar bort allt som fortfarande hålls.
editor¶
Fullständig synkron åtkomst till de aktiva dokumenten. Varje metod blockerar den
anropande tråden tills värden svarar (vanligtvis mikrosekunder när arbetaren och
redigeraren delar maskin). Tillåtet från vilken hanterare för kommando eller händelse
som helst; aldrig från ett återanrop med async def (se anteckningen om trådar
högst upp på den här sidan).
Räkna upp dokument¶
doc = ctx.editor.active_document() # 0 when no document is open
docs = ctx.editor.open_documents() # list[int]
ctx.editor.set_active_document(doc)
path = ctx.editor.file_path(doc) # str or None for untitled
Buffertåtkomst¶
text = ctx.editor.text(doc) # whole buffer
slice = ctx.editor.text_range(doc, start, end) # [start, end) bytes
n = ctx.editor.length(doc) # int64
ctx.editor.set_text(doc, "...")
text_range undviker att dra hela bufferten genom RPC:n när du bara behöver en
bit — föredra den vid sortering och omvandling av stora filer.
Markering¶
sel = ctx.editor.selection_range(doc) # mte.MatchRange(start, end)
s = ctx.editor.selected_text(doc)
ctx.editor.replace_selection(doc, "new text")
ctx.editor.set_selection(doc, mte.MatchRange(4, 9))
ranges = ctx.editor.selection_ranges(doc) # list[MatchRange]: one per caret
block = ctx.editor.is_rectangular_selection(doc) # Alt+drag column block?
Markör¶
pos = ctx.editor.caret(doc) # mte.EditorPosition(line, column)
ctx.editor.set_caret(doc, mte.EditorPosition(line=7, column=3))
Filoperationer¶
new_doc = ctx.editor.open_file("/path/to/file") # doc-id or 0 on failure
here = ctx.editor.open_file_at("/path/to/file", mte.EditorPosition(41, 0))
ctx.editor.reveal_line(doc, 120) # scroll there; caret and focus stay
ok = ctx.editor.save_document(doc)
closed = ctx.editor.close_document(doc) # prompts if modified; False = user cancelled
dirty = ctx.editor.is_modified(doc)
ctx.editor.set_modified(doc, True) # force the tab asterisk
ro = ctx.editor.is_read_only(doc) # e.g. Monitoring (tail -f) mode
enc = ctx.editor.document_encoding(doc) # mte.DocumentEncoding, e.g. UTF8_BOM
ctx.editor.set_document_encoding(doc, mte.DocumentEncoding.UTF8) # takes effect on the next save
eol = ctx.editor.line_ending(doc) # mte.LineEnding, e.g. CRLF
ctx.editor.set_line_ending(doc, mte.LineEnding.LF) # Enter inserts it; the next save writes it
scratch = ctx.editor.create_untitled("text", "Untitled-3") # new buffer, doc-id
sid = ctx.editor.stable_id(doc) # survives Save As; compare with ==
clip = ctx.editor.clipboard_text() # system clipboard, "" if no text
# Declare that you snapshot unsaved buffers (the host then stops asking
# "save changes?" when the window closes):
ctx.editor.set_unsaved_changes_preserved(True)
ctx.editor.unsaved_changes_preserved() # bool, global flag
open_file_at är det enda anropet för «ta användaren dit» (ett byggfel, en
stackram, en sökträff): det öppnar filen eller aktiverar dess flik, placerar
markören utan markering, fäller ut en vikning som döljer raden, rullar raden
in i vyn — centrerad om den var utanför skärmen — och ger redigeraren fokus.
Rad och kolumn räknas från 0 (kolumnen i UTF-8-byte) och begränsas, avvisas
aldrig; det returnerar dokument-id:t, eller 0 om filen inte kunde öppnas, och
då har ingenting flyttats. reveal_line visar bara en rad: det flyttar inte
markören, byter inte flik och tar inte fokus.
Bokmärken, markeringar, URL-färgmarkering¶
ctx.editor.toggle_bookmark(doc, line) # 0-based line
lines = ctx.editor.bookmarked_lines(doc) # list[int]
nxt = ctx.editor.next_bookmark(doc, from_line, wrap=True) # -1 = none
prv = ctx.editor.previous_bookmark(doc, from_line, wrap=True)
ctx.editor.clear_bookmarks(doc)
ctx.editor.mark_range(doc, mte.MatchRange(4, 9)) # "mark" indicator
ctx.editor.clear_marks(doc)
ctx.editor.set_url_highlight_enabled(doc, True)
ctx.editor.is_url_highlight_enabled(doc)
url = ctx.editor.url_at_caret(doc) # "" when none
ctx.editor.open_url_at_caret(doc) # bool
Sökning¶
opts = mte.SearchOptions(match_case=True, mode="regex")
hit = ctx.editor.find_in_range(doc, r"TODO\(.*\)", 0,
ctx.editor.length(doc), opts)
if hit.valid:
new_end = ctx.editor.replace_target(doc, hit, "DONE()", opts)
SearchOptions.mode: "normal" / "extended" / "regex".
SearchOptions.direction: "forward" / "backward". Se mte.SearchOptions för
hela fältlistan.
Inkrementell sökning (mönstret växer medan användaren skriver, från ett fastnålat ankare):
ctx.editor.set_incremental_search_anchor(doc)
hit = ctx.editor.incremental_search(doc, "TOD", opts) # MatchRange
Indrag och syntax¶
style = ctx.editor.indentation(doc) # IndentationStyle(use_tabs, width)
ctx.editor.set_indentation(doc,
mte.IndentationStyle(use_tabs=False, width=2))
# Where word-wrapped continuation rows start:
# "left" (window edge) / "same" (align with the line; default) / "indent"
ctx.editor.set_wrap_indent(doc, "same")
mode = ctx.editor.wrap_indent(doc) # same strings back
lang = ctx.editor.colorizer_id(doc) # e.g. "python", "" if none
# Semantic classification from the active colorizer (cheap, no re-lex):
# list[mte.CategorySpan(start, length, category)] with category names
# "comment" / "string" / "keyword" / ... -- a spell checker keeps to
# comments and strings this way.
spans = ctx.editor.text_categories(doc, 0, ctx.editor.length(doc))
ctx.editor.trigger_completion(doc) # pop the list as if Ctrl+Space
Delad vy / flikgrupper¶
n = ctx.editor.group_count() # >= 1
g = ctx.editor.group_of(doc) # -1 if not open
w = ctx.editor.group_weights() # relative widths
ctx.editor.move_document_to_group(doc, 1) # creates trailing groups on demand
ctx.editor.set_group_weights([2, 1])
ctx.editor.set_document_tab_index(doc, 0) # reorder within its pane
gs = ctx.editor.groups_of(doc) # EVERY pane showing it
ctx.editor.show_document_in_group(doc, 1) # split it into pane 1
Ett dokument som visas i två rutor är ett dokument med ett id, så group_of()
kan bara namnge en av dess platser. groups_of() rapporterar dem alla, och
show_document_in_group() skapar en andra delad vy — samma sak som Visa >
Dela dokumentet höger gör, så ingen händelse document_opened väcks.
ctx.editor speglar numera den inhemska IEditorService ett till ett.
languages¶
Syntaxfärgning är en funktion i kärnan — värden identifierar ett dokuments språk
och färgar det utan någon insticksmodul. ctx.languages låter dig vara med: lista
vad värden känner till, läsa eller åsidosätta ett dokuments språk, köra om
identifieringen, och bidra med hela språkdefinitioner.
for lang in ctx.languages.available(): # list[mte.LanguageInfo(id, name)]
...
lang_id = ctx.languages.language_for(doc) # "" when uncoloured
ok = ctx.languages.set_language_for(doc, "json") # "" clears; False = unknown
ctx.languages.auto_detect_for(doc) # undo a manual override
handler = ctx.languages.register(
id="mylang", name="My Language", lexer="cpp",
extensions=["mylang", "ml2"],
keywords=[{"group": "Primary keywords and identifiers",
"words": "if else while"}],
styles={"SCE_C_COMMENT": "comment", "SCE_C_STRING": "string"})
...
ctx.languages.unregister(handler) # restores what it displaced
register tar samma form som en medföljande fil colorizers/*.json (eller en hel
ordbok definition=): lexer namnger en befintlig lexer i backenden (värden får
inga nya lexrar under körning), styles kopplar den lexerns egna stilnamn till
semantiska kategorier, och keywords adresserar dess ordlistor med lexerns egen
platsbeskrivning — värden löser båda precis som för en medföljande fil, så du
behöver aldrig skriva ut motorns id:n. Att registrera ett id som redan finns ersätter
det så länge du håller hanterarens id; att insticksmodulen avslutas drar tillbaka
allt som fortfarande hålls.
document_settings¶
Skrivskyddade inställningar med dokumentomfattning som skiktar ett projekts
.editorconfig över dina egna ctx.settings. För nyckeln k och dokumentet doc
löser en läsning mte.<plugin.id>.<k> från den .editorconfig som gäller för det
dokumentet, faller sedan tillbaka på ctx.settings, och sedan på ditt
standardvärde:
wrap = ctx.document_settings.get_bool(doc, "wrap", False)
width = ctx.document_settings.get_int(doc, "width", 80)
ratio = ctx.document_settings.get_double(doc, "ratio", 1.0)
name = ctx.document_settings.get_string(doc, "profile", "")
ctx.document_settings.contains(doc, "wrap") # set in either layer?
Skrivningar går genom ctx.settings — aldrig tillbaka in i .editorconfig.
diagnostics¶
Skjut ut våglinjer och marginaltecken för ett dokument — från en statisk analysator,
en språkserver, en byggogg. Värden ritar dem; du rör aldrig vyn. Att publicera
ersätter allt du tidigare publicerat för det paret (dokument, källa), så skicka
hela mängden varje gång:
ctx.diagnostics.publish(doc, "mylinter", [
mte.Diagnostic(start=12, end=18, message="unused variable",
severity=mte.SEVERITY_WARNING, code="W0612"),
])
ctx.diagnostics.publish(doc, "mylinter", []) # retract only your own
ctx.diagnostics.clear(doc) # clears EVERY producer's set on doc
source är obligatorisk — det är den som håller din mängd skild från andra
producenters (LSP-insticksmodulen, stavningskontrollen) i samma dokument.
Allvarlighetsgrader: SEVERITY_ERROR / SEVERITY_WARNING /
SEVERITY_INFORMATION / SEVERITY_HINT. Förskjutningar är halvöppna
byteintervall [start, end).
theme¶
Registrera de färger ditt eget gränssnitt målar som namngivna, temabara kategorier; Temaredigeraren listar dem automatiskt och användarens åsidosättningar går fram och tillbaka i temafilen:
h = ctx.theme.register_category("com.example.my/match", "Match highlight",
default_light=0xFFF3B0, default_dark=0x8A6D00)
rgb = ctx.theme.resolve_color("com.example.my/match") # 0xRRGGBB int
themed = ctx.theme.has_override("com.example.my/match")
variant = ctx.theme.active_variant() # "light" / "dark" / ""
cats = ctx.theme.categories() # list[mte.ThemeCategory]
ctx.theme.unregister_category(h)
Läs om resolve_color vid ctx.events.on_theme_changed.
compare¶
Öppnar (eller lyfter fram) redigerarens jämförelsevy sida vid sida över två källor.
Varje sida är en fil på disken (path=), bokstavlig text (content=), eller båda
(innehållet visas, medan sökvägen ger sessionsbevarandet en återställningsbar
identitet):
shown = ctx.compare.open(
mte.CompareSource("before", path="/tmp/a.txt"),
mte.CompareSource("after", content=new_text),
ignore_whitespace=True) # ignore_case / ignore_eol too
Returnerar False när en sida inte går att läsa.
settings¶
Bestående typad nyckel/värde-lagring, begränsad till plugins/<your.id>/ på
värden. Alla metoder är synkrona; get och set härleder typen från standardvärdets
Python-typ.
ctx.settings.set("wrap", True)
ctx.settings.set("count", 42)
ctx.settings.set("theme", "dark")
wrap = ctx.settings.get("wrap", False) # returns bool
count = ctx.settings.get("count", 0) # returns int
theme = ctx.settings.get("theme", "light") # returns str
Typer:
Python-typ på default |
Protokolltyp | Typ som get() returnerar |
|---|---|---|
bool |
bool |
bool |
int (icke-bool) |
int |
int |
float |
double |
float |
str (eller None) |
string |
str |
Dessutom:
ctx.settings.contains("wrap") # bool
ctx.settings.remove("stale_key")
ctx.settings.sync() # optional; host also syncs on shutdown
Reagera på ändringar som gjorts i dialogrutan Inställningar genom att prenumerera på
settings.changed (ctx.events.on_settings_changed; se
Händelsereferensen).
app_settings¶
Skrivskyddad vy av redigerarens programövergripande konfiguration. De välkända
nyckelnamnen exporteras som konstanter i mte.keys (en spegling av C++-headern
PluginApi/AppSettingsKeys.h, som förblir den auktoritativa listan):
import mte
size = ctx.app_settings.get(mte.keys.EDITOR_FONT_SIZE, mte.keys.DEFAULT_EDITOR_FONT_SIZE)
family = ctx.app_settings.get(mte.keys.EDITOR_FONT_FAMILY, "") # "" = host default
theme = ctx.app_settings.get(mte.keys.UI_THEME, mte.keys.DEFAULT_UI_THEME) # "system"/"light"/"dark"
Sättarna, remove() och sync() på ctx.app_settings kastar RuntimeError —
vyn är med avsikt skrivskyddad.
completions¶
Registrera en kompletteringsleverantör som deltar i redigerarens popup-fönster:
@ctx.completions.provider(languages=["python"])
def _complete(req: mte.CompletionRequest):
if not req.prefix:
return []
return [mte.CompletionItem(text=w, kind=mte.KIND_KEYWORD)
for w in ("hello", "hello_world", "helper")
if w.startswith(req.prefix)]
Det inkapslade anropbara anropas vid varje tangenttryckning (eller vid Ctrl+Space)
vars dokument matchar språkfiltret. Fält på req:
| Fält | Typ | Betydelse |
|---|---|---|
document |
int |
Dokumentet som redigeras |
prefix |
str |
Ordfragmentet omedelbart före markören |
position |
int |
Markörens byteförskjutning |
language_id |
str |
Colorizer-id ("python", "cpp", …) |
manual |
bool |
True när användaren tryckte Ctrl+Space; False vid automatisk utlösning |
Returnera en lista med mte.CompletionItem (eller vanliga strängar, eller ordböcker
med {text, detail, kind}). Sorter: mte.KIND_KEYWORD / KIND_WORD /
KIND_SYMBOL / KIND_SNIPPET / KIND_UNKNOWN.
Deadline
Värden skickar din leverantör en begäran knuten till tangenttryckningen med en deadline på ~30 ms. Missar ditt återanrop den visas popup-fönstret utan dina kandidater för den tangenttryckningen. Bygg varje dyrt index i bakgrunden och svara från cachen — gör aldrig I/O i återanropet. Nätverkskomplettering i LSP-stil hör hemma i en framtida asynkron yta, inte här.
languages=[] (eller utelämnat) betyder «varje dokument». Flera leverantörer per
insticksmodul är tillåtna; varje dekorator registrerar sin egen.
data_dir¶
Returnerar den skrivbara mappen per insticksmodul som värden redan skapat åt dig.
Använd den för cachar, index och alla data som är dina.
ctx.diagnostics_dir() returnerar på samma sätt den delade mappen för krascher och
loggar (skrivskyddad för dig — kraschrapportören skriver dit).
docks¶
Deklarativa dockpaneler — ett sidopanelsgränssnitt byggt från en
formulärspecifikation, inte från Qt-widgetar. Se mte.ui för
byggarnas vokabulär.
from mte.ui import Column, Row, Label, LineEdit, Checkbox, Button
def register(ctx):
def _on_needle_changed(value: str) -> None:
ctx.log.info(f"needle -> {value!r}")
needle_handler = ctx.docks.on_change(_on_needle_changed)
panel_id = ctx.docks.add_panel(
id="renamer.panel",
title="Renamer",
area="right", # left / right / top / bottom
form=Column([
Label(text="Search:"),
LineEdit(id="needle", placeholder="text...",
binds="settings:lastNeedle",
on_change=needle_handler),
Checkbox(id="cs", label="Case sensitive",
binds="settings:caseSensitive"),
Row([Button(text="Run", on_click="renamer.run")]),
]),
)
ctx.docks.add_panel(*, id=None, title="", area="right", form, initially_visible=True, proportional_size=0.0, tab_group="") -> str¶
Registrerar en dockpanel. Returnerar panelens id (automatiskt tilldelat när id är
None).
id(str, valfri) — stabil identifierare somctx.ui.get / ctx.ui.setanvänder för att adressera kontroller inuti den här panelen. Ger du ingen tilldelar värden ett idpy-dock/Noch returnerar det.title(str) — visas i dockans rubrik.area(str) —"left"/"right"/"top"/"bottom". Okända värden faller tillbaka på"right".form(dict) — en specifikation byggd med byggarna imte.ui.initially_visible(bool) — om dockan visas när insticksmodulen läses in. StandardTrue. SkickaFalseför en panel som inte ska dyka upp vid start, och visa den senare medset_panel_visible.proportional_size(float) — panelens standardstorlek som en andel av huvudfönstret: av bredden i områdena"left"/"right", av höjden i"top"/"bottom".0.0(standardvärdet) behåller det pixelbeteende en verktygspanel vill ha — panelen håller sin storlek och att ändra fönstrets storlek lämnar den i fred. En bråkdel som0.4är vad en förhandsgranskning vill: att maximera fönstret breddar panelen med det. Bara standardvärdet är ditt; så snart användaren drar panelens avdelare tar värden över och kommer ihåg deras andel. Värden utanför intervallet klipps, och värden behåller en minsta utsträckning för både panelen och editorn. De fullständiga reglerna står underproportionalSize.tab_group(str) — paneler med samma icke-tomma grupp-id i sammaareadelar en docka som flikar i stället för att ligga sida vid sida. Ge id:t ett namnområde som ett panel-id ("renamer/panels"). Bara standardarrangemanget: en layout som användaren har sparat går före. SetabGroup.
ctx.docks.is_panel_visible(handler_id) -> bool¶
Om en panel som lagts till med add_panel är öppen — en panel som ligger
som flik bakom en annan räknas som öppen; en som registrerats med
initially_visible=False och aldrig visats, en stängd panel och ett okänt id
gör det inte. Kombinera det med set_panel_visible för att visa dina paneler
under en uppgift och sedan återställa det användaren hade:
was_open = ctx.docks.is_panel_visible(panel)
ctx.docks.set_panel_visible(panel, True)
# ... when the task ends:
ctx.docks.set_panel_visible(panel, was_open)
ctx.docks.set_panel_visible(handler_id, visible)¶
Visar eller döljer en panel som lagts till med add_panel. Att visa lyfter också
panelen över de dockor den delar område med, så att ett kommando som «öppnar» sin
panel verkligen sätter den framför användaren. Idempotent — upprepa samma värde
fritt, inget tillstånd att hålla reda på; tyst ignorerat för ett okänt id.
panel = ctx.docks.add_panel(id="mine.panel", title="Mine", form=form,
initially_visible=False)
@ctx.commands.command("mine.show", title="Show My Panel")
def _show():
ctx.docks.set_panel_visible(panel, True)
ctx.docks.on_change(callback) -> str¶
Tilldelar ett hanterar-id för att koppla en kontrolls fält on_change till ett
anropbart Python-objekt. Återanropet tar emot det nya värdet som en sträng;
booleska kontroller levererar "true" / "false", numeriska levererar heltalet som
en sträng.
def _on_volume(value: str) -> None:
ctx.log.info(f"volume={int(value)}")
handler = ctx.docks.on_change(_on_volume)
form = Column([NumberSpin(id="volume", default=5, on_change=handler)])
Hanterar-id:n för on_change föregås av py-dock-cb/N och krockar aldrig med
hanterar-id:n för kommandon, händelser eller kompletteringar.
Återanropet anropas på arbetarens händelseloop, INTE på trådpoolen — håll det
snabbt. Använd ctx.log, ändra ett litet tillstånd i minnet, eller avfyra ett
uppföljande kommando; gör inte tungt arbete här.
line_markers¶
Marginalsymboler och toningar av hela rader, fästa vid dokumentrader som följer användarens redigeringar — brytpunkter, en pil för aktuell sats, ändringsremsor — och anspråket på klicket i bokmärkeskolumnen. Den inbyggda tjänsten och alla dess regler beskrivs under Radmärken och marginalklick.
bp_colour = ctx.theme.register_category("myplugin.breakpoint", "Breakpoint",
default_light=0xCC0000,
default_dark=0xFF5555)
bp = ctx.line_markers.register_kind(glyph=mte.GLYPH_CIRCLE,
glyph_color="myplugin.breakpoint",
line_tint="", z_order=50)
marker = ctx.line_markers.add_marker(bp, doc, line) # 0 = refused
ctx.line_markers.line_of(marker) # where it is NOW; -1 = gone
ctx.line_markers.markers(bp, doc) # [mte.LineMarkerPlacement(id, line)]
ctx.line_markers.remove_marker(marker)
ctx.line_markers.clear_markers(bp, doc) # doc 0 = every document
ctx.line_markers.release(bp) # removes the kind and its markers
register_kind(glyph=, glyph_color=, line_tint=, z_order=)—glyphär en avmte.GLYPH_NONE(bara toning),GLYPH_CIRCLE,GLYPH_CIRCLE_HOLLOW,GLYPH_CIRCLE_BAR,GLYPH_ARROW,GLYPH_STRIP; färgerna är id:n för kategorier du har registrerat medctx.theme. Redigeraren har bara ett fåtal märkesplatser för alla insticksmoduler tillsammans, så detta kan misslyckas — då kastasRpcError. Registrera dina typer en gång, iregister().add_marker(kind, doc, line)— rad räknad från 0, inte begränsad: en rad utanför dokumentet returnerar 0 och placerar ingenting.- Märken flyttar med sina rader, överlever en omläsning på samma radnummer
och försvinner med sitt dokument — lägg till dina igen i
ctx.events.on_document_opened.
Marginalklicket¶
@ctx.events.on_margin_clicked
def _clicked(ev): # mte.MarginClicked
if ev.modifiers & mte.MODIFIER_CONTROL:
edit_condition(ev.document, ev.line)
else:
toggle_breakpoint(ev.document, ev.line)
claim = ctx.line_markers.claim_margin_click()
Varje vänsterklick i bokmärkeskolumnen publiceras som margin.clicked
(document, line, margin = "symbol", modifiers — bitar
mte.MODIFIER_SHIFT / MODIFIER_CONTROL / MODIFIER_ALT /
MODIFIER_META). Som standard växlar redigeraren också ett bokmärke vid
klicket; så länge någon modul håller claim_margin_click() gör den inte det
och lämnar klicket åt modulerna. ctx.line_markers.release(claim) ger
tillbaka klicket.
settings_page¶
Lägger till en sida under redigerarens dialogruta Inställningar. Samma
vokabulär för formulärspecifikationer som dockpaneler; samma bevarande med
binds="settings:<key>".
from mte.ui import Column, Checkbox, NumberSpin
def register(ctx):
ctx.settings_page.register(
category="Plugins/Renamer", # slash-separated tree path
title="Renamer",
form=Column([
Checkbox(id="cs", label="Case sensitive by default",
binds="settings:caseSensitive"),
NumberSpin(id="results", default=100, min=1, max=10_000,
binds="settings:resultCap"),
]),
)
ctx.settings_page.register(*, category, title, form, scope="") -> str¶
category(str, obligatorisk) — snedstreckseparerad sökväg i Inställningsträdet, till exempel"Plugins/My Plugin". Nästlade kategorier kommer fram som noder i trädet.title(str, obligatorisk) — rubrik som visas ovanför sidan.form(dict, obligatorisk) — en specifikation byggd med byggarna imte.ui.scope(str, valfri) — den händelseomfattning som sänds ut vidSettingsChangedefter att användaren tryckt OK / Verkställ.
Returnerar hanterar-id:t.
Avbryt återställer INTE ändringar
Kontroller med binds="settings:<key>" sparar ivrigt medan användaren redigerar,
så att trycka Avbryt i dialogrutan Inställningar ångrar dem inte. Det är en
känd begränsning i fas 3; en framtida förfining kommer att kapsla in de
underliggande inställningarna i en buffrande proxy.
ui¶
Läser och skriver kontrollvärden inuti paneler du redan registrerat. På protokollet är värden alltid strängar.
def register(ctx):
panel_id = ctx.docks.add_panel(id="renamer.panel", ...)
@ctx.commands.command(id="renamer.echo", title="Echo needle")
def _echo():
needle = ctx.ui.get(panel_id, "needle") or ""
cs = (ctx.ui.get(panel_id, "cs") or "false") == "true"
ctx.status.show(f"needle={needle!r} case={cs}")
@ctx.commands.command(id="renamer.clear", title="Clear needle")
def _clear():
ctx.ui.set(panel_id, "needle", "")
ctx.ui.get(panel_id, control_id) -> Optional[str]¶
Läser en kontrolls aktuella värde. Returnerar None när panelen eller kontrollen
är okänd (inget fel — behandla både None och "" som «tomt»).
- Label / Button / LineEdit / ComboBox — returnerar texten.
- Checkbox — returnerar
"true"eller"false". - NumberSpin — returnerar heltalet som en decimalsträng.
- List — returnerar texten i den just nu markerade posten (tom sträng om ingen är markerad).
ctx.ui.set(panel_id, control_id, value) -> bool¶
Skriver ett värde i en kontroll programmatiskt. Returnerar True när skrivningen
lyckades, False när panelen eller kontrollen är okänd eller värdet inte går att
omvandla (till exempel ett icke-heltal skickat till en NumberSpin).
Python-booleanerna True / False omvandlas till "true" / "false" före
sändning; allt annat går genom str().
En set som drivs av värden utlöser INTE kontrollens on_change på nytt, så att du
kan uppdatera dina egna widgetar från en kommandohanterare utan oändliga loopar.
Sammanfattning av bindningar under körning (fas 3)¶
| Fält i formulärspecifikationen | Beteende under körning |
|---|---|
id="…" |
Adressera kontrollen från ctx.ui.get / ctx.ui.set. |
binds="settings:<key>" |
Läser startvärdet från ctx.settings; skriver tillbaka vid varje användarändring. |
on_click="<cmd>" |
Knappar anropar det registrerade kommando-id:t vid klick. |
on_change=handler |
Utlöser vid användarändring det återanrop som registrerats genom ctx.docks.on_change(...). |
Samma regler gäller i dockpaneler och på Inställningssidor.