Preskočiť na obsah

ctx — kontext zásuvného modulu

register(ctx) dostáva jediný argument: PluginContext. Sprístupňuje nižšie uvedené služby (celú plochu IPluginContext, ktorá sa zmestí do modelu mimo procesu). Každá z nich je malou triedou v Pythone, ktorú pracovný proces stavia nad kanálom RPC; zdrojové kódy sú v 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

Dve časti natívneho kontraktu zatiaľ nemajú náprotivok v Pythone: poskytovatelia hoveru (prioritné pásma hoveru sa preto na moduly v Pythone nevzťahujú) a poskytovatelia kontextovej ponuky editora (preto ani oblasť text / okraj kontextovej ponuky). Na obe sa odpovedá, kým používateľ čaká, a to si vyžaduje rovnaké zaobchádzanie s termínmi, aké majú poskytovatelia dopĺňania, než bude cesta k pracovnému procesu a späť bezpečná.

Neukladajte si ctx do globálnej premennej s tým, že ho použijete neskôr — hoci sú atribúty počas života zásuvného modulu stabilné, berte ctx ako viazané na jednu registráciu. Ak ich potrebujete, ponechajte si odkazy na jednotlivé služby (napríklad log = ctx.log).


commands

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

Dekorátor, ktorý zaregistruje príkaz a obalenú volateľnú funkciu naviaže ako jeho obslužný kód.

  • id (str, povinné): jedinečný identifikátor v štýle reverzného DNS (napr. "wordcount.count"). Tie isté pravidlá ako v plugin.json — musí byť globálne jedinečný.
  • title (str, povinné): čitateľný popis zobrazený všade, kde sa príkaz objaví (ponuky, paleta príkazov).
  • shortcut (str, voliteľné): prenosný predvolený reťazec so skratkou, napr. "Ctrl+Alt+W". Ctrl sa automaticky mapuje na hlavný modifikátor platformy (Cmd na macOS). Nechajte prázdne, ak žiadnu nechcete. Používateľ ju môže premapovať alebo zrušiť v Predvoľby ▸ Skratky; pri príkazoch viazaných na ponuku uplatní premapovanie hostiteľ za vás.
  • category (str, voliteľné): voľný popis na zoskupenie — zobrazuje sa ako kategória príkazu v Mapovaní skratiek.
  • scope (str, voliteľné): ""/"application" (predvolené — hostiteľ skratku naviaže na akciu ponuky daného príkazu) alebo "pluginWindow" pri kombináciách žijúcich v rozhraní vlastnenom zásuvným modulom: hostiteľ príkaz iba vypíše v mapovaní; pri zostavovaní svojich widgetov si prečítajte ctx.commands.effective_shortcut(id) a pri udalosti "command.shortcutChanged" priradenie uplatnite znova.
ctx.commands.effective_shortcut(command_id) -> str

Skratka, ktorá práve platí po premapovaní používateľom a po vyriešení konfliktov ("" = bez priradenia alebo neznáme). Z obslužného kódu sa dá bezpečne volať.

Obalená funkcia neprijíma žiadne argumenty a nič nevracia. Výnimky vnútri nej zachytí pracovný proces a zaznamená ich na stderr; editor nezhavarujú.

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

Ako to funguje vnútri: strana Pythonu si lokálne pridelí handlerId, zaregistruje zavolanie na spojení RPC a vyšle hostiteľovi commands.registerCommand. Keď používateľ príkaz aktivuje (kliknutím v ponuke, skratkou, paletou), hostiteľ pošle RPC callback a vaša funkcia sa spustí.

Id príkazov platia v celom procese, nie v rámci zásuvného modulu. Predchádzajte kolíziám tým, že id opatríte svojím menným priestorom v tvare reverzného DNS (wordcount.…).


Cesty v ponukách používajú ako oddeľovač /. Najľavejší segment je hlavnou ponukou (Plugins, Tools, …); medziľahlé segmenty vytvárajú podponuky na požiadanie; posledný segment je popisom položky.

ctx.menus.add_item(path, command_id)

