Gå til innhold

ctx — programtilleggets kontekst

register(ctx) mottar ett argument: en PluginContext. Den blottlegger tjenestene nedenfor (hele den IPluginContext-flaten som passer til modellen med en egen prosess). Hver er en liten Python-klasse som arbeideren bygger oppå RPC-kanalen; kildekoden er 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

To deler av den innebygde kontrakten har ennå ingen motpart i Python: hover-leverandører (så prioritetsbåndene for hover gjelder ikke for Python-programtillegg) og leverandører for redigererens kontekstmeny (så heller ikke kontekstmenyens område tekst / marg). Begge besvares mens brukeren venter, og det krever den samme behandlingen med frister som fullføringsleverandørene har, før en tur-retur til arbeideren er trygg.

Lagre ikke ctx i en global variabel i påvente av å bruke den senere — selv om attributtene er stabile programtilleggets levetid ut, så behandl ctx som noe som hører til registreringen. Behold henvisninger til de enkelte tjenestene du trenger (log = ctx.log, for eksempel).


commands

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

En dekorator som registrerer en kommando og binder den innpakkede funksjonen som behandleren dens.

  • id (str, påkrevd): en entydig identifikator i omvendt DNS-stil (for eksempel "wordcount.count"). Samme regler som i plugin.json — må være globalt entydig.
  • title (str, påkrevd): den lesbare merkelappen som vises overalt der kommandoen kommer til syne (menyer, kommandopaletten).
  • shortcut (str, valgfri): den flyttbare standardhurtigtasten, for eksempel "Ctrl+Alt+W". Ctrl oversettes av seg selv til plattformens viktigste modifikator (Cmd på macOS). La den stå tom for ingen. Brukeren kan gi den en ny binding eller ingen under Innstillinger ▸ Hurtigtaster; for menybundne kommandoer tar verten omtildelingen i bruk for deg.
  • category (str, valgfri): en fritt formulert grupperingsmerkelapp — vises som kommandoens kategori i hurtigtasttildelingen.
  • scope (str, valgfri): ""/"application" (standard — verten binder hurtigtasten på kommandoens menyhandling) eller "pluginWindow" for tastekombinasjoner som hører hjemme i et grensesnitt programtillegget eier: verten bare lister kommandoen i tildelingen; les ctx.commands.effective_shortcut(id) når du bygger kontrollene dine, og ta den i bruk på nytt ved hendelsen "command.shortcutChanged".
ctx.commands.effective_shortcut(command_id) -> str

Den hurtigtasten som faktisk gjelder etter brukerens omtildeling og løsningen av konflikter ("" = uten binding eller ukjent). Trygt å kalle fra behandlere.

Den innpakkede funksjonen tar ingen argumenter og gir ingenting tilbake. Unntak inne i den fanges av arbeideren og noteres til stderr; de får ikke redigereren til å krasje.

@ctx.commands.command(id="wordcount.count",
                      title="Count Words",
                      shortcut="Ctrl+Alt+W",
                      category="Text")
def _count():
    ctx.status.show("counted")

Under panseret: Python-siden tildeler en handlerId lokalt, registrerer tilbakekallet på RPC-forbindelsen og sender commands.registerCommand til verten. Når brukeren aktiverer kommandoen (et menyklikk, hurtigtasten, paletten), sender verten et callback-RPC, og funksjonen din kjører.

Kommando-id-er gjelder for hele prosessen, ikke per programtillegg. Sett ditt omvendte DNS-navnerom foran id-ene dine (wordcount.…) for å unngå kollisjoner.


Menystier bruker / som skilletegn. Det ytterste venstre leddet er en øverste meny (Plugins, Tools …); mellomliggende ledd lager undermenyer ved behov; det siste leddet er postens merkelapp.

ctx.menus.add_item(path, command_id)

ctx.menus.add_item("Plugins/Word Count/Count", "wordcount.count")
  • path (str): "Øverst/Under/…/Merkelapp". Mellomliggende undermenyer lages ved behov.
  • command_id (str): id-en til en kommando som er registrert tidligere. Et klikk på posten kaller kommandoen.

Gir en ugjennomsiktig handlerId-tekststreng tilbake. Behold den om du senere trenger å gjøre unsubscribe på posten (sjelden i fase 1).

