Ga naar inhoud

ctx — de plug-incontext

register(ctx) ontvangt één argument: een PluginContext. Dat stelt de diensten hieronder beschikbaar (het hele vlak van IPluginContext dat in het model buiten het proces past). Elk ervan is een kleine Python-klasse die de worker boven op het RPC-kanaal bouwt; de broncode staat in 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

Twee delen van het native contract hebben nog geen Python-tegenhanger: hover-leveranciers (de prioriteitsbanden van hover gelden dus niet voor Python-plug-ins) en leveranciers voor het contextmenu van de editor (het onderscheid tekst / marge in het contextmenu dus ook niet). Beide worden beantwoord terwijl de gebruiker wacht, en dat vergt dezelfde behandeling met een deadline die aanvullingsleveranciers hebben voordat een retour naar de worker veilig is.

Bewaar ctx niet in een globale variabele in de verwachting hem later te gebruiken — hoewel de attributen stabiel zijn zolang de plug-in leeft, behandelt u ctx als gebonden aan één registratie. Houd verwijzingen naar de afzonderlijke diensten vast wanneer u ze nodig hebt (bijvoorbeeld log = ctx.log).


commands

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

Decorator die een opdracht registreert en de omhulde aanroepbare eraan koppelt als afhandelaar.

  • id (str, verplicht): unieke aanduiding in omgekeerde DNS-stijl (bijvoorbeeld "wordcount.count"). Dezelfde regels als in plugin.json — moet wereldwijd uniek zijn.
  • title (str, verplicht): leesbaar label dat overal getoond wordt waar de opdracht opduikt (menu's, opdrachtenpalet).
  • shortcut (str, optioneel): draagbare standaardsneltoetstekenreeks, bijvoorbeeld "Ctrl+Alt+W". Ctrl wordt automatisch aan de primaire modificatietoets van het platform gekoppeld (Cmd op macOS). Laat leeg voor geen. De gebruiker kan hem in Voorkeuren ▸ Sneltoetsen opnieuw toewijzen of loskoppelen; voor menu-gebonden opdrachten past de host de nieuwe toewijzing voor u toe.
  • category (str, optioneel): vrij groeperingslabel — getoond als de categorie van de opdracht in de sneltoetstoewijzing.
  • scope (str, optioneel): ""/"application" (standaard — de host koppelt de sneltoets aan de menu-actie van de opdracht) of "pluginWindow" voor combinaties die in een interface wonen die de plug-in bezit: de host somt de opdracht alleen in de toewijzing op; lees ctx.commands.effective_shortcut(id) terwijl u uw widgets bouwt en pas hem opnieuw toe bij de gebeurtenis "command.shortcutChanged".
ctx.commands.effective_shortcut(command_id) -> str

De sneltoets die op dit moment geldt na het opnieuw toewijzen door de gebruiker en het oplossen van conflicten ("" = losgekoppeld of onbekend). Veilig aan te roepen vanuit afhandelaars.

De omhulde functie neemt geen argumenten en levert niets op. Uitzonderingen erin worden door de worker opgevangen en naar stderr genoteerd; ze laten de editor niet crashen.

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

Onder de motorkap: de Python-kant kent lokaal een handlerId toe, registreert de callback op de RPC-verbinding, en vuurt commands.registerCommand op de host af. Wanneer de gebruiker de opdracht in gang zet (menuklik, sneltoets, palet) stuurt de host een RPC callback en draait uw functie.

Opdracht-id's gelden voor het hele proces, niet per plug-in. Zet uw omgekeerde DNS-naamruimte voor uw id's (wordcount.…) om botsingen te vermijden.


Menupaden gebruiken / als scheiding. Het meest linkse segment is een menu van het hoogste niveau (Plugins, Tools, …); tussenliggende segmenten maken submenu's op verzoek; het laatste segment is het label van het item.

ctx.menus.add_item(path, command_id)

ctx.menus.add_item("Plugins/Word Count/Count", "wordcount.count")
  • path (str): "Top/Sub/…/Label". Tussenliggende submenu's worden gemaakt wanneer ze niet bestaan.
  • command_id (str): id van een eerder geregistreerde opdracht. Het item aanklikken roept de opdracht aan.

Levert een ondoorzichtige tekenreeks handlerId op. Houd die vast wanneer u het item later moet uitschrijven met unsubscribe (zeldzaam in fase 1).

Het meest linkse segment ("Plugins" hierboven) moet overeenkomen met de title van een van uw items in menus[] in plugin.json, zodat de plaats op de menubalk opgepikt wordt.

ctx.menus.add_separator(path)

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

Voegt een scheiding in een bestaand submenu in.

ctx.menus.set_item_checked(handler_id, checked)

Maakt van het eerder toegevoegde item een aanvinkbaar menu-item en zet de aangevinkte staat ervan — het standaardpatroon voor een blijvende aan/uit-schakelaar die een zichtbaar vinkje toont.

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)