ctx.menus.add_item("Plugins/Word Count/Count", "wordcount.count")
  • path (str): "Top/Sub/…/Label". Medziľahlé podponuky sa vytvoria, ak neexistujú.
  • command_id (str): id predtým zaregistrovaného príkazu. Kliknutím na položku sa príkaz zavolá.

Vracia nepriehľadný reťazec handlerId. Ponechajte si ho, ak budete neskôr potrebovať položku odregistrovať cez unsubscribe (vo fáze 1 zriedkavé).

Najľavejší segment (vyššie "Plugins") musí zodpovedať poľu title niektorej z vašich položiek menus[] v plugin.json, aby sa umiestnenie v paneli ponuky prevzalo.

ctx.menus.add_separator(path)

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

Vloží oddeľovač do existujúcej podponuky.

ctx.menus.set_item_checked(handler_id, checked)

Premení predtým pridanú položku na začiarkavaciu a nastaví jej stav — štandardná zvyklosť pre trvalý prepínač zapnuté/vypnuté s viditeľnou značkou začiarknutia.

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)

Prvé volanie položku povýši na začiarkavaciu; ďalšie volania už iba prepínajú značku. Odovzdanie handler_id, ktoré hostiteľ nepozná, je tichou prázdnou operáciou (dá sa bezpečne volať aj po zahodení vášho tokenu, napríklad z oneskoreného zavolania počas ukončovania).

ctx.menus.set_item_enabled(handler_id, enabled)

Zapne alebo zosivie predtým pridanú položku. Použite to, keď príkaz potrebuje splnenú podmienku — niečo na zatvorenie, niečo na obnovenie — aby ponuka ukázala, že položka nie je dostupná, namiesto toho, aby ponúkala niečo, čo potichu neurobí nič.

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)

Vypnutá položka ostáva viditeľná, ale nedá sa vyvolať ani kliknutím, ani skratkou. Volanie je idempotentné, takže si predchádzajúci stav nikdy nemusíte pamätať, a neznáme handler_id je tichou prázdnou operáciou, ako vyššie. Položky sú na začiatku zapnuté: jedno volanie hneď po add_item nastaví položku, ktorá pri spustení nie je dostupná.


events

Prihláste sa na odber udalostí editora. Dve rovnocenné rozhrania — odporúčajú sa pohodlné dekorátory; surové subscribe() je k dispozícii, ak chcete tému vypočítať za behu.

Pohodlné dekorátory

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

Obalená funkcia prijíma jeden argument: typovanú dátovú triedu zodpovedajúcu danej téme. Úplnú tabuľku téma ↔ dátová trieda nájdete v Referencii udalostí.

Surové API

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

Dostupné pohodlné vlastnosti (všetko dekorátory):

Vlastnosť Vyvolá sa pri
on_document_opened Otvorení dokumentu v karte
on_document_closed Zatvorení dokumentu
on_document_saved Zapísaní dokumentu na disk
on_selection_changed Presune kurzora alebo výberu v aktívnom dokumente
on_active_document_changed Zmene karty v popredí
on_document_modified_changed Prepnutí príznaku čistý/zmenený (modified: bool)
on_text_changed Každej úprave (position, added_bytes, removed_bytes) — držte to triviálne
on_settings_changed Uplatnení Predvolieb (scope: "" v celom editore, "plugins/<id>")
on_theme_changed Prepnutí alebo úprave motívu (variant) — načítajte farby ctx.theme znova
on_workspace_root_changed Otvorení či zatvorení priečinka pracovného priestoru (root)
on_workspace_files_changed Zmene množiny súborov pracovného priestoru (files)
on_host_window_shown Raz za spustenie, po prvej snímke — naplánujte sem pomalú prácu pri štarte
on_app_about_to_quit Zatváraní editora — posledná šanca niečo spustiť
on_command_shortcut_changed Zmene skutočnej skratky príkazu (premapovanie používateľom, import mapy klávesov, riešenie konfliktu); obsah: command_id, sequence ("" = bez priradenia)