Det ytterste venstre leddet ("Plugins" ovenfor) må passe til title i en av menus[]-postene dine i plugin.json, så plasseringen på menylinjen blir med.

ctx.menus.add_separator(path)

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

Setter inn en skillelinje i en undermeny som alt finnes.

ctx.menus.set_item_checked(handler_id, checked)

Gjør den posten som er lagt til tidligere, om til en avkryssbar menypost og setter tilstanden dens — det vanlige idiomet for en varig av/på-bryter som viser et synlig 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ørste kallet gjør posten avkryssbar; de følgende kallene snur bare krysset. Å gi en handler_id verten ikke kjenner, gjør i stillhet ingenting (trygt å kalle etter at symbolet ditt er droppet, for eksempel fra et forsinket tilbakekall som utløses under nedstengningen).

ctx.menus.set_item_enabled(handler_id, enabled)

Slår en post som er lagt til tidligere, av eller på. Bruk den når kommandoen trenger en forutsetning — noe å lukke, noe å gjenopprette — så menyen viser at den ikke er tilgjengelig, i stedet for å tilby en post som i stillhet ikke gjør noe.

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 post som er slått av, blir værende synlig, men kan ikke utløses, verken med et klikk eller med hurtigtasten sin. Kallet kan gjentas, så du aldri trenger å huske den forrige tilstanden, og en ukjent handler_id gjør i stillhet ingenting, som ovenfor. Poster begynner påslått: ett kall rett etter add_item setter opp en post som ikke er tilgjengelig ved oppstart.


events

Abonner på redigererhendelser. To likeverdige API-er — de lette dekoratorene anbefales; den rå subscribe() er der om du vil regne ut emnet under kjøringen.

Lette dekoratorer

@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 innpakkede funksjonen tar ett argument: en typet dataklasse som svarer til emnet. Se Hendelsesdokumentasjonen for hele tabellen over emne ↔ dataklasse.

Det rå API-et

handler_id = ctx.events.subscribe("document.saved", _saved)
# later:
ctx.events.unsubscribe(handler_id)

Tilgjengelige lette egenskaper (alle sammen dekoratorer):

Egenskap Utløses ved
on_document_opened Et dokument ble åpnet i en fane
on_document_closed Et dokument ble lukket
on_document_saved Et dokument ble skrevet til disken
on_selection_changed Markøren eller merkingen flyttet seg i det aktive dokumentet
on_active_document_changed Den fremste fanen endret seg
on_document_modified_changed Flagget for ren eller endret snudde (modified: bool)
on_text_changed Hver endring (position, added_bytes, removed_bytes) — hold den helt enkel
on_settings_changed Innstillingene ble tatt i bruk (scope: "" hele redigereren, "plugins/<id>")
on_theme_changed Temaet ble byttet eller endret (variant) — les fargene fra ctx.theme på nytt
on_workspace_root_changed En arbeidsområdemappe ble åpnet eller lukket (root)
on_workspace_files_changed Arbeidsområdets sett av filer endret seg (files)
on_host_window_shown Én gang per kjøring, etter det første bildet — legg tregt oppstartsarbeid i kø her
on_app_about_to_quit Redigereren lukkes — siste sjanse til å kjøre
on_command_shortcut_changed En kommandos faktiske hurtigtast endret seg (brukerens omtildeling, et importert tastaturoppsett eller en løst konflikt); innhold: command_id, sequence ("" = uten binding)

Ukjente emnenavn kaster ValueError ved abonneringen. Abonnenter kan kjøre etter den tilsvarende innebygde behandleren; behandl leveringen av hendelser som opplysende og ikke som avgjørende.

Å publisere

ctx.events.publish(topic, payload) kringkaster til både innebygde og Python-abonnenter — det tas bare imot for de to arbeidsområdeemnene (workspace.rootChanged, workspace.filesChanged); alt annet kaster ValueError. Se Hendelsesdokumentasjonen.


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")

Hvert nivå svarer til vertens ILogger. Linjer får prefikset [python:<plugin.id>], så utdataene dine kan finnes i redigererens diagnostikklogg:

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

Bruk aldri print() fra et programtillegg — arbeiderens stdout er RPC-kanalen, og å skrive til den ødelegger protokollen. ctx.log.* er den eneste godkjente måten å sende ut diagnostisk tekst på.