De eerste aanroep maakt het item aanvinkbaar; latere aanroepen kantelen alleen het vinkje. Een handler_id meegeven die de host niet kent is een stille niet-bewerking (veilig aan te roepen nadat uw token losgelaten is, bijvoorbeeld vanuit een vertraagde callback die tijdens het afsluiten afgaat).

ctx.menus.set_item_enabled(handler_id, enabled)

Schakelt een eerder toegevoegd item in of maakt het grijs. Gebruik het wanneer de opdracht een voorwaarde nodig heeft — iets om te sluiten, iets om te herstellen — zodat het menu toont dat het niet beschikbaar is in plaats van een item aan te bieden dat stilletjes niets doet.

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)

Een uitgeschakeld item blijft zichtbaar maar kan niet in gang gezet worden, niet met een klik en niet met zijn sneltoets. De aanroep is idempotent, zodat u de vorige staat nooit hoeft te onthouden, en een onbekende handler_id is een stille niet-bewerking zoals hierboven. Items beginnen ingeschakeld: één aanroep vlak na add_item zet een item klaar dat bij het starten niet beschikbaar is.


events

Schrijf u in op editorgebeurtenissen. Twee gelijkwaardige API's — de zoete decorators zijn aanbevolen; de kale subscribe() is beschikbaar wanneer u het onderwerp tijdens het draaien wilt berekenen.

Zoete decorators

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

De omhulde functie neemt één argument: een getypeerde dataclass die bij het onderwerp hoort. Zie de Naslag voor gebeurtenissen voor de volledige tabel onderwerp ↔ dataclass.

Kale API

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

Beschikbare zoete eigenschappen (allemaal decorators):

Eigenschap Gaat af bij
on_document_opened Document in een tabblad geopend
on_document_closed Document gesloten
on_document_saved Document naar de schijf geschreven
on_selection_changed Cursor / selectie verplaatst in het actieve document
on_active_document_changed Voorgrondtabblad gewijzigd
on_document_modified_changed De vlag schoon/vuil is gekanteld (modified: bool)
on_text_changed Elke bewerking (position, added_bytes, removed_bytes) — houd het triviaal
on_settings_changed Voorkeuren toegepast (scope: "" hele editor, "plugins/<id>")
on_theme_changed Thema gewisseld of bewerkt (variant) — lees de kleuren van ctx.theme opnieuw
on_workspace_root_changed Werkruimtemap geopend / gesloten (root)
on_workspace_files_changed Verzameling werkruimtebestanden gewijzigd (files)
on_host_window_shown Eén keer per uitvoering, na het eerste beeld — plan hier traag opstartwerk
on_app_about_to_quit De editor sluit — de laatste kans om iets te doen
on_command_shortcut_changed De effectieve sneltoets van een opdracht is gewijzigd (opnieuw toewijzen door de gebruiker / toetsindeling importeren / conflict opgelost); inhoud: command_id, sequence ("" = losgekoppeld)

Onbekende onderwerpnamen werpen bij het inschrijven een ValueError. Inschrijvers kunnen na de bijbehorende inheemse afhandelaar draaien; behandel het afleveren van gebeurtenissen als informatief, niet als gezaghebbend.

Publiceren

ctx.events.publish(topic, payload) zendt gelijkelijk naar inheemse en Python-inschrijvers uit — alleen aanvaard voor de twee onderwerpen van de werkruimte (workspace.rootChanged, workspace.filesChanged); al het andere werpt een ValueError. Zie de Naslag voor gebeurtenissen.


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

Elk niveau komt overeen met de ILogger van de host. Regels krijgen het voorvoegsel [python:<plugin.id>], zodat uw uitvoer in het diagnostieklogboek van de editor terug te vinden is:

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

Gebruik nooit print() vanuit een plug-in — de stdout van de worker is het RPC-kanaal, en erin schrijven bederft het protocol. ctx.log.* is de enige toegestane manier om diagnostische tekst uit te zenden.


status

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

