Gå til indholdet

ctx — udvidelsesmodulets kontekst

register(ctx) modtager ét argument: en PluginContext. Den blotlægger tjenesterne nedenfor (hele den IPluginContext-flade, der passer til modellen med en selvstændig proces). Hver er en lille Python-klasse, som arbejderen bygger oven på 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 dele af den indbyggede kontrakt har endnu ingen modpart i Python: hover-leverandører (så prioritetsbåndene for hover gælder ikke for Python-moduler) og leverandører til editorens kontekstmenu (så heller ikke kontekstmenuens område tekst / margen). Begge besvares, mens brugeren venter, og det kræver den samme behandling med frister, som fuldførelsesleverandørerne har, før en rundtur til arbejderen er tryg.

Gem ikke ctx i en global variabel i forventning om at bruge den senere — selv om attributterne er stabile udvidelsesmodulets liv ud, så behandl ctx som noget, der hører til registreringen. Behold henvisninger til de enkelte tjenester, hvis du har brug for dem (log = ctx.log, for eksempel).


commands

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

En dekorator, der registrerer en kommando og binder den indpakkede funktion som dens håndtering.

  • id (str, påkrævet): en entydig identifikator i omvendt DNS-stil (f.eks. "wordcount.count"). Samme regler som i plugin.json — skal være globalt entydig.
  • title (str, påkrævet): den læsbare mærkat, der vises overalt, hvor kommandoen kommer til syne (menuer, kommandopaletten).
  • shortcut (str, valgfri): den flytbare standardgenvejstast, f.eks. "Ctrl+Alt+W". Ctrl oversættes af sig selv til platformens primære modifikator (Cmd på macOS). Lad den stå tom for ingen. Brugeren kan tildele den en anden tast eller slet ingen under Indstillinger ▸ Genvejstaster; for menubundne kommandoer anvender værten omtildelingen for dig.
  • category (str, valgfri): en frit formuleret grupperingsmærkat — vises som kommandoens kategori i genvejstastoversigten.
  • scope (str, valgfri): ""/"application" (standard — værten binder genvejstasten på kommandoens menuhandling) eller "pluginWindow" for tastekombinationer, der hører hjemme i en brugerflade, udvidelsesmodulet ejer: værten opregner kun kommandoen i oversigten; læs ctx.commands.effective_shortcut(id), når du bygger dine kontroller, og anvend den igen ved hændelsen "command.shortcutChanged".
ctx.commands.effective_shortcut(command_id) -> str

Den genvejstast, der faktisk gælder efter brugerens omtildeling og løsningen af konflikter ("" = uden tildeling eller ukendt). Trygt at kalde fra håndteringer.

Den indpakkede funktion tager ingen argumenter og giver ingenting tilbage. Undtagelser inde i den opfanges af arbejderen og noteres til stderr; de får ikke editoren til at gå ned.

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

Under motorhjelmen: Python-siden tildeler et handlerId lokalt, registrerer tilbagekaldet på RPC-forbindelsen og sender commands.registerCommand til værten. Når brugeren aktiverer kommandoen (et menuklik, genvejstasten, paletten), sender værten et callback-RPC, og din funktion kører.

Kommando-id'er gælder for hele processen og ikke pr. udvidelsesmodul. Sæt dit omvendte DNS-navnerum foran dine id'er (wordcount.…) for at undgå sammenstød.


Menustier bruger / som skilletegn. Det yderste venstre led er en øverste menu (Plugins, Tools …); mellemliggende led opretter undermenuer efter behov; det sidste led er punktets mærkat.

ctx.menus.add_item(path, command_id)

ctx.menus.add_item("Plugins/Word Count/Count", "wordcount.count")
  • path (str): "Øverst/Under/…/Mærkat". Mellemliggende undermenuer oprettes, hvis de ikke findes.
  • command_id (str): id'et på en tidligere registreret kommando. Et klik på punktet kalder kommandoen.

Giver en uigennemsigtig handlerId-tekststreng tilbage. Behold den, hvis du senere får brug for at unsubscribe punktet (sjældent i fase 1).

Det yderste venstre led ("Plugins" ovenfor) skal passe til title i en af dine menus[]-poster i plugin.json, så placeringen på menulinjen kommer med.

ctx.menus.add_separator(path)

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

Indsætter en skillelinje i en eksisterende undermenu.

ctx.menus.set_item_checked(handler_id, checked)

Gør det tidligere tilføjede punkt til et afkrydseligt menupunkt og sætter dets tilstand — det sædvanlige idiom for en vedvarende til/fra-kontakt, der viser et synligt flueben.

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 kald gør punktet afkrydseligt; de følgende kald vender bare fluebenet. At give et handler_id, værten ikke kender, gør i stilhed ingenting (trygt at kalde, efter at dit symbol er droppet, f.eks. fra et forsinket tilbagekald, der udløses under nedlukningen).

ctx.menus.set_item_enabled(handler_id, enabled)

Slår et tidligere tilføjet punkt til eller nedtoner det. Brug den, når kommandoen kræver en forudsætning — noget at lukke, noget at gendanne — så menuen viser, at den ikke er tilgængelig, i stedet for at tilbyde en post, der i stilhed ikke gør noget.

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)

Et frakoblet punkt bliver ved med at være synligt, men kan ikke udløses, hverken med et klik eller med sin genvejstast. Kaldet kan gentages, så du aldrig behøver at huske den forrige tilstand, og et ukendt handler_id gør i stilhed ingenting som ovenfor. Punkter begynder tilkoblede: ét kald lige efter add_item sætter et punkt op, der ikke er tilgængeligt ved opstart.


events

Abonnér på editorhændelser. To ligeværdige API'er — de lette dekoratorer anbefales; den rå subscribe() er der, hvis du vil beregne emnet under kørslen.

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 indpakkede funktion tager ét argument: en typet dataklasse, der svarer til emnet. Se Hændelsesdokumentationen for hele tabellen over emne ↔ dataklasse.

Den rå API

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

Tilgængelige lette egenskaber (alle sammen dekoratorer):

Egenskab Udløses ved
on_document_opened Et dokument blev åbnet i et faneblad
on_document_closed Et dokument blev lukket
on_document_saved Et dokument blev skrevet til disken
on_selection_changed Markøren eller markeringen flyttede sig i det aktive dokument
on_active_document_changed Det forreste faneblad skiftede
on_document_modified_changed Flaget for ren eller ændret vendte (modified: bool)
on_text_changed Hver ændring (position, added_bytes, removed_bytes) — hold den ganske enkel
on_settings_changed Indstillingerne blev anvendt (scope: "" hele editoren, "plugins/<id>")
on_theme_changed Temaet blev skiftet eller redigeret (variant) — læs farverne fra ctx.theme igen
on_workspace_root_changed En arbejdsområdemappe blev åbnet eller lukket (root)
on_workspace_files_changed Arbejdsområdets sæt af filer ændrede sig (files)
on_host_window_shown Én gang pr. kørsel, efter det første billede — læg langsomt opstartsarbejde i kø her
on_app_about_to_quit Editoren lukker — sidste chance for at køre
on_command_shortcut_changed En kommandos faktiske genvejstast ændrede sig (brugerens omtildeling, en importeret tastaturopsætning eller en løst konflikt); indhold: command_id, sequence ("" = uden tildeling)

Ukendte emnenavne kaster ValueError ved abonneringen. Abonnenter kan køre efter den tilsvarende indbyggede håndtering; behandl leveringen af hændelser som oplysende og ikke som afgørende.

At udgive

ctx.events.publish(topic, payload) udsender til både indbyggede og Python-abonnenter — der tages kun imod de to emner om arbejdsområdet (workspace.rootChanged, workspace.filesChanged); alt andet kaster ValueError. Se Hændelsesdokumentationen.


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 niveau svarer til værtens ILogger. Linjer får præfikset [python:<plugin.id>], så dit output kan findes i editorens diagnostiklog:

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

Brug aldrig print() fra et udvidelsesmodul — arbejderens stdout er RPC-kanalen, og at skrive til den ødelægger protokollen. ctx.log.* er den eneste godkendte måde at udsende diagnostisk tekst på.


status

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

Send en forbigående meddelelse til statuslinjen.

  • message (str): det, der skal vises. Kun korte tekststrenge — statuslinjen har en fast bredde.
  • timeout_ms (int, 0 som standard): hvor længe den skal blive stående på skærmen; 0 lader værten vælge en fornuftig standard (nogle få sekunder).
  • level (str, "info" som standard): "info" / "warning" / "error". Højere niveauer kan holde tiden på skærmen lidt længere, så en fejl ikke øjeblikkeligt viskes ud af en rutinemæssig info-besked.
ctx.status.show("saved", timeout_ms=1500)
ctx.status.show("no results", level="warning")

ctx.status.show sender og glemmer. Har værten ingen synlig statuslinje (en hovedløs eller minimal bygning), kasseres kaldet i stilhed. ctx.status.clear() fjerner den forbigående meddelelse, der står på skærmen.

Vedvarende afsnit

Til levende tilstand (et ordtal, en serverstatus) skal du bruge et afsnit, du selv ejer og opdaterer — det ødelægger aldrig en meddelelse, 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 modulernes afsnit fra venstre mod højre (lavere = længere til venstre; uafgjorte beholder registreringsrækkefølgen). Når et udvidelsesmodul lukkes ned, fjernes alt, det stadig holder.


editor

Fuld synkron adgang til det aktive dokument eller de aktive dokumenter. Hver metode spærrer den kaldende tråd, indtil værten svarer (typisk mikrosekunder, når arbejderen og editoren deler en maskine). Lovligt fra enhver kommando- eller hændelseshåndtering; aldrig fra et async def-tilbagekald (se bemærkningen om tråde øverst på denne side).

At opregne 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

Adgang 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 undgår at trække hele bufferen over RPC'en, når du kun har brug for et stykke — foretræk den til sortering og omdannelse i store 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))

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 kald til »tag brugeren derhen« (en byggefejl, en stakramme, et søgeresultat): det åbner filen eller aktiverer dens faneblad, placerer markøren uden markering, folder en foldning ud, der skjuler linjen, ruller linjen ind i visningen — centreret, når den var uden for skærmen — og giver editoren fokus. Linje og kolonne tælles fra 0 (kolonnen i UTF-8-byte) og afgrænses, afvises aldrig; det returnerer dokument-id'et, eller 0, når filen ikke kunne åbnes, og så har intet flyttet sig. reveal_line viser kun en linje: det flytter ikke markøren, skifter ikke faneblad og tager ikke fokus.

Bogmærker, markeringer og URL-fremhævning

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

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 af felter.

Trinvis søgning (udvid mønsteret, mens brugeren skriver, fra et fastholdt anker):

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

Indrykning 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

Opdelt visning og fanebladsgrupper

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, der vises i to ruder, er ét dokument med ét id, så group_of() kan kun navngive den ene af dets pladser. groups_of() melder dem alle sammen, og show_document_in_group() opretter endnu en delt visning — det samme, som Vis > Opdel dokumentet til højre gør, så der rejses ingen document_opened-hændelse.

ctx.editor afspejler nu den indbyggede IEditorService én til én.


languages

Syntaksfarvning er en kernefunktion — værten afgør et dokuments sprog og farver det uden noget udvidelsesmodul. ctx.languages lader dig være med: opregn, hvad værten kender, læs eller tilsidesæt et dokuments sprog, kør bestemmelsen igen og bidrag med hele sprogdefinitioner.

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 tager den samme form som en medleveret colorizers/*.json-fil (eller en hel definition=-ordbog): lexer navngiver en eksisterende backend-lexer (værten får ingen nye lexere under kørslen), styles knytter den lexers egne stilnavne til semantiske kategorier, og keywords adresserer dens ordlister efter lexerens egen pladsbeskrivelse — værten slår begge dele op præcis som for en medleveret fil, så du aldrig staver motorens id'er. At registrere et id, der allerede findes, erstatter det, så længe du holder på håndterings-id'et; når udvidelsesmodulet lukkes ned, trækkes alt, det stadig holder, tilbage.


document_settings

Skrivebeskyttede indstillinger, der er afgrænset til dokumentet, og som lægger et projekts .editorconfig oven på dine egne ctx.settings. For en nøgle k og et dokument doc finder et opslag mte.<plugin.id>.<k> i den .editorconfig, der gælder for det dokument, falder derefter tilbage på ctx.settings og derefter på din standardværdi:

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 gennem ctx.settings — aldrig tilbage i .editorconfig.


diagnostics

Skub bølgelinjer og margentegn ud for et dokument — fra en statisk analysator, en sprogserver eller en byggelog. Værten tegner dem; du rører aldrig visningen. At udgive erstatter alt, du tidligere har udgivet for det par af (dokument, kilde), så send hele sættet 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åkrævet — det er dét, der holder dit sæt adskilt fra andre producenters (LSP-udvidelsesmodulet, stavekontrollen) på det samme dokument. Alvorsgrader: SEVERITY_ERROR / SEVERITY_WARNING / SEVERITY_INFORMATION / SEVERITY_HINT. Positioner er halvåbne [start, end)-byteområder.


theme

Registrér de farver, din egen brugerflade tegner, som navngivne kategorier, der kan indgå i temaer; Temaeditoren opregner dem af sig selv, og brugerens tilsidesættelser gemmes og læses igen 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 resolve_color igen ved ctx.events.on_theme_changed.


compare

Åbn (eller fokusér) editorens sammenligningsvisning side om side over to kilder. Hver side er en fil på disken (path=), bogstavelig tekst (content=) eller begge dele (indholdet vises, og stien giver sessionsbevaringen en identitet, der kan gendannes):

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

Giver False tilbage, når en side ikke kan læses.


settings

Et vedvarende typet nøgle/værdi-lager, afgrænset til plugins/<your.id>/ hos værten. Alle metoder er synkrone; get og set udledes af standardværdiens 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 for default Type på ledningen Den type, get() giver tilbage
bool bool bool
int (ikke bool) int int
float double float
str (eller None) string str

Desuden:

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

Reagér på ændringer, der er lavet i indstillingsvinduet, ved at abonnere på settings.changed (ctx.events.on_settings_changed; se Hændelsesdokumentationen).

app_settings

En skrivebeskyttet visning af editorens opsætning for hele programmet. De velkendte nøglenavne eksporteres som konstanter i mte.keys (som afspejler C++-headerfilen PluginApi/AppSettingsKeys.h, der bliver ved med at være den afgørende liste):

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ættere, remove() og sync() på ctx.app_settings kaster RuntimeError — visningen er med vilje skrivebeskyttet.


completions

Registrér en fuldførelsesleverandør, der er med i editorens 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 indpakkede funktion kaldes ved hvert tastetryk (eller ved Ctrl+Mellemrum), hvis dokument passer til sprogfilteret. Felter på req:

Felt Type Betydning
document int Det dokument, der redigeres
prefix str Det ordstykke, der står umiddelbart før markøren
position int Markørens byteposition
language_id str Farvelæggerens id ("python", "cpp" …)
manual bool True, når brugeren trykkede Ctrl+Mellemrum; False ved automatisk udløsning

Giv en liste af mte.CompletionItem tilbage (eller almindelige tekststrenge eller ordbøger med {text, detail, kind}). Typer: mte.KIND_KEYWORD / KIND_WORD / KIND_SYMBOL / KIND_SNIPPET / KIND_UNKNOWN.

Frist

Værten sender din leverandør en forespørgsel pr. tastetryk med en frist på cirka 30 ms. Overskrider dit tilbagekald den, vises listen uden dine kandidater for det tastetryk. Byg ethvert dyrt indeks i baggrunden, og svar fra et mellemlager — lav aldrig ind- og uddata i tilbagekaldet. Netværksfuldførelse i LSP-stil hører hjemme i en fremtidig asynkron flade og ikke her.

languages=[] (eller ingenting) betyder »hvert dokument«. Flere leverandører pr. udvidelsesmodul er tilladt; hver dekorator registrerer sin egen.


data_dir

path = ctx.data_dir()                    # pathlib.Path

Giver den skrivbare mappe pr. udvidelsesmodul tilbage, som værten allerede har oprettet til dig. Brug den til mellemlagre, indekser og alle data, du ejer. ctx.diagnostics_dir() giver tilsvarende den fælles mappe til nedbrud og logfiler tilbage (skrivebeskyttet for dig — det er nedbrudsrapporteringen, der skriver der).


docks

Deklarative dokpaneler — en sidepanelbrugerflade bygget af en formularbeskrivelse og ikke af 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 dokpanel. Giver panelets id tilbage (tildelt af sig selv, når id er None).

  • id (str, valgfri) — en stabil identifikator, som ctx.ui.get / ctx.ui.set bruger til at adressere kontroller inde i dette panel. Giver du ikke en, tildeler værten et py-dock/N-id og giver det tilbage.
  • title (str) — vises på panelets overskrift.
  • area (str) — "left" / "right" / "top" / "bottom". Ukendte værdier falder tilbage på "right".
  • form (dict) — en beskrivelse bygget med mte.ui-byggerne.
  • initially_visible (bool) — om panelet vises, når udvidelsesmodulet indlæses. True som standard. Giv False til et panel, der ikke skal springe frem ved opstart, og vis det senere med set_panel_visible.
  • proportional_size (float) — panelets standardstørrelse som en andel af hovedvinduet: af bredden i områderne "left" / "right", af højden i "top" / "bottom". 0.0 (standardvalget) bevarer den pixelopførsel, et værktøjspanel vil have — panelet holder sin størrelse, og det at ændre vinduets størrelse lader det være. En brøk som 0.4 er, hvad en forhåndsvisning vil: det at maksimere vinduet gør panelet bredere med det. Kun standarden er din; så snart brugeren trækker panelets deler, tager værten deres andel i brug og husker den. Værdier uden for intervallet klemmes, og værten bevarer en mindste udstrækning for både panelet og editoren. De fulde regler står under proportionalSize.
  • tab_group (str) — paneler med det samme ikke-tomme gruppe-id i det samme area deler én dok som faneblade i stedet for at ligge side om side. Giv det et navnerum som et panel-id ("renamer/panels"). Kun standardopstillingen: et layout, som brugeren har gemt, går forud. Se tabGroup.

ctx.docks.is_panel_visible(handler_id) -> bool

Om et panel, der er tilføjet med add_panel, er åbent — et panel, der ligger som faneblad bag et andet, tæller som åbent; et, der blev registreret med initially_visible=False og aldrig vist, et lukket panel og et ukendt id gør ikke. Brug det sammen med set_panel_visible til at vise dine paneler under en opgave og bagefter sætte det tilbage, som brugeren havde:

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, der er tilføjet med add_panel. At vise det løfter også panelet op over de paneler, det deler område med, så en kommando, der »åbner« sit panel, virkelig sætter det foran brugeren. Kan gentages — gentag den samme værdi frit, der er ingen tilstand at holde styr på; ses der i stilhed bort fra ved et ukendt 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 et håndterings-id til at knytte en kontrols on_change-felt til en Python-funktion. Tilbagekaldet modtager den nye værdi som en tekststreng; boolske kontroller leverer "true" / "false", og talkontroller 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)])

Håndterings-id'er til on_change har præfikset py-dock-cb/N og støder aldrig sammen med håndterings-id'er til kommandoer, hændelser eller fuldførelser.

Tilbagekaldet kaldes på arbejderens hændelsesløkke og IKKE på trådpuljen — hold det hurtigt. Brug ctx.log, ændr en lille tilstand i hukommelsen, eller udløs en efterfølgende kommando; lav ikke tungt arbejde her.


line_markers

Margensymboler og farvning af hele linjer, fæstnet til dokumentlinjer, der følger brugerens redigeringer — stoppunkter, en pil for den aktuelle sætning, ændringsstriber — og kravet på klikket i bogmærkekolonnen. Den indbyggede tjeneste og alle dens regler er beskrevet under Linjemærker og margenklik.

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 af mte.GLYPH_NONE (kun farvning), GLYPH_CIRCLE, GLYPH_CIRCLE_HOLLOW, GLYPH_CIRCLE_BAR, GLYPH_ARROW, GLYPH_STRIP; farverne er id'er på kategorier, du har registreret med ctx.theme. Editoren har kun få mærkepladser til alle udvidelsesmoduler tilsammen, så dette kan mislykkes — så kastes RpcError. Registrér dine typer én gang, i register().
  • add_marker(kind, doc, line) — linje talt fra 0, ikke afgrænset: en linje uden for dokumentet returnerer 0 og placerer intet.
  • Mærker flytter sig med deres linjer, overlever en genindlæsning på samme linjenummer og forsvinder med deres dokument — tilføj dine igen i ctx.events.on_document_opened.

Margenklikket

@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 venstreklik i bogmærkekolonnen udgives som margin.clicked (document, line, margin = "symbol", modifiers — bits mte.MODIFIER_SHIFT / MODIFIER_CONTROL / MODIFIER_ALT / MODIFIER_META). Som standard slår editoren også et bogmærke til eller fra ved klikket; så længe et modul holder claim_margin_click(), gør den det ikke og overlader klikket til modulerne. ctx.line_markers.release(claim) giver klikket tilbage.


settings_page

Tilføjer en side under editorens indstillingsvindue. Samme ordforråd til formularbeskrivelser som dokpaneler; 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åkrævet) — en sti med skråstreger i indstillingstræet, f.eks. "Plugins/My Plugin". Indlejrede kategorier kommer frem som knuder i træet.
  • title (str, påkrævet) — den overskrift, der vises over siden.
  • form (dict, påkrævet) — en beskrivelse bygget med mte.ui-byggerne.
  • scope (str, valgfri) — det hændelsesomfang, der udsendes ved SettingsChanged, efter at brugeren har trykket OK eller Anvend.

Giver håndterings-id'et tilbage.

Annullér ruller IKKE ændringer tilbage

Kontroller med binds="settings:<key>" gemmer med det samme, mens brugeren redigerer, så et tryk på Annullér i indstillingsvinduet fortryder dem ikke. Det er en kendt begrænsning i fase 3; en senere forfining vil lægge de underliggende indstillinger bag et mellemled med buffer.


ui

Læser og skriver kontrollers værdier inde i paneler, du allerede har registreret. Værdier er altid tekststrenge 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]

Læser en kontrols nuværende værdi. Giver None tilbage, når panelet eller kontrollen er ukendt (ikke en fejl — behandl både None og "" som »tom«).

  • Label / Button / LineEdit / ComboBox — giver teksten tilbage.
  • Checkbox — giver "true" eller "false" tilbage.
  • NumberSpin — giver heltallet tilbage som en decimaltekststreng.
  • List — giver teksten på den markerede post tilbage (tom, når intet er markeret).

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

Skriver en værdi ind i en kontrol programmatisk. Giver True tilbage, når skrivningen lykkedes, og False, når panelet eller kontrollen er ukendt, eller værdien ikke kan omformes (f.eks. et tal, der ikke er et heltal, sendt til en NumberSpin).

Pythons True / False omsættes til "true" / "false", inden de sendes; alt andet går gennem str().

Et set fra værten udløser IKKE kontrollens on_change-tilbagekald igen, så du kan opdatere dine egne kontroller fra en kommandohåndtering uden uendelige løkker.


Sammendrag af bindinger under kørslen (fase 3)

Felt i formularbeskrivelsen Adfærd under kørslen
id="…" Adressér kontrollen fra ctx.ui.get / ctx.ui.set.
binds="settings:<key>" Læser udgangsværdien fra ctx.settings; skriver tilbage ved hver ændring fra brugeren.
on_click="<cmd>" Knapper kalder det registrerede kommando-id ved et klik.
on_change=håndtering Udløser det tilbagekald, der er registreret via ctx.docks.on_change(...), ved en ændring fra brugeren.

De samme regler gælder for dokpaneler og indstillingssider.