Gå till innehållet

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

ctx.commands.command(*, id, title, shortcut="", category="", scope="")

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 i plugin.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". Ctrl kopplas automatiskt till plattformens primära modifierare (Cmd på 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äs ctx.commands.effective_shortcut(id) när du bygger dina widgetar och tillämpa på nytt vid händelsen "command.shortcutChanged".
ctx.commands.effective_shortcut(command_id) -> str

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.


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)

ctx.menus.add_item("Plugins/Word Count/Count", "wordcount.count")
  • 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)

ctx.menus.add_separator("Plugins/Word Count")

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:

[python:org.example.wordcount] counted 42 words

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

ctx.status.show(message, timeout_ms=0, level="info")

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; 0 lå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("saved", timeout_ms=1500)
ctx.status.show("no results", level="warning")

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

path = ctx.data_dir()                    # pathlib.Path

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 som ctx.ui.get / ctx.ui.set använder för att adressera kontroller inuti den här panelen. Ger du ingen tilldelar värden ett id py-dock/N och 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 i mte.ui.
  • initially_visible (bool) — om dockan visas när insticksmodulen läses in. Standard True. Skicka False för en panel som inte ska dyka upp vid start, och visa den senare med set_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 som 0.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 under proportionalSize.
  • tab_group (str) — paneler med samma icke-tomma grupp-id i samma area delar 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. Se tabGroup.

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 av mte.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 med ctx.theme. Redigeraren har bara ett fåtal märkesplatser för alla insticksmoduler tillsammans, så detta kan misslyckas — då kastas RpcError. Registrera dina typer en gång, i register().
  • 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 i mte.ui.
  • scope (str, valfri) — den händelseomfattning som sänds ut vid SettingsChanged efter 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.