ctx — kontext zásuvného modulu¶
register(ctx) dostává jeden argument: PluginContext. Zpřístupňuje níže uvedené služby (celou
plochu IPluginContext, která se hodí k modelu mimo proces). Každá z nich je malá třída Pythonu,
kterou pracovník staví nad kanálem RPC; zdroje jsou 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
Dvě části nativního kontraktu zatím nemají protějšek v Pythonu: poskytovatelé hoveru (prioritní pásma hoveru se proto na moduly v Pythonu nevztahují) a poskytovatelé místní nabídky editoru (proto ani oblast text / okraj místní nabídky). Obojí se zodpovídá, zatímco uživatel čeká, a to vyžaduje totéž ošetření termínem, jaké mají poskytovatelé dokončování, než bude cesta k pracovníkovi a zpět bezpečná.
Neukládejte ctx do globální proměnné s tím, že jej použijete později — atributy jsou sice po
dobu života modulu stabilní, ale s ctx zacházejte jako s věcí jedné registrace. Potřebujete-li
je, podržte si odkazy na jednotlivé služby (například log = ctx.log).
commands¶
Dekorátor, který registruje příkaz a jako jeho obsluhu naváže obalený volatelný objekt.
id(str, povinné): jedinečný identifikátor ve stylu obráceného DNS (například"wordcount.count"). Stejná pravidla jako vplugin.json— musí být globálně jedinečný.title(str, povinné): čitelný popisek zobrazovaný všude, kde se příkaz objeví (nabídky, paleta příkazů).shortcut(str, volitelné): přenositelný výchozí řetězec zkratky, například"Ctrl+Alt+W".Ctrlse automaticky mapuje na hlavní modifikátor dané platformy (na macOSCmd). Ponechte prázdné, nechcete-li žádnou. Uživatel ji může přiřadit znovu nebo její vazbu zrušit v Předvolby ▸ Klávesové zkratky; u příkazů vázaných na nabídku uplatní přemapování hostitel za vás.category(str, volitelné): volný seskupovací popisek — zobrazuje se jako kategorie příkazu v Mapování klávesových zkratek.scope(str, volitelné):""/"application"(výchozí — hostitel zkratku naváže na akci nabídky daného příkazu) nebo"pluginWindow"pro kombinace bydlící v rozhraní vlastněném modulem: hostitel příkaz pouze vypíše v mapování; při stavbě svých widgetů si přečtětectx.commands.effective_shortcut(id)a při události"command.shortcutChanged"jej uplatněte znovu.
Zkratka právě účinná po přemapování uživatelem a po vyřešení konfliktů ("" = bez vazby nebo
neznámá). Volat ji z obsluh je bezpečné.
Obalená funkce nebere žádné argumenty a nic nevrací. Výjimky uvnitř ní pracovník zachytí a zaznamená na stderr; editor neshodí.
@ctx.commands.command(id="wordcount.count",
title="Count Words",
shortcut="Ctrl+Alt+W",
category="Text")
def _count():
ctx.status.show("counted")
Pod kapotou: strana Pythonu si místně přidělí handlerId, zaregistruje zavolání na spojení RPC a
u hostitele vyvolá commands.registerCommand. Když uživatel příkaz aktivuje (kliknutí v nabídce,
zkratka, paleta), pošle hostitel RPC callback a vaše funkce se spustí.
Id příkazů platí v celém procesu, nikoli pro jednotlivý modul. Aby nedocházelo ke srážkám,
opatřete svá id předponou svého jmenného prostoru v obráceném DNS (wordcount.…).
menus¶
Cesty v nabídkách používají jako oddělovač /. Nejlevější segment je nabídka nejvyšší úrovně
(Plugins, Tools, …); mezilehlé segmenty vytvářejí podnabídky na vyžádání; poslední segment je
popisek položky.
ctx.menus.add_item(path, command_id)¶
path(str):"Nejvyšší/Pod/…/Popisek". Mezilehlé podnabídky se vytvoří, pokud neexistují.command_id(str): id dříve zaregistrovaného příkazu. Kliknutí na položku příkaz zavolá.
Vrací neprůhledný řetězec handlerId. Podržte si jej, budete-li později potřebovat položku
odhlásit (ve fázi 1 vzácné).
Nejlevější segment (výše "Plugins") musí odpovídat title některé z vašich položek menus[] v
plugin.json, aby se umístění v nabídkové liště převzalo.
ctx.menus.add_separator(path)¶
Vloží oddělovač do existující podnabídky.
ctx.menus.set_item_checked(handler_id, checked)¶
Promění dříve přidanou položku v zaškrtávatelnou položku nabídky a nastaví její zaškrtnutý stav — standardní idiom pro trvalý přepínač zapnuto/vypnuto s viditelnou značkou.
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)
První volání položku povýší na zaškrtávatelnou; další volání už jen přehazují značku. Předání
handler_id, které hostitel nezná, je tichou prázdnou operací (bezpečné volat i poté, co jste svůj
token zahodili, například ze zpožděného zavolání spouštějícího se během ukončování).
ctx.menus.set_item_enabled(handler_id, enabled)¶
Zapíná nebo zašedne dříve přidanou položku. Použijte to, když příkaz potřebuje splněnou podmínku — něco k zavření, něco k obnovení — aby nabídka ukázala, že je nedostupný, místo aby nabízela položku, která tiše nic neudělá.
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 zůstává viditelná, ale nelze ji spustit, ani kliknutím, ani zkratkou. Volání je
idempotentní, takže si nikdy nemusíte pamatovat předchozí stav, a neznámé handler_id je tichou
prázdnou operací jako výše. Položky začínají zapnuté: jedno volání hned po add_item nastaví
položku, která při spuštění není dostupná.
events¶
Odebírejte události editoru. Dvě rovnocenná rozhraní — doporučené jsou sladké dekorátory; surové
subscribe() je k dispozici, chcete-li téma spočítat za běhu.
Sladké 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á funkce bere jeden argument: typovanou dataclass odpovídající danému tématu. Úplnou tabulku téma ↔ dataclass najdete v referenci událostí.
Surové rozhraní¶
handler_id = ctx.events.subscribe("document.saved", _saved)
# later:
ctx.events.unsubscribe(handler_id)
Dostupné sladké vlastnosti (samé dekorátory):
| Vlastnost | Spouští se při |
|---|---|
on_document_opened |
Otevření dokumentu v kartě |
on_document_closed |
Zavření dokumentu |
on_document_saved |
Zápisu dokumentu na disk |
on_selection_changed |
Pohybu kurzoru či výběru v aktivním dokumentu |
on_active_document_changed |
Změně karty v popředí |
on_document_modified_changed |
Přehození příznaku čistý/změněný (modified: bool) |
on_text_changed |
Každé úpravě (position, added_bytes, removed_bytes) — držte ji triviální |
on_settings_changed |
Uplatnění Předvoleb (scope: "" celý editor, "plugins/<id>") |
on_theme_changed |
Přepnutí či úpravě motivu (variant) — přečtěte znovu barvy z ctx.theme |
on_workspace_root_changed |
Otevření či zavření složky pracovního prostoru (root) |
on_workspace_files_changed |
Změně sady souborů pracovního prostoru (files) |
on_host_window_shown |
Jednou za běh, po prvním snímku — naplánujte sem pomalou práci při startu |
on_app_about_to_quit |
Zavírání editoru — poslední příležitost něco udělat |
on_command_shortcut_changed |
Změně účinné zkratky příkazu (přemapování uživatelem / import mapy kláves / řešení konfliktu); data: command_id, sequence ("" = bez vazby) |
Neznámé názvy témat vyhodí při odběru ValueError. Odběratelé mohou běžet až po odpovídající
nativní obsluze; doručení události berte jako informativní, nikoli závazné.
Publikování¶
ctx.events.publish(topic, payload) rozhlašuje nativním i pythonovským odběratelům — přijímá se
pouze pro dvě témata o pracovním prostoru (workspace.rootChanged, workspace.filesChanged);
cokoli jiného vyhodí ValueError. Viz reference událostí.
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ň se mapuje na ILogger hostitele. Řádky mají předponu [python:<plugin.id>], takže
je váš výstup v diagnostickém protokolu editoru snadno k nalezení:
Ze zásuvného modulu nikdy nevolejte print() — stdout pracovníka je kanálem RPC a zápis do něj
protokol poškozuje. ctx.log.* je jediným posvěceným způsobem, jak vypsat diagnostický text.
status¶
Pošle do stavového řádku pomíjivé oznámení.
message(str): co zobrazit. Jen krátké řetězce — stavový řádek má pevnou šířku.timeout_ms(int, výchozí 0): jak dlouho je držet na obrazovce;0nechá hostitele zvolit rozumnou výchozí hodnotu (několik vteřin).level(str, výchozí"info"):"info"/"warning"/"error". Vyšší úrovně mohou minimum na obrazovce podržet o něco déle, aby chybu okamžitě nesmazala rutinní informace.
ctx.status.show funguje ve stylu spusť a zapomeň. Nemá-li hostitel viditelný stavový řádek
(bezhlavé či minimální sestavení), volání se tiše zahodí. ctx.status.clear() smaže jakékoli
pomíjivé oznámení na obrazovce.
Trvalé části¶
Pro živý stav (počet slov, stav serveru) použijte část, kterou vlastníte a obnovujete — nikdy nepřebije oznámení 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 řadí části modulů zleva doprava (nižší = dál vlevo; při shodě zůstává pořadí
registrace). Ukončení modulu odebere vše, co ještě drží.
editor¶
Plný synchronní přístup k aktivním dokumentům. Každá metoda blokuje volající vlákno, dokud
hostitel neodpoví (obvykle mikrosekundy, když pracovník i editor sdílejí stroj). Legální z
libovolné obsluhy příkazu či události; nikdy ze zavolání async def (viz poznámka o vláknech
na začátku této stránky).
Vyjmenování dokumentů¶
doc = ctx.editor.active_document() # 0 when no document is open
docs = ctx.editor.open_documents() # list[int]
ctx.editor.set_active_document(doc)
path = ctx.editor.file_path(doc) # str or None for untitled
Pří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 zabrání tažení celého bufferu přes RPC, když potřebujete jen úsek — u řazení a
transformací velkých souborů mu dávejte přednost.
Výběr¶
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))
Operace se soubory¶
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é volání pro »přenes uživatele tam« (chyba sestavení,
rámec zásobníku, výsledek hledání): otevře soubor nebo aktivuje jeho kartu,
umístí kurzor bez výběru, rozbalí sbalení, které řádek skrývá, posune řádek do
zobrazení — na střed, pokud byl mimo obrazovku — a předá fokus editoru. Řádek
a sloupec se počítají od 0 (sloupec v bajtech UTF-8) a ořezávají se, nikdy se
neodmítají; vrací id dokumentu, nebo 0, když soubor nešlo otevřít, a pak se
nic nepohnulo. reveal_line jen ukáže řádek: nepřesune kurzor, nepřepne
kartu a nevezme fokus.
Záložky, označení, zvýrazňování 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
Hledání¶
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ý seznam polí najdete u mte.SearchOptions.
Přírůstkové hledání (vzor roste, jak uživatel píše, od připnutého ukotvení):
ctx.editor.set_incremental_search_anchor(doc)
hit = ctx.editor.incremental_search(doc, "TOD", opts) # MatchRange
Odsazení a syntaxe¶
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
Rozdělené zobrazení a skupiny karet¶
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ý ve dvou panelech je jeden dokument s jedním id, takže group_of() může
pojmenovat jen jedno z jeho míst. groups_of() hlásí všechna a show_document_in_group() vytvoří
druhé sdílené zobrazení — totéž, co dělá Zobrazit > Rozdělit dokument vpravo, takže se
nevyvolá žádná událost document_opened.
ctx.editor nyní zrcadlí nativní IEditorService jedna ku jedné.
languages¶
Obarvování syntaxe je funkcí jádra — hostitel rozpozná jazyk dokumentu a obarví jej bez jakéhokoli
modulu. ctx.languages vám umožňuje se přidat: vypsat, co hostitel zná, přečíst nebo přebít jazyk
dokumentu, znovu spustit rozpoznání a přispět celými definicemi jazyků.
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 bere týž tvar jako dodávaný soubor colorizers/*.json (nebo celý slovník
definition=): lexer pojmenovává existující lexer backendu (hostitel za běhu nové lexery
nezískává), styles mapuje názvy stylů daného lexeru na významové kategorie a keywords
adresuje jeho seznamy slov podle vlastního popisu slotů lexeru — hostitel obojí vyhodnotí přesně
tak, jak to dělá u dodávaného souboru, takže nikdy nepíšete id enginu. Registrace id, které už
existuje, jej nahradí na tak dlouho, dokud držíte id popisovače; ukončení modulu stáhne vše, co
ještě drží.
document_settings¶
Nastavení dokumentu jen ke čtení, která kladou projektový .editorconfig nad vaše vlastní
ctx.settings. Pro klíč k a dokument doc čtení vyhodnotí mte.<plugin.id>.<k> z
.editorconfig platného pro onen dokument, pak se vrátí k ctx.settings a pak k vaší výchozí
hodnotě:
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 jdou přes ctx.settings — nikdy zpět do .editorconfig.
diagnostics¶
Vysílejte pro dokument vlnovky a značky na okraji — z linteru, z jazykového serveru, z protokolu
sestavení. Hostitel je vykresluje; zobrazení se nikdy nedotknete. Publikování nahradí vše, co jste
pro danou dvojici (dokument, zdroj) publikovali dřív, proto pokaždé posílejte úplnou sadu:
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ávě on drží vaši sadu oddělenou od sad jiných producentů (modulu LSP,
kontroly pravopisu) na témž dokumentu. Závažnosti: SEVERITY_ERROR / SEVERITY_WARNING /
SEVERITY_INFORMATION / SEVERITY_HINT. Posuny jsou polootevřené bajtové rozsahy [start, end).
theme¶
Zaregistrujte barvy, které vaše vlastní rozhraní vykresluje, jako pojmenované kategorie z motivu; Editor motivů je automaticky vypíše a přebití od uživatele se obousměrně přenášejí v souboru motivu:
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)
Při ctx.events.on_theme_changed si resolve_color přečtěte znovu.
compare¶
Otevře (nebo zaměří) zobrazení porovnání vedle sebe nad dvěma zdroji. Každá strana je soubor na
disku (path=), doslovný text (content=), nebo obojí (zobrazí se obsah, cesta dá uchovávání
relace obnovitelnou 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
Vrací False, když některou stranu nelze přečíst.
settings¶
Trvalé typované úložiště klíč/hodnota, na hostiteli omezené na plugins/<your.id>/. Všechny
metody jsou synchronní; get a set se odvozují z typu výchozí hodnoty v Pythonu.
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 default v Pythonu |
Typ na přenosu | Návratový typ z get() |
|---|---|---|
bool |
bool |
bool |
int (mimo bool) |
int |
int |
float |
double |
float |
str (nebo None) |
string |
str |
Dále:
ctx.settings.contains("wrap") # bool
ctx.settings.remove("stale_key")
ctx.settings.sync() # optional; host also syncs on shutdown
Na změny provedené v okně Předvolby reagujte odběrem settings.changed
(ctx.events.on_settings_changed; viz reference událostí).
app_settings¶
Pohled na konfiguraci celého editoru jen ke čtení. Známé názvy klíčů se exportují jako konstanty v
mte.keys (zrcadlí hlavičku C++ PluginApi/AppSettingsKeys.h, která zůstává závazným seznamem):
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"
Settery, remove() i sync() na ctx.app_settings vyhazují RuntimeError — pohled je záměrně
jen ke čtení.
completions¶
Zaregistrujte poskytovatele dokončování, který se účastní nabídky v editoru:
@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ý volatelný objekt se vyvolá při každém stisku klávesy (nebo při Ctrl+Space), jehož dokument
odpovídá jazykovému filtru. Pole na req:
| Pole | Typ | Význam |
|---|---|---|
document |
int |
Upravovaný dokument |
prefix |
str |
Fragment slova bezprostředně před kurzorem |
position |
int |
Bajtový posun kurzoru |
language_id |
str |
Id obarvovače ("python", "cpp", …) |
manual |
bool |
True, když uživatel stiskl Ctrl+Space; False u automatického vyvolání |
Vraťte seznam mte.CompletionItem (nebo prosté řetězce, nebo slovníky s {text, detail, kind}).
Druhy: mte.KIND_KEYWORD / KIND_WORD / KIND_SYMBOL / KIND_SNIPPET / KIND_UNKNOWN.
Termín
Hostitel posílá vašemu poskytovateli požadavek na stisk klávesy s termínem asi 30 ms. Nestihne-li jej vaše zavolání, nabídka se pro daný stisk zobrazí bez vašich kandidátů. Jakýkoli drahý index budujte na pozadí a odpovídejte z mezipaměti — v zavolání nikdy neprovádějte vstupně-výstupní operace. Síťové dokončování ve stylu LSP patří do budoucí asynchronní plochy, nikoli sem.
languages=[] (nebo vynechání) znamená »každý dokument«. Několik poskytovatelů na modul je
povoleno; každý dekorátor registruje svého vlastního.
data_dir¶
Vrací zapisovatelný adresář daného modulu, který pro vás hostitel už vytvořil. Používejte jej pro
mezipaměti, indexy a jakákoli data, která vlastníte. ctx.diagnostics_dir() obdobně vrací sdílený
adresář s pády a protokoly (pro vás jen ke čtení — zapisuje tam hlášení o pádech).
docks¶
Deklarativní ukotvitelné panely — rozhraní postranního panelu postavené ze specifikace formuláře,
nikoli z widgetů Qt. Slovník stavitelů najdete u 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¶
Registruje ukotvitelný panel. Vrací id panelu (přidělené automaticky, je-li id rovno None).
id(str, volitelné) — stabilní identifikátor, kterýmctx.ui.get / ctx.ui.setadresují ovládací prvky uvnitř tohoto panelu. Pokud žádné nedodáte, hostitel přidělí idpy-dock/Na vrátí je.title(str) — zobrazuje se v hlavičce doku.area(str) —"left"/"right"/"top"/"bottom". Neznámé hodnoty se vrátí k"right".form(dict) — specifikace postavená stavitelimte.ui.initially_visible(bool) — zda se dok zobrazí, když se modul načte. VýchozíTrue. PředejteFalseu panelu, který by při spuštění vyskakovat neměl, a odkryjte jej později pomocíset_panel_visible.proportional_size(float) — výchozí velikost panelu jako podíl hlavního okna: jeho šířky v oblastech"left"/"right", jeho výšky v"top"/"bottom".0.0(výchozí hodnota) zachovává pixelové chování, které chce nástrojový panel — panel si drží svou velikost a změna velikosti okna se ho nedotkne. Zlomek jako0.4je to, co chce náhled: maximalizace okna panel rozšíří spolu s ním. Vaše je jen výchozí hodnota; jakmile uživatel přetáhne oddělovač panelu, hostitel jeho podíl přijme a zapamatuje si jej. Hodnoty mimo rozsah se omezí a hostitel zachová minimální rozměr pro panel i editor. Úplná pravidla najdete uproportionalSize.tab_group(str) — panely se stejným neprázdným id skupiny ve stejnéareasdílejí jeden dok jako karty, místo aby ležely vedle sebe. Dejte mu jmenný prostor jako id panelu ("renamer/panels"). Jen výchozí uspořádání: rozvržení uložené uživatelem má přednost. ViztabGroup.
ctx.docks.is_panel_visible(handler_id) -> bool¶
Zda je panel přidaný přes add_panel otevřený — panel schovaný jako karta
za jiným se počítá jako otevřený; panel zaregistrovaný s
initially_visible=False a nikdy nezobrazený, zavřený panel a neznámé id ne.
Kombinujte ho se set_panel_visible, chcete-li zobrazit své panely po dobu
úlohy a potom vrátit, co měl uživatel:
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)¶
Odkrývá nebo skrývá panel přidaný pomocí add_panel. Zobrazení panel zároveň zvedne nad doky, s
nimiž sdílí oblast, takže příkaz, který svůj panel »otevírá«, jej uživateli skutečně postaví před
oči. Idempotentní — tutéž hodnotu opakujte, jak chcete, není co sledovat; u neznámého id se tiše
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¶
Přidělí id obsluhy pro propojení pole on_change ovládacího prvku s volatelným objektem v Pythonu.
Zavolání dostane novou hodnotu jako řetězec; logické prvky doručují "true" / "false", číselné
prvky doručují celé číslo jako řetězec.
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 obsluh on_change mají předponu py-dock-cb/N a nikdy se nesrazí s id obsluh příkazů,
událostí ani dokončování.
Zavolání se provádí ve smyčce událostí pracovníka, NIKOLI ve fondu vláken — držte je rychlé.
Používejte ctx.log, měňte malý stav v paměti, nebo spusťte navazující příkaz; těžkou práci tu
nedělejte.
line_markers¶
Symboly na okraji a podbarvení celých řádků připojené k řádkům dokumentu, které sledují úpravy uživatele — zarážky, šipka aktuálního příkazu, pruhy změn — a nárok na kliknutí do sloupce záložek. Nativní službu a všechna její pravidla popisuje stránka Značky řádků a kliknutí 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=)—glyphje jedna z hodnotmte.GLYPH_NONE(jen podbarvení),GLYPH_CIRCLE,GLYPH_CIRCLE_HOLLOW,GLYPH_CIRCLE_BAR,GLYPH_ARROW,GLYPH_STRIP; barvy jsou id kategorií, které jste zaregistrovali přesctx.theme. Editor má pro všechny moduly dohromady jen několik slotů pro značky, takže volání může selhat — pak vyvoláRpcError. Své druhy registrujte jednou, vregister().add_marker(kind, doc, line)— řádek počítaný od 0, neořezávaný: řádek mimo dokument vrátí 0 a nic neumístí.- Značky se posouvají se svými řádky, přežijí znovunačtení na stejném čísle
řádku a mizí se svým dokumentem — své značky přidávejte znovu v
ctx.events.on_document_opened.
Kliknutí 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é kliknutí levým tlačítkem do sloupce záložek se publikuje jako
margin.clicked (document, line, margin = "symbol", modifiers —
bity mte.MODIFIER_SHIFT / MODIFIER_CONTROL / MODIFIER_ALT /
MODIFIER_META). Ve výchozím stavu editor při něm také přepne záložku;
dokud některý modul drží claim_margin_click(), nedělá to a kliknutí
přenechá modulům. ctx.line_markers.release(claim) kliknutí vrátí.
settings_page¶
Přidává stránku do okna Předvolby editoru. Týž slovník specifikace formuláře jako u
ukotvitelných panelů; totéž uchovávání pomocí 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 ve stromu Předvoleb oddělená lomítky, například"Plugins/My Plugin". Vnořené kategorie přicházejí jako uzly stromu.title(str, povinné) — nadpis zobrazený nad stránkou.form(dict, povinné) — specifikace postavená stavitelimte.ui.scope(str, volitelné) — rozsah události vydávaný vSettingsChangedpoté, co uživatel stiskne OK / Použít.
Vrací id obsluhy.
Zrušit změny NEVRACÍ
Ovládací prvky s binds="settings:<key>" se ukládají dychtivě, jak uživatel upravuje, takže
stisk Zrušit v okně Předvolby je nevrátí. Je to známé omezení fáze 3; budoucí upřesnění
podkladová nastavení obalí do proxy s vyrovnávací pamětí.
ui¶
Čte a zapisuje hodnoty ovládacích prvků uvnitř panelů, které jste už zaregistrovali. Hodnoty jsou na přenosu vždy řetězce.
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]¶
Přečte aktuální hodnotu ovládacího prvku. Vrací None, když je panel nebo prvek neznámý (není to
chyba — None i "" berte jako »prázdné«).
- Label / Button / LineEdit / ComboBox — vrací text.
- Checkbox — vrací
"true"nebo"false". - NumberSpin — vrací celé číslo jako desítkový řetězec.
- List — vrací text právě vybrané položky (prázdný, když není vybráno nic).
ctx.ui.set(panel_id, control_id, value) -> bool¶
Zapíše hodnotu do ovládacího prvku programově. Vrací True, když zápis uspěl, a False, když je
panel či prvek neznámý nebo hodnotu nelze převést (například necelé číslo poslané do
NumberSpin).
Pythonovské True / False se před odesláním převádějí na "true" / "false"; vše ostatní
prochází přes str().
set řízený hostitelem znovu NESPOUŠTÍ zavolání on_change daného prvku, takže můžete své
widgety aktualizovat z obsluhy příkazu bez nekonečných smyček.
Shrnutí vazeb za běhu (fáze 3)¶
| Pole specifikace formuláře | Chování za běhu |
|---|---|
id="…" |
Adresuje ovládací prvek z ctx.ui.get / ctx.ui.set. |
binds="settings:<key>" |
Načte počáteční hodnotu z ctx.settings; při každé úpravě uživatelem zapíše zpět. |
on_click="<cmd>" |
Tlačítka při kliknutí vyvolají zaregistrované id příkazu. |
on_change=handler |
Při úpravě uživatelem spustí zavolání zaregistrované přes ctx.docks.on_change(...). |
Tatáž pravidla platí u ukotvitelných panelů i u stránek Předvoleb.