Neznáme názvy tém vyvolajú pri odbere ValueError. Odberatelia sa môžu spustiť po príslušnom natívnom obslužnom kóde; berte doručovanie udalostí ako informatívne, nie záväzné.

Zverejňovanie

ctx.events.publish(topic, payload) rozošle udalosť natívnym aj pythonovským odberateľom — prijímajú sa iba dve témy pracovného priestoru (workspace.rootChanged, workspace.filesChanged); čokoľvek iné vyvolá ValueError. Pozri Referenciu udalostí.


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

Každá úroveň zodpovedá hostiteľskému ILogger. Riadky majú predponu [python:<plugin.id>], takže sa váš výstup v diagnostickom logu editora dá nájsť:

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

Zo zásuvného modulu nikdy nevolajte print() — stdout pracovného procesu je kanálom RPC a zápis do neho protokol poškodí. ctx.log.* je jediný schválený spôsob, ako vypisovať diagnostický text.


status

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

Odošle dočasné upozornenie do stavového riadka.

  • message (str): čo sa má zobraziť. Iba krátke reťazce — stavový riadok má pevnú šírku.
  • timeout_ms (int, predvolene 0): ako dlho ho nechať na obrazovke; 0 nechá hostiteľa zvoliť rozumnú predvoľbu (niekoľko sekúnd).
  • level (str, predvolene "info"): "info" / "warning" / "error". Vyššie úrovne môžu minimálny čas na obrazovke o niečo predĺžiť, aby chybu hneď nezmazala rutinná informácia.
ctx.status.show("saved", timeout_ms=1500)
ctx.status.show("no results", level="warning")

ctx.status.show sa odošle a ďalej sa oň nestaráte. Ak hostiteľ nemá viditeľný stavový riadok (bezhlavá alebo minimálna zostava), volanie sa potichu zahodí. ctx.status.clear() zmaže dočasné upozornenie, ktoré je práve na obrazovke.

Trvalé segmenty

Na živý stav (počet slov, stav servera) použite segment, ktorý vlastníte a obnovujete — upozornenie nikdy neprebije a naopak:

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 zoraďuje segmenty zásuvných modulov zľava doprava (nižšie = viac vľavo; pri zhode sa zachová poradie registrácie). Ukončenie zásuvného modulu odstráni všetko, čo si ešte drží.


editor

Plný synchrónny prístup k aktívnym dokumentom. Každá metóda blokuje volajúce vlákno, kým hostiteľ neodpovie (zvyčajne mikrosekundy, keď sú pracovný proces a editor na jednom počítači). Dá sa volať z ktoréhokoľvek obslužného kódu príkazu či udalosti; nikdy nie zo zavolania typu async def (pozri poznámku o vláknach navrchu tejto stránky).

Výpis dokumentov

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

Prístup k bufferu

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 sa vyhýba ťahaniu celého bufferu cez RPC, keď potrebujete iba jeho časť — pri zoraďovaní či transformácii veľkých súborov ho uprednostnite.

Výber

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?

Kurzor

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

Operácie so súbormi

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 je jediné volanie na „prenes používateľa tam“ (chyba zostavenia, rámec zásobníka, výsledok hľadania): otvorí súbor alebo aktivuje jeho kartu, umiestni kurzor bez výberu, rozbalí zbalenie, ktoré riadok skrýva, posunie riadok do zobrazenia — na stred, ak bol mimo obrazovky — a odovzdá fokus editoru. Riadok a stĺpec sa počítajú od 0 (stĺpec v bajtoch UTF-8) a orezávajú sa, nikdy sa neodmietajú; vracia id dokumentu, alebo 0, keď sa súbor nepodarilo otvoriť, a vtedy sa nič nepohlo. reveal_line iba ukáže riadok: nepresunie kurzor, neprepne kartu a nezoberie fokus.

Záložky, označenia, zvýrazňovanie URL

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

Hľadanie

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". Úplný zoznam polí nájdete v mte.SearchOptions.

Prírastkové hľadanie (vzor rastie, ako používateľ píše, od pripnutej kotvy):

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

Odsadenie a syntax