status

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

Send en forbigående melding til statuslinjen.

  • message (str): det som skal vises. Bare korte tekststrenger — statuslinjen har en fast bredde.
  • timeout_ms (int, 0 som standard): hvor lenge den skal bli stående på skjermen; 0 lar verten velge en fornuftig standard (noen få sekunder).
  • level (str, "info" som standard): "info" / "warning" / "error". Høyere nivåer kan holde tiden på skjermen litt lenger, så en feil ikke straks viskes ut av en rutinemessig info-melding.
ctx.status.show("saved", timeout_ms=1500)
ctx.status.show("no results", level="warning")

ctx.status.show sender og glemmer. Har verten ingen synlig statuslinje (et hodeløst eller minimalt bygg), kastes kallet i stillhet. ctx.status.clear() fjerner den forbigående meldingen som står på skjermen.

Varige avsnitt

Til levende tilstand (en ordtelling, en tjenerstatus) skal du bruke et avsnitt du eier selv og oppdaterer — det ødelegger aldri en melding, og omvendt:

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 ordner programtilleggenes avsnitt fra venstre mot høyre (lavere = lenger til venstre; uavgjorte beholder registreringsrekkefølgen). Når et programtillegg stenges ned, fjernes alt det fortsatt holder.


editor

Full synkron tilgang til det aktive dokumentet eller de aktive dokumentene. Hver metode sperrer den kallende tråden inntil verten svarer (typisk mikrosekunder når arbeideren og redigereren deler en maskin). Lovlig fra enhver kommando- eller hendelsesbehandler; aldri fra et async def-tilbakekall (se merknaden om tråder øverst på denne siden).

Å liste dokumenter

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

Tilgang til bufferen

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 unngår å dra hele bufferen over RPC-en når du bare trenger et stykke — foretrekk den til sortering og omforming i store filer.

Merking

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))

Filhandlinger

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 er det ene kallet for «ta brukeren dit» (en byggefeil, en stakkramme, et søketreff): det åpner filen eller aktiverer fanen dens, plasserer markøren uten merking, folder ut en bretting som skjuler linjen, ruller linjen inn i visningen — sentrert når den var utenfor skjermen — og gir redigereren fokus. Linje og kolonne telles fra 0 (kolonnen i UTF-8-byte) og avgrenses, avvises aldri; det returnerer dokument-ID-en, eller 0 når filen ikke kunne åpnes, og da har ingenting flyttet seg. reveal_line viser bare en linje: det flytter ikke markøren, bytter ikke fane og tar ikke fokus.

Bokmerker, merker og URL-utheving

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øk

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 for hele listen av felter.

Inkrementelt søk (utvid mønsteret mens brukeren skriver, fra et fastholdt anker):

ctx.editor.set_incremental_search_anchor(doc)
hit = ctx.editor.incremental_search(doc, "TOD", opts)   # MatchRange

Innrykk og syntaks

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

Delt visning og fanegrupper

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

Et dokument som vises i to ruter, er ett dokument med én id, så group_of() kan bare navngi den ene av plassene sine. groups_of() melder dem alle, og show_document_in_group() lager enda en delt visning — det samme som Vis > Del dokumentet gjør, så det reises ingen document_opened-hendelse.

ctx.editor speiler nå den innebygde IEditorService én til én.


languages