Plaats een vluchtige melding op de statusbalk.

  • message (str): wat er getoond wordt. Alleen korte teksten — de statusbalk heeft een vaste breedte.
  • timeout_ms (int, standaard 0): hoe lang hij op het scherm blijft; 0 laat de host een verstandige standaard kiezen (een paar seconden).
  • level (str, standaard "info"): "info" / "warning" / "error". Hogere niveaus kunnen het minimum op het scherm iets langer vasthouden, zodat een fout niet meteen door een routinematig informatiebericht weggevaagd wordt.
ctx.status.show("saved", timeout_ms=1500)
ctx.status.show("no results", level="warning")

ctx.status.show is afvuren-en-vergeten. Heeft de host geen zichtbare statusbalk (een build zonder scherm of een minimale build), dan wordt de aanroep in stilte verworpen. ctx.status.clear() wist welke vluchtige melding er ook op het scherm staat.

Blijvende segmenten

Voor levende staat (een woordentelling, de status van een server) gebruikt u een segment dat u bezit en ververst — het overschrijft nooit een melding, en omgekeerd:

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 ordent de segmenten van plug-ins van links naar rechts (lager = verder naar links; bij gelijke stand blijft de registratievolgorde). Het afsluiten van een plug-in verwijdert alles wat nog vastgehouden wordt.


editor

Volledige synchrone toegang tot de actieve documenten. Elke methode blokkeert de aanroepende thread totdat de host antwoordt (doorgaans microseconden wanneer de worker en de editor dezelfde machine delen). Toegestaan vanuit elke afhandelaar van een opdracht of een gebeurtenis; nooit vanuit een callback met async def (zie de notitie over threads boven aan deze pagina).

Documenten opsommen

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

Toegang tot de buffer

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 vermijdt dat de hele buffer door de RPC getrokken wordt wanneer u maar een stuk nodig hebt — geef er de voorkeur aan bij sorteren en transformeren van grote bestanden.

Selectie

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?

Cursor

pos = ctx.editor.caret(doc)                         # mte.EditorPosition(line, column)
ctx.editor.set_caret(doc, mte.EditorPosition(line=7, column=3))

Bestandsbewerkingen

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 is de ene aanroep voor «breng de gebruiker daarheen» (een bouwfout, een stackframe, een zoekresultaat): hij opent het bestand of activeert het tabblad ervan, zet de cursor zonder selectie, klapt een vouw open die de regel verbergt, scrolt de regel in beeld — gecentreerd als hij buiten beeld stond — en geeft de editor de focus. Regel en kolom beginnen bij 0 (de kolom in UTF-8-bytes) en worden begrensd, nooit geweigerd; hij geeft de document-id terug, of 0 als het bestand niet geopend kon worden, en dan is er niets verschoven. reveal_line toont alleen een regel: hij verplaatst de cursor niet, wisselt niet van tabblad en neemt de focus niet.

Bladwijzers, markeringen, URL-markering

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

Zoeken

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". Zie mte.SearchOptions voor de volledige veldenlijst.

Incrementeel zoeken (het patroon groeit terwijl de gebruiker typt, vanaf een vastgezet anker):

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

Inspringing en syntaxis

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

Gesplitste weergave / tabbladgroepen

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

Een document dat in twee deelvensters getoond wordt is één document met één id, dus group_of() kan maar een van zijn plekken noemen. groups_of() meldt ze allemaal, en show_document_in_group() maakt een tweede gedeelde weergave — hetzelfde wat Beeld > Document rechts splitsen doet, dus er wordt geen gebeurtenis document_opened opgeworpen.

ctx.editor spiegelt de inheemse IEditorService inmiddels één op één.


languages