style = ctx.editor.indentation(doc)                 # IndentationStyle(use_tabs, width)
ctx.editor.set_indentation(doc,
    mte.IndentationStyle(use_tabs=False, width=2))

# Where word-wrapped continuation rows start:
# "left" (window edge) / "same" (align with the line; default) / "indent"
ctx.editor.set_wrap_indent(doc, "same")
mode = ctx.editor.wrap_indent(doc)                  # same strings back

lang  = ctx.editor.colorizer_id(doc)                # e.g. "python", "" if none

# Semantic classification from the active colorizer (cheap, no re-lex):
# list[mte.CategorySpan(start, length, category)] with category names
# "comment" / "string" / "keyword" / ... -- a spell checker keeps to
# comments and strings this way.
spans = ctx.editor.text_categories(doc, 0, ctx.editor.length(doc))

ctx.editor.trigger_completion(doc)                  # pop the list as if Ctrl+Space

Rozdelené zobrazenie a skupiny kariet

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

Dokument zobrazený v dvoch tablách je jeden dokument s jedným id, takže group_of() dokáže pomenovať iba jedno z jeho miest. groups_of() ich uvedie všetky a show_document_in_group() vytvorí druhé zdieľané zobrazenie — to isté, čo robí Zobraziť > Rozdeliť dokument vpravo, takže sa nevyvolá udalosť document_opened.

ctx.editor dnes zrkadlí natívne IEditorService jedna k jednej.


languages