Syntaksfarging er en kjernefunksjon — verten finner ut et dokuments språk og farger det uten noe programtillegg. ctx.languages lar deg være med: list det verten kjenner, les eller overstyr et dokuments språk, kjør bestemmelsen på nytt, og bidra med hele språkdefinisjoner.

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 den samme formen som en medfølgende colorizers/*.json-fil (eller en hel definition=-ordbok): lexer navngir en leksikograf som alt finnes i backenden (verten får ingen nye leksikografer under kjøringen), styles knytter den leksikografens egne stilnavn til meningsbærende kategorier, og keywords adresserer ordlistene dens etter leksikografens egen plassbeskrivelse — verten slår opp begge deler nøyaktig som for en medfølgende fil, så du aldri staver motorens id-er. Å registrere en id som alt finnes, erstatter den så lenge du holder behandler-id-en; når programtillegget stenges ned, trekkes alt det fortsatt holder, tilbake.


document_settings

Skrivebeskyttede innstillinger avgrenset til dokumentet, som legger et prosjekts .editorconfig oppå dine egne ctx.settings. For en nøkkel k og et dokument doc finner et oppslag mte.<plugin.id>.<k> i den .editorconfig-filen som gjelder for det dokumentet, faller så tilbake på ctx.settings og deretter på standardverdien din:

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?

Skrivninger går gjennom ctx.settings — aldri tilbake i .editorconfig.


diagnostics

Skyv bølgelinjer og margtegn ut for et dokument — fra en statisk analysator, en språkserver eller en byggelogg. Verten tegner dem; du rører aldri visningen. Å publisere erstatter alt du tidligere har publisert for det paret av (dokument, kilde), så send hele settet hver gang:

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 er påkrevd — det er dét som holder settet ditt atskilt fra andre produsenters (LSP-programtillegget, stavekontrollen) på det samme dokumentet. Alvorsgrader: SEVERITY_ERROR / SEVERITY_WARNING / SEVERITY_INFORMATION / SEVERITY_HINT. Posisjoner er halvåpne [start, end)-byteområder.


theme

Registrer de fargene ditt eget grensesnitt tegner, som navngitte kategorier som kan inngå i temaer; Temaredigereren lister dem av seg selv, og brukerens overstyringer lagres og leses igjen 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)

Les resolve_color på nytt ved ctx.events.on_theme_changed.


compare

Åpne (eller gi fokus til) redigererens sammenligningsvisning side ved side over to kilder. Hver side er en fil på disken (path=), bokstavelig tekst (content=) eller begge deler (innholdet vises, og stien gir øktbevaringen en identitet som kan gjenopprettes):

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

Gir False tilbake når en side ikke kan leses.


settings

Et varig, typet nøkkel/verdi-lager, avgrenset til plugins/<your.id>/ hos verten. Alle metodene er synkrone; get og set utledes av standardverdiens Python-type.

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-typen til default Type på ledningen Typen get() gir tilbake
bool bool bool
int (ikke bool) int int
float double float
str (eller None) string str

Dessuten:

ctx.settings.contains("wrap")           # bool
ctx.settings.remove("stale_key")
ctx.settings.sync()                     # optional; host also syncs on shutdown

Reager på endringer som gjøres i innstillingsvinduet, ved å abonnere på settings.changed (ctx.events.on_settings_changed; se Hendelsesdokumentasjonen).

app_settings

En skrivebeskyttet visning av redigererens oppsett for hele programmet. De velkjente nøkkelnavnene eksporteres som konstanter i mte.keys (som speiler C++-hodefilen PluginApi/AppSettingsKeys.h, som blir værende den avgjørende listen):

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"

Settere, remove() og sync() på ctx.app_settings kaster RuntimeError — visningen er med vilje skrivebeskyttet.


completions

Registrer en fullføringsleverandør som er med i redigererens liste:

@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)]

Den innpakkede funksjonen kalles ved hvert tastetrykk (eller ved Ctrl+Mellomrom) der dokumentet passer til språkfilteret. Felter på req:

Felt Type Betydning
document int Det dokumentet som redigeres
prefix str Det ordstykket som står rett før markøren
position int Markørens byteposisjon
language_id str Fargeleggerens id ("python", "cpp" …)
manual bool True når brukeren trykket Ctrl+Mellomrom; False ved automatisk utløsning

Gi en liste av mte.CompletionItem tilbake (eller alminnelige tekststrenger eller ordbøker med {text, detail, kind}). Typer: mte.KIND_KEYWORD / KIND_WORD / KIND_SYMBOL / KIND_SNIPPET / KIND_UNKNOWN.

Frist

Verten sender leverandøren din en forespørsel per tastetrykk med en frist på omtrent 30 ms. Overskrider tilbakekallet ditt den, vises listen uten kandidatene dine for det tastetrykket. Bygg enhver dyr indeks i bakgrunnen, og svar fra et mellomlager — gjør aldri inn- og utdata i tilbakekallet. Nettverksfullføring i LSP-stil hører hjemme i en framtidig asynkron flate og ikke her.

languages=[] (eller ingenting) betyr «hvert dokument». Flere leverandører per programtillegg er tillatt; hver dekorator registrerer sin egen.


data_dir

path = ctx.data_dir()                    # pathlib.Path

Gir den skrivbare mappen per programtillegg tilbake som verten alt har laget for deg. Bruk den til mellomlagre, indekser og alle data du eier. ctx.diagnostics_dir() gir tilsvarende den felles mappen for krasj og logger tilbake (skrivebeskyttet for deg — det er krasjrapporteringen som skriver der).


docks

Deklarative dokkpaneler — et sidepanelgrensesnitt bygget av en skjemabeskrivelse og ikke av Qt-kontroller. Se mte.ui for byggernes ordforråd.

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

Registrerer et dokkpanel. Gir panelets id tilbake (tildelt av seg selv når id er None).

  • id (str, valgfri) — en stabil identifikator som ctx.ui.get / ctx.ui.set bruker til å adressere kontroller inne i dette panelet. Gir du ikke en, tildeler verten en py-dock/N-id og gir den tilbake.
  • title (str) — vises på panelets overskrift.
  • area (str) — "left" / "right" / "top" / "bottom". Ukjente verdier faller tilbake på "right".
  • form (dict) — en beskrivelse bygget med mte.ui-byggerne.
  • initially_visible (bool) — om dokken vises når programtillegget lastes inn. True som standard. Gi False til et panel som ikke skal sprette fram ved oppstart, og vis det så senere med set_panel_visible.
  • proportional_size (float) — standardstørrelsen til panelet som en andel av hovedvinduet: av bredden i områdene "left" / "right", av høyden i "top" / "bottom". 0.0 (standardvalget) beholder den pikseloppførselen et verktøypanel vil ha — panelet holder størrelsen sin, og det å endre vindusstørrelsen lar det være. En brøk som 0.4 er hva en forhåndsvisning vil: det å maksimere vinduet gjør panelet bredere med det. Bare standarden er din; så snart brukeren drar skilleren til panelet, tar verten i bruk og husker andelen deres. Verdier utenfor området klemmes, og verten beholder en minste utstrekning for både panelet og editoren. De fullstendige reglene står under proportionalSize.
  • tab_group (str) — paneler med den samme ikke-tomme gruppe-ID-en i det samme area deler én dokk som faner i stedet for å ligge side om side. Gi ID-en et navnerom som en panel-ID ("renamer/panels"). Bare standardoppstillingen: et oppsett brukeren har lagret, går foran. Se tabGroup.

ctx.docks.is_panel_visible(handler_id) -> bool

Om et panel som er lagt til med add_panel, er åpent — et panel som ligger som fane bak et annet, regnes som åpent; et som ble registrert med initially_visible=False og aldri vist, et lukket panel og en ukjent ID gjør det ikke. Bruk det sammen med set_panel_visible for å vise panelene dine under en oppgave og så sette tilbake det brukeren hadde:

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)

Viser eller skjuler et panel som er lagt til med add_panel. Å vise det løfter også panelet over de dokkene det deler område med, så en kommando som «åpner» panelet sitt, virkelig setter det foran brukeren. Kan gjentas — gjenta den samme verdien fritt, det er ingen tilstand å holde styr på; det ses i stillhet bort fra ved en ukjent 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

Tildeler en behandler-id for å knytte en kontrolls on_change-felt til en Python-funksjon. Tilbakekallet mottar den nye verdien som en tekststreng; boolske kontroller leverer "true" / "false", og tallkontroller leverer heltallet som en tekststreng.

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)])

Behandler-id-er for on_change har prefikset py-dock-cb/N og kolliderer aldri med behandler-id-er for kommandoer, hendelser eller fullføringer.

Tilbakekallet kalles på arbeiderens hendelsesløkke og IKKE på trådpuljen — hold det raskt. Bruk ctx.log, endre en liten tilstand i minnet, eller utløs en oppfølgende kommando; gjør ikke tungt arbeid her.


line_markers

Margsymboler og farging av hele linjer, festet til dokumentlinjer som følger brukerens redigeringer — stoppunkter, en pil for gjeldende setning, endringsstriper — og kravet på klikket i bokmerkekolonnen. Den innebygde tjenesten og alle reglene dens er beskrevet under Linjemerker og margklikk.

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 er en av mte.GLYPH_NONE (bare farging), GLYPH_CIRCLE, GLYPH_CIRCLE_HOLLOW, GLYPH_CIRCLE_BAR, GLYPH_ARROW, GLYPH_STRIP; fargene er ID-er for kategorier du har registrert med ctx.theme. Redigereren har bare noen få merkeplasser for alle programtillegg til sammen, så dette kan mislykkes — da kastes RpcError. Registrer typene dine én gang, i register().
  • add_marker(kind, doc, line) — linje talt fra 0, ikke avgrenset: en linje utenfor dokumentet returnerer 0 og plasserer ingenting.
  • Merker flytter seg med linjene sine, overlever en ny innlasting på samme linjenummer og forsvinner med dokumentet sitt — legg til dine igjen i ctx.events.on_document_opened.

Margklikket

@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()

Hvert venstreklikk i bokmerkekolonnen publiseres som margin.clicked (document, line, margin = "symbol", modifiers — biter mte.MODIFIER_SHIFT / MODIFIER_CONTROL / MODIFIER_ALT / MODIFIER_META). Som standard slår redigereren også et bokmerke av eller på ved klikket; så lenge et programtillegg holder claim_margin_click(), gjør den ikke det og overlater klikket til programtilleggene. ctx.line_markers.release(claim) gir klikket tilbake.


settings_page

Legger til en side under redigererens innstillingsvindu. Samme ordforråd for skjemabeskrivelser som dokkpaneler; samme bevaring 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, påkrevd) — en sti med skråstreker i innstillingstreet, for eksempel "Plugins/My Plugin". Nøstede kategorier kommer fram som noder i treet.
  • title (str, påkrevd) — den overskriften som vises over siden.
  • form (dict, påkrevd) — en beskrivelse bygget med mte.ui-byggerne.
  • scope (str, valgfri) — det hendelsesomfanget som sendes ut ved SettingsChanged, etter at brukeren har trykket OK eller Bruk.

Gir behandler-id-en tilbake.

Avbryt ruller IKKE tilbake endringene

Kontroller med binds="settings:<key>" lagres med det samme mens brukeren redigerer, så et trykk på Avbryt i innstillingsvinduet angrer dem ikke. Det er en kjent begrensning i fase 3; en senere forfining vil legge de underliggende innstillingene bak et mellomledd med buffer.


ui

Leser og skriver kontrollers verdier inne i paneler du alt har registrert. Verdier er alltid tekststrenger på ledningen.

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]

Leser en kontrolls gjeldende verdi. Gir None tilbake når panelet eller kontrollen er ukjent (ikke en feil — behandl både None og "" som «tom»).

  • Label / Button / LineEdit / ComboBox — gir teksten tilbake.
  • Checkbox — gir "true" eller "false" tilbake.
  • NumberSpin — gir heltallet tilbake som en desimaltekststreng.
  • List — gir teksten på den merkede posten tilbake (tom når ingenting er merket).

ctx.ui.set(panel_id, control_id, value) -> bool

Skriver en verdi inn i en kontroll programmatisk. Gir True tilbake når skrivningen lyktes, og False når panelet eller kontrollen er ukjent, eller verdien ikke kan omformes (for eksempel et tall som ikke er et heltall, sendt til en NumberSpin).

Pythons True / False gjøres om til "true" / "false" før de sendes; alt annet går gjennom str().

Et set fra verten utløser IKKE kontrollens on_change på nytt, så du kan oppdatere dine egne kontroller fra en kommandobehandler uten uendelige løkker.


Sammendrag av bindinger under kjøringen (fase 3)

Felt i skjemabeskrivelsen Oppførsel under kjøringen
id="…" Adresser kontrollen fra ctx.ui.get / ctx.ui.set.
binds="settings:<key>" Leser startverdien fra ctx.settings; skriver tilbake ved hver endring fra brukeren.
on_click="<cmd>" Knapper kaller den registrerte kommando-id-en ved et klikk.
on_change=behandler Utløser det tilbakekallet som er registrert gjennom ctx.docks.on_change(...), ved en endring fra brukeren.

De samme reglene gjelder for dokkpaneler og innstillingssider.