Syntaxiskleuring is een functie van de kern — de host herkent de taal van een document en kleurt het zonder enige plug-in. ctx.languages laat u meedoen: somt op wat de host kent, leest of overschrijft de taal van een document, voert de herkenning opnieuw uit, en draagt hele taaldefinities bij.

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 neemt dezelfde vorm als een meegeleverd bestand colorizers/*.json (of een heel woordenboek definition=): lexer noemt een bestaande lexer van de backend (de host krijgt tijdens het draaien geen nieuwe lexers), styles koppelt de eigen stijlnamen van die lexer aan semantische categorieën, en keywords spreekt zijn woordenlijsten aan met de eigen plaatsbeschrijving van de lexer — de host lost beide precies op zoals bij een meegeleverd bestand, zodat u nooit motor-id's hoeft uit te schrijven. Een id registreren die al bestaat vervangt die zolang u de id van de afhandelaar vasthoudt; het afsluiten van de plug-in trekt in wat nog vastgehouden wordt.


document_settings

Alleen-lezen instellingen met documentbereik die de .editorconfig van een project over uw eigen ctx.settings leggen. Voor sleutel k en document doc lost een lezing mte.<plugin.id>.<k> op uit de .editorconfig die op dat document van toepassing is, valt dan terug op ctx.settings, en dan op uw standaardwaarde:

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?

Schrijfacties gaan via ctx.settings — nooit terug de .editorconfig in.


diagnostics

Duw golflijnen en margetekens voor een document — vanuit een statische analysator, een taalserver, een bouwlogboek. De host tekent ze; u raakt de weergave nooit aan. Publiceren vervangt alles wat u eerder voor dat paar (document, bron) gepubliceerd hebt, dus stuur elke keer de volledige verzameling:

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 is verplicht — dat is wat uw verzameling gescheiden houdt van die van andere producenten (de LSP-plug-in, de spellingcontrole) op hetzelfde document. Ernstgraden: SEVERITY_ERROR / SEVERITY_WARNING / SEVERITY_INFORMATION / SEVERITY_HINT. Verschuivingen zijn halfopen bytebereiken [start, end).


theme

Registreer de kleuren die uw eigen interface schildert als benoemde, tematiseerbare categorieën; de Thema-editor somt ze automatisch op en de overschrijvingen van de gebruiker gaan heen en weer in het themabestand:

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)

Lees resolve_color opnieuw bij ctx.events.on_theme_changed.


compare

Opent (of brengt naar voren) de vergelijkingsweergave naast elkaar van de editor over twee bronnen. Elke kant is een bestand op de schijf (path=), letterlijke tekst (content=), of beide (de inhoud wordt getoond, het pad geeft de sessiebewaring een herstelbare identiteit):

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

Levert False op wanneer een kant niet gelezen kan worden.


settings

Blijvende getypeerde sleutel/waarde-opslag, op de host beperkt tot plugins/<your.id>/. Alle methoden zijn synchroon; get en set leiden het type af uit het Python-type van de standaardwaarde.

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

Typen:

Python-type van default Draadtype Type dat get() oplevert
bool bool bool
int (niet-bool) int int
float double float
str (of None) string str

Verder:

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

Reageer op wijzigingen die in het venster Voorkeuren gemaakt worden door u in te schrijven op settings.changed (ctx.events.on_settings_changed; zie de Naslag voor gebeurtenissen).

app_settings

Alleen-lezen kijk op de configuratie van de hele toepassing. De bekende sleutelnamen zijn als constanten in mte.keys uitgevoerd (een spiegel van de C++-header PluginApi/AppSettingsKeys.h, die de gezaghebbende lijst blijft):

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"

De setters, remove() en sync() op ctx.app_settings werpen een RuntimeError — de kijk is met opzet alleen-lezen.


completions

Registreer een aanvullingsleverancier die aan de pop-up van de editor meedoet:

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

De omhulde aanroepbare wordt bij elke toetsaanslag (of bij Ctrl+Space) aangeroepen waarvan het document op het taalfilter past. Velden op req:

Veld Type Betekenis
document int Document dat bewerkt wordt
prefix str Woordfragment direct vóór de cursor
position int Byteverschuiving van de cursor
language_id str Colorizer-id ("python", "cpp", …)
manual bool True wanneer de gebruiker Ctrl+Space indrukte; False bij automatisch afgaan

Lever een lijst met mte.CompletionItem op (of gewone tekenreeksen, of woordenboeken met {text, detail, kind}). Soorten: mte.KIND_KEYWORD / KIND_WORD / KIND_SYMBOL / KIND_SNIPPET / KIND_UNKNOWN.

Deadline

De host stuurt uw leverancier een toetsaanslagverzoek met een deadline van ~30 ms. Mist uw callback die, dan verschijnt de pop-up voor die toetsaanslag zonder uw kandidaten. Bouw elke dure index op de achtergrond en antwoord uit de cache — doe nooit I/O in de callback. Netwerkaanvulling in LSP-stijl hoort in een toekomstig asynchroon vlak, niet hier.

languages=[] (of weggelaten) betekent «elk document». Meerdere leveranciers per plug-in zijn toegestaan; elke decorator registreert de zijne.


data_dir

path = ctx.data_dir()                    # pathlib.Path

Levert de beschrijfbare map per plug-in op die de host al voor u gemaakt heeft. Gebruik hem voor caches, indexen en alle gegevens die van u zijn. ctx.diagnostics_dir() levert op dezelfde manier de gedeelde map voor crashes en logboeken op (voor u alleen-lezen — de crashmelder schrijft daarheen).


docks

Declaratieve dokpanelen — een zijpaneelinterface die uit een formulierspecificatie gebouwd wordt, niet uit Qt-widgets. Zie mte.ui voor het vocabulaire van de bouwers.

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

Registreert een dokpaneel. Levert de paneel-id op (automatisch toegekend wanneer id None is).

  • id (str, optioneel) — stabiele aanduiding die ctx.ui.get / ctx.ui.set gebruiken om besturingselementen binnen dit paneel aan te spreken. Levert u er geen, dan kent de host een id py-dock/N toe en levert die op.
  • title (str) — getoond in de kop van het dok.
  • area (str) — "left" / "right" / "top" / "bottom". Onbekende waarden vallen terug op "right".
  • form (dict) — een specificatie gebouwd met de bouwers van mte.ui.
  • initially_visible (bool) — of het dok getoond wordt wanneer de plug-in laadt. Standaard True. Geef False mee voor een paneel dat bij het starten niet moet opduiken en toon het later met set_panel_visible.
  • proportional_size (float) — de standaardgrootte van het paneel als aandeel van het hoofdvenster: van de breedte in de gebieden "left" / "right", van de hoogte in "top" / "bottom". 0.0 (de standaard) houdt het pixelgedrag dat een gereedschapspaneel wil: het paneel houdt zijn grootte en het venster van grootte veranderen laat het met rust. Een breuk als 0.4 is wat een voorbeeld wil: het venster maximaliseren maakt het paneel ermee breder. Alleen de standaard is van u; zodra de gebruiker de scheiding van het paneel versleept, neemt de host zijn aandeel over en onthoudt het. Waarden buiten het bereik worden afgekapt, en de host houdt een minimale omvang voor zowel het paneel als de editor. De volledige regels staan bij proportionalSize.
  • tab_group (str) — panelen met dezelfde niet-lege groeps-id in dezelfde area delen één dok als tabbladen in plaats van naast elkaar te staan. Geef de id een naamruimte zoals een paneel-id ("renamer/panels"). Alleen de standaardindeling: een door de gebruiker opgeslagen indeling gaat voor. Zie tabGroup.

ctx.docks.is_panel_visible(handler_id) -> bool

Of een met add_panel toegevoegd paneel open is — een paneel dat als tabblad achter een ander zit, telt als open; een paneel dat met initially_visible=False is geregistreerd en nooit getoond, een gesloten paneel en een onbekende id niet. Combineer het met set_panel_visible om je panelen voor een taak te tonen en daarna terug te zetten wat de gebruiker had:

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)

Toont of verbergt een paneel dat met add_panel toegevoegd is. Tonen brengt het paneel ook boven de dokken waarmee het een gebied deelt, zodat een opdracht die haar paneel «opent» het werkelijk voor de gebruiker zet. Idempotent — herhaal dezelfde waarde gerust, er valt geen staat te volgen; voor een onbekende id wordt het in stilte genegeerd.

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

Kent een afhandelaars-id toe om het veld on_change van een besturingselement aan een Python-aanroepbare te koppelen. De callback ontvangt de nieuwe waarde als tekenreeks; booleaanse besturingselementen leveren "true" / "false", numerieke leveren het gehele getal als tekenreeks.

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

Afhandelaars-id's van on_change krijgen het voorvoegsel py-dock-cb/N en botsen nooit met de id's van afhandelaars voor opdrachten, gebeurtenissen of aanvullingen.

De callback wordt op de gebeurtenislus van de worker aangeroepen, NIET op de threadpool — houd hem snel. Gebruik ctx.log, wijzig een kleine staat in het geheugen, of vuur een vervolgopdracht af; doe hier geen zwaar werk.


line_markers

Margesymbolen en kleuringen van hele regels, vastgemaakt aan documentregels die de bewerkingen van de gebruiker volgen — onderbrekingspunten, een pijl voor de huidige instructie, wijzigingsstroken — en de claim op de klik in de bladwijzerkolom. De native dienst en al haar regels staan beschreven onder Regelmarkeringen en margeklik.

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 is een van mte.GLYPH_NONE (alleen kleuring), GLYPH_CIRCLE, GLYPH_CIRCLE_HOLLOW, GLYPH_CIRCLE_BAR, GLYPH_ARROW, GLYPH_STRIP; de kleuren zijn id's van categorieën die je met ctx.theme hebt geregistreerd. De editor heeft voor alle plug-ins samen maar een paar markeringsplaatsen, dus dit kan mislukken — dan wordt RpcError opgeworpen. Registreer je soorten één keer, in register().
  • add_marker(kind, doc, line) — regel vanaf 0, niet begrensd: een regel buiten het document geeft 0 terug en plaatst niets.
  • Markeringen bewegen mee met hun regels, overleven een herlading op hetzelfde regelnummer en verdwijnen met hun document — voeg de jouwe opnieuw toe in ctx.events.on_document_opened.

De margeklik

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

Elke linkerklik in de bladwijzerkolom wordt gepubliceerd als margin.clicked (document, line, margin = "symbol", modifiers — bits mte.MODIFIER_SHIFT / MODIFIER_CONTROL / MODIFIER_ALT / MODIFIER_META). Standaard schakelt de editor daarbij ook een bladwijzer om; zolang een plug-in claim_margin_click() vasthoudt, doet hij dat niet en laat hij de klik aan de plug-ins over. ctx.line_markers.release(claim) geeft de klik terug.


settings_page

Voegt een pagina onder het venster Voorkeuren van de editor toe. Hetzelfde vocabulaire voor formulierspecificaties als bij dokpanelen; dezelfde bewaring met 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, verplicht) — door schuine strepen gescheiden pad in de boom van Voorkeuren, bijvoorbeeld "Plugins/My Plugin". Geneste categorieën komen als knooppunten in de boom aan.
  • title (str, verplicht) — kop die boven de pagina getoond wordt.
  • form (dict, verplicht) — een specificatie gebouwd met de bouwers van mte.ui.
  • scope (str, optioneel) — het gebeurtenisbereik dat bij SettingsChanged uitgezonden wordt nadat de gebruiker op OK / Toepassen drukt.

Levert de afhandelaars-id op.

Annuleren draait wijzigingen NIET terug

Besturingselementen met binds="settings:<key>" bewaren gretig terwijl de gebruiker bewerkt, dus Annuleren in het venster Voorkeuren maakt ze niet ongedaan. Dat is een bekende beperking van fase 3; een latere verfijning zal de onderliggende instellingen in een bufferende proxy omhullen.


ui

Leest en schrijft waarden van besturingselementen binnen panelen die u al geregistreerd hebt. Op de draad zijn waarden altijd tekenreeksen.

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]

Leest de huidige waarde van een besturingselement. Levert None op wanneer het paneel of het besturingselement onbekend is (geen fout — behandel zowel None als "" als «leeg»).

  • Label / Button / LineEdit / ComboBox — levert de tekst op.
  • Checkbox — levert "true" of "false" op.
  • NumberSpin — levert het gehele getal als decimale tekenreeks op.
  • List — levert de tekst op van het op dat moment geselecteerde item (leeg wanneer er niets geselecteerd is).

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

Schrijft programmatisch een waarde in een besturingselement. Levert True op wanneer het schrijven gelukt is, False wanneer het paneel of het besturingselement onbekend is of de waarde niet omgezet kan worden (bijvoorbeeld een niet-geheel getal dat naar een NumberSpin gestuurd wordt).

De Python-booleans True / False worden vóór het versturen naar "true" / "false" omgezet; al het overige gaat door str().

Een set die door de host aangestuurd wordt laat de callback on_change van het besturingselement NIET opnieuw afgaan, zodat u uw eigen widgets vanuit een opdrachtafhandelaar kunt bijwerken zonder oneindige lussen.


Samenvatting van de koppelingen tijdens het draaien (fase 3)

Veld van de formulierspecificatie Gedrag tijdens het draaien
id="…" Spreek het besturingselement aan vanuit ctx.ui.get / ctx.ui.set.
binds="settings:<key>" Leest de beginwaarde uit ctx.settings; schrijft bij elke bewerking van de gebruiker terug.
on_click="<cmd>" Knoppen roepen bij een klik de geregistreerde opdracht-id aan.
on_change=handler Laat bij een bewerking van de gebruiker de callback afgaan die via ctx.docks.on_change(...) geregistreerd is.

Dezelfde regels gelden op dokpanelen en op Voorkeurenpagina's.