Farebné zvýrazňovanie syntaxe je funkciou jadra — hostiteľ rozpozná jazyk dokumentu a zafarbí ho bez akéhokoľvek zásuvného modulu. ctx.languages je spôsob, ako sa pridať: vypísať, čo hostiteľ pozná, prečítať alebo prekryť jazyk dokumentu, znova spustiť rozpoznávanie a prispieť celými definíciami jazykov.

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 má rovnaký tvar ako dodávaný súbor colorizers/*.json (prípadne celý slovník definition=): lexer pomenúva existujúci lexer backendu (hostiteľ za behu žiadne nové lexery nezíska), styles mapuje názvy štýlov tohto lexera na sémantické kategórie a keywords adresuje jeho zoznamy slov popisom priehradiek daného lexera — hostiteľ obidve vyhodnotí presne tak ako pri dodávanom súbore, takže id mechanizmu nikdy nevypisujete. Zaregistrovaním už existujúceho id sa pôvodné nahradí na tak dlho, ako držíte id popisovača; ukončenie zásuvného modulu stiahne všetko, čo si ešte drží.


document_settings

Nastavenia v rozsahu dokumentu iba na čítanie, ktoré ukladajú .editorconfig projektu ako vrstvu nad vaše ctx.settings. Pre kľúč k a dokument doc vyhodnotí čítanie mte.<plugin.id>.<k> zo súboru .editorconfig platného pre tento dokument, potom sa vráti k ctx.settings a napokon k vašej predvolenej hodnote:

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?

Zápisy idú cez ctx.settings — nikdy nie späť do .editorconfig.


diagnostics

Posielajte pre dokument vlnovky a značky na okraji — zo statického analyzátora, z jazykového servera, z logu zostavenia. Hostiteľ ich vykreslí; vy sa zobrazenia nikdy nedotknete. Zverejnenie nahradí všetko, čo ste predtým zverejnili pre danú dvojicu (dokument, zdroj), takže zakaždým posielajte celú množinu:

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 je povinný — práve on drží vašu množinu oddelene od množín ostatných producentov (zásuvného modulu LSP, kontroly pravopisu) v tom istom dokumente. Závažnosti: SEVERITY_ERROR / SEVERITY_WARNING / SEVERITY_INFORMATION / SEVERITY_HINT. Posuny sú polootvorené bajtové rozsahy [start, end).


theme

Zaregistrujte farby, ktoré vykresľuje vaše vlastné rozhranie, ako pomenované kategórie z motívu; Editor motívov ich automaticky vypíše a prekrytia používateľa putujú tam a späť v súbore motívu:

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)

Pri ctx.events.on_theme_changed zavolajte resolve_color znova.


compare

Otvorte (alebo zamerajte) zobrazenie porovnania editora vedľa seba nad dvoma zdrojmi. Každá strana je súbor na disku (path=), doslovný text (content=) alebo obidve (zobrazí sa obsah, cesta dáva uchovávaniu relácie obnoviteľnú identitu):

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

Vracia False, keď sa niektorá strana nedá prečítať.


settings

Trvalé typované úložisko kľúč/hodnota s rozsahom plugins/<your.id>/ na strane hostiteľa. Všetky metódy sú synchrónne; typ pri get aj set sa odvodí z typu predvolenej hodnoty v Pythone.

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

Typy:

Typ hodnoty default v Pythone Prenosový typ Návratový typ z get()
bool bool bool
int (nie bool) int int
float double float
str (alebo None) string str

Ďalej:

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

Na zmeny urobené v dialógu Predvoľby reagujte prihlásením na settings.changed (ctx.events.on_settings_changed; pozri Referenciu udalostí).

app_settings

Pohľad na konfiguráciu celej aplikácie iba na čítanie. Známe názvy kľúčov sú vyexportované ako konštanty v mte.keys (zrkadlia hlavičku C++ PluginApi/AppSettingsKeys.h, ktorá ostáva záväzným zoznamom):

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"

Metódy na zápis, remove() a sync() na ctx.app_settings vyvolajú RuntimeError — pohľad je zámerne iba na čítanie.


completions

Zaregistrujte poskytovateľa dopĺňania, ktorý sa zapojí do vyskakovacieho okna editora:

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

Obalená volateľná funkcia sa vyvolá pri každom stlačení klávesu (alebo pri Ctrl+Space), ktorého dokument zodpovedá filtru jazykov. Polia na req:

Pole Typ Význam
document int Upravovaný dokument
prefix str Časť slova bezprostredne pred kurzorom
position int Bajtový posun kurzora
language_id str Id zvýrazňovača ("python", "cpp", …)
manual bool True, keď používateľ stlačil Ctrl+Space; False pri automatickom vyvolaní

Vráťte zoznam mte.CompletionItem (prípadne obyčajné reťazce či slovníky s {text, detail, kind}). Druhy: mte.KIND_KEYWORD / KIND_WORD / KIND_SYMBOL / KIND_SNIPPET / KIND_UNKNOWN.

Termín

Hostiteľ posiela vášmu poskytovateľovi požiadavku na stlačenie klávesu s termínom ~30 ms. Ak ho vaše zavolanie nestihne, vyskakovacie okno sa pri tomto stlačení zobrazí bez vašich kandidátov. Akýkoľvek nákladný index si vybudujte na pozadí a odpovedajte z vyrovnávacej pamäte — v zavolaní nikdy nerobte vstupno-výstupné operácie. Sieťové dopĺňanie v štýle LSP patrí do budúcej asynchrónnej plochy, nie sem.

languages=[] (alebo vynechané) znamená „každý dokument“. Viacero poskytovateľov na jeden zásuvný modul je povolených; každý dekorátor registruje vlastného.


data_dir

path = ctx.data_dir()                    # pathlib.Path

Vracia zapisovateľný priečinok pre daný zásuvný modul, ktorý už hostiteľ za vás vytvoril. Používajte ho na vyrovnávacie pamäte, indexy a všetky údaje, ktoré vlastníte. ctx.diagnostics_dir() podobne vracia zdieľaný priečinok s pádmi a logmi (pre vás iba na čítanie — zapisuje doň nástroj na hlásenie pádov).


docks

Deklaratívne ukotviteľné panely — rozhranie bočného panela zostavené zo špecifikácie formulára, nie z widgetov Qt. Slovník tvorcov nájdete v mte.ui.

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

Zaregistruje ukotviteľný panel. Vracia id panela (pridelené automaticky, keď je id None).

  • id (str, voliteľné) — stabilný identifikátor, ktorým ctx.ui.get a ctx.ui.set adresujú ovládacie prvky vnútri tohto panela. Ak ho nezadáte, hostiteľ pridelí id py-dock/N a vráti ho.
  • title (str) — zobrazuje sa v hlavičke panela.
  • area (str) — "left" / "right" / "top" / "bottom". Neznáme hodnoty sa vrátia k "right".
  • form (dict) — špecifikácia zostavená tvorcami z mte.ui.
  • initially_visible (bool) — či sa panel zobrazí po načítaní zásuvného modulu. Predvolene True. Pri paneli, ktorý pri štarte nemá vyskočiť, odovzdajte False a zobrazte ho neskôr cez set_panel_visible.
  • proportional_size (float) — predvolená veľkosť panela ako podiel hlavného okna: jeho šírky v oblastiach "left" / "right", jeho výšky v "top" / "bottom". 0.0 (predvolená hodnota) zachováva pixelové správanie, ktoré chce nástrojový panel — panel si drží svoju veľkosť a zmena veľkosti okna sa ho nedotkne. Zlomok ako 0.4 je to, čo chce náhľad: maximalizácia okna panel rozšíri spolu s ním. Vaša je len predvolená hodnota; len čo používateľ pretiahne oddeľovač panela, hostiteľ jeho podiel prijme a zapamätá si ho. Hodnoty mimo rozsah sa obmedzia a hostiteľ zachová minimálny rozmer pre panel aj editor. Úplné pravidlá nájdete pri proportionalSize.
  • tab_group (str) — panely s rovnakým neprázdnym id skupiny v rovnakej area zdieľajú jeden dok ako karty, namiesto aby ležali vedľa seba. Dajte mu menný priestor ako id panela ("renamer/panels"). Iba predvolené usporiadanie: rozloženie uložené používateľom má prednosť. Pozri tabGroup.

ctx.docks.is_panel_visible(handler_id) -> bool

Či je panel pridaný cez add_panel otvorený — panel schovaný ako karta za iným sa počíta ako otvorený; panel zaregistrovaný s initially_visible=False a nikdy nezobrazený, zatvorený panel a neznáme id nie. Kombinujte ho so set_panel_visible, ak chcete zobraziť svoje panely počas úlohy a potom vrátiť, čo mal používateľ:

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)

Zobrazí alebo skryje panel pridaný cez add_panel. Zobrazenie panel zároveň vynesie nad panely, s ktorými zdieľa oblasť, takže príkaz, ktorý svoj panel „otvára“, ho naozaj postaví pred používateľa. Je idempotentné — tú istú hodnotu môžete pokojne opakovať, netreba sledovať žiadny stav; pri neznámom id sa potichu ignoruje.

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

Pridelí id obslužného kódu na prepojenie poľa on_change ovládacieho prvku s volateľnou funkciou v Pythone. Zavolanie dostane novú hodnotu ako reťazec; logické ovládacie prvky doručujú "true" / "false", číselné doručujú celé číslo ako reťazec.

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

Id obslužných kódov on_change majú predponu py-dock-cb/N a nikdy nekolidujú s id obslužných kódov príkazov, udalostí ani dopĺňania.

Zavolanie prebieha v slučke udalostí pracovného procesu, NIE v zásobe vlákien — držte ho rýchle. Použite ctx.log, zmeňte malý stav v pamäti alebo spustite nadväzujúci príkaz; nerobte tu ťažkú prácu.


line_markers

Symboly na okraji a podfarbenie celých riadkov pripojené k riadkom dokumentu, ktoré sledujú úpravy používateľa — zarážky, šípka aktuálneho príkazu, pásy zmien — a nárok na kliknutie do stĺpca záložiek. Natívnu službu a všetky jej pravidlá opisuje stránka Značky riadkov a kliknutie na okraj.

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 je jedna z hodnôt mte.GLYPH_NONE (iba podfarbenie), GLYPH_CIRCLE, GLYPH_CIRCLE_HOLLOW, GLYPH_CIRCLE_BAR, GLYPH_ARROW, GLYPH_STRIP; farby sú id kategórií, ktoré ste zaregistrovali cez ctx.theme. Editor má pre všetky moduly spolu iba niekoľko slotov na značky, takže volanie môže zlyhať — vtedy vyvolá RpcError. Svoje druhy registrujte raz, v register().
  • add_marker(kind, doc, line) — riadok počítaný od 0, neorezávaný: riadok mimo dokumentu vráti 0 a nič neumiestni.
  • Značky sa posúvajú so svojimi riadkami, prežijú opätovné načítanie na rovnakom čísle riadka a miznú so svojím dokumentom — svoje značky pridávajte znova v ctx.events.on_document_opened.

Kliknutie na okraj

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

Každé kliknutie ľavým tlačidlom do stĺpca záložiek sa zverejní ako margin.clicked (document, line, margin = "symbol", modifiers — bity mte.MODIFIER_SHIFT / MODIFIER_CONTROL / MODIFIER_ALT / MODIFIER_META). Predvolene editor pri ňom tiež prepne záložku; kým niektorý modul drží claim_margin_click(), nerobí to a kliknutie prenechá modulom. ctx.line_markers.release(claim) kliknutie vráti.


settings_page

Pridáva stránku do dialógu Predvoľby editora. Ten istý slovník špecifikácie formulára ako pri ukotviteľných paneloch; to isté uchovávanie cez 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, povinné) — cesta v strome Predvolieb oddelená lomkami, napr. "Plugins/My Plugin". Vnorené kategórie prídu ako uzly stromu.
  • title (str, povinné) — nadpis zobrazený nad stránkou.
  • form (dict, povinné) — špecifikácia zostavená tvorcami z mte.ui.
  • scope (str, voliteľné) — rozsah udalosti vyslanej v SettingsChanged po tom, čo používateľ stlačí OK alebo Použiť.

Vracia id obslužného kódu.

Zrušiť zmeny NEVRÁTI

Ovládacie prvky s binds="settings:<key>" sa uchovávajú priebežne počas úprav používateľom, takže stlačenie Zrušiť v dialógu Predvoľby ich nevráti späť. Je to známe obmedzenie fázy 3; budúce spresnenie podkladové nastavenia obalí vyrovnávacím zástupcom.


ui

Číta a zapisuje hodnoty ovládacích prvkov vnútri panelov, ktoré ste už zaregistrovali. Hodnoty sú na prenosovej úrovni vždy reťazcami.

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]

Prečíta aktuálnu hodnotu ovládacieho prvku. Vracia None, keď je panel alebo prvok neznámy (nie je to chyba — None aj "" berte ako „prázdne“).

  • Label / Button / LineEdit / ComboBox — vracia text.
  • Checkbox — vracia "true" alebo "false".
  • NumberSpin — vracia celé číslo ako desiatkový reťazec.
  • List — vracia text práve vybranej položky (prázdny, keď nie je nič vybrané).

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

Zapíše hodnotu do ovládacieho prvku programovo. Vracia True, keď sa zápis podaril, a False, keď je panel či prvok neznámy alebo sa hodnota nedá previesť (napríklad necelé číslo poslané do NumberSpin).

Pythonovské True a False sa pred odoslaním prevedú na "true" a "false"; všetko ostatné prejde cez str().

Zápis cez set zo strany hostiteľa NEVYVOLÁ znova zavolanie on_change daného prvku, takže môžete svoje widgety aktualizovať z obslužného kódu príkazu bez nekonečných slučiek.


Zhrnutie väzieb za behu (fáza 3)

Pole špecifikácie formulára Správanie za behu
id="…" Adresuje ovládací prvok z ctx.ui.get a ctx.ui.set.
binds="settings:<key>" Číta počiatočnú hodnotu z ctx.settings; pri každej úprave používateľom zapisuje späť.
on_click="<cmd>" Tlačidlá po kliknutí vyvolajú zaregistrované id príkazu.
on_change=handler Pri úprave používateľom vyvolá zavolanie zaregistrované cez ctx.docks.on_change(...).

Tie isté pravidlá platia pre ukotviteľné panely aj pre stránky Predvolieb.