commit 9ca7632c5c0c8a8d4c1eb39a82d6c59bea848898 (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Thu May 30 09:44:03 2024 +0300 * lisp/dired-aux.el (shell-command-guess-xdg): Check nil xdg-mime (bug#71270). diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index ddacd2600d0..d2adf71f143 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -1413,7 +1413,8 @@ This excludes `dired-guess-shell-alist-user' and (shell-command-to-string (concat "xdg-mime query filetype " (shell-quote-argument (car files))))))) - (xdg-mime-apps (unless (string-empty-p xdg-mime) + (xdg-mime-apps (unless (or (null xdg-mime) + (string-empty-p xdg-mime)) (xdg-mime-apps xdg-mime))) (xdg-commands (mapcar (lambda (desktop) commit 6bf0f5c9fb8e896a5efc3f4c9595e097b3489d77 Author: Jakub Ječmínek Date: Wed May 29 20:37:37 2024 +0200 Update TUTORIAL.cs and TUTORIAL.translators * etc/tutorials/TUTORIAL.cs: Update TUTORIAL.cs according to its English counterpart. * etc/tutorials/TUTORIAL.translators: Update maintaner of TUTORIAL.cs. diff --git a/etc/tutorials/TUTORIAL.cs b/etc/tutorials/TUTORIAL.cs index b2a709e6415..d30501507d2 100644 --- a/etc/tutorials/TUTORIAL.cs +++ b/etc/tutorials/TUTORIAL.cs @@ -1,25 +1,27 @@ -Tutoriál k Emacsu. Podmínky viz na konci. +Tutoriál k Emacsu. Podmínky viz na konci souboru. Do češtiny přeložil Milan Zamazal . -Máte před sebou tutoriál k Emacsu. - -Příkazy Emacsu obecně využívají klávesu CONTROL (občas označovanou CTRL nebo -CTL) nebo klávesu META (občas označovanou EDIT nebo ALT). Abychom tyto názvy -nemuseli stále psát v plném znění, budeme používat následující zkratky: +Příkazy Emacsu obecně využívají klávesu CONTROL (často označovanou jako +CTRL) nebo klávesu META (obvykle označovanou jako ALT). Abychom tyto +názvy nemuseli stále dokola psát v plném znění, budeme používat +následující zkratky: C- znamená přidržet klávesu CONTROL a stisknout znak . Tedy C-f znamená: přidržte klávesu CONTROL a stiskněte f. - M- znamená přidržet klávesu META, EDIT nebo ALT a stisknout . - Pokud žádnou z kláves META, EDIT ani ALT nemáte, tak místo toho - stiskněte a pusťte klávesu ESC a poté . Klávesu ESC budeme - značit . + M- znamená přidržet klávesu META nebo ALT a stisknout . + Pokud nemáte klávesu META ani ALT, tak místo toho stiskněte a + pusťte klávesu ESC a poté . Klávesu ESC budeme značit + . Důležitá poznámka: práci s Emacsem ukončíte stiskem C-x C-c (dva znaky). +Částečně inicializovaný příkaz můžete ukončit stisknutím C-g. +Chcete-li tento tutoriál ukončit, stiskněte C-x k a poté na výzvu dále +stiskněte . Znaky ">>" na levém okraji značí místa, kde si máte vyzkoušet příkaz. Například: <> -[Prostřední část obrazovky je prázdná záměrně. Text pokračuje níže.] ->> Nyní stiskněte C-v (view next screen) pro posun na další obrazovku. +[Prostřední část obrazovky je prázdná záměrně. Text pokračuje níže.] +>> Nyní stiskněte C-v (View next screen) pro posun na další obrazovku. (Směle do toho, proveďte to přidržením klávesy CONTROL a stiskem v.) Od této chvíle byste toto měli provádět kdykoliv dočtete zobrazenou obrazovku. @@ -28,42 +30,55 @@ Všimněte si, že při posuvu obrazovek vždy zůstávají zobrazeny dva řádk z předchozí obrazovky; to poskytuje určitou návaznost při postupném čtení textu. -První věc, kterou potřebujete vědět, je jak se v textu pohybovat -z jednoho místa na druhé. Už víte, jak se posunout o jednu obrazovku -vpřed, pomocí C-v. K přechodu o obrazovku zpět použijte M-v -(přidržte klávesu META a stiskněte v nebo stiskněte v, jestliže -nemáte žádnou z kláves META, EDIT nebo ALT). +Toto je kopie textu tutoriálu k Emacsu, mírně přizpůsobená pro vás. +Později vám ukážeme, jak použít různé příkazy k úpravě tohoto textu. +Nebojte se, pokud text změníte ještě předtím, než o to budete požádáni; +tomu se říká "úprava textu" a právě k tomu je Emacs určen. + +První věc, kterou potřebujete vědět, je, jak se v textu pohybovat z +jednoho místa na druhé. Už víte, jak se posunout o jednu obrazovku +vpřed, pomocí C-v. K přechodu o obrazovku zpět použijte M-v (přidržte +klávesu META a stiskněte v nebo stiskněte v, pakliže nemáte klávesu +META ani ALT). ->> Zkuste stisknout M-v a pak C-v, několikrát to zopakujte. +>> Zkuste stisknout M-v a pak C-v párkrát za sebou. +Je samozřejmě v pořádku posouvat tento text i jinými způsoby, pokud víte +jak. * SHRNUTÍ --------- -K prohlížení obrazovkových stránek jsou užitečné následující příkazy: +K prohlížení celých stránek jsou užitečné následující příkazy: C-v Posun o obrazovku vpřed M-v Posun o obrazovku zpět - C-l Smazání obrazovky a znovuzobrazení celého textu, + C-l Smazání obrazovky a znovu zobrazení celého textu, přitom se text pod kurzorem přesune ke středu obrazovky. (Jedná se o CONTROL-L a ne CONTROL-1.) ->> Najděte kurzor a zapamatujte si, jaký text je kolem něj. - Pak stiskněte C-l. - Najděte kurzor znovu a všimněte si, že je kolem něj tentýž text. +>> Najděte kurzor a zapamatujte si, jaký text se okolo něj nachází. Pak + stiskněte C-l. Opět najděte kurzor a všimněte si, že je kolem něj + tentýž text, ale nyní je ve středu obrazovky. Pokud stisknete C-l + znovu, tento kus textu se přesune na horní okraj + obrazovky. Opakovaným stiskem tentýž klávesy se text přesune se na + spodní okraj. +K posunu po celých obrazovkách můžete také použít klávesy PageUp a +PageDn, pokud je váš terminál má k dispozici. Později ale uvidíte, že +efektivnější je používat C-v a M-v. * ZÁKLADNÍ OVLÁDÁNÍ KURZORU --------------------------- -Pohyb mezi obrazovkami je užitečný, ale jak se přemístíte na konkrétní -místo v textu na obrazovce? +Posouvání po celých obrazovkách je užitečné, ale jak se přemístíte na +konkrétní místo v textu na obrazovce? -Je toho možno dosáhnout několika způsoby. Nejzákladnějším způsobem je -použití příkazů C-p, C-b, C-f a C-n. Každý z těchto příkazů přesune -kurzor na obrazovce o jeden řádek nebo sloupec v daném směru. -Zde je tabulka znázorňující směr posuvu kurzoru vyvolaný těmito čtyřmi -příkazy: +Existuje několik způsobů, jak toho dosáhnout. Můžete buď použít +standardní kurzorové klávesy (tj. klávesy se šipkami), anebo příkazy +C-p, C-b, C-f a C-n. Druhý zmíněný způsob je efektivnější, protože ruce +zůstávají ve výchozí poloze na klávesnici. Zde je tabulka znázorňující +směr posuvu kurzoru vyvolaný příkazy C-p, C-b, C-f a C-n: Předchozí řádek, C-p : @@ -73,13 +88,13 @@ příkazy: : Následující řádek, C-n ->> Přesuňte kurzor na prostřední řádek tohoto diagramu pomocí - C-n nebo C-p. Potom stiskněte C-l, abyste na obrazovce viděli celý - diagram vycentrován. +>> Přesuňte kurzor na prostřední řádek diagramu výše pomocí C-n nebo + C-p. Potom stiskněte C-l, abyste na obrazovce viděli celý diagram + vycentrován. -Pravděpodobně se vám budou tyto příkazy snadno pamatovat podle -počátečních písmen anglických názvů: P jako previous (předchozí), -N jako next (následující), B jako backward (zpět), F jako forward (vpřed). +Pravděpodobně se vám budou tyto příkazy snadněji pamatovat podle +počátečních písmen anglických názvů: P jako previous (předchozí), N jako +next (následující), B jako backward (zpět), F jako forward (vpřed). Jsou to základní příkazy pro pohyb kurzoru a budete je používat neustále, takže by bylo velmi vhodné, kdybyste se je teď naučili. @@ -92,45 +107,45 @@ Každý řádek textu končí znakem nového řádku, který jej odděluje od ř následujícího. Znakem nového řádku by měl být ukončen i poslední řádek souboru (přestože to Emacs nevyžaduje). ->> Vyzkoušejte C-b na začátku řádku. Kurzor by se měl přesunout na konec - předchozího řádku, neboť jej tím přesunete přes znak nového řádku. +>> Vyzkoušejte C-b na začátku řádku. Kurzor by se měl přesunout na + konec předchozího řádku, neboť jej tím přesunete přes znak nového + řádku. C-f funguje analogicky jako C-b, tj. na konci řádku dojde k přesunu na další řádek. ->> Proveďte několik C-b, takže uvidíte, kde se nachází kurzor. - Pak provádějte C-f, abyste se vrátili na konec řádku. - Pak proveďte ještě jednou C-f, abyste se přesunuli na následující - řádek. +>> Proveďte několik C-b, tak abyste navnímali, kde se kurzor nachází. + Pak provádějte C-f, abyste se vrátili na konec řádku. Pak proveďte + ještě jednou C-f, abyste se přesunuli na následující řádek. Když kurzorem přejdete přes horní nebo dolní okraj obrazovky, posune se text za příslušným okrajem na obrazovku. Tato vlastnost se nazývá -"scrollování". Umožňuje přemístit kurzor na libovolné místo v textu, -aniž by kurzor opustil obrazovku. +"scrollování". Scrollování umožňuje přemístit kurzor na libovolné místo +v textu, aniž by kurzor opustil obrazovku. ->> Zkuste posunout kurzor pod dolní okraj obrazovky pomocí C-n a pozorujte, - co se stane. +>> Zkuste posunout kurzor pod dolní okraj obrazovky pomocí C-n a + pozorujte, co se stane. -Jestliže je posun po znacích příliš pomalý, můžete se pohybovat po -slovech. M-f (META-f) provádí posun o slovo vpřed a M-b provádí posun -o slovo zpět. +Pokud je posun po znacích příliš pomalý, můžete se pohybovat po slovech. +M-f (META-f) provádí posun o slovo vpřed a M-b provádí posun o slovo +zpět. >> Stiskněte několikrát M-f a M-b. Pokud se kurzor nachází uprostřed slova, M-f provede přesun na konec tohoto slova. Nachází-li se kurzor v mezeře mezi slovy, M-f provede -přesun na konec následujícího slova. M-b pracuje analogicky v opačném -směru. +přesun na konec následujícího slova. M-b funguje totožně, akorát že v +opačném směru. ->> Stiskněte několikrát M-f a M-b proloženě s C-f a C-b, abyste viděli +>> Stiskněte několikrát M-f a M-b a mezi tím C-f a C-b, abyste viděli výsledky příkazů M-f a M-b prováděných z různých míst uvnitř slov a mezi nimi. Všimněte si analogie mezi C-f a C-b na jedné straně a M-f a M-b na -straně druhé. Znaky s klávesou META jsou velmi často využívány pro operace -vztahující se k entitám definovaným jazykem (slova, věty, odstavce), -zatímco znaky s klávesou CONTROL pracují na základních prvcích -nezávislých na tom, co zrovna editujete (znaky, řádky, apod.). +straně druhé. Znaky s klávesou META jsou velmi často využívány pro +operace vztahující se k entitám definovaným jazykem (slova, věty, +odstavce), zatímco znaky s klávesou CONTROL pracují na základních +prvcích nezávislých na tom, co zrovna editujete (znaky, řádky apod.). Tato analogie platí také pro řádky a věty: C-a a C-e provádí přesun na začátek a konec řádku, M-a a M-e provádí přesun na začátek a konec @@ -140,11 +155,11 @@ věty. Zkuste několikrát M-a a poté několikrát M-e. Všimněte si, že opakované C-a nedělá nic, zatímco opakované M-a vždy -provádí posun o další větu. Principu analogie to sice příliš +provádí posun o další větu. Principu analogie to sice příliš neodpovídá, ale přesto je toto chování možno považovat za přirozené. Pozice kurzoru v textu se také nazývá "bod" ("point"). Abychom to -parafrázovali, kurzor je vidět na obrazovce v místě, kde je bod umístěn +parafrázovali, kurzor je na obrazovce vidět v místě, kde je bod umístěn v textu. Zde je přehled jednoduchých operací pro pohyb kurzoru včetně příkazů pro @@ -165,7 +180,7 @@ pohyb mezi slovy a větami: M-a Přesun zpět na začátek věty M-e Přesun vpřed na konec věty ->> Vyzkoušejte si teď několikrát všechny tyto příkazy pro procvičení. +>> Procvičte si teď několikrát všechny tyto příkazy. Jsou to nejpoužívanější příkazy. Další dva důležité příkazy pro pohyb kurzoru jsou M-< (META menší-než), @@ -174,7 +189,7 @@ který provede přesun na konec celého textu. Na většině terminálů je "<" nad čárkou, takže pro vyvolání tohoto znaku musíte použít klávesu Shift. Na těchto terminálech je tedy nutno použít -klávesu Shift i v případě příkazu M-<; bez klávesy Shift byste provedli +klávesu shift i v případě příkazu M-<; bez klávesy shift byste provedli M-čárka. >> Zkuste teď M-< pro přesun na začátek tutoriálu. @@ -183,48 +198,52 @@ M-čárka. >> Zkuste teď M-> pro přesun na konec tutoriálu. Použijte pak opakovaně M-v, abyste se opět vrátili sem. -Kurzor můžete přesouvat také pomocí kurzorových kláves (klávesy -se šipkami), pokud je váš terminál má. My však doporučujeme naučit se -C-b, C-f, C-n a C-p, a to ze tří důvodů. Za prvé, tyto klávesy fungují -na všech typech terminálů. Za druhé, jakmile jednou získáte cvik -v používání Emacsu, zjistíte, že používání těchto CTRL znaků je +Kurzor můžete přesouvat také pomocí kurzorových kláves (klávesy se +šipkami), pokud je váš terminál má. My však doporučujeme naučit se C-b, +C-f, C-n a C-p, a to ze tří důvodů. Za prvé, tyto klávesy fungují na +všech typech terminálů. Za druhé, jakmile jednou získáte cvik v +používání Emacsu, zjistíte, že používání těchto Control znaků je rychlejší než používání kurzorových kláves (protože nemusíte přesouvat ruku z psací pozice). Za třetí, zvyknete-li si používat tyto CTRL-znak příkazy, snadno se naučíte používat jiné pokročilé příkazy pro pohyb kurzoru. -Většina příkazů Emacsu akceptuje numerický argument; ten pro většinu +Většina příkazů Emacsu akceptuje numerický argument, který pro většinu příkazů slouží jako opakovač. Počet opakování příkazu zadáte prostřednictvím stisku C-u následovaného stiskem příslušných číslic před -vyvoláním příkazu. Máte-li META (nebo EDIT či ALT) klávesu, existuje +vyvoláním příkazu. Máte-li META (nebo ALT) klávesu, existuje alternativní možnost zadání numerického argumentu: přidržte klávesu META a stiskněte příslušné číslice. Doporučujeme naučit se C-u metodu, -protože ta funguje na jakémkoliv terminálu. +protože ta funguje na jakémkoliv terminálu. Numerický argument se +taktéž nazývá "prefixový argument", protože jej zadáváte před příkazem, +na který se vztahuje. Například C-u 8 C-f provede přesun o osm znaků vpřed. +>> Zkuste si procvičit použití C-n nebo C-p s numerickým argumentem tak, + abyste kurzor posunuli někam poblíž tohoto řádku. + Většina příkazů používá numerický argument jako opakovač. Jisté -výjimečné příkazy jej používají jiným způsobem. Mezi tyto výjimky patří -C-v a M-v. Dostanou-li numerický argument, posunou obrazovku nahoru -nebo dolů o odpovídající počet řádků místo obrazovek. Například -C-u 4 C-v posune obrazovku o 4 řádky. +výjimečné příkazy jej používají jiným způsobem. Řada příkazů (žádný +však, který jste se dosud naučili) ho používá jako přepínač, +tzn. přítomnost prefixového argumentu způsobí, že příkaz udělá něco +jiného (tj. bez ohledu na jeho hodnotu). ->> Zkuste teď stisknout C-u 8 C-v. +Příkazy C-v a M-v patří do jiné kategorie výjimek. Dostanou-li +numerický argument, posunou obrazovku nahoru nebo dolů o odpovídající +počet řádků. Například C-u 8 C-v posune obrazovku o 8 řádků. -To by mělo posunout obrazovku o 8 řádků nahoru. Pokud byste ji chtěli -posunout zpět dolů, můžete dát argument příkazu M-v. +>> Zkuste nyní zadat C-u 8 C-v. -Používáte-li X Window, měli byste mít na levé straně emacsového okna -vysokou obdélníkovou oblast, nazývanou scrollbar. Můžete pak text -posouvat klikáním myší na scrollbar. +Tento příkaz by měl posunout text o 8 řádků nahoru. Pokud chcete text +posunout zpět, použijte stejný prefixový argument s příkazem M-v. ->> Zkuste stisknout prostřední tlačítko na vrcholu zvýrazněné oblasti - uvnitř scrollbaru. To by mělo text posunout na pozici danou tím, jak - vysoko nebo nízko jste kliknuli. +Používáte-li grafické uživatelské prostředí, jako například X Window, +měli byste mít na jedné straně emacsového okna vysokou obdélníkovou +oblast, nazývanou jako scrollbar (posuvník). Pakliže máte posuvník k +dispozici, text můžete posouvat jím. ->> Zkuste při stisknutém prostředním tlačítku posouvat myší nahoru a - dolů. Uvidíte, jak se text posouvá nahoru a dolů podle toho, jak - posouváte myší. +Pokud máte k dispozici myš s rolovacím kolečkem, můžete používat i to. * KDYŽ EMACS NEREAGUJE @@ -267,50 +286,59 @@ provést, odpovězte na tuto otázku pomocí "n". ------ Emacs může mít několik oken (windows), z nichž každé zobrazuje svůj -vlastní text. Jak více oken používat, objasníme později. Nyní chceme -objasnit, jak se zbavit nadbytečných oken a vrátit se do základní -jednookenní editace. Je to jednoduché: +vlastní text. Způsob, jak více oken používat současně, objasníme +později. Nyní bychom rádi objasnili, jak se zbavit nadbytečných oken a +vrátit se do základní jednookenní editace. Je to jednoduché: C-x 1 Jedno okno (tj. zrušení všech ostatních oken) -Tedy vložení CONTROL-x následované číslicí 1. C-x 1 rozšíří okno +Tedy vložení CONTROL-x následované číslicí 1. Příkaz C-x 1 rozšíří okno obsahující kurzor přes celou obrazovku. Zruší to všechna ostatní okna. +>> Posuňte kurzor na tento řádek a stiskněte C-u 0 C-l. >> Stiskněte C-h k C-f. Pozorujte, jak se aktuální okno zmenší a objeví se nové okno za účelem zobrazení dokumentace k příkazu C-f. >> Stiskněte C-x 1 a pozorujte, jak okno s dokumentací zmizí. +Existuje celá řada příkazů, které začínají CONTROL-x. Mnoho z těchto +příkazů se týká oken, souborů, bufferů a podobných věcí. Tyto příkazy +mají dva, tři nebo čtyři znaky. + * VKLÁDÁNÍ A MAZÁNÍ ------------------- -Chcete-li vložit text, prostě jej napište. Znaky, které vidíte, -jako A, 7, *, atd., jsou Emacsem chápány jako text a vkládány okamžitě. -Pro vložení znaku nového řádku stiskněte (klávesu Enter). +Chcete-li vložit text, prostě jej napište. Běžné znaky, které vidíte, +jako A, 7, *, atd., jsou Emacsem chápány jako prostý text a jsou +vkládány okamžitě. Pro vložení znaku nového řádku stiskněte +(klávesu Enter). -Poslední znak, který jste napsali, můžete smazat stiskem . - je klávesa, která může být na klávesnici označena "Del". -V některých případech jako slouží klávesa "Backspace", avšak ne -vždy! +Poslední znak, který jste napsali, můžete smazat stiskem . Jedná +se o stejnou klávesu, kterou mimo Emacs běžně používáte ke smazání +posledního znaku, který jste napsali. Tato klávesa je většinou označena +jako "Backspace". -Obecněji, maže znak bezprostředně před momentální pozicí -kurzoru. +Na klávesnici je běžně k dispozici i klávesa označena jako . +Nejedná se však o klávesu, kterou v prostředí Emacsu označujeme jako +. >> Proveďte to teď -- napište několik znaků a pak je smažte několika - stisky . Nebojte se změn v tomto souboru; originální + stisky . Nebojte se změn v tomto souboru; originální tutoriál se nezmění. Toto je vaše osobní kopie. -Když se řádek textu zvětší natolik, že přesáhne jeden řádek obrazovky, -je zobrazen na více řádcích obrazovky. Řádek textu, který pokračuje na -dalším řádku obrazovky, je indikován zpětným lomítkem („\“) na pravém -okraji obrazovky. +Pokud se řádek textu zvětší natolik, že přesáhne jeden řádek obrazovky, +pak je zobrazen na dalším řádku obrazovky. Pokud používáte grafické +uživatelské prostředí, po stranách se objeví malé zakřivené šipky, které +naznačují, kde řádek pokračuje. Pakliže používáte textový terminál, +řádek textu, který pokračuje na dalším řádku obrazovky, je indikován +zpětným lomítkem („\“) na pravém okraji. >> Vkládejte text, až dosáhnete pravého okraje, a pokračujte ve vkládání. Objeví se vám pokračovací řádek. ->> Použijte pro smazání textu, až se řádek textu opět vejde na +>> Použijte pro smazání textu, až se řádek textu opět vejde na jeden řádek obrazovky. Pokračovací řádek zmizí. Znak nového řádku můžete smazat jako kterýkoliv jiný znak. Smazání @@ -318,10 +346,23 @@ znaku nového řádku mezi dvěma řádky způsobí jejich spojení do jediného řádku. Je-li výsledný řádek příliš dlouhý na to, aby se vešel na šířku obrazovky, bude zobrazen pokračovacím řádkem. ->> Přesuňte kurzor na začátek řádku a stiskněte . To tento +>> Přesuňte kurzor na začátek řádku a stiskněte . To tento řádek spojí s řádkem předchozím. ->> Stiskněte pro znovuvložení smazaného znaku nového řádku. +>> Stiskněte pro znovu vložení smazaného znaku nového řádku. + +Klávesa je speciální v tom, že její stisknutí může udělat více +než jen vložit znak nového řádku. V závislosti na okolním textu může +také vložit prázdné mezery, čímž text zarovná s předchozím řádkem. +Tento způsob chování (kdy stisk klávesy udělá více než že jen vloží +příslušný znak) nazýváme "elektrický" (angl. "electric"). + +>> Zde je příklad, kdy je klávesa elektrická. + Stiskněte na konci tohoto řádku. + +Měli byste vidět, že po stisknutí klávesy Emacs vloží prázdné +mezery na začátek řádku tak, aby se kurzor dostal pod písmeno "S" ve +slově "Stiskněte". Vzpomeňte si, že většina příkazů Emacsu může dostat počet opakování; včetně textových znaků. Opakování textových znaků je vloží několikrát. @@ -332,29 +373,30 @@ Teď už znáte nejzákladnější způsoby, jak něco v Emacsu napsat a jak opravovat chyby. Můžete ovšem také mazat po slovech nebo po řádcích. Zde je shrnutí operací pro mazání textu: - Smazání znaku bezprostředně před kurzorem + Smazání znaku bezprostředně před kurzorem C-d Smazání znaku následujícího za kurzorem - M- Zrušení slova bezprostředně před kurzorem + M- Zrušení slova bezprostředně před kurzorem M-d Zrušení slova následujícího za kurzorem C-k Zrušení textu od pozice kurzoru do konce řádku M-k Zrušení textu do konce aktuální věty -Všimněte si, že a C-d, resp. M- a M-d, rozšiřují -paralelu započatou C-f a M-f (pravda, opravdu není CONTROL -znak, ale netrapme se tím). C-k a M-k jsou jako C-e a M-e ve smyslu -vztahu řádků k větám. +Všimněte si, že a C-d, resp. M- a M-d, rozšiřují paralelu +započatou C-f a M-f (pravda, opravdu není CONTROL znak, ale +netrapme se tím). C-k a M-k jsou jako C-e a M-e ve smyslu vztahu řádků +k větám. -Libovolnou část bufferu můžete též zrušit následující metodou. -Přesuňte se na jeden konec této části a stiskněte C-@ nebo C-SPC -(libovolnou z těchto kombinací). (SPC označuje mezerník.) Přesuňte -se na druhý konec této části a stiskněte C-w. Text mezi těmito -pozicemi bude zrušen. +Libovolnou část textu můžete rovněž zrušit následující metodou. +Přesuňte se na jeden konec této části a stiskněte C- ( +označuje mezerník). Dále se přesuňte se na druhý konec části, kterou +chcete zrušit. Jakmile začnete tuto akci provádět, Emacs zvýrazní text +mezi kurzorem a pozicí, kde jste stiskli C-. Na závěr celé +sekvence stiskněte C-w, čímž se text mezi oběma pozicemi zruší. >> Přesuňte kurzor na písmeno L na začátku předchozího odstavce. ->> Stiskněte C-SPC. Emacs by měl ve spodním řádku obrazovky - zobrazit zprávu "Mark set". +>> Stiskněte C-. Emacs by měl ve spodní části obrazovky zobrazit + zprávu "Mark set". >> Přesuňte kurzor na písmeno c ve slově "konec" na druhém řádku odstavce. >> Stiskněte C-w. Text začínající písmenem L a končící před písmenem @@ -362,26 +404,32 @@ pozicemi bude zrušen. Uvědomte si, že rozdíl mezi "rušením" ("killing") a "mazáním" ("deleting") je ten, že "zrušené" věci mohou být zpět vhozeny, zatímco -"smazané" nikoliv. Obecně příkazy, které mohou smazat větší množství -textu, ukládají text, zatímco příkazy, které mažou jediný znak nebo -pouze prázdné řádky a mezery, mazaný text neukládají. +"smazané" nikoliv (můžete však smazané věci vrátit zpět, viz níže). +Zpětné vložení zrušeného textu se nazývá "vhazování" (angl. "yanking"). +Představte si opětovné vhazování jako vracení dříve odstraněného textu +zpět. Obecně platí, že příkazy, které mohou odstranit větší množství +textu provádějí "rušení" a mohou být tedy vhozeny zpět. Naopak příkazy, +které mažou pouze jeden znak, mezery či řádek, provádějí mazání, a tudíž +nemohou být vhozeny zpět. Příkazy a C-d (bez jakéhokoliv +argumentu) provádějí mazání. Pokud však dostanou prefixový argument, +pak provádějí rušení. >> Přesuňte kurzor na začátek neprázdného řádku. Pak stiskněte C-k pro zrušení textu na tomto řádku. >> Stiskněte C-k podruhé. Uvidíte, že to zruší znak nového řádku, který - je za tímto řádkem. + za tímto řádkem následuje. Všimněte si, že jedno C-k zruší obsah řádku a druhé C-k zruší řádek -samotný a posune všechny další řádky nahoru. C-k zpracovává numerický -argument speciálně: zruší odpovídající počet řádků VČETNĚ jejich +samotný a posune všechny další řádky nahoru. C-k pracuje s numerickým +argumentem speciálně: zruší odpovídající počet řádků VČETNĚ jejich obsahu. To už není opakování. C-u 2 C-k zruší dva řádky a jejich obsah; dvojitý stisk C-k by toto obvykle neudělal. -Vracení textu zpět se nazývá "vhazování" ("yanking"). (Představte -si opětovné vhazování, vracení dříve odstraněného textu zpátky.) -Zrušený text můžete vhodit buď na stejné místo, kde byl zrušen, -nebo na jiné místo v bufferu, nebo dokonce i do jiného souboru. -Text můžete vhodit i vícekrát, vytváříte tak jeho další kopie. +Zrušený text můžete vhodit buď na stejné místo, kde byl zrušen, nebo na +jiné místo v textu, který upravujete, ba dokonce i do jiného souboru. +Text můžete vhodit i vícekrát, vytváříte tak jeho další kopie. Některé +jiné textové editory nazývají rušení jako "vyjmutí" a vhazování jako +"vkládání" (viz Glossary v Manuálu Emacs). Příkazem pro vhazování je C-y. Tento příkaz vloží poslední smazaný text na pozici, na které se nachází kurzor. @@ -395,18 +443,18 @@ společně tak, aby bylo možné vhodit zpět všechny řádky najednou. Nyní obnovte posledně zrušený text: ->> Stiskněte C-y. Pak posuňte kurzor o několik řádků níže a stiskněte - C-y znova. Nyní vidíte, jak lze text kopírovat. +>> Stiskněte C-y. Následně posuňte kurzor o několik řádků níže a + stiskněte C-y znova. Nyní vidíte, jak lze text kopírovat. Co když máte nějaký text, který byste rádi vhodili zpět a pak zrušíte -něco jiného? C-y by vložilo poslední zrušený text. Avšak předchozí -text není ztracen. Můžete jej získat zpět použitím příkazu M-y. Poté, -co provedete C-y pro získání posledního zrušeného textu, stisk M-y -vymění tento vhozený text za předchozí zrušený text. Dalšími a -dalšími stisky M-y dostáváte předcházející a předcházející zrušené -texty. Když dosáhnete textu, který hledáte, nemusíte s ním pro jeho -uchování nic dalšího provádět. Jednoduše vhozený text ponechejte, kde -je, a pokračujte v editaci. +něco jiného? Vzpomeňte, že C-y by vložilo poslední zrušený text. +Nebojte, předchozí text není ztracen. Můžete jej získat zpět použitím +příkazu M-y. Poté, co provedete C-y pro získání posledního zrušeného +textu, stisk M-y vymění tento vhozený text za předchozí zrušený text. +Dalšími a dalšími stisky M-y dostáváte předcházející a předcházející +zrušené texty. Jakmile dosáhnete textu, který hledáte, nemusíte s ním +pro jeho uchování nic dalšího provádět. Jednoduše vhozený text +ponechejte, kde je, a pokračujte v editaci. Pokud opakujete M-y dostatečně dlouho, dostanete se zpátky k výchozímu bodu (posledně zrušenému textu). @@ -423,108 +471,99 @@ bodu (posledně zrušenému textu). ------ Jestliže provedete v textu změnu a pak zjistíte, že to byl omyl, můžete -změnu vrátit příkazem undo, C-x u. +změnu vrátit příkazem undo, C-/. -C-x u obvykle vrátí změny provedené jedním příkazem; pokud C-x u -zopakujete několikrát za sebou, každé opakování vrátí jeden další -příkaz. +C-/ obvykle vrátí změny provedené jedním příkazem; pokud C-/ zopakujete +několikrát za sebou, každé opakování vrátí jeden další příkaz. -Jsou ale dvě výjimky: příkazy, které nemění text, se nepočítají (to +Existují ale dvě výjimky: příkazy, které nemění text, se nepočítají (to zahrnuje příkazy pro pohyb kurzoru a scrollování) a znaky vkládající -samy sebe jsou obvykle zpracovávány ve skupinách až po 20. (To je kvůli -tomu, aby se zredukoval počet C-x u nutných pro vrácení vkládaného -textu.) +samy sebe jsou obvykle zpracovávány ve skupinách až po 20. To je kvůli +tomu, aby se zredukoval počet C-/ nutných pro vrácení vkládaného textu. ->> Zrušte tento řádek pomocí C-k, stiskněte pak C-x u a řádek by se měl +>> Zrušte tento řádek pomocí C-k, stiskněte pak C-/ a řádek by se měl znovu objevit. -Alternativní undo příkaz je C-_; pracuje stejně jako C-x u, je však -méně pracné jej aplikovat několikrát za sebou. Nevýhodou C-_ je, že -na některých klávesnicích není zřejmé, jak jej vyvolat. To je důvod, -proč nabízíme i C-x u. Na některých terminálech můžete C-_ vyvolat -stiskem / při stisknutém CTRL. +Alternativním příkazem undo je příkaz C-_. Na některých terminálech +můžete při vkládání příkazu C-_ vynechat klávesu shift. Pokud na +některých terminálech vložíte příkaz C-/, Emacs ve skutečnosti obdrží +příkaz C-_. Další alternativou je příkaz C-x u, který funguje totožně +jako C-/, avšak může být méně pohodlný. Numerický argument pro C-_ a C-x u funguje jako počet opakování. Pomocí příkazu undo můžete vrátit zrušený stejně jako smazaný text. Rozdíl mezi mazáním a rušením textu ovlivňuje možnost vhození tohoto -textu pomocí C-y, neovlivňuje možnosti příkazu undo. +textu pomocí C-y, neovlivňuje možnosti příkazu zpět. * SOUBORY --------- Aby text, který editujete, zůstal trvale uchován, musíte jej uložit do -souboru. Jinak by byl po ukončení Emacsu ztracen. Svoji editaci -spojíte se souborem "vyhledáním" ("finding") souboru. (Také se to -nazývá "navštívení" ("visiting") souboru.) +souboru. V opačném případě by byl po ukončení Emacsu ztracen. Abyste +mohli vložit text do nějakého souboru, musíte ho nejprve "vyhledat" +(angl. "find"). (Také se to nazývá "navštívení" ("visiting") souboru.) Vyhledání souboru znamená, že vidíte jeho obsah v Emacsu. V mnoha -ohledech je to, jako byste editovali přímo ten soubor. Nicméně změny, +ohledech je to jako byste editovali přímo ten soubor. Nicméně změny, které prostřednictvím Emacsu činíte, se nestanou trvalými, dokud tyto -změny do souboru "neuložíte" ("save"). Tím se zamezí nechtěnému ponechání -částečně změněného souboru v systému. Dokonce i když soubor uložíte, -Emacs uchová původní soubor pod změněným názvem pro případ, že byste -zjistili, že vaše úpravy byly chybné. +změny do souboru "neuložíte" ("save"). Tím se zamezí nechtěnému +ponechání částečně změněného souboru v systému. Dokonce i když soubor +uložíte, Emacs uchová původní soubor pod změněným názvem pro případ, že +byste zjistili, že vaše úpravy byly chybné. -Když se podíváte do dolní části obrazovky, uvidíte řádek, který začíná a -končí pomlčkami a na začátku má "2J:-- TUTORIAL.cs" nebo něco podobného. +Když se podíváte do dolní části obrazovky, uvidíte řádek, který začíná +pomlčkami a na začátku má " -:--- TUTORIAL.cs" nebo něco podobného. Tato část obrazovky obvykle obsahuje jméno souboru, který je právě navštíven. Zrovna teď máte navštíven soubor nazvaný "TUTORIAL.cs", -který je vaší osobní čmárací kopií tutoriálu Emacsu. Když v Emacsu -vyhledáte soubor, jeho jméno se objeví přesně na tom místě. +který je vaší osobní pracovní kopií tutoriálu Emacsu. Když v Emacsu +vyhledáte soubor, jeho jméno se objeví přesně na tomto místě. -Příkazy pro vyhledávání a ukládání souborů se na rozdíl od ostatních -příkazů, které jste se zatím naučili, skládají ze dvou znaků. Oba -začínají znakem CONTROL-x. Existuje celá řada příkazů začínajících na -CONTROL-x; mnoho z nich pracuje se soubory, buffery a podobnými věcmi. -Tyto příkazy jsou dlouhé dva, tři nebo čtyři znaky. - -Další věcí ohledně příkazu pro vyhledání souboru je to, že musíte říct, -které jméno souboru chcete. Říkáme, že příkaz "čte argument -z terminálu" (v tomto případě je argumentem jméno souboru). Poté co -vyvoláte příkaz +Příkaz pro vyhledání souboru je speciální v tom, že musíte Emacsu +nejprve sdělit jeho název. V takovýchto případech hovoříme o tom, že +Emacs "čte argument" (argumentem je v tomto konkrétním případě název +daného souboru). Poté co vyvoláte příkaz C-x C-f Vyhledání souboru -Emacs se vás zeptá na jméno souboru. Jméno souboru, které píšete, se -objevuje ve spodním řádku obrazovky, který se v této situaci nazývá +se vás Emacs zeptá na jméno souboru. Jméno souboru, které píšete, se +objevuje ve spodním řádku obrazovky. Toto dialogové okno se nazývá minibuffer. Pro editaci jména souboru můžete používat obvyklé editační příkazy Emacsu. Zadávání jména souboru (obecně kterýkoliv vstup z minibufferu) můžete zrušit příkazem C-g. ->> Stiskněte C-x C-f a pak C-g. To minibuffer zruší a taktéž to zruší - příkaz C-x C-f, který minibuffer použil. Takže nevyhledáte žádný - soubor. +>> Stiskněte C-x C-f a pak C-g. Tato klávesová kombinace zruší + minibuffer a taktéž to zruší příkaz C-x C-f, který daný minibuffer + vytvořil. Jakmile příkaz zrušíte, tak soubor nebude vyhledán. -Po napsání jména souboru stiskněte . -Příkaz C-x C-f pak začne pracovat a vyhledá soubor, který jste zvolili. -Po skončení příkazu C-x C-f minibuffer zmizí. +Pokud chcete příkaz dokončit, pak po napsání jména souboru stiskněte +. Příkaz C-x C-f pak začne pracovat a vyhledá soubor, který +jste zvolili. Po malé chvilce se obsah souboru objeví na obrazovce a můžete jej editovat. Když chcete změny trvale uložit, použijte příkaz C-x C-s Uložení souboru -To zkopíruje text z Emacsu do souboru. Když to provedete poprvé, Emacs -přejmenuje původní soubor na soubor s novým jménem, aby nebyl ztracen. -Nové jméno je vytvořeno přidáním "~" na konec původního jména souboru. - -Když je ukládání dokončeno, Emacs zobrazí jméno zapsaného souboru. -Měli byste ukládat rozumně často, abyste neztratili příliš mnoho práce -v případě pádu systému. +Na tomto místě Emacs zkopíruje text do příslušného souboru. Když to +provedete poprvé, Emacs přejmenuje původní soubor na soubor s novým +jménem, aby nebyl ztracen. Název nového souboru je vytvořeno přidáním +"~" na konec původního jména souboru. Když je ukládání dokončeno, Emacs +zobrazí jméno zapsaného souboru. ->> Stiskněte C-x C-s pro uložení vaší kopie tutoriálu. - Mělo by to zobrazit "Wrote ...TUTORIAL.cs" ve spodním řádku obrazovky. +>> Stiskněte C-x C-s TUTORIAL.cs . + Mělo by se vám zobrazit "Wrote ...TUTORIAL.cs" ve spodní části + obrazovky. Existující soubor můžete vyhledat, abyste jej mohli prohlížet nebo -editovat. Můžete také vyhledat soubor, který ještě neexistuje. To je -způsob, jakým lze vytvořit soubor v Emacsu: vyhledejte soubor, který +editovat. Můžete ale také vyhledat soubor, který ještě neexistuje. To +je způsob, jakým lze vytvořit soubor v Emacsu: vyhledejte soubor, který bude na začátku prázdný a pak začněte vkládat text určený pro tento -soubor. Když požádáte o uložení, Emacs skutečně vytvoří soubor -s textem, který jste vložili. Od té chvíle se pak můžete cítit, jako +soubor. Když požádáte o uložení, Emacs skutečně vytvoří soubor s +textem, který jste vložili. Od té chvíle se pak můžete cítit, jako kdybyste editovali již existující soubor. @@ -536,28 +575,51 @@ zůstává. Můžete se do něj zpět přepnout jeho opětovným vyhledáním pomocí C-x C-f. Tímto způsobem můžete do Emacsu dostat poměrně hodně souborů. ->> Vytvořte soubor pojmenovaný "foo" stiskem C-x C-f foo . - Potom vložte nějaký text, zeditujte jej a uložte "foo" stiskem C-x C-s. - Nakonec stiskněte C-x C-f TUTORIAL.cs , čímž se vrátíte zpět do - tutoriálu. - Emacs ukládá text každého souboru do objektu nazývaného "buffer". Vyhledání souboru vytvoří v Emacsu nový buffer. Chcete-li vidět seznam -bufferů, které momentálně existují ve vašem procesu Emacs, stiskněte: +bufferů, které momentálně existují ve vašem Emacsu, stiskněte: C-x C-b Seznam bufferů >> Zkuste teď C-x C-b. -Podívejte se, že každý buffer má v seznamu jméno a může tam mít také jméno -souboru, jehož text obsahuje. Některé buffery neodpovídají souborům. -Například buffer pojmenovaný "*Buffer List*" nemá žádný soubor. Je to -buffer, který obsahuje seznam bufferů vytvořený pomocí C-x C-b. -JAKÝKOLIV text, který vidíte v emacsovém okně, je vždy součástí -nějakého bufferu. +Podívejte se, že každý buffer má v seznamu jméno a může tam mít také +jméno souboru, ke daný buffer kterému patří. JAKÝKOLIV text, který +vidíte v emacsovém okně, je vždy součástí nějakého bufferu. >> Stiskněte C-x 1, abyste se zbavili seznamu bufferů. +Pokud v Emacsu máte vícero aktivních bufferů, pouze jeden z nich může +být "aktuální". Aktuální buffer je vždy ten, který právě editujete. +Pokud chcete provádět úpravy v jiném bufferu, pak se do něj musíte +"přepnout" (angl. "switch"). Pakliže se chcete přepnout do bufferu, +který odpovídá nějakému souboru na disku, můžete tak učinit příkazem C-x +C-f, který daný soubor navštíví. Existuje ale lehčí způsob: použijte +příkaz C-x b. Abyste tento příkaz dokončili, musíte vložit název +bufferu. + +>> Vytvořte soubor nazvaný "foo" takto: C-x C-f foo . + Dále proveďte C-x b TUTORIAL.cs tak, abyste se vrátili zpět + do tohoto tutoriálu. + +Ve většině případů název bufferu odpovídá názvu souboru (bez cesty k +němu). Není tomu ale tak vždy. Seznam bufferů, který se vám ukáže po +provedení příkazu C-x C-b, zobrazuje jak název bufferu, tak i souboru. + +Nějaké buffery ovšem nepatří k žádnému souboru. Například buffer +nazvaný "*Buffer List*" obsahuje seznam bufferů, který Emacs vytvořil po +provedení příkazu C-x C-b. Tutoriál, který právě čtete, dříve nepatřil +k žádnému souboru. Nyní ale ano, protože jste ho v předchozí části +pomocí C-x C-s uložili do souboru na disk. + +Buffer nazvaný "*Messages*" taktéž nepatří k žádnému souboru. Tento +buffer obsahuje seznam zpráv, které se vám zobrazují ve spodním řádku +vašeho Emacsu. + +>> Proveďte C-x b *Messages* abyste se podívali, jaké zprávy + tento buffer obsahuje. + Dále proveďte C-x b TUTORIAL.cs abyste se vrátili zpět. + Pokud provedete změny textu jednoho souboru a pak vyhledáte jiný soubor, nezpůsobí to uložení prvního souboru. Jeho změny zůstávají v Emacsu uchovány v jemu odpovídajícím bufferu. Vytvoření a editace druhého @@ -588,58 +650,62 @@ obchází prostřednictvím X (eXtend) příkazu. Ten vzniká dvěma způsoby: M-x Pojmenovaný příkaz eXtend. Následován dlouhým názvem. To jsou příkazy, které jsou obecně užitečné, avšak méně často používané -než ty, které jste se již naučili. Už jste viděli dva z nich: souborové -příkazy C-x C-f pro vyhledání a C-x C-s pro uložení. Jiný příklad je -příkaz pro ukončení Emacsu -- tj. příkaz C-x C-c. (Nemějte obavy -o ztrátu změn, které jste provedli; C-x C-c nabídne uložení každého -změněného souboru, než Emacs ukončí.) +než ty, které jste se již naučili. Už jste viděli některé z nich, jako +například souborové příkazy C-x C-f pro vyhledání a C-x C-s pro uložení. +Jiný příklad je příkaz pro ukončení Emacsu -- tj. příkaz C-x C-c. +(Nemějte obavy o ztrátu změn, které jste provedli; C-x C-c nabídne +uložení každého změněného souboru, než Emacs ukončí.) + +Pokud používáte grafické uživatelské prostředí, nemusíte používat žádný +speciální příkaz pro přepnutí do jiné aplikace. Můžete tak učinit myší, +anebo pomocí příkazů vašeho správce oken. Pakliže ale používáte +textový terminál, který může v jeden okamžik zobrazit pouze jednu +aplikaci, potřebujete příkaz na "pozastavení" (angl. "suspend") Emacsu, +abyste se mohli věnovat práci v jiné aplikaci. C-z je příkaz na *dočasné* opuštění Emacsu -- můžete se po něm do -spuštěného Emacsu vrátit. - -Na systémech, které to umožňují, C-z Emacs "pozastaví"; tzn. vrátí vás -do shellu, avšak Emacs neukončí. V nejběžnějších shellech se můžete do -Emacsu vrátit příkazem "fg" nebo pomocí "%emacs". - -Na systémech, které pozastavování procesů nemají implementováno, C-z -vytvoří subshell běžící pod Emacsem, aby vám dal šanci spustit jiné -programy a pak se do Emacsu vrátit; neprovede tedy pravé opuštění -Emacsu. V tom případě je obvyklou cestou návratu ze subshellu do Emacsu -shellovský příkaz "exit". +spuštěného Emacsu vrátit. Pakliže je Emacs spuštěn na textovém +terminálu, C-z Emacs "pozastaví"; tzn. vrátí vás do shellu, avšak Emacs +neukončí. V nejběžnějších shellech se můžete do Emacsu vrátit příkazem +"fg" nebo pomocí "%emacs". Chvíle pro použití C-x C-c nastane, když se chystáte odhlásit ze systému. Správné je to také při ukončování Emacsu vyvolaného poštovním -programem a různými jinými utilitami, protože ty nemusí vědět, jak si -poradit s pozastavením Emacsu. Nicméně za normálních okolností, pokud -se nechystáte odlogovat, je lépe Emacs pozastavit pomocí C-z než jej -ukončit. +programem a různými jinými utilitami. -Existuje mnoho C-x příkazů. Zde je seznam těch, které jste se již naučili: +Existuje mnoho C-x příkazů. Zde je seznam těch, které jste se již +naučili: C-x C-f Vyhledání souboru C-x C-s Uložení soubor + C-x s Uložení některých bufferů C-x C-b Seznam bufferů + C-x b Přepnutí bufferu C-x C-c Ukončení Emacsu - C-x u Undo + C-x 1 Jedno okno (tj. zrušení všech ostatních oken) + C-x u Undo (tj. zpět) Pojmenované eXtended příkazy jsou příkazy, které jsou používány ještě méně, nebo příkazy, které jsou používány jenom v jistých módech. Příkladem je příkaz replace-string, který globálně nahradí jeden řetězec -jiným. Když stisknete M-x, vypíše se na spodním řádku obrazovky prompt -M-x a vy byste měli zadat jméno příkazu; v tomto případě +jiným. Když stisknete M-x, Emacs vypíše na spodním řádku obrazovky +prompt M-x a vy byste měli zadat název příkazu, v tomto případě "replace-string". Jednoduše napište "repl s" a Emacs název doplní. -Dokončete zadávání jména příkazu pomocí . +( vyjadřuje klávesu tabulátor, která se běžně nachází nad klávesou +Caps Lock nebo Shift na levé straně klávesnice.) Dokončete zadávání +názvu příkazu pomocí . Příkaz replace-string vyžaduje dva argumenty -- řetězec, který má být nahrazen, a řetězec, který jej má nahradit. Každý argument musíte ukončit pomocí . ->> Přesuňte kurzor na prázdný řádek dva řádky pod tímto. +>> Přesuňte kurzor na prázdný řádek, který se nachází dva řádky pod + tímto. Pak napište M-x repl szměnilmodifikoval. - Všimněte si, jak se tento řádek změnil: nahradili jste slovo - z-m-ě-n-i-l slovem "modifikoval", kdekoliv se za aktuální pozicí - kurzoru vyskytlo. + Všimněte si, jak se tento řádek změnil: nahradili jste slovo "změnil" + slovem "modifikoval", kdekoliv se za aktuální pozicí kurzoru + vyskytlo. * AUTOMATICKÉ UKLÁDÁNÍ @@ -647,17 +713,17 @@ ukončit pomocí . Jestliže jste provedli změny v souboru, ale nemáte je ještě uloženy, mohou být v případě pádu systému ztraceny. Aby vás Emacs od toho -uchránil, periodicky zapisuje "auto save" soubor pro každý soubor, který -editujete. Jméno auto save souboru má na začátku a na konci #; +uchránil, Emacs periodicky zapisuje "auto save" soubor pro každý soubor, +který editujete. Jméno auto save souboru má na začátku a na konci #; například jestliže se váš soubor jmenuje "hello.c", jeho auto save -soubor se jmenuje "#hello.c#". Když soubor uložíte normálním způsobem, +soubor se jmenuje "#hello.c#". Když soubor uložíte běžným způsobem, Emacs auto save soubor smaže. Jestliže dojde k pádu systému, můžete svoji editaci obnovit z auto-save souboru, a to normálním vyhledáním souboru (toho, který jste editovali, -ne auto save souboru) a následnou aplikací M-x recover file. -Na žádost o potvrzení odpovězte zadáním yes pro pokračování a -obnovení auto-save dat. +ne auto save souboru) a následnou aplikací M-x recover-this-file +. Na žádost o potvrzení odpovězte zadáním yes pro +pokračování a obnovení auto-save dat. * ECHO OBLAST @@ -671,60 +737,63 @@ dolní řádek obrazovky. * STAVOVÝ ŘÁDEK --------------- -Řádek bezprostředně nad echo oblastí se nazývá "stavový řádek" ("mode line"). -Stavový řádek říká něco jako: +Řádek bezprostředně nad echo oblastí se nazývá "stavový řádek" ("mode +line"). Stavový řádek ukazuje něco jako: -2J:** TUTORIAL.cs (Fundamental)--L670--58%---------------- + -:**- TUTORIAL.cs 63% L749 (Fundamental) Tento řádek podává užitečnou informaci o stavu Emacsu a textu, který editujete. Už víte, co znamená jméno souboru -- je to soubor, který jste vyhledali. --NN%-- označuje vaši aktuální pozici v textu; říká, že NN procent textu -je nad horním okrajem obrazovky. Je-li začátek souboru na obrazovce, je -zde --Top-- a ne --00%--. Je-li konec textu na obrazovce, je zde ---Bot--. Jestliže se díváte na tak malý text, že se celý vejde na -obrazovku, stavový řádek říká --All--. +NN% označuje vaši aktuální pozici v textu; říká, že NN procent textu je +nad horním okrajem obrazovky. Je-li začátek souboru na obrazovce, Emacs +typicky ve stavovém řádku ukazuje "Top" a nikoliv " 0%". Je-li konec +textu na obrazovce, ve stavovém řádku bude "Bot". Jestliže se díváte na +tak malý text, že se celý vejde na obrazovku, stavový řádek říká "All". + +Písmeno L a číslice za ním vyjadřují pozici kurzoru číslem řádku, na +kterém se nachází. Hvězdičky poblíž začátku znamenají, že jste text změnili. Těsně po vyhledání nebo uložení souboru v této části stavového řádku nejsou žádné hvězdičky, pouze pomlčky. Část stavového řádku v závorkách říká, v jakých editačních módech se -nacházíte. Implicitní mód je Fundamental, což je ten, který momentálně -používáte. Je příkladem hlavního módu ("major mode"). +nacházíte. Výchozí mód je Fundamental, což je ten, který momentálně +používáte. Jedná se o příklad hlavního módu ("major mode"). Emacs má celou řadu hlavních módů. Některé z nich jsou určeny pro editaci různých programovacích jazyků a/nebo textů jako třeba Lisp mód, -Text mód, atd. V libovolném okamžiku je aktivní právě jeden hlavní mód a -jeho jméno lze nalézt ve stavovém řádku na místě, kde je teď -"Fundamental". +Text mód atd. V libovolném okamžiku je aktivní právě jeden hlavní mód +a jeho jméno lze nalézt ve stavovém řádku na místě, kde se momentálně +nachází "Fundamental". Každý hlavní mód mění chování některých příkazů. Například existují příkazy pro vytváření komentářů v programu, a protože každý programovací -programovací jazyk má jinou představu o tom, jak má komentář vypadat, -musí každý hlavní mód vkládat komentáře jinak. Každý hlavní mód je -vlastně jméno extended příkazu, kterým se do tohoto módu můžete -přepnout. Například M-x fundamental-mode je příkaz pro přepnutí se do -Fundamental módu. +jazyk má jinou představu o tom, jak má komentář vypadat, musí každý +hlavní mód vkládat komentáře jinak. Každý hlavní mód je vlastně jméno +extended příkazu, kterým se do tohoto módu můžete přepnout. Například +M-x fundamental-mode je příkaz pro přepnutí se do Fundamental módu. Chystáte-li se editovat český text, jako třeba tento soubor, pravděpodobně byste měli použít Text mód. ->> Napište M-x text-mode. + +>> Napište M-x text-mode . Nebojte se, žádný z příkazů, které jste se naučili, chování Emacsu nijak -významně nezmění. Můžete si ale všimnout, že M-f a M-b nyní pracují -s apostrofy jako se součástmi slov. Předtím, ve Fundamental módu, M-f a +významně nezmění. Můžete si ale všimnout, že M-f a M-b nyní pracují s +apostrofy jako se součástmi slov. Předtím, ve Fundamental módu, M-f a M-b pracovaly s apostrofy coby oddělovači slov. Hlavní módy obvykle dělají menší změny, jako byla tato: příkazy většinou -dělají "totéž", ale v každém hlavním módu pracují trošku jinak. +dělají "totéž", ale v každém hlavním módu pracují trochu jinak. Dokumentaci k aktuálnímu hlavnímu módu si můžete zobrazit stiskem C-h m. ->> Jednou nebo několikrát použijte C-u C-v, abyste tento řádek dostali - k vrcholu obrazovky. ->> Stiskněte C-h m, abyste viděli, jak se Text mód liší od Fundamental +>> Posuňte kurzor na řádek pod tímto. +>> Stiskněte C-l C-l, abyste tento řádek posunuli úplně nahoru. +>> Stiskněte C-h m abyste viděli, jak se Text mód liší od Fundamental módu. >> Stiskněte C-x 1 pro odstranění dokumentace z obrazovky. @@ -736,18 +805,18 @@ hlavním módu. Takže nemusíte používat žádný vedlejší mód nebo můž používat jeden vedlejší mód nebo libovolnou kombinaci několika vedlejších módů. -Jedním z velmi užitečných vedlejších módů, zejména pro editaci českých -textů, je Auto Fill mód. Když je tento mód zapnut, Emacs zalomí řádek +Jedním z velmi užitečných vedlejších módů, zejména pro editaci prostého +textu, je Auto Fill mód. Když je tento mód zapnutý, Emacs zalomí řádek mezi dvěma slovy, kdykoliv vkládáte text a řádek se stane příliš dlouhým. -Auto Fill mód můžete zapnout provedením M-x auto-fill-mode. -Je-li tento mód zapnut, můžete jej vypnout provedením M-x -auto-fill-mode. Je-li mód vypnut, tento příkaz jej zapíná, -a je-li mód zapnut, tak jej tento příkaz vypíná. Říkáme, že tento -příkaz přepíná ("toggles") tento mód. +Auto Fill mód můžete zapnout provedením M-x auto-fill-mode . +Je-li tento mód zapnutý, můžete jej vypnout provedením M-x +auto-fill-mode . Je-li mód vypnut, tento příkaz jej zapíná, a +je-li mód zapnut, tak jej tento příkaz vypíná. Říkáme, že tento příkaz +přepíná ("toggles") tento mód. ->> Napište teď M-x auto-fill-mode. Pak vkládejte "asdf " stále +>> Napište teď M-x auto-fill-mode . Pak vkládejte "asdf " stále dokola tak dlouho, až uvidíte, jak se vkládaný řádek rozdělí na dva řádky. Do textu musíte vkládat mezery proto, že Auto Fill mód zalamuje řádky pouze v mezerách. @@ -776,46 +845,41 @@ Emacs umí v textu vyhledávat řetězce (tj. skupiny spojených znaků nebo slov) směrem vpřed nebo vzad. Hledání řetězce je příkaz přesunující kurzor; přesune kurzor na nejbližší místo, kde se tento řetězec nachází. -Vyhledávací příkaz Emacsu se liší od vyhledávacích příkazů většiny -editorů v tom smyslu, že je "inkrementální". To znamená, že vyhledávání +Vyhledávací příkaz Emacsu je "inkrementální". To znamená, že vyhledávání se provádí už v okamžiku, kdy zadáváte vyhledávací řetězec. Příkaz pro zahájení hledání vpřed je C-s a pro hledání vzad C-r. -ALE POZOR! Nezkoušejte to ještě. +ALE POZOR! Nezkoušejte je ihned. Když stisknete C-s, uvidíte v echo oblasti prompt "I-search". To vám říká, že Emacs se nachází ve stavu, který se nazývá inkrementální hledání, -a čeká, až mu zadáte, co chcete hledat. hledání ukončí. +a čeká, až mu zadáte, co chcete hledat. hledání ukončí. >> Nyní zahajte hledání stiskem C-s. POMALU, písmeno po písmenu, pište slovo "kurzor". Po každém písmenu si všimněte, co se děje s kurzorem. Teď jste vyhledali "kurzor" poprvé. ->> Stiskněte C-s znovu, abyste nalezli další výskyt "kurzor". ->> Nyní čtyřikrát stiskněte a pozorujte, jak se kurzor - přesunuje. ->> Stiskněte pro ukončení hledání. +>> Stiskněte C-s znovu, abyste nalezli další výskyt slova "kurzor". +>> Nyní čtyřikrát stiskněte a pozorujte, jak se kurzor přesouvá. +>> Stiskněte pro ukončení hledání. Viděli jste, co se stalo? Emacs se v inkrementálním hledání pokouší přejít na další výskyt řetězce, který jste dosud napsali. Chcete-li -přejít na další výskyt "kurzor", jednoduše stiskněte C-s znovu. +přejít na další výskyt slova "kurzor", jednoduše stiskněte C-s znovu. Jestliže už žádný takový výskyt není, Emacs pípne a řekne vám, že hledání momentálně "selhává", C-g hledání ukončí. -POZNÁMKA: Na některých systémech stisk C-s způsobí ztuhnutí -obrazovky a nevidíte žádný další výstup z Emacsu. To znamená, že -"vlastnost" operačního systému zvaná "flow control" zachycuje C-s a -nepropustí jej k Emacsu. Pro odtuhnutí obrazovky stiskněte C-q. Pak -v sekci "Spontaneous Entry to Incremental Search" v manuálu Emacsu -vyhledejte radu, jak se vypořádat s touto "vlastností". - -Jestliže uprostřed inkrementálního hledání stisknete , uvidíte, -že poslední znak v hledaném řetězci zmizí a hledání se vrací na poslední -místo hledání. Předpokládejme například, že jste napsali "c", abyste -našli první výskyt "k". Jestliže nyní stisknete "u", kurzor se přesune na -první výskyt "ku". Teď stiskněte . To vymaže "u" z hledaného -řetězce a kurzor se přesune zpět na první výskyt "k". - -Jestliže uprostřed hledání stisknete CONTROL nebo META znak (s několika +Jestliže uprostřed inkrementálního hledání stisknete , uvidíte, že +poslední znak v hledaném řetězci zmizí a hledání se vrací na poslední +místo hledání. Pokud stisknete ihned potom, co jste zmáčkli C-s, +abyste se přesunuli na další výskyt hledaného řetězce, vrátí +kurzor zpět na poslední místo hledání. Pokud neexistují žádné dřívější +výskyty, vymaže poslední znak ve vyhledávacím řetězci. +Předpokládejme například, že jste napsali "c", abyste našli první výskyt +"c". Jestliže nyní stisknete "u", kurzor se přesune na první výskyt +"cu". Teď stiskněte . To vymaže "u" z hledaného řetězce a +kurzor se přesune zpět na první výskyt "c". + +Jestliže uprostřed hledání stisknete CONTROL nebo znak META (s několika výjimkami -- znaky, které jsou speciální v hledání, jako C-s a C-r), hledání se ukončí. @@ -829,17 +893,21 @@ kromě toho, že směr hledání je opačný. ----------- Jednou z pěkných vlastností Emacsu je to, že může na obrazovce zobrazit -více oken současně. +více oken současně. Na tomto místě je důležité podotknout, že Emacs +používá termín "rámy" (angl. "frames") pro to, co ostatní aplikace +nazývají slovem okno (angl. "window"). Manuál k Emacsu obsahuje sekci +Glossary of Emacs terms, kde jsou tyto termíny popsány podrobněji. ->> Přesuňte kurzor na tento řádek a stiskněte C-u 0 C-l. +>> Přesuňte kurzor na tento řádek a stiskněte C-l C-l. ->> Teď stiskněte C-x 2, což rozdělí obrazovku na dvě okna. +>> Nyní stiskněte C-x 2, což rozdělí obrazovku na dvě okna. Obě okna zobrazují tento tutoriál. Kurzor zůstává navrchu okna. >> Tiskněte C-M-v pro scrollování spodního okna. - (Nemáte-li skutečnou klávesu META, stiskněte ESC C-v.) + (Nemáte-li skutečnou klávesu META, stiskněte C-v.) ->> Stiskněte C-x o ("o" jako "other") pro přesun kurzoru do dolního okna. +>> Stiskněte C-x o ("o" jako "other") pro přesun kurzoru do dolního + okna. >> Použijte C-v a M-v ve spodním okně pro jeho scrollování. Pokračujte ve čtení těchto instrukcí v horním okně. @@ -847,29 +915,33 @@ více oken současně. >> Znovu stiskněte C-x o pro přesun kurzoru zpět do horního okna. Kurzor v horním okně je přesně na místě, kde byl původně. -Můžete dále používat C-x o pro přepínání mezi okny. Každé okno má svoji -vlastní pozici kurzoru, ale jenom jedno okno kurzor skutečně zobrazuje. -Všechny obvyklé editační příkazy platí pro okno, ve kterém se nachází -kurzor. Toto okno nazýváme "aktivní okno" ("selected window"). +Můžete dále používat C-x o pro přepínání mezi okny. "Aktivní okno" +(tj. okno, kde provádíte většinu úprav) je to s výrazným kurzorem, který +bliká, když právě nepíšete. Všechna ostatní okna mají svou vlastní +pozici kurzoru; pokud používáte Emacs v rámci grafického uživatelského +prostředí, tak je poznáte podle toho, že jsou vykresleny jako prázdné +rámečky, které neblikají. Příkaz C-M-v je velmi užitečný, jestliže v jednom okně editujete text a druhé okno používáte pouze pro přehled. Můžete kurzor nechávat stále v okně, kde editujete, a postupovat po druhém okně pomocí C-M-v. -C-M-v je příkladem CONTROL-META znaku. Máte-li skutečnou META klávesu, -můžete vyvolat C-M-v přidržením obou kláves CTRL a META při stisku v. -Nezáleží na tom, zda je prvně stisknuta CTRL nebo META, protože obě tyto -klávesy fungují jako modifikátory kláves, které tisknete. +C-M-v je příkladem CONTROL-META znaku. Máte-li skutečnou META (nebo +Alt) klávesu, můžete vyvolat C-M-v přidržením obou kláves CONTROL a META +zatímco stisknete klávesu v. Nezáleží na tom, zda je prvně stisknuta +CONTROL nebo META, protože obě tyto klávesy fungují jako modifikátory +kláves, které tisknete. -Pokud nemáte skutečnou META klávesu, můžete místo ní použít ESC, na -pořadí záleží: musíte stisknout ESC a následně CTRL-v; CTRL-ESC v by -nefungovalo. To proto, že ESC je samostatný znak, nikoliv modifikátor. +Pokud nemáte skutečnou META klávesu, můžete místo ní použít , na +pořadí v tomto případě záleží: musíte nejprve stisknout a následně +CONTROL-v; CONTROL- v by nefungovalo. To proto, že je +samostatný znak, nikoliv modifikátor. >> Stiskněte C-x 1 (v horním okně), abyste se zbavili dolního okna. (Kdybyste C-x 1 stiskli v dolním okně, odstranilo by to horní okno. Chápejte tento příkaz jako "ponechej právě jedno okno -- to, ve kterém -zrovna jsem".) +zrovna jste".) Nemusíte v obou oknech zobrazovat tentýž buffer. Jestliže použijete C-x C-f pro vyhledání souboru v jednom z oken, druhé okno se nezmění. @@ -885,6 +957,31 @@ Zde je další způsob, jak využít dvě okna ke zobrazení dvou různých věc dolního okna. +* VÍCE RÁMÚ +----------- + +Emacs může kromě oken vytvořit také více rámů (angl. "frames"). Rám je +kolekce oken, jednotlivých položek v menu, posuvníku, echo oblasti +atd. V rámci grafického uživatelského prostředí "rámem" nazýváme to, co +většina ostatních aplikací nazývá "oknem". V jeden okamžik můžeme +zobrazit klidně několik grafických rámů. Na textových terminálech může +být zobrazen pouze jeden rám v jeden okamžik. + +>> Stiskněte C-x 5 2. + Pozorujte, jak se na obrazovce vytvoří nový rám. + +V tomto novém (tj. druhém) rámu můžete provádět vše, co jste dělali v +rámu prvním. V ničem se neliší od prvního rámu. + +>> Stiskněte C-x 5 0. + Tento příkaz odstraní právě zvolený rám. + +Jakýkoliv rám je možné také odstranit běžnými metodami, které máte v +rámci grafického uživatelského prostředí k dispozici (např. kliknutím na +tlačítko "X" v horním rohu rámu). Pokud tímto způsobem odstraníte +poslední rám dané Emacsové instance, dojde k jejímu ukončení. + + * REKURZIVNÍ EDITAČNÍ ÚROVNĚ ---------------------------- @@ -893,12 +990,12 @@ Občas se dostanete do něčeho, co se nazývá "rekurzivní editační úroveň stavovém řádku obklopujícími závorky okolo jména hlavního módu. Například můžete vidět [(Fundamental)] místo (Fundamental). -Abyste se dostali z rekurzivní editační úrovně, stiskněte ESC ESC ESC. -To je obecný "vyskakovací" příkaz. Můžete jej použít též pro odstranění -některých oken a vyskočení z minibufferu. +Abyste se dostali z rekurzivní editační úrovně, stiskněte +. To je obecný "vyskakovací" příkaz. Můžete jej použít též pro +odstranění některých oken a vyskočení z minibufferu. >> Stiskněte M-x, abyste se dostali do minibufferu; pak stiskněte - ESC ESC ESC, abyste se z něj dostali ven. + , abyste se z něj dostali ven. Z rekurzivní editační úrovně nemůžete vyskočit pomocí C-g. To proto, že C-g je využíváno pro rušení příkazů a argumentů UVNITŘ rekurzivní @@ -909,11 +1006,11 @@ editační vrstvy. ------------------------ V tomto tutoriálu jsme se pokusili poskytnout vám dostatek informací, -abyste mohli začít Emacs používat. V Emacsu je toho tolik, že by bylo -nemožné to zde všechno objasnit. Nicméně se o Emacsu můžete naučit -více, protože má mnoho užitečných vlastností. Emacs nabízí příkazy pro -čtení dokumentace svých příkazů. Všechny tyto "help" příkazy -začínají znakem CONTROL-h, který se nazývá "help znak". +abyste mohli začít Emacs okamžitě používat. V Emacsu je toho ale tolik, +že by bylo nemožné to zde všechno objasnit. Nicméně se o Emacsu můžete +naučit více, protože má mnoho užitečných vlastností. Emacs nabízí +příkazy pro čtení dokumentace svých příkazů. Všechny tyto "help" +příkazy začínají znakem CONTROL-h, který se nazývá "help znak". Pro použití vlastností nápovědy stiskněte znak C-h a pak znak říkající, jaký druh nápovědy žádáte. Jste-li OPRAVDU ztraceni, stiskněte C-h ? a @@ -921,11 +1018,8 @@ Emacs vám sdělí, jaké druhy nápovědy vám může poskytnout. Jestliže jste stiskli C-h a pak jste se rozhodli, že žádnou nápovědu nechcete, jednoduše to zrušte stiskem C-g. -(Na některých počítačích je význam znaku C-h změněn. To by opravdu -nemělo být obecným nastavením pro všechny uživatele, takže máte právo -stěžovat si systémovému administrátorovi. Do té doby, jestliže C-h -nezobrazuje hlášení o nápovědě v dolní části obrazovky, zkuste místo -toho používat klávesu F1 nebo M-x help RET.) +(Jestliže C-h nezobrazuje hlášení o nápovědě v dolní části obrazovky, +zkuste místo toho používat klávesu F1 nebo M-x help .) Nejzákladnější help příkaz je C-h c. Stiskněte C-h, znak c a klávesový příkaz; Emacs pak zobrazí velmi stručný popis příkazu. @@ -935,14 +1029,13 @@ příkaz; Emacs pak zobrazí velmi stručný popis příkazu. C-p runs the command previous-line -To vám sděluje "jméno funkce". Jména funkcí jsou používána zejména pro -konfiguraci a rozšiřování Emacsu. Ale protože jména funkcí jsou volena -tak, aby naznačovala, co odpovídající příkaz dělá, mohou sloužit také -jako velmi stručná dokumentace -- dostatečná k tomu, aby vám připomenula +To vám sděluje "jméno funkce". Jelikož jména funkcí jsou volena tak, +aby naznačovala, co odpovídající příkaz dělá, mohou sloužit také jako +velmi stručná dokumentace -- dostatečná k tomu, aby vám připomenula příkazy, které jste se již naučili. -Víceznakové příkazy jako C-x C-s a (pokud nemáte META, EDIT ani ALT -klávesu) v jsou po C-h c povoleny také. +Víceznakové příkazy jako C-x C-s a v (pokud nemáte META ani ALT +klávesu) jsou po C-h c povoleny také. K získání více informací o příkazu místo C-h c použijte C-h k. @@ -955,15 +1048,15 @@ nápovědy a teprve pak stisknout C-x 1. Zde jsou další užitečné C-h volby: - C-h f Popis funkce. Zadáváte jméno funkce. + C-h x Popis funkce. Zadáváte jméno funkce. ->> Zkuste napsat C-h f previous-line. +>> Zkuste napsat C-h x previous-line . To vypíše veškeré informace, které Emacs má o funkci implementující příkaz C-p. Podobný příkaz C-h v zobrazí dokumentaci proměnné, jejíž hodnotu můžete nastavit a změnit tím chování Emacsu. Jméno proměnné zadáte, až -se na ně Emacs zeptá. +se na ni Emacs zeptá. C-h a Příkazové apropos. Zadejte klíčové slovo a Emacs vypíše všechny příkazy, jejichž jména obsahují toto klíčové @@ -972,7 +1065,7 @@ se na ně Emacs zeptá. také jedno nebo dvouznakové sekvence, které provádějí tentýž příkaz. ->> Napište C-h a file. +>> Napište C-h a file . To zobrazí v druhém okně seznam všech M-x příkazů obsahujících "file" ve svém názvu. Znakové příkazy jako C-x C-f uvidíte vypsané vedle @@ -994,16 +1087,52 @@ odpovídajících jmen příkazů jako find-file. primární dokumentaci. +* VÍCE FUNKCÍ +------------- + +O Emacsu se můžete dozvědět více přečtením jeho manuálu. Manuál k Emacsu +je dostupný jak v tištěné, tak i elektronické verzi (použijte menu +nápovědy nebo příkaz C-h r). Mezi mimořádně užitečné funkce, o kterých +jsme se doposud nezmínili, patří našeptávač, který usnadňuje psaní, a +aplikace "Dired", která zjednodušuje práci s diskovými soubory. + +Vestavěný našeptávač vám pomůže vyhnout se nadbytečnému psaní. Pokud se +například chcete přepnout do bufferu s názvem "*Messages*", můžete +použít C-x b *M a Emacs za vás doplní zbylý text kam až to půjde. +Našeptávač také funguje pro názvy souborů a příkazů. Pokud se o +našeptávači chcete dozvědět více, navštivte sekci "Completion" v +emacsovém manuálu. + +Aplikace "Dired" je nástroj, který vám pomůže rychle a snadno získat +seznam souborů v dané složce (a případně i podsložkách) na vašem +počítači, navštívit soubory na kterých vám záleží, přejmenovat je, +smazat či jinak manipulovat. Aplikace "Dired" je popsána v manuálu ve +stejnojmenné sekci. + +Emacs obsahuje mnoho dalších užitečných aplikací a funkcí. + + +* INSTALACE BALÍKŮ +------------------ + +Komunita okolo Emacsu vytvořila bohatou sadu balíků, které mohohou Emacs +rozšířit o nové funkce a dovednosti. Tyto balíky mohou obsahovat +podporu nových jazyků, vzhledů, nástroje pro práci s externími +aplikacemi a mnoho, opravdu mnoho, dalšího. + +Pakliže se chcete podívat na seznam všech dostupných balíků, použijte +M-x list-packages. V tomto seznamu se můžete pohybovat, číst popisy +jednotlivých balíků a případně je i nainstalovat a odinstalovat. + + * ZÁVĚR ------- -Nezapomeňte, Emacs ukončíte provedením příkazu C-x C-c. Pro dočasný -odskok do shellu, ze kterého se do Emacsu můžete opět vrátit, -použijte C-z. +Pokud chcete Emacs opustit, použijte C-x C-c. -Záměrem tohoto tutoriálu je být srozumitelný všem novým uživatelům, takže -narazíte-li na něco nejasného, tak neusedejte a neklaďte to za vinu sobě --- stěžujte si! +Záměrem tohoto tutoriálu je být srozumitelný všem novým uživatelům, +takže narazíte-li na něco nejasného, tak neusedejte a neklaďte to za +vinu sami sobě -- stěžujte si! KOPÍROVÁNÍ @@ -1013,25 +1142,33 @@ Tento tutoriál vychází z dlouhé řady emacsových tutoriálů zahájené tutoriálem napsaným Stuartem Cracraftem pro původní Emacs. Tato verze tutoriálu je, podobně jako GNU Emacs, chráněna copyrightem a -je šířena se svolením distribuovat kopie za jistých podmínek: +je šířena se svolením distribuovat kopie za jistých podmínek +(pozn. překl. oficiální verze licence není v českém překladu k +dispozici): + + Copyright (C) 1985, 1996, 1998, 2001-2024 Free Software Foundation, + Inc. + + This file is part of GNU Emacs. -Copyright (C) 1985, 1996, 1998, 2001-2024 Free Software Foundation, Inc. + GNU Emacs is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - Každému je zaručeno právo vytvářet a distribuovat přesné kopie tohoto - dokumentu tak, jak jej obdržel, na jakémkoliv médiu, s tím, že bude - zachována tato poznámka o autorství a poznámka o svolení a že - distributor zaručuje příjemci právo na další redistribuci povolenou - touto poznámkou. + GNU Emacs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - Je zaručeno právo distribuovat modifikované verze tohoto dokumentu - nebo jeho částí pod výše uvedenými podmínkami za předpokladu, že - obsahuje jasné poznámky uvádějící, kdo provedl poslední modifikace. + You should have received a copy of the GNU General Public License + along with GNU Emacs. If not, see . -Podmínky pro kopírování Emacsu samotného jsou složitější, avšak ve -stejném duchu. Přečtěte si prosím soubor COPYING a pak předávejte kopie -GNU Emacsu svým přátelům. Pomáhejte potírat softwarovou obstrukci -("vlastnictví") používáním, psaním a sdílením free softwaru! +Přečtěte si prosím soubor COPYING a pak předávejte kopie programu GNU +Emacs svým přátelům. Pomáhejte potírat softwarovou obstrukci +("vlastnictví") používáním, psaním a sdílením svobodného softwaru! ;;; Local Variables: ;;; coding: utf-8 +;;; mode: fundamental ;;; End: diff --git a/etc/tutorials/TUTORIAL.translators b/etc/tutorials/TUTORIAL.translators index 30e169d263f..e81e6c665f4 100644 --- a/etc/tutorials/TUTORIAL.translators +++ b/etc/tutorials/TUTORIAL.translators @@ -13,7 +13,8 @@ Maintainer: Xue Fuqiao * TUTORIAL.cs: Author: Milan Zamazal Pavel Janík -Maintainer: Maintainer needed. + +Maintainer: Jakub Ječmínek * TUTORIAL.de: Author: Werner Lemberg commit 1df3554f0766c91a9452cd40f74f469ed612eda6 Author: Jim Porter Date: Wed May 29 19:21:09 2024 -0700 Make Eshell's "which" command extensible Since 'eshell-named-command-hook' already makes execution of commands extensible, "which" should be too. This makes sure that "which" returns the right result for quoted commands like "/:cat". * lisp/eshell/em-alias.el (eshell-aliases-file): Allow it to be nil. (eshell-read-aliases-list, eshell-write-aliases-list): Check if 'eshell-aliases-file' is nil. (eshell-maybe-replace-by-alias--which): New function... (eshell-maybe-replace-by-alias): ... use it. * lisp/eshell/esh-cmd.el (eshell-named-command-hook): Update docstring. (eshell/which): Make extensible. (eshell--find-plain-lisp-command, eshell-plain-command--which): New functions. (eshell-plain-command): Use 'eshell--find-plain-lisp-command'. * lisp/eshell/esh-ext.el (eshell-explicit-command--which): New function... (eshell-explicit-command): ... unise it. (eshell-quoted-file-command--which): New function... (eshell-quoted-file-command): ... use it. (eshell-external-command--which): New function. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/which/plain/eshell-builtin) (esh-cmd-test/which/plain/external-program) (esh-cmd-test/which/plain/not-found, esh-cmd-test/which/alias) (esh-cmd-test/which/explicit, esh-cmd-test/which/explicit/not-found) (esh-cmd-test/which/quoted-file) (esh-cmd-test/which/quoted-file/not-found): New tests. * test/lisp/eshell/eshell-tests-helpers.el (with-temp-eshell-settings): Don't load or save aliases. (eshell-command-result--match,eshell-command-result--match-explainer) (eshell-command-result-match): New functions. diff --git a/lisp/eshell/em-alias.el b/lisp/eshell/em-alias.el index d12b382d885..ff0620702cf 100644 --- a/lisp/eshell/em-alias.el +++ b/lisp/eshell/em-alias.el @@ -107,7 +107,9 @@ it will be written to this file. Thus, alias definitions (and deletions) are always permanent. This approach was chosen for the sake of simplicity, since that's pretty much the only benefit to be gained by using this module." - :type 'file + :version "30.1" + :type '(choice (const :tag "Don't save aliases" nil) + file) :group 'eshell-alias) (defcustom eshell-bad-command-tolerance 3 @@ -186,29 +188,30 @@ file named by `eshell-aliases-file'.") "Read in an aliases list from `eshell-aliases-file'. This is useful after manually editing the contents of the file." (interactive) - (let ((file eshell-aliases-file)) - (when (file-readable-p file) - (setq eshell-command-aliases-list - (with-temp-buffer - (let (eshell-command-aliases-list) - (insert-file-contents file) - (while (not (eobp)) - (if (re-search-forward - "^alias\\s-+\\(\\S-+\\)\\s-+\\(.+\\)") - (setq eshell-command-aliases-list - (cons (list (match-string 1) - (match-string 2)) - eshell-command-aliases-list))) - (forward-line 1)) - eshell-command-aliases-list)))))) + (when (and eshell-aliases-file + (file-readable-p eshell-aliases-file)) + (setq eshell-command-aliases-list + (with-temp-buffer + (let (eshell-command-aliases-list) + (insert-file-contents eshell-aliases-file) + (while (not (eobp)) + (if (re-search-forward + "^alias\\s-+\\(\\S-+\\)\\s-+\\(.+\\)") + (setq eshell-command-aliases-list + (cons (list (match-string 1) + (match-string 2)) + eshell-command-aliases-list))) + (forward-line 1)) + eshell-command-aliases-list))))) (defun eshell-write-aliases-list () "Write out the current aliases into `eshell-aliases-file'." - (if (file-writable-p (file-name-directory eshell-aliases-file)) - (let ((eshell-current-handles - (eshell-create-handles eshell-aliases-file 'overwrite))) - (eshell/alias) - (eshell-close-handles 0 'nil)))) + (when (and eshell-aliases-file + (file-writable-p (file-name-directory eshell-aliases-file))) + (let ((eshell-current-handles + (eshell-create-handles eshell-aliases-file 'overwrite))) + (eshell/alias) + (eshell-close-handles 0 'nil)))) (defsubst eshell-lookup-alias (name) "Check whether NAME is aliased. Return the alias if there is one." @@ -216,18 +219,26 @@ This is useful after manually editing the contents of the file." (defvar eshell-prevent-alias-expansion nil) +(defun eshell-maybe-replace-by-alias--which (command) + (unless (and eshell-prevent-alias-expansion + (member command eshell-prevent-alias-expansion)) + (when-let ((alias (eshell-lookup-alias command))) + (concat command " is an alias, defined as \"" (cadr alias) "\"")))) + (defun eshell-maybe-replace-by-alias (command _args) "Call COMMAND's alias definition, if it exists." (unless (and eshell-prevent-alias-expansion (member command eshell-prevent-alias-expansion)) - (let ((alias (eshell-lookup-alias command))) - (if alias - (throw 'eshell-replace-command - `(let ((eshell-command-name ',eshell-last-command-name) - (eshell-command-arguments ',eshell-last-arguments) - (eshell-prevent-alias-expansion - ',(cons command eshell-prevent-alias-expansion))) - ,(eshell-parse-command (nth 1 alias)))))))) + (when-let ((alias (eshell-lookup-alias command))) + (throw 'eshell-replace-command + `(let ((eshell-command-name ',eshell-last-command-name) + (eshell-command-arguments ',eshell-last-arguments) + (eshell-prevent-alias-expansion + ',(cons command eshell-prevent-alias-expansion))) + ,(eshell-parse-command (nth 1 alias))))))) + +(put 'eshell-maybe-replace-by-alias 'eshell-which-function + #'eshell-maybe-replace-by-alias--which) (defun eshell-alias-completions (name) "Find all possible completions for NAME. diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index a093b7face9..ee9143db765 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -154,7 +154,8 @@ To prevent a command from executing at all, set :type 'hook) (defcustom eshell-named-command-hook nil - "A set of functions called before a named command is invoked. + "A set of functions called before +a named command is invoked. Each function will be passed the command name and arguments that were passed to `eshell-named-command'. @@ -173,7 +174,12 @@ For example: Although useless, the above code will cause any non-glob, non-Lisp command (i.e., `ls' as opposed to `*ls' or `(ls)') to be replaced by a -call to `cd' using the arguments that were passed to the function." +call to `cd' using the arguments that were passed to the function. + +When adding a function to this hook, you should also set the property +`eshell-which-function' for the function. This property should hold a +function that takes a single COMMAND argument and returns a string +describing where Eshell will find the function." :type 'hook) (defcustom eshell-pre-rewrite-command-hook @@ -1299,34 +1305,18 @@ have been replaced by constants." (defun eshell/which (command &rest names) "Identify the COMMAND, and where it is located." (dolist (name (cons command names)) - (let (program alias direct) - (if (eq (aref name 0) eshell-explicit-command-char) - (setq name (substring name 1) - direct t)) - (if (and (not direct) - (fboundp 'eshell-lookup-alias) - (setq alias - (eshell-lookup-alias name))) - (setq program - (concat name " is an alias, defined as \"" - (cadr alias) "\""))) - (unless program - (setq program - (let* ((esym (eshell-find-alias-function name)) - (sym (or esym (intern-soft name)))) - (if (and (or esym (and sym (fboundp sym))) - (or eshell-prefer-lisp-functions (not direct))) - (or (with-output-to-string - (require 'help-fns) - (princ (format "%s is " sym)) - (help-fns-function-description-header sym)) - name) - (eshell-search-path name))))) - (if (not program) - (eshell-error (format "which: no %s in (%s)\n" - name (string-join (eshell-get-path t) - (path-separator)))) - (eshell-printn program))))) + (condition-case error + (eshell-printn + (catch 'found + (run-hook-wrapped + 'eshell-named-command-hook + (lambda (hook) + (when-let (((symbolp hook)) + (which-func (get hook 'eshell-which-function)) + (result (funcall which-func command))) + (throw 'found result)))) + (eshell-plain-command--which name))) + (error (eshell-error (format "which: %s\n" (cadr error))))))) (put 'eshell/which 'eshell-no-numeric-conversions t) @@ -1376,17 +1366,31 @@ COMMAND may result in an alias being executed, or a plain command." (if (functionp sym) sym)))) +(defun eshell--find-plain-lisp-command (command) + "Look for `eshell/COMMAND' and return it when COMMAND should use it." + (let* ((esym (eshell-find-alias-function command)) + (sym (or esym (intern-soft command)))) + (when (and sym (fboundp sym) + (or esym eshell-prefer-lisp-functions + (not (eshell-search-path command)))) + sym))) + +(defun eshell-plain-command--which (command) + (if-let ((sym (eshell--find-plain-lisp-command command))) + (or (with-output-to-string + (require 'help-fns) + (princ (format "%s is " sym)) + (help-fns-function-description-header sym)) + command) + (eshell-external-command--which command))) + (defun eshell-plain-command (command args) "Insert output from a plain COMMAND, using ARGS. COMMAND may result in either a Lisp function being executed by name, or an external command." - (let* ((esym (eshell-find-alias-function command)) - (sym (or esym (intern-soft command)))) - (if (and sym (fboundp sym) - (or esym eshell-prefer-lisp-functions - (not (eshell-search-path command)))) - (eshell-lisp-command sym args) - (eshell-external-command command args)))) + (if-let ((sym (eshell--find-plain-lisp-command command))) + (eshell-lisp-command sym args) + (eshell-external-command command args))) (defun eshell-exec-lisp (printer errprint func-or-form args form-p) "Execute a Lisp FUNC-OR-FORM, maybe passing ARGS. diff --git a/lisp/eshell/esh-ext.el b/lisp/eshell/esh-ext.el index df8f7198917..3c4deb32601 100644 --- a/lisp/eshell/esh-ext.el +++ b/lisp/eshell/esh-ext.el @@ -182,6 +182,11 @@ commands on your local host by using the \"/local:\" prefix, like (add-hook 'eshell-named-command-hook #'eshell-quoted-file-command nil t) (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t)) +(defun eshell-explicit-command--which (command) + (when (and (> (length command) 1) + (eq (aref command 0) eshell-explicit-command-char)) + (eshell-external-command--which (substring command 1)))) + (defun eshell-explicit-command (command args) "If a command name begins with \"*\", always call it externally. This bypasses all Lisp functions and aliases." @@ -194,6 +199,13 @@ This bypasses all Lisp functions and aliases." (error "%s: external command not found" (substring command 1)))))) +(put 'eshell-explicit-command 'eshell-which-function + #'eshell-explicit-command--which) + +(defun eshell-quoted-file-command--which (command) + (when (file-name-quoted-p command) + (eshell-external-command--which (file-name-unquote command)))) + (defun eshell-quoted-file-command (command args) "If a command name begins with \"/:\", always call it externally. Similar to `eshell-explicit-command', this bypasses all Lisp functions @@ -201,6 +213,9 @@ and aliases, but it also ignores file name handlers." (when (file-name-quoted-p command) (eshell-external-command (file-name-unquote command) args))) +(put 'eshell-quoted-file-command 'eshell-which-function + #'eshell-quoted-file-command--which) + (defun eshell-remote-command (command args) "Insert output from a remote COMMAND, using ARGS. A \"remote\" command in Eshell is something that executes on a different @@ -239,6 +254,11 @@ current working directory." (eshell-gather-process-output (car interp) (append (cdr interp) args))))) +(defun eshell-external-command--which (command) + (or (eshell-search-path command) + (error "no %s in (%s)" command + (string-join (eshell-get-path t) (path-separator))))) + (defun eshell-external-command (command args) "Insert output from an external COMMAND, using ARGS." (cond diff --git a/test/lisp/eshell/esh-cmd-tests.el b/test/lisp/eshell/esh-cmd-tests.el index d84f8802bdc..4d27f6db2ee 100644 --- a/test/lisp/eshell/esh-cmd-tests.el +++ b/test/lisp/eshell/esh-cmd-tests.el @@ -517,4 +517,50 @@ NAME is the name of the test case." ;; Make sure we can call another command after throwing. (eshell-match-command-output "echo again" "\\`again\n"))) + +;; `which' command + +(ert-deftest esh-cmd-test/which/plain/eshell-builtin () + "Check that `which' finds Eshell built-in functions." + (eshell-command-result-match "which cat" "\\`eshell/cat")) + +(ert-deftest esh-cmd-test/which/plain/external-program () + "Check that `which' finds external programs." + (skip-unless (executable-find "sh")) + (eshell-command-result-equal "which sh" + (concat (executable-find "sh") "\n"))) + +(ert-deftest esh-cmd-test/which/plain/not-found () + "Check that `which' reports an error for not-found commands." + (skip-when (executable-find "nonexist")) + (eshell-command-result-match "which nonexist" "\\`which: no nonexist in")) + +(ert-deftest esh-cmd-test/which/alias () + "Check that `which' finds aliases." + (with-temp-eshell + (eshell-insert-command "alias cat '*cat $@*'") + (eshell-match-command-output "which cat" "\\`cat is an alias"))) + +(ert-deftest esh-cmd-test/which/explicit () + "Check that `which' finds explicitly-external programs." + (skip-unless (executable-find "cat")) + (eshell-command-result-match "which *cat" + (concat (executable-find "cat") "\n"))) + +(ert-deftest esh-cmd-test/which/explicit/not-found () + "Check that `which' reports an error for not-found explicit commands." + (skip-when (executable-find "nonexist")) + (eshell-command-result-match "which *nonexist" "\\`which: no nonexist in")) + +(ert-deftest esh-cmd-test/which/quoted-file () + "Check that `which' finds programs with quoted file names." + (skip-unless (executable-find "cat")) + (eshell-command-result-match "which /:cat" + (concat (executable-find "cat") "\n"))) + +(ert-deftest esh-cmd-test/which/quoted-file/not-found () + "Check that `which' reports an error for not-found quoted commands." + (skip-when (executable-find "nonexist")) + (eshell-command-result-match "which /:nonexist" "\\`which: no nonexist in")) + ;; esh-cmd-tests.el ends here diff --git a/test/lisp/eshell/eshell-tests-helpers.el b/test/lisp/eshell/eshell-tests-helpers.el index a15fe611676..bfd829c95e9 100644 --- a/test/lisp/eshell/eshell-tests-helpers.el +++ b/test/lisp/eshell/eshell-tests-helpers.el @@ -30,6 +30,8 @@ (require 'esh-mode) (require 'eshell) +(defvar eshell-aliases-file nil) +(defvar eshell-command-aliases-list nil) (defvar eshell-history-file-name nil) (defvar eshell-last-dir-ring-file-name nil) @@ -60,6 +62,8 @@ beginning of the test file." ;; just enable this selectively when needed.) See also ;; `eshell-test-command-result' below. (eshell-debug-command (cons 'process eshell-debug-command)) + (eshell-aliases-file nil) + (eshell-command-aliases-list nil) (eshell-history-file-name nil) (eshell-last-dir-ring-file-name nil) (eshell-module-loading-messages nil)) @@ -196,6 +200,28 @@ inserting the command." (eshell-test-command-result command) result))))) +(defun eshell-command-result--match (_command regexp actual) + "Compare the ACTUAL result of a COMMAND with REGEXP." + (string-match regexp actual)) + +(defun eshell-command-result--match-explainer (command regexp actual) + "Explain the result of `eshell-command-result--match'." + `(mismatched-result + (command ,command) + (result ,actual) + (regexp ,regexp))) + +(put 'eshell-command-result--match 'ert-explainer + #'eshell-command-result--match-explainer) + +(defun eshell-command-result-match (command regexp) + "Execute COMMAND non-interactively and compare it to REGEXP." + (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") + (let ((eshell-module-loading-messages nil)) + (should (eshell-command-result--match + command regexp + (eshell-test-command-result command)))))) + (provide 'eshell-tests-helpers) ;;; eshell-tests-helpers.el ends here commit 6a0f4d333a35543cd99bd88e053995a44020dadb Author: Jim Porter Date: Wed May 29 17:27:01 2024 -0700 ; Improve recent change to deferred evaluation in Eshell * lisp/eshell/esh-cmd.el (eshell-do-eval): Move active check later. diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 1072646ec15..a093b7face9 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -1282,14 +1282,13 @@ have been replaced by constants." (setcdr form (cdr new-form))) (eshell-do-eval form synchronous-p)) (if-let (((memq (car form) eshell-deferrable-commands)) - (procs (eshell-make-process-list result)) - (active (seq-some #'eshell-process-active-p procs))) + (procs (eshell-make-process-list result))) (if synchronous-p (apply #'eshell/wait procs) (eshell-manipulate form "inserting ignore form" (setcar form 'ignore) (setcdr form nil)) - (when active + (when (seq-some #'eshell-process-active-p procs) (throw 'eshell-defer procs))) (list 'quote result)))))))))))) commit 69a9fdd6bb004cd4ef96bddef457dd2b8e53303c Author: Stefan Kangas Date: Wed May 29 23:29:21 2024 +0200 Use `scheme-mode` for the Guile init file * lisp/files.el (auto-mode-alist): Use `scheme-mode` for the Guile init file (~/.guile). diff --git a/lisp/files.el b/lisp/files.el index 079d48e69fb..b25cca00bb3 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2931,7 +2931,7 @@ since only a single case-insensitive search through the alist is made." ("\\.emacs-places\\'" . lisp-data-mode) ("\\.el\\'" . emacs-lisp-mode) ("Project\\.ede\\'" . emacs-lisp-mode) - ("\\.\\(scm\\|sls\\|sld\\|stk\\|ss\\|sch\\)\\'" . scheme-mode) + ("\\(?:\\.\\(?:scm\\|sls\\|sld\\|stk\\|ss\\|sch\\)\\|/\\.guile\\)\\'" . scheme-mode) ("\\.l\\'" . lisp-mode) ("\\.li?sp\\'" . lisp-mode) ("\\.[fF]\\'" . fortran-mode) commit 9280a619ab3141c0b3b8f4ae876f82e6a38c757f Author: Jim Porter Date: Mon May 20 08:59:02 2024 -0700 Fix calling Eshell scripts outside of Eshell * lisp/eshell/em-script.el (eshell-source-file): Make obsolete. (eshell--source-file): Adapt from 'eshell-source-file'... (eshell-script-initialize, eshell/source, eshell/.): ... use it. (eshell-princ-target): New struct. (eshell-output-object-to-target, eshell-target-line-oriented-p): New implementations for 'eshell-princ-target'. (eshell-execute-file, eshell-batch-file): New functions. * lisp/eshell/esh-mode.el (eshell-mode): Just warn if we can't create the Eshell directory. * test/lisp/eshell/em-script-tests.el (em-script-test/execute-file): (em-script-test/execute-file/args), em-script-test/batch-file): New tests. * test/lisp/eshell/eshell-tests-helpers.el (with-temp-eshell-settings): New function... (with-temp-eshell): ... use it. * doc/misc/eshell.texi (Control Flow): Update documentation. * etc/NEWS: Announce this change (bug#70847). diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 2da132e01eb..873d14aff32 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -1686,13 +1686,20 @@ treat it as a list of one element. If you specify multiple @node Scripts @section Scripts @cmindex source -@fnindex eshell-source-file +@fnindex eshell-execute-file +@fnindex eshell-batch-file You can run Eshell scripts much like scripts for other shells; the main difference is that since Eshell is not a system command, you have to run it from within Emacs. An Eshell script is simply a file containing a -sequence of commands, as with almost any other shell script. Scripts -are invoked from Eshell with @command{source}, or from anywhere in Emacs -with @code{eshell-source-file}. +sequence of commands, as with almost any other shell script. You can +invoke scripts from within Eshell with @command{source}, or from +anywhere in Emacs with @code{eshell-execute-file}. Additionally, you +can make an Eshell script file executable by calling +@code{eshell-batch-file} in the interpreter directive: + +@example +#!/usr/bin/env -S emacs --batch -f eshell-batch-file +@end example Like with aliases (@pxref{Aliases}), Eshell scripts can accept any number of arguments. Within the script, you can refer to these with diff --git a/etc/NEWS b/etc/NEWS index 9416ced5a0d..c9334e18e2d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -870,6 +870,13 @@ using this new option. (Or set 'display-buffer-alist' directly.) ** Eshell ++++ +*** You can now run Eshell scripts in batch mode. +By adding the following interpreter directive to an Eshell script, you +can make it executable like other shell scripts: + + #!/usr/bin/env -S emacs --batch -f eshell-batch-file + +++ *** New builtin Eshell command 'compile'. This command runs another command, sending its output to a compilation diff --git a/lisp/eshell/em-script.el b/lisp/eshell/em-script.el index 254a11ea114..6e2ca7ca781 100644 --- a/lisp/eshell/em-script.el +++ b/lisp/eshell/em-script.el @@ -24,6 +24,7 @@ ;;; Code: (require 'esh-mode) +(require 'esh-io) ;;;###esh-module-autoload (progn @@ -75,42 +76,106 @@ This includes when running `eshell-command'." eshell-login-script (file-readable-p eshell-login-script) (eshell-do-eval - (list 'eshell-commands - (catch 'eshell-replace-command - (eshell-source-file eshell-login-script))) + `(eshell-commands ,(eshell--source-file eshell-login-script)) t)) (and eshell-rc-script (file-readable-p eshell-rc-script) (eshell-do-eval - (list 'eshell-commands - (catch 'eshell-replace-command - (eshell-source-file eshell-rc-script))) t)))) + `(eshell-commands ,(eshell--source-file eshell-rc-script)) + t)))) -(defun eshell-source-file (file &optional args subcommand-p) - "Execute a series of Eshell commands in FILE, passing ARGS. -Comments begin with `#'." +(defun eshell--source-file (file &optional args subcommand-p) + "Return a Lisp form for executig the Eshell commands in FILE, passing ARGS. +If SUBCOMMAND-P is non-nil, execute this as a subcommand." (let ((cmd (eshell-parse-command `(:file . ,file)))) (when subcommand-p (setq cmd `(eshell-as-subcommand ,cmd))) - (throw 'eshell-replace-command - `(let ((eshell-command-name ',file) - (eshell-command-arguments ',args) - ;; Don't print subjob messages by default. - ;; Otherwise, if this function was called as a - ;; subjob, then *all* commands in the script would - ;; print start/stop messages. - (eshell-subjob-messages nil)) - ,cmd)))) - -(defun eshell/source (&rest args) - "Source a file in a subshell environment." - (eshell-source-file (car args) (cdr args) t)) + `(let ((eshell-command-name ',file) + (eshell-command-arguments ',args) + ;; Don't print subjob messages by default. Otherwise, if + ;; this function was called as a subjob, then *all* commands + ;; in the script would print start/stop messages. + (eshell-subjob-messages nil)) + ,cmd))) + +(defun eshell-source-file (file &optional args subcommand-p) + "Execute a series of Eshell commands in FILE, passing ARGS. +Comments begin with `#'." + (declare (obsolete nil "30.1")) + (throw 'eshell-replace-command + (eshell--source-file file args subcommand-p))) + +;;;###autoload +(defun eshell-execute-file (file &optional args destination) + "Execute a series of Eshell commands in FILE, passing ARGS. +Comments begin with `#'." + (let ((eshell-non-interactive-p t) + (stdout (if (eq destination t) (current-buffer) destination))) + (with-temp-buffer + (eshell-mode) + (eshell-do-eval + `(let ((eshell-current-handles + (eshell-create-handles ,stdout 'insert)) + (eshell-current-subjob-p)) + ,(eshell--source-file file args)) + t)))) + +(cl-defstruct (eshell-princ-target + (:include eshell-generic-target) + (:constructor nil) + (:constructor eshell-princ-target-create + (&optional printcharfun))) + "A virtual target calling `princ' (see `eshell-virtual-targets')." + printcharfun) + +(cl-defmethod eshell-output-object-to-target (object + (target eshell-princ-target)) + "Output OBJECT to the `princ' function TARGET." + (princ object (eshell-princ-target-printcharfun target))) + +(cl-defmethod eshell-target-line-oriented-p ((_target eshell-princ-target)) + "Return non-nil to indicate that the display is line-oriented." + t) + +;;;###autoload +(defun eshell-batch-file () + "Execute an Eshell script as a batch script from the command line. +Inside your Eshell script file, you can add the following at the +top in order to make it into an executable script: + + #!/usr/bin/env -S emacs --batch -f eshell-batch-file" + (let ((file (pop command-line-args-left)) + (args command-line-args-left) + (eshell-non-interactive-p t) + (eshell-module-loading-messages nil) + (eshell-virtual-targets + (append `(("/dev/stdout" ,(eshell-princ-target-create) nil) + ("/dev/stderr" ,(eshell-princ-target-create + #'external-debugging-output) + nil)) + eshell-virtual-targets))) + (setq command-line-args-left nil) + (with-temp-buffer + (eshell-mode) + (eshell-do-eval + `(let ((eshell-current-handles + (eshell-create-handles "/dev/stdout" 'append + "/dev/stderr" 'append)) + (eshell-current-subjob-p)) + ,(eshell--source-file file args)) + t)))) + +(defun eshell/source (file &rest args) + "Source a FILE in a subshell environment." + (throw 'eshell-replace-command + (eshell--source-file file args t))) (put 'eshell/source 'eshell-no-numeric-conversions t) -(defun eshell/. (&rest args) - "Source a file in the current environment." - (eshell-source-file (car args) (cdr args))) +(defun eshell/. (file &rest args) + "Source a FILE in the current environment." + (throw 'eshell-replace-command + (eshell--source-file file args))) (put 'eshell/. 'eshell-no-numeric-conversions t) diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 7290c29b008..7c030639955 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -376,7 +376,8 @@ and the hook `eshell-exit-hook'." (eshell-load-modules eshell-modules-list) (unless (file-exists-p eshell-directory-name) - (eshell-make-private-directory eshell-directory-name t)) + (with-demoted-errors "Error creating Eshell directory: %s" + (eshell-make-private-directory eshell-directory-name t))) ;; Initialize core Eshell modules, then extension modules, for this session. (eshell-initialize-modules (eshell-subgroups 'eshell)) diff --git a/test/lisp/eshell/em-script-tests.el b/test/lisp/eshell/em-script-tests.el index f77c4568ea8..f3adbae9df7 100644 --- a/test/lisp/eshell/em-script-tests.el +++ b/test/lisp/eshell/em-script-tests.el @@ -24,6 +24,7 @@ ;;; Code: (require 'ert) +(require 'ert-x) (require 'esh-mode) (require 'eshell) (require 'em-script) @@ -94,4 +95,34 @@ (eshell-match-command-output (format "source %s a b c" temp-file) "a\nb\nc\n")))) +(ert-deftest em-script-test/execute-file () + "Test running an Eshell script file via `eshell-execute-file'." + (ert-with-temp-file temp-file + :text "echo hi\necho bye" + (with-temp-buffer + (with-temp-eshell-settings + (eshell-execute-file temp-file nil t)) + (should (equal (buffer-string) "hibye"))))) + +(ert-deftest em-script-test/execute-file/args () + "Test running an Eshell script file with args via `eshell-execute-file'." + (ert-with-temp-file temp-file + :text "+ $@*" + (with-temp-buffer + (with-temp-eshell-settings + (eshell-execute-file temp-file '(1 2 3) t)) + (should (equal (buffer-string) "6"))))) + +(ert-deftest em-script-test/batch-file () + "Test running an Eshell script file as a batch script." + (ert-with-temp-file temp-file + :text (format + "#!/usr/bin/env -S %s --batch -f eshell-batch-file\necho hi" + (expand-file-name invocation-name invocation-directory)) + (set-file-modes temp-file #o744) + (with-temp-buffer + (with-temp-eshell-settings + (call-process temp-file nil '(t nil))) + (should (equal (buffer-string) "hi\n"))))) + ;; em-script-tests.el ends here diff --git a/test/lisp/eshell/eshell-tests-helpers.el b/test/lisp/eshell/eshell-tests-helpers.el index 3f1c55f420d..a15fe611676 100644 --- a/test/lisp/eshell/eshell-tests-helpers.el +++ b/test/lisp/eshell/eshell-tests-helpers.el @@ -47,24 +47,30 @@ beginning of the test file." (file-directory-p ert-remote-temporary-file-directory) (file-writable-p ert-remote-temporary-file-directory)))) +(defmacro with-temp-eshell-settings (&rest body) + "Configure Eshell to leave no trace behind, and then evaluate BODY." + (declare (indent 0)) + `(ert-with-temp-directory eshell-directory-name + (let (;; We want no history file, so prevent Eshell from falling + ;; back on $HISTFILE. + (process-environment (cons "HISTFILE" process-environment)) + ;; Enable process debug instrumentation. We may be able to + ;; remove this eventually once we're confident that all the + ;; process bugs have been worked out. (At that point, we can + ;; just enable this selectively when needed.) See also + ;; `eshell-test-command-result' below. + (eshell-debug-command (cons 'process eshell-debug-command)) + (eshell-history-file-name nil) + (eshell-last-dir-ring-file-name nil) + (eshell-module-loading-messages nil)) + ,@body))) + (defmacro with-temp-eshell (&rest body) "Evaluate BODY in a temporary Eshell buffer." + (declare (indent 0)) `(save-current-buffer - (ert-with-temp-directory eshell-directory-name - (let* (;; We want no history file, so prevent Eshell from falling - ;; back on $HISTFILE. - (process-environment (cons "HISTFILE" process-environment)) - ;; Enable process debug instrumentation. We may be able - ;; to remove this eventually once we're confident that - ;; all the process bugs have been worked out. (At that - ;; point, we can just enable this selectively when - ;; needed.) See also `eshell-test-command-result' - ;; below. - (eshell-debug-command (cons 'process eshell-debug-command)) - (eshell-history-file-name nil) - (eshell-last-dir-ring-file-name nil) - (eshell-module-loading-messages nil) - (eshell-buffer (eshell t))) + (with-temp-eshell-settings + (let ((eshell-buffer (eshell t))) (unwind-protect (with-current-buffer eshell-buffer ,@body) commit eac608cb8041222ba3b2eac48ac6f76ac36bab16 Author: Jim Porter Date: Thu May 23 14:55:59 2024 -0700 Rework how 'eshell-ensure-newline-p' adds newlines This allows for other output targets (see the following commit) to be "line-oriented". * lisp/eshell/esh-io.el (eshell-ensure-newline-p): Move from esh-cmd.el. (eshell-target-line-oriented-p, eshell--output-maybe-n) (eshell-print-maybe-n, eshell-error-maybe-n) (eshell-maybe-output-newline): New functions. * lisp/eshell/esh-cmd.el (eshell-lisp-command): Don't print a newline in this function directly; instead, use 'eshell-print-maybe-n' and 'eshell-error-maybe-n'. (eshell-ensure-newline-p): Move to esh-io.el. * lisp/eshell/em-unix.el (eshell/cat): Remove now-unnecessary override of 'eshell-ensure-newline-p'. diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el index 7f976d22681..4137c05fa41 100644 --- a/lisp/eshell/em-unix.el +++ b/lisp/eshell/em-unix.el @@ -683,9 +683,7 @@ Concatenate FILE(s), or standard input, to standard output.") (with-current-buffer curbuf (eshell-buffered-print str))) (forward-line))))) - (eshell-flush) - ;; if the file does not end in a newline, do not emit one - (setq eshell-ensure-newline-p nil)))) + (eshell-flush)))) (put 'eshell/cat 'eshell-no-numeric-conversions t) (put 'eshell/cat 'eshell-filename-arguments t) diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 57aeff59266..1072646ec15 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -254,11 +254,6 @@ the command." :type 'sexp :risky t) -(defvar eshell-ensure-newline-p nil - "If non-nil, ensure that a newline is emitted after a Lisp form. -This can be changed by Lisp forms that are evaluated from the Eshell -command line.") - ;;; Internal Variables: ;; These variables have been merged into `eshell-foreground-command'. @@ -1499,7 +1494,7 @@ a string naming a Lisp function." (catch 'eshell-external ; deferred to an external command (setq eshell-last-command-status 0 eshell-last-arguments args) - (let* ((eshell-ensure-newline-p (eshell-interactive-output-p)) + (let* ((eshell-ensure-newline-p t) (command-form-p (functionp object)) (result (if command-form-p @@ -1526,14 +1521,13 @@ a string naming a Lisp function." (setq args (cdr args)))) (setq eshell-last-command-name (concat "#")) - (eshell-apply object eshell-last-arguments)) + (eshell-apply* #'eshell-print-maybe-n + #'eshell-error-maybe-n + object eshell-last-arguments)) (setq eshell-last-command-name "#") - (eshell-eval object)))) - (if (and eshell-ensure-newline-p - (save-excursion - (goto-char eshell-last-output-end) - (not (bolp)))) - (eshell-print "\n")) + (eshell-eval* #'eshell-print-maybe-n + #'eshell-error-maybe-n + object)))) (eshell-close-handles ;; If `eshell-lisp-form-nil-is-failure' is non-nil, Lisp forms ;; that succeeded but have a nil result should have an exit diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 9b35125cd28..c7017ee1d70 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -162,6 +162,12 @@ ordinary function or `eshell-generic-target' as desribed above)." (define-error 'eshell-pipe-broken "Pipe broken") +(defvar eshell-ensure-newline-p nil + "If non-nil, ensure that a newline is emitted after a Lisp form. +This can be changed by Lisp forms that are evaluated from the +Eshell command line. This behavior only applies to line-oriented +output targets (see `eshell-target-line-oriented-p'.") + ;;; Internal Variables: (defconst eshell-redirection-operators-alist @@ -493,15 +499,37 @@ after all printing is over with no argument." "Output OBJECT to the standard error handle." (eshell-output-object object eshell-error-handle)) +(defsubst eshell-printn (object) + "Output OBJECT followed by a newline to the standard output handle." + (eshell-print object) + (eshell-print "\n")) + (defsubst eshell-errorn (object) "Output OBJECT followed by a newline to the standard error handle." (eshell-error object) (eshell-error "\n")) -(defsubst eshell-printn (object) - "Output OBJECT followed by a newline to the standard output handle." - (eshell-print object) - (eshell-print "\n")) +(defun eshell--output-maybe-n (object handle) + "Output OBJECT to HANDLE. +For any line-oriented output targets on HANDLE, ensure the output +ends in a newline." + (eshell-output-object object handle) + (when (and eshell-ensure-newline-p + (not (and (stringp object) + (string-suffix-p object "\n")))) + (eshell-maybe-output-newline handle))) + +(defsubst eshell-print-maybe-n (object) + "Output OBJECT to the standard output handle. +For any line-oriented output targets, ensure the output ends in a +newline." + (eshell--output-maybe-n object eshell-output-handle)) + +(defsubst eshell-error-maybe-n (object) + "Output OBJECT to the standard error handle. +For any line-oriented output targets, ensure the output ends in a +newline." + (eshell--output-maybe-n object eshell-error-handle)) (cl-defstruct (eshell-generic-target (:constructor nil)) "An Eshell target. @@ -678,6 +706,16 @@ Returns what was actually sent, or nil if nothing was sent.") "Output OBJECT to the Eshell function TARGET." (funcall (eshell-function-target-output-function target) object)) +(cl-defgeneric eshell-target-line-oriented-p (_target) + "Return non-nil if the specified TARGET is line-oriented. +Line-oriented targets are those that expect a newline after +command output when `eshell-ensure-newline-p' is non-nil." + nil) + +(cl-defmethod eshell-target-line-oriented-p ((_target (eql t))) + "Return non-nil to indicate that the display is line-oriented." + t) + (defun eshell-output-object (object &optional handle-index handles) "Insert OBJECT, using HANDLE-INDEX specifically. If HANDLE-INDEX is nil, output to `eshell-output-handle'. @@ -688,5 +726,18 @@ HANDLES is the set of file handles to use; if nil, use (dolist (target targets) (eshell-output-object-to-target object target)))) +(defun eshell-maybe-output-newline (&optional handle-index handles) + "Maybe insert a newline, using HANDLE-INDEX specifically. +This inserts a newline for all line-oriented output targets. + +If HANDLE-INDEX is nil, output to `eshell-output-handle'. +HANDLES is the set of file handles to use; if nil, use +`eshell-current-handles'." + (let ((targets (caar (aref (or handles eshell-current-handles) + (or handle-index eshell-output-handle))))) + (dolist (target targets) + (when (eshell-target-line-oriented-p target) + (eshell-output-object-to-target "\n" target))))) + (provide 'esh-io) ;;; esh-io.el ends here commit 9daf1085a9b11e056079edce8dccca92d69bf891 Author: Jim Porter Date: Thu May 23 14:52:07 2024 -0700 Add ability for Eshell virtual targets to handle closing the target This was documented to work by calling the output function with 'nil', but that was never actually implemented. Instead, for compatibility, we now support a new (optional) close function. * lisp/eshell/esh-io.el (eshell-virtual-targets): Update docstring. (eshell-generic-target): New struct... (eshell-function-target): ... inherit from it, and rename from 'eshell-virtual-target'. (eshell-get-target): Handle already-created 'eshell-generic-target'. (eshell-close-target): Call the target's close function if present. * test/lisp/eshell/esh-io-tests.el (esh-io-test/virtual/device-close): New test. * doc/misc/eshell.texi (Redirection): Document the new behavior. diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 57ee3bf3e9f..2da132e01eb 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -2392,18 +2392,35 @@ Adds the text passed to it to the clipboard. @end table @vindex eshell-virtual-targets -You can, of course, define your own virtual targets. They are defined -by adding a list of the form @samp{("/dev/name" @var{function} -@var{mode})} to @code{eshell-virtual-targets}. The first element is -the device name; @var{function} may be either a lambda or a function -name. If @var{mode} is @code{nil}, then the function is the output -function; if it is non-@code{nil}, then the function is passed the -redirection mode as a symbol--@code{overwrite} for @code{>}, -@code{append} for @code{>>}, or @code{insert} for @code{>>>}--and the -function is expected to return the output function. - -The output function is called once on each line of output until -@code{nil} is passed, indicating end of output. +@vindex eshell-generic-target +@findex eshell-output-object-to-target +@findex eshell-close-target +You can, of course, define your own virtual targets. These are entries +in @code{eshell-virtual-targets} with the form @samp{(@var{filename} +@var{output-function} @var{pass-mode})}. The first element, +@var{filename}, is the device name, usually of the form +@samp{"/dev/@var{name}"}. The second, @var{output-function}, should be a +function: Eshell will repeatedly call it with the redirected output. +This argument can also be an @code{eshell-generic-target} instance. In +this case, Eshell will repeatedly call the generic function +@code{eshell-output-object-to-target} with the output; once the +redirection has completed, Eshell will then call the generic function +@code{eshell-close-target}, passing non-@code{nil} if the redirected +command succeeded. + +If @var{pass-mode} is non-@code{nil}, then Eshell will pass the +redirection mode as an argument to @code{output-function} as a +symbol: @code{overwrite} for @code{>}, @code{append} for @code{>>}, or +@code{insert} for @code{>>>}. In this case, @code{output-function} +should return the real output function (either an ordinary function or +an @code{eshell-generic-target} as described above). + +@defun eshell-function-target-create output-function &optional close-function +Create a new virtual target for Eshell that repeatedly calls +@var{output-function} with the redirected output, as described above. +If @var{close-function} is non-nil, Eshell will call it when closing the +target, passing non-@code{nil} if the redirected command succeeded. +@end defun @node Pipelines @section Pipelines diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 4487389bf26..9b35125cd28 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -135,18 +135,22 @@ from executing while Emacs is redisplaying." #'eshell-clipboard-append) t)) "Map virtual devices name to Emacs Lisp functions. -If the user specifies any of the filenames above as a redirection -target, the function in the second element will be called. - -If the third element is non-nil, the redirection mode is passed as an -argument (which is the symbol `overwrite', `append' or `insert'), and -the function is expected to return another function -- which is the -output function. Otherwise, the second element itself is the output -function. - -The output function is then called repeatedly with single strings, -which represents successive pieces of the output of the command, until nil -is passed, meaning EOF." +Each member is of the following form: + + (FILENAME OUTPUT-FUNCTION [PASS-MODE]) + +When the user specifies FILENAME as a redirection target, Eshell will +repeatedly call the OUTPUT-FUNCTION with the redirected output as +strings. OUTPUT-FUNCTION can also be an `eshell-generic-target' +instance. In this case, Eshell will repeatedly call the function in the +`output-function' slot with the string output; once the redirection has +completed, Eshell will then call the function in the `close-function' +slot, passing the exit status of the redirected command. + +If PASS-MODE is non-nil, Eshell will pass the redirection mode as an +argument (which is the symbol `overwrite', `append' or `insert') to +OUTPUT-FUNCTION, which should return the real output function (either an +ordinary function or `eshell-generic-target' as desribed above)." :version "30.1" :type '(repeat (list (string :tag "Target") @@ -499,11 +503,18 @@ after all printing is over with no argument." (eshell-print object) (eshell-print "\n")) -(cl-defstruct (eshell-virtual-target +(cl-defstruct (eshell-generic-target (:constructor nil)) + "An Eshell target. +This is mainly useful for creating virtual targets (see +`eshell-virtual-targets').") + +(cl-defstruct (eshell-function-target + (:include eshell-generic-target) (:constructor nil) - (:constructor eshell-virtual-target-create (output-function))) - "A virtual target (see `eshell-virtual-targets')." - output-function) + (:constructor eshell-function-target-create + (output-function &optional close-function))) + "An Eshell target that calls an OUTPUT-FUNCTION." + output-function close-function) (cl-defgeneric eshell-get-target (raw-target &optional _mode) "Convert RAW-TARGET, which is a raw argument, into a valid output target. @@ -514,14 +525,16 @@ it defaults to `insert'." (cl-defmethod eshell-get-target ((raw-target string) &optional mode) "Convert a string RAW-TARGET into a valid output target using MODE. If TARGET is a virtual target (see `eshell-virtual-targets'), -return an `eshell-virtual-target' instance; otherwise, return a +return an `eshell-generic-target' instance; otherwise, return a marker for a file named TARGET." (setq mode (or mode 'insert)) (if-let ((redir (assoc raw-target eshell-virtual-targets))) - (eshell-virtual-target-create - (if (nth 2 redir) - (funcall (nth 1 redir) mode) - (nth 1 redir))) + (let ((target (if (nth 2 redir) + (funcall (nth 1 redir) mode) + (nth 1 redir)))) + (unless (eshell-generic-target-p target) + (setq target (eshell-function-target-create target))) + target) (let ((exists (get-file-buffer raw-target)) (buf (find-file-noselect raw-target t))) (with-current-buffer buf @@ -602,9 +615,10 @@ If status is nil, prompt before killing." (throw 'done nil)) (process-send-eof target)))) -(cl-defmethod eshell-close-target ((_target eshell-virtual-target) _status) - "Close a virtual TARGET." - nil) +(cl-defmethod eshell-close-target ((target eshell-function-target) status) + "Close an Eshell function TARGET." + (when-let ((close-function (eshell-function-target-close-function target))) + (funcall close-function status))) (cl-defgeneric eshell-output-object-to-target (object target) "Output OBJECT to TARGET. @@ -660,9 +674,9 @@ Returns what was actually sent, or nil if nothing was sent.") object) (cl-defmethod eshell-output-object-to-target (object - (target eshell-virtual-target)) - "Output OBJECT to the virtual TARGET." - (funcall (eshell-virtual-target-output-function target) object)) + (target eshell-function-target)) + "Output OBJECT to the Eshell function TARGET." + (funcall (eshell-function-target-output-function target) object)) (defun eshell-output-object (object &optional handle-index handles) "Insert OBJECT, using HANDLE-INDEX specifically. diff --git a/test/lisp/eshell/esh-io-tests.el b/test/lisp/eshell/esh-io-tests.el index 188570161c7..b4e8c0b4a9a 100644 --- a/test/lisp/eshell/esh-io-tests.el +++ b/test/lisp/eshell/esh-io-tests.el @@ -381,4 +381,19 @@ stdout originally pointed (the terminal)." (eshell-insert-command "echo three >> /dev/kill") (should (equal (car kill-ring) "twothree")))) +(ert-deftest esh-io-test/virtual/device-close () + "Check that the close function for `eshell-function-target' works." + (let* ((data nil) + (status nil) + (eshell-virtual-targets + `(("/dev/virtual" + ,(eshell-function-target-create + (lambda (d) (setq data d)) + (lambda (s) (setq status s))) + nil)))) + (with-temp-eshell + (eshell-insert-command "echo hello > /dev/virtual") + (should (equal data "hello")) + (should (equal status t))))) + ;;; esh-io-tests.el ends here commit 4c924a53334035dc4089b24174012b54c020631b Author: Jim Porter Date: Sun May 19 22:01:31 2024 -0700 Use 'esh-module-autoload' for Eshell modules' defgroups This will let modules define their own, regular autoloads, independent of the core Eshell machinery for defining modules. * lisp/eshell/em-alias.el (em-alias): * lisp/eshell/em-banner.el (em-banner): * lisp/eshell/em-basic.el (em-basic): * lisp/eshell/em-cmpl.el (em-cmpl): * lisp/eshell/em-dirs.el (em-dirs): * lisp/eshell/em-elecslash.el (em-elecslash): * lisp/eshell/em-extpipe.el: * lisp/eshell/em-glob.el (em-glob): * lisp/eshell/em-hist.el (em-hist): * lisp/eshell/em-ls.el (em-ls): * lisp/eshell/em-pred.el (em-pred): * lisp/eshell/em-prompt.el (em-prompt): * lisp/eshell/em-rebind.el (em-rebind): * lisp/eshell/em-script.el (em-script): * lisp/eshell/em-smart.el (em-smart): * lisp/eshell/em-term.el (em-term): * lisp/eshell/em-tramp.el (em-tramp): * lisp/eshell/em-unix.el (em-unix): * lisp/eshell/em-xtra.el (em-xtra): Use 'esh-module-autoload'. * lisp/eshell/esh-module.el ("esh-module-loaddefs"): Load this instead of "esh-groups". * .gitignore: Change esh-groups.el to esh-module-loaddefs.el diff --git a/.gitignore b/.gitignore index 1557c085fad..52d328a9357 100644 --- a/.gitignore +++ b/.gitignore @@ -128,7 +128,7 @@ lisp/cedet/semantic/wisent/js-wy.el lisp/cedet/semantic/wisent/python-wy.el lisp/cedet/srecode/srt-wy.el lisp/cedet/semantic/grammar-wy.el -lisp/eshell/esh-groups.el +lisp/eshell/esh-module-loaddefs.el lisp/finder-inf.el lisp/leim/ja-dic/ leim/small-ja-dic-option diff --git a/lisp/eshell/em-alias.el b/lisp/eshell/em-alias.el index 832e14418d0..d12b382d885 100644 --- a/lisp/eshell/em-alias.el +++ b/lisp/eshell/em-alias.el @@ -92,7 +92,7 @@ (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-alias nil "Command aliases allow for easy definition of alternate commands." @@ -268,9 +268,4 @@ These are all the command aliases which begin with NAME." (eshell-parse-command alias)))))))))) (provide 'em-alias) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-alias.el ends here diff --git a/lisp/eshell/em-banner.el b/lisp/eshell/em-banner.el index e6dcbb24475..626624c7bfe 100644 --- a/lisp/eshell/em-banner.el +++ b/lisp/eshell/em-banner.el @@ -44,7 +44,7 @@ (require 'esh-util) (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-banner nil "This sample module displays a welcome banner at login. @@ -82,9 +82,4 @@ This can be any sexp, and should end with at least two newlines." (eshell-interactive-print msg)))) (provide 'em-banner) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-banner.el ends here diff --git a/lisp/eshell/em-basic.el b/lisp/eshell/em-basic.el index 6ec53ef9412..82cddd7385f 100644 --- a/lisp/eshell/em-basic.el +++ b/lisp/eshell/em-basic.el @@ -58,7 +58,7 @@ (require 'esh-opt) (require 'esh-util) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-basic nil "The \"basic\" code provides a set of convenience functions which @@ -225,9 +225,4 @@ are: (while (pcomplete-here '("error" "form" "process")))) (provide 'em-basic) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-basic.el ends here diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el index 201beb5071d..4c79f7b187a 100644 --- a/lisp/eshell/em-cmpl.el +++ b/lisp/eshell/em-cmpl.el @@ -76,7 +76,7 @@ (eval-when-compile (require 'cl-lib)) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-cmpl nil "This module provides a programmable completion function bound to @@ -518,9 +518,4 @@ to writing a completion function." (define-obsolete-function-alias 'eshell-pcomplete #'completion-at-point "27.1") (provide 'em-cmpl) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-cmpl.el ends here diff --git a/lisp/eshell/em-dirs.el b/lisp/eshell/em-dirs.el index 07063afc286..a3d1a349540 100644 --- a/lisp/eshell/em-dirs.el +++ b/lisp/eshell/em-dirs.el @@ -47,7 +47,7 @@ (require 'ring) (require 'esh-opt) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-dirs nil "Directory navigation involves changing directories, examining the @@ -599,9 +599,4 @@ in the minibuffer: 'no-message)))))))) (provide 'em-dirs) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-dirs.el ends here diff --git a/lisp/eshell/em-elecslash.el b/lisp/eshell/em-elecslash.el index 93eadcfe1ff..60f2c6e4039 100644 --- a/lisp/eshell/em-elecslash.el +++ b/lisp/eshell/em-elecslash.el @@ -32,7 +32,7 @@ (require 'esh-mode) ;; This makes us an option when customizing `eshell-modules-list'. -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-elecslash nil "Electric forward slash in remote Eshells. @@ -108,9 +108,4 @@ insertion." (insert "/"))))) (provide 'em-elecslash) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; esh-elecslash.el ends here diff --git a/lisp/eshell/em-extpipe.el b/lisp/eshell/em-extpipe.el index 057eead9297..4c92653a3a3 100644 --- a/lisp/eshell/em-extpipe.el +++ b/lisp/eshell/em-extpipe.el @@ -36,7 +36,7 @@ (eval-when-compile (require 'files-x)) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-extpipe nil "Native shell pipelines. diff --git a/lisp/eshell/em-glob.el b/lisp/eshell/em-glob.el index 89a40151d00..36e4f90aed2 100644 --- a/lisp/eshell/em-glob.el +++ b/lisp/eshell/em-glob.el @@ -53,7 +53,7 @@ (require 'esh-module) (require 'esh-util) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-glob nil "This module provides extended globbing syntax, similar what is used @@ -417,9 +417,4 @@ directories and files." (eshell-glob-entries rdir globs only-dirs))))) (provide 'em-glob) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-glob.el ends here diff --git a/lisp/eshell/em-hist.el b/lisp/eshell/em-hist.el index 2749749bb93..8865cc745a3 100644 --- a/lisp/eshell/em-hist.el +++ b/lisp/eshell/em-hist.el @@ -60,7 +60,7 @@ (require 'esh-opt) (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-hist nil "This module provides command history management." @@ -1067,9 +1067,4 @@ If N is negative, search backwards for the -Nth previous match." (remove-hook 'kill-emacs-hook 'eshell-save-some-history)) (provide 'em-hist) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-hist.el ends here diff --git a/lisp/eshell/em-ls.el b/lisp/eshell/em-ls.el index 62ad7ff72a1..82d4b01393f 100644 --- a/lisp/eshell/em-ls.el +++ b/lisp/eshell/em-ls.el @@ -32,7 +32,7 @@ (require 'esh-proc) (require 'esh-cmd) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-ls nil "This module implements the \"ls\" utility fully in Lisp. @@ -956,9 +956,4 @@ to use, and each member of which is the width of that column (eshell-ls-disable-in-dired)) (provide 'em-ls) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-ls.el ends here diff --git a/lisp/eshell/em-pred.el b/lisp/eshell/em-pred.el index c3997dc72c3..0be262641ea 100644 --- a/lisp/eshell/em-pred.el +++ b/lisp/eshell/em-pred.el @@ -48,7 +48,7 @@ (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-pred nil "This module allows for predicates to be applied to globbing @@ -576,9 +576,4 @@ If INVERT-P is non-nil, include only members not matching a regexp." lst)))) (provide 'em-pred) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-pred.el ends here diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index 3662c1fa895..b6556d29544 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -29,7 +29,7 @@ (require 'esh-mode) (require 'text-property-search) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-prompt nil "This module provides command prompts, and navigation between them, @@ -231,9 +231,4 @@ first (see `move-beginning-of-line' for more information)." (move-beginning-of-line arg))) (provide 'em-prompt) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-prompt.el ends here diff --git a/lisp/eshell/em-rebind.el b/lisp/eshell/em-rebind.el index c6ee1a329b6..aad65f66f41 100644 --- a/lisp/eshell/em-rebind.el +++ b/lisp/eshell/em-rebind.el @@ -25,7 +25,7 @@ (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-rebind nil "This module allows for special keybindings that only take effect @@ -250,9 +250,4 @@ input." (eshell-delete-backward-char (- arg))))) (provide 'em-rebind) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-rebind.el ends here diff --git a/lisp/eshell/em-script.el b/lisp/eshell/em-script.el index 066063c4cc2..254a11ea114 100644 --- a/lisp/eshell/em-script.el +++ b/lisp/eshell/em-script.el @@ -25,7 +25,7 @@ (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-script nil "This module allows for the execution of files containing Eshell @@ -115,9 +115,4 @@ Comments begin with `#'." (put 'eshell/. 'eshell-no-numeric-conversions t) (provide 'em-script) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-script.el ends here diff --git a/lisp/eshell/em-smart.el b/lisp/eshell/em-smart.el index 91fe02e5545..670b956476d 100644 --- a/lisp/eshell/em-smart.el +++ b/lisp/eshell/em-smart.el @@ -70,7 +70,7 @@ (require 'esh-mode) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-smart nil "This module combines the facility of normal, modern shells with @@ -303,9 +303,4 @@ and the end of the buffer are still visible." (remove-hook 'window-configuration-change-hook #'eshell-smart-scroll)) (provide 'em-smart) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-smart.el ends here diff --git a/lisp/eshell/em-term.el b/lisp/eshell/em-term.el index 8f29e2d8509..2f8c06a0baa 100644 --- a/lisp/eshell/em-term.el +++ b/lisp/eshell/em-term.el @@ -36,7 +36,7 @@ (require 'esh-ext) (require 'term) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-term nil "This module causes visual commands (e.g., `vi') to be executed by @@ -330,9 +330,4 @@ the buffer." ; (use-local-map term-old-mode-map)) (provide 'em-term) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-term.el ends here diff --git a/lisp/eshell/em-tramp.el b/lisp/eshell/em-tramp.el index efb37225651..cfc8a47327e 100644 --- a/lisp/eshell/em-tramp.el +++ b/lisp/eshell/em-tramp.el @@ -35,7 +35,7 @@ ;; There are no items in this custom group, but eshell modules (ab)use ;; custom groups. -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-tramp nil "This module defines commands that use Tramp in a way that is @@ -152,9 +152,4 @@ Execute a COMMAND as the superuser or another USER.") (put 'eshell/doas 'eshell-no-numeric-conversions t) (provide 'em-tramp) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-tramp.el ends here diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el index 855efa26033..7f976d22681 100644 --- a/lisp/eshell/em-unix.el +++ b/lisp/eshell/em-unix.el @@ -38,7 +38,7 @@ (require 'esh-mode) (require 'pcomplete) -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-unix nil "This module defines many of the more common UNIX utilities as @@ -1096,9 +1096,4 @@ Show wall-clock time elapsed during execution of COMMAND.") (define-obsolete-function-alias 'eshell-diff-quit #'ignore "30.1") (provide 'em-unix) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-unix.el ends here diff --git a/lisp/eshell/em-xtra.el b/lisp/eshell/em-xtra.el index a62073e2183..0a032395fd3 100644 --- a/lisp/eshell/em-xtra.el +++ b/lisp/eshell/em-xtra.el @@ -28,7 +28,7 @@ ;; There are no items in this custom group, but eshell modules (ab)use ;; custom groups. -;;;###autoload +;;;###esh-module-autoload (progn (defgroup eshell-xtra nil "This module defines some extra alias functions which are entirely @@ -85,9 +85,4 @@ naturally accessible within Emacs." (defalias 'eshell/gf #'find-grep-dired) (provide 'em-xtra) - -;; Local Variables: -;; generated-autoload-file: "esh-groups.el" -;; End: - ;;; em-xtra.el ends here diff --git a/lisp/eshell/esh-module.el b/lisp/eshell/esh-module.el index 88221638e16..db808f8597a 100644 --- a/lisp/eshell/esh-module.el +++ b/lisp/eshell/esh-module.el @@ -38,7 +38,7 @@ customizing the variable `eshell-modules-list'." ;; `eshell-modules-list'. We use "(progn (defgroup ..." in each file ;; to force the autoloader into including the entire defgroup, rather ;; than an abbreviated version. -(load "esh-groups" nil 'nomessage) +(load "esh-module-loaddefs" nil 'nomessage) ;;; User Variables: commit 6c2f21e4a6ff65cc1697cb8a6ba0e1ef1a52ae1c Author: Jim Porter Date: Fri Mar 10 19:02:26 2023 -0800 Consolidate Eshell module loading/unloading code This also adds the ability to suppress module loading/unloading messages, which will be necessary to support running Eshell scripts as batch scripts. * lisp/eshell/esh-mode.el (eshell-mode): Move module loading/initialization to... * lisp/eshell/esh-module.el (eshell-load-modules) (eshell-initialize-modules): ... here. (eshell-module-loading-messages): New option. (eshell-module--feature-name): Improve docstring. (eshell-unload-modules): Display a real warning if unable to unload a module. * test/lisp/eshell/eshell-tests-helpers.el (with-temp-eshell) (eshell-command-result-equal): * test/lisp/eshell/eshell-tests-unload.el (load-eshell): Silence Eshell loading messages. diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index c4ae55afe3f..7290c29b008 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -372,36 +372,15 @@ and the hook `eshell-exit-hook'." ;; strong R2L character. (setq bidi-paragraph-direction 'left-to-right) - ;; load extension modules into memory. This will cause any global - ;; variables they define to be visible, since some of the core - ;; modules sometimes take advantage of their functionality if used. - (dolist (module eshell-modules-list) - (let ((module-fullname (symbol-name module)) - module-shortname) - (if (string-match "^eshell-\\(.*\\)" module-fullname) - (setq module-shortname - (concat "em-" (match-string 1 module-fullname)))) - (unless module-shortname - (error "Invalid Eshell module name: %s" module-fullname)) - (unless (featurep (intern module-shortname)) - (condition-case nil - (load module-shortname) - (error (lwarn 'eshell :error - "Unable to load module `%s' (defined in `eshell-modules-list')" - module-fullname)))))) + ;; Load extension modules into memory. + (eshell-load-modules eshell-modules-list) (unless (file-exists-p eshell-directory-name) (eshell-make-private-directory eshell-directory-name t)) - ;; Load core Eshell modules, then extension modules, for this session. - (dolist (module (append (eshell-subgroups 'eshell) eshell-modules-list)) - (let ((load-hook (intern-soft (format "%s-load-hook" module))) - (initfunc (intern-soft (format "%s-initialize" module)))) - (when (and load-hook (boundp load-hook)) - (if (memq initfunc (symbol-value load-hook)) (setq initfunc nil)) - (run-hooks load-hook)) - ;; So we don't need the -initialize functions on the hooks (bug#5375). - (and initfunc (fboundp initfunc) (funcall initfunc)))) + ;; Initialize core Eshell modules, then extension modules, for this session. + (eshell-initialize-modules (eshell-subgroups 'eshell)) + (eshell-initialize-modules eshell-modules-list) (if eshell-send-direct-to-subprocesses (add-hook 'pre-command-hook #'eshell-intercept-commands t t)) diff --git a/lisp/eshell/esh-module.el b/lisp/eshell/esh-module.el index fbd5ae4b9b8..88221638e16 100644 --- a/lisp/eshell/esh-module.el +++ b/lisp/eshell/esh-module.el @@ -49,6 +49,12 @@ customizing the variable `eshell-modules-list'." :group 'eshell-module) (make-obsolete-variable 'eshell-module-unload-hook nil "30.1") +(defcustom eshell-module-loading-messages t + "If non-nil, display messages when loading/unloading Eshell modules." + :type 'boolean + :group 'eshell-module + :version "30.1") + (defcustom eshell-modules-list '(eshell-alias eshell-banner @@ -87,7 +93,9 @@ Changes will only take effect in future Eshell buffers." ;;; Code: (defsubst eshell-module--feature-name (module &optional kind) - "Get the feature name for the specified Eshell MODULE." + "Get the feature name for the specified Eshell MODULE. +KIND can be either `core' for a core module or `extension' for an +extension module; if nil, KIND defaults to `extension'." (let ((module-name (symbol-name module)) (prefix (cond ((eq kind 'core) "esh-") ((memq kind '(extension nil)) "em-") @@ -102,17 +110,57 @@ The MODULE should be a symbol corresponding to that module's customization group. Example: `eshell-cmpl' for that module." (memq module eshell-modules-list)) -(defun eshell-unload-modules (modules &optional kind) - "Try to unload the specified Eshell MODULES." +(defun eshell-load-modules (modules) + "Load Eshell MODULES into memory. +This will cause any global variables they define to be visible so +that other modules can take advantage of their functionality if +desired." + (let ((verbose eshell-module-loading-messages)) + (dolist (module modules) + (let ((module-feature-name (eshell-module--feature-name module))) + (unless (featurep (intern module-feature-name)) + (when verbose (message "Loading %s..." module)) + (condition-case-unless-debug nil + (progn + (load module-feature-name nil t) + (when verbose (message "Loading %s...done" module))) + (error (when verbose (message "Loading %s...failed" module)) + (lwarn 'eshell :error + "Unable to load Eshell module `%s'" + module)))))))) + +(defun eshell-initialize-modules (modules) + "Initialize Eshell MODULES. +This calls `MODULE-load-hook' and `MODULE-initialize' for each +MODULE, if they're defined." (dolist (module modules) - (let ((module-feature (intern (eshell-module--feature-name module kind)))) - (when (featurep module-feature) - (message "Unloading %s..." (symbol-name module)) - (condition-case-unless-debug _ - (progn - (unload-feature module-feature) - (message "Unloading %s...done" (symbol-name module))) - (error (message "Unloading %s...failed" (symbol-name module)))))))) + (let ((load-hook (intern-soft (format "%s-load-hook" module))) + (initfunc (intern-soft (format "%s-initialize" module)))) + (when (and load-hook (boundp load-hook)) + (if (memq initfunc (symbol-value load-hook)) (setq initfunc nil)) + (run-hooks load-hook)) + ;; So we don't need the -initialize functions on the hooks (bug#5375). + (and initfunc (fboundp initfunc) (funcall initfunc))))) + +(defun eshell-unload-modules (modules &optional kind) + "Try to unload the specified Eshell MODULES. +KIND can be either `core' for core modules or `extension' for +extension modules; if nil, KIND defaults to `extension'." + ;; We're about to unload this module, but we need to remember whether + ;; to print messages. + (let ((verbose eshell-module-loading-messages)) + (dolist (module modules) + (let ((module-feature (intern (eshell-module--feature-name module kind)))) + (when (featurep module-feature) + (when verbose (message "Unloading %s..." module)) + (condition-case-unless-debug nil + (progn + (unload-feature module-feature) + (when verbose (message "Unloading %s...done" module))) + (error (when verbose (message "Unloading %s...failed" module)) + (lwarn 'eshell :error + "Unable to unload Eshell module `%s'" + module)))))))) (defun eshell-unload-extension-modules () "Try to unload all currently-loaded Eshell extension modules." diff --git a/test/lisp/eshell/eshell-tests-helpers.el b/test/lisp/eshell/eshell-tests-helpers.el index 652146fefcc..3f1c55f420d 100644 --- a/test/lisp/eshell/eshell-tests-helpers.el +++ b/test/lisp/eshell/eshell-tests-helpers.el @@ -63,6 +63,7 @@ beginning of the test file." (eshell-debug-command (cons 'process eshell-debug-command)) (eshell-history-file-name nil) (eshell-last-dir-ring-file-name nil) + (eshell-module-loading-messages nil) (eshell-buffer (eshell t))) (unwind-protect (with-current-buffer eshell-buffer @@ -183,10 +184,11 @@ inserting the command." (defun eshell-command-result-equal (command result) "Execute COMMAND non-interactively and compare it to RESULT." (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") - (should (eshell-command-result--equal - command - (eshell-test-command-result command) - result)))) + (let ((eshell-module-loading-messages nil)) + (should (eshell-command-result--equal + command + (eshell-test-command-result command) + result))))) (provide 'eshell-tests-helpers) diff --git a/test/lisp/eshell/eshell-tests-unload.el b/test/lisp/eshell/eshell-tests-unload.el index bf8291ba47a..479090e8f8c 100644 --- a/test/lisp/eshell/eshell-tests-unload.el +++ b/test/lisp/eshell/eshell-tests-unload.el @@ -33,6 +33,7 @@ (defvar eshell-history-file-name) (defvar eshell-last-dir-ring-file-name) (defvar eshell-modules-list) +(defvar eshell-module-loading-messages) (declare-function eshell-module--feature-name "esh-module" (module &optional kind)) @@ -51,6 +52,7 @@ See `unload-eshell'.") (process-environment (cons "HISTFILE" process-environment)) (eshell-history-file-name nil) (eshell-last-dir-ring-file-name nil) + (eshell-module-loading-messages nil) (eshell-buffer (eshell t))) (let (kill-buffer-query-functions) (kill-buffer eshell-buffer)))))) commit 98149ad31ea2cfd5a82f95443affd665c9da667b Author: Eli Zaretskii Date: Wed May 29 21:43:57 2024 +0300 ; Improve documentation of new Imenu features * doc/emacs/programs.texi (Imenu): Update documentation of 'imenu-flatten'. * etc/NEWS: Fix wording of 'imenu-flatten's entry. * lisp/imenu.el (imenu-flatten): Fix doc string and value descriptions. (Bug#70846) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index 8ab5033795d..b944d24d91f 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -331,20 +331,43 @@ a negative argument moves back to the start of a sentence. @cindex index of buffer definitions @cindex buffer definitions index - The Imenu facility offers a way to find the major definitions in -a file by name. It is also useful in text formatter major modes, -where it treats each chapter, section, etc., as a definition. -(@xref{Xref}, for a more powerful feature that handles multiple files -together.) + The Imenu facility offers a way to find the major definitions in a +file by name. It is useful both in programming-language major modes, +where the definitions are variables, functions, etc., and in text +formatter major modes, where it treats each chapter, section, etc., as a +definition. (@xref{Xref}, for a more powerful feature that handles +multiple files together.) @findex imenu @vindex imenu-flatten If you type @kbd{M-g i} (@code{imenu}), it reads the name of a definition using the minibuffer, then moves point to that definition. -You can use completion to specify the name; the command always -displays the whole list of valid names. If you set @code{imenu-flatten} -to a non-@code{nil} value, then instead of the nested menu -you can select a completion candidate from the flat list. +You can use completion to specify the name; the command displays the +list of matching valid names in the completions buffer. If the index is +hierarchical, then by default the completion candidates are also shown +hierarchically, as a nested list: first you need to choose a section, +then a subsection, etc., and finally the name of the definition. +However, if you set @code{imenu-flatten} to a non-@code{nil} value, then +instead of the nested menu you can select a completion candidate from a +flattened list of definitions. How the sections and subsections are +shown in the flattened list of completion candidates depends on the +value of @code{imenu-flatten}, which can be one of the following: + +@table @code +@vindex imenu-level-separator +@item prefix +This shows each candidate prefixed by names of its section, subsection, +subsubsection, etc., with each level separated from the next by the +string that is the value of @code{imenu-level-separator}, by default +@samp{:}. + +@item annotation +This shows the section names as annotations, following each definition +name. + +@item group +This shows the completion candidates grouped by their sections. +@end table @findex imenu-add-menubar-index Alternatively, you can bind the command @code{imenu} to a mouse diff --git a/etc/NEWS b/etc/NEWS index d8d55494128..9416ced5a0d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1059,8 +1059,8 @@ point is not in a comment or a string. It is by default bound to +++ *** New user option 'imenu-flatten'. -It defines whether to flatten the list of sections in an imenu -or show it nested. +It controls whether to flatten the list of sections in an imenu, and +how to display the sections in the flattened list. +++ *** The sort order of Imenu completions can now be customized. diff --git a/lisp/imenu.el b/lisp/imenu.el index 3aec32fa708..708a39a0f71 100644 --- a/lisp/imenu.el +++ b/lisp/imenu.el @@ -151,18 +151,22 @@ Used for flattening nested indexes with name concatenation." (defcustom imenu-flatten nil "Whether to flatten the list of sections in an imenu or show it nested. If nil, use nested indexes. -If `prefix', pop up the completion buffer with a flattened menu -where section names are used as a prefix. -If `annotation', use completion annotation as a suffix -to append section names after the index names. -If `group', split completions into groups. - -The string from `imenu-level-separator' is used to separate names of -nested levels while flattening nested indexes with name concatenation." - :type '(choice (const :tag "Nested" nil) - (const :tag "By prefix" prefix) - (const :tag "By annotation" annotation) - (const :tag "By group" group)) +If the value is `prefix', pop up the completion buffer with a +flattened menu where section names are prepended to completion +candidates as prefixes. +If the value is `annotation', annotate each completion candidate +with a suffix that is the section name to which it belongs. +If the value is `group', split completion candidates into groups +according to the sections. +Any other value is treated as `prefix'. + +The value of `imenu-level-separator', a string, is used to separate +names from different flattened levels, such as section names, from the +names of completion candidates." + :type '(choice (const :tag "Show nested list" nil) + (const :tag "Flat list with sections as prefix" prefix) + (const :tag "Flat list annotated with sections" annotation) + (const :tag "Flat list grouped by sections" group)) :version "30.1") (defcustom imenu-generic-skip-comments-and-strings t commit f469ab73a777c9930582af8b4fd22967f07808aa Author: Juri Linkov Date: Wed May 29 21:05:20 2024 +0300 * lisp/imenu.el (imenu-flatten): Add new choice 'group' (bug#70846). (imenu-flatten): For prefix use the implicit symbol 'prefix'. (imenu--completion-buffer): Use :group-function if imenu-flatten is 'group'. (imenu--flatten-index-alist): Handle the value 'group' of imenu-flatten in a way similar to the value 'annotation'. diff --git a/lisp/imenu.el b/lisp/imenu.el index 93d84106ec1..3aec32fa708 100644 --- a/lisp/imenu.el +++ b/lisp/imenu.el @@ -151,15 +151,18 @@ Used for flattening nested indexes with name concatenation." (defcustom imenu-flatten nil "Whether to flatten the list of sections in an imenu or show it nested. If nil, use nested indexes. -If t, pop up the completion buffer with a flattened menu. +If `prefix', pop up the completion buffer with a flattened menu +where section names are used as a prefix. If `annotation', use completion annotation as a suffix to append section names after the index names. +If `group', split completions into groups. The string from `imenu-level-separator' is used to separate names of nested levels while flattening nested indexes with name concatenation." :type '(choice (const :tag "Nested" nil) - (const :tag "By prefix" t) - (const :tag "By suffix" annotation)) + (const :tag "By prefix" prefix) + (const :tag "By annotation" annotation) + (const :tag "By group" group)) :version "30.1") (defcustom imenu-generic-skip-comments-and-strings t @@ -758,7 +761,13 @@ Return one of the entries in index-alist or nil." ,@(when (eq imenu-flatten 'annotation) `(:annotation-function ,(lambda (s) (get-text-property - 0 'imenu-section s)))))) + 0 'imenu-section s)))) + ,@(when (eq imenu-flatten 'group) + `(:group-function + ,(lambda (s transform) + (if transform s + (get-text-property + 0 'imenu-section s))))))) (unless imenu-eager-completion-buffer (minibuffer-completion-help))) (setq name (completing-read prompt @@ -801,15 +810,20 @@ Returns t for rescan and otherwise an element or subelement of INDEX-ALIST." (new-prefix (and concat-names (if prefix (concat prefix imenu-level-separator name) - (if (eq imenu-flatten 'annotation) - (propertize name 'imenu-choice item) - name))))) + name)))) (cond ((not (imenu--subalist-p item)) - (list (cons (if (and (eq imenu-flatten 'annotation) prefix) - (propertize name 'imenu-section - (format " (%s)" prefix)) - new-prefix) + (list (cons (pcase imenu-flatten + ('annotation + (if prefix + (propertize name + 'imenu-section (format " (%s)" prefix) + 'imenu-choice item) + (propertize new-prefix 'imenu-choice item))) + ('group (propertize name + 'imenu-section (or prefix "*") + 'imenu-choice item)) + (_ new-prefix)) pos))) (t (imenu--flatten-index-alist pos concat-names new-prefix))))) commit 17c23a46c3d834e7dc9f153e86a51242b3867b74 Author: Michael Albinus Date: Wed May 29 18:29:52 2024 +0200 Add Tramp distrobox method * doc/misc/tramp.texi (Inline methods) : Add. * etc/NEWS: Mention Tramp distrobox method. * lisp/net/tramp-container.el (tramp-distrobox-program): New defcustom. (tramp-distrobox-method): New defconst. (tramp-distrobox--completion-function) (tramp-enable-distrobox-method): New defuns. (Bug#71200) (tramp-docker-method, tramp-dockercp-method) (tramp-podman-method, tramp-podmancp-method) (tramp-kubernetes-method, tramp-flatpak-method) (tramp-apptainer-method, tramp-nspawn-method): Adapt docstring. * test/lisp/net/tramp-tests.el (tramp--test-box-p): Rename from `tramp--test-toolbox-p'. Add distrobox. Adapt callees. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index ca6703998c4..74081767caa 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -1005,15 +1005,19 @@ in a pod is used. This method does not support user names. @item @option{toolbox} +@item @option{distrobox} @cindex method @option{toolbox} @cindex @option{toolbox} method +@cindex method @option{distrobox} +@cindex @option{distrobox} method -Integration of Toolbox system containers. The host name may be either -a container's name or ID, as returned by @samp{toolbox list -c}. -Without a host name, the default Toolbox container for the host will -be used. +Integration of Toolbox or Distrobox system containers, respectively. +The host name may be either a container's name or ID, as returned by +@samp{toolbox list -c} or @samp{distrobox list}. Without a host name, +the default Toolbox container for the host will be used. There is no +such default for Distrobox. -This is an optional method, @pxref{Optional methods}. It does not +These are optional methods, @pxref{Optional methods}. They do not support user names. @item @option{flatpak} diff --git a/etc/NEWS b/etc/NEWS index 6cdf6afbcea..d8d55494128 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1079,7 +1079,7 @@ mode line. 'header' will display in the header line; +++ *** New user option 'which-func-update-delay'. This replaces the user option 'idle-update-delay', which was previously -used to control the delay before `which-function-mode` updated its +used to control the delay before 'which-function-mode' updated its display. The user option 'idle-update-delay', which was only used by Which Function mode, is now obsolete. @@ -1108,10 +1108,11 @@ These are the external methods counterparts of "docker" and "podman". +++ *** New optional connection methods for containers. -Tere are new optional connection methods "toolbox", "flatpak", -"apptainer" and "nspawn". They allow accessing system containers -provided by Toolbox, sandboxes provided by Flatpak, instances managed by -Apptainer, or accessing systemd-based light-weight containers.. +There are new optional connection methods "toolbox", "distrobox", +"flatpak", "apptainer" and "nspawn". They allow accessing system +containers provided by Toolbox or Distrobox, sandboxes provided by +Flatpak, instances managed by Apptainer, or accessing systemd-based +light-weight containers.. +++ *** Connection method "kubernetes" supports now optional container name. @@ -2080,7 +2081,7 @@ and 'closure' is the common parent type of 'interpreted-function' and 'byte-code-function'. Those new types come with the associated new predicates 'closurep' and -`interpreted-function-p' as well as a new constructor +'interpreted-function-p' as well as a new constructor 'make-interpreted-closure'. ** New function 'help-fns-function-name'. diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el index 2886e25d16b..cc1d9f457cc 100644 --- a/lisp/net/tramp-container.el +++ b/lisp/net/tramp-container.el @@ -76,6 +76,17 @@ ;; ;; ;; +;; Open a file on an existing Distrobox container: +;; +;; C-x C-f /distrobox:CONTAINER:/path/to/file +;; +;; Where: +;; CONTAINER is the container to connect to. +;; +;; If the container is not running, it is started. +;; +;; +;; ;; Open a file on a running Flatpak sandbox: ;; ;; C-x C-f /flatpak:SANDBOX:/path/to/file @@ -153,6 +164,14 @@ If it is nil, the default context will be used." :type '(choice (const "toolbox") (string))) +;;;###tramp-autoload +(defcustom tramp-distrobox-program "distrobox" + "Name of the Distrobxx client program." + :group 'tramp + :version "30.1" + :type '(choice (const "distrobox") + (string))) + ;;;###tramp-autoload (defcustom tramp-flatpak-program "flatpak" "Name of the Flatpak client program." @@ -178,41 +197,45 @@ If it is nil, the default context will be used." ;;;###tramp-autoload (defconst tramp-docker-method "docker" - "Tramp method name to use to connect to Docker containers.") + "Tramp method name to connect to Docker containers.") ;;;###tramp-autoload (defconst tramp-dockercp-method "dockercp" - "Tramp method name to use to connect to Docker containers. + "Tramp method name to connect to Docker containers. This is for out-of-band connections.") ;;;###tramp-autoload (defconst tramp-podman-method "podman" - "Tramp method name to use to connect to Podman containers.") + "Tramp method name to connect to Podman containers.") ;;;###tramp-autoload (defconst tramp-podmancp-method "podmancp" - "Tramp method name to use to connect to Podman containers. + "Tramp method name to connect to Podman containers. This is for out-of-band connections.") ;;;###tramp-autoload (defconst tramp-kubernetes-method "kubernetes" - "Tramp method name to use to connect to Kubernetes containers.") + "Tramp method name to connect to Kubernetes containers.") ;;;###tramp-autoload (defconst tramp-toolbox-method "toolbox" - "Tramp method name to use to connect to Toolbox containers.") + "Tramp method name to connect to Toolbox containers.") + +;;;###tramp-autoload +(defconst tramp-distrobox-method "distrobox" + "Tramp method name to connect to Distrobox containers.") ;;;###tramp-autoload (defconst tramp-flatpak-method "flatpak" - "Tramp method name to use to connect to Flatpak sandboxes.") + "Tramp method name to connect to Flatpak sandboxes.") ;;;###tramp-autoload (defconst tramp-apptainer-method "apptainer" - "Tramp method name to use to connect to Apptainer instances.") + "Tramp method name to connect to Apptainer instances.") ;;;###tramp-autoload (defconst tramp-nspawn-method "nspawn" - "Tramp method name to use to connect to systemd-nspawn containers.") + "Tramp method name to connect to systemd-nspawn containers.") ;;;###tramp-autoload (defmacro tramp-skeleton-completion-function (method &rest body) @@ -381,6 +404,7 @@ see its function help for a description of the format." (when-let ((raw-list (shell-command-to-string (concat program " list -c"))) ;; Ignore header line. (lines (cdr (split-string raw-list "\n" 'omit))) + ;; We do not show container IDs. (names (tramp-compat-seq-keep (lambda (line) (when (string-match @@ -391,6 +415,28 @@ see its function help for a description of the format." lines))) (mapcar (lambda (name) (list nil name)) names)))) +;;;###tramp-autoload +(defun tramp-distrobox--completion-function (method) + "List Distrobox containers available for connection. + +This function is used by `tramp-set-completion-function', please +see its function help for a description of the format." + (tramp-skeleton-completion-function method + (when-let ((raw-list (shell-command-to-string (concat program " list"))) + ;; Ignore header line. + (lines (cdr (split-string raw-list "\n" 'omit))) + ;; We do not show container IDs. + (names (tramp-compat-seq-keep + (lambda (line) + (when (string-match + (rx bol (1+ (not space)) + (1+ space) "|" (1+ space) + (group (1+ (not space))) space) + line) + (match-string 1 line))) + lines))) + (mapcar (lambda (name) (list nil name)) names)))) + ;;;###tramp-autoload (defun tramp-flatpak--completion-function (method) "List Flatpak sandboxes available for connection. @@ -595,6 +641,26 @@ see its function help for a description of the format." tramp-toolbox-method `((tramp-toolbox--completion-function ,tramp-toolbox-method)))) +;;;###tramp-autoload +(defun tramp-enable-distrobox-method () + "Enable connection to Distrobox containers." + (add-to-list 'tramp-methods + `(,tramp-distrobox-method + (tramp-login-program ,tramp-distrobox-program) + (tramp-login-args (("enter") + ("-n" "%h") + ("--" "%l"))) + ;(tramp-direct-async (,tramp-default-remote-shell "-c")) + (tramp-remote-shell ,tramp-default-remote-shell) + (tramp-remote-shell-login ("-l")) + (tramp-remote-shell-args ("-c")))) + + (add-to-list 'tramp-completion-multi-hop-methods tramp-distrobox-method) + + (tramp-set-completion-function + tramp-distrobox-method + `((tramp-distrobox--completion-function ,tramp-distrobox-method)))) + ;;;###tramp-autoload (defun tramp-enable-flatpak-method () "Enable connection to Flatpak sandboxes." diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 2c61efb04d8..516ba53c0f3 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -7071,6 +7071,13 @@ This is used in tests which we don't want to tag (not (and (tramp--test-adb-p) (string-match-p (rx multibyte) default-directory))))) +(defun tramp--test-box-p () + "Check, whether the toolbox or distrobox method is used. +This does not support `tramp-test45-asynchronous-requests'." + (string-match-p + (rx bol (| "toolbox" "distrobox") eol) + (file-remote-p ert-remote-temporary-file-directory 'method))) + (defun tramp--test-container-p () "Check, whether a container method is used. This does not support some special file names." @@ -7221,12 +7228,6 @@ This does not support special file names." (string-equal "telnet" (file-remote-p ert-remote-temporary-file-directory 'method))) -(defun tramp--test-toolbox-p () - "Check, whether the toolbox method is used. -This does not support `tramp-test45-asynchronous-requests'." - (string-equal - "toolbox" (file-remote-p ert-remote-temporary-file-directory 'method))) - (defun tramp--test-windows-nt-p () "Check, whether the locale host runs MS Windows." (eq system-type 'windows-nt)) @@ -7715,7 +7716,7 @@ process sentinels. They shall not disturb each other." (skip-unless (not (tramp--test-container-p))) (skip-unless (not (tramp--test-sshfs-p))) (skip-unless (not (tramp--test-telnet-p))) - (skip-unless (not (tramp--test-toolbox-p))) + (skip-unless (not (tramp--test-box-p))) (skip-unless (not (tramp--test-windows-nt-p))) (with-timeout commit be9de8082e37aed4938aaffab45f4adb84c08569 Merge: 62a3d72bee3 fd61cf3976c Author: Eli Zaretskii Date: Wed May 29 11:50:23 2024 -0400 Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs commit 62a3d72bee3f7a0b92b48ac17ddac2528e8ccdb7 Author: Eli Zaretskii Date: Wed May 29 11:48:27 2024 -0400 ; Update ldefs-boot.el diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el index 8f9b11e3df1..cc791fa8453 100644 --- a/lisp/ldefs-boot.el +++ b/lisp/ldefs-boot.el @@ -1108,7 +1108,7 @@ archive. \\{archive-mode-map} (fn &optional FORCE)") -(register-definition-prefixes "arc-mode" '("arc")) +(register-definition-prefixes "arc-mode" '("arc" "tar-archive-from-tar")) ;;; Generated autoloads from cedet/srecode/args.el @@ -2939,7 +2939,7 @@ Optional argument ARG is passed as second argument ARG to and corresponding effects. (fn &optional ARG)") -(register-definition-prefixes "bytecomp" '("batch-byte-compile-file" "byte" "displaying-byte-compile-warnings" "emacs-lisp-" "no-byte-compile")) +(register-definition-prefixes "bytecomp" '("batch-byte-compile-file" "byte" "compilation-safety" "displaying-byte-compile-warnings" "emacs-lisp-" "no-byte-compile")) ;;; Generated autoloads from cedet/semantic/bovine/c.el @@ -4839,14 +4839,14 @@ With prefix arg ECHO, echo output in process buffer. If NO-DISPLAY is non-nil, do not show the output buffer. -(fn COMMAND OUTPUT-BUFFER ECHO &optional NO-DISPLAY)" t) +(fn COMMAND OUTPUT-BUFFER ECHO &optional NO-DISPLAY)" '(comint-mode)) (autoload 'comint-redirect-send-command-to-process "comint" "\ Send COMMAND to PROCESS, with output to OUTPUT-BUFFER. With prefix arg, echo output in process buffer. If NO-DISPLAY is non-nil, do not show the output buffer. -(fn COMMAND OUTPUT-BUFFER PROCESS ECHO &optional NO-DISPLAY)" t) +(fn COMMAND OUTPUT-BUFFER PROCESS ECHO &optional NO-DISPLAY)" '(comint-mode)) (autoload 'comint-redirect-results-list "comint" "\ Send COMMAND to current process. Return a list of expressions in the output which match REGEXP. @@ -4931,11 +4931,12 @@ variable \"NATIVE_DISABLED\" is set, only byte compile.") (autoload 'comp-function-type-spec "comp-common" "\ Return the type specifier of FUNCTION. -This function returns a cons cell whose car is the function -specifier, and cdr is a symbol, either `inferred' or `know'. -If the symbol is `inferred', the type specifier is automatically -inferred from the code itself by the native compiler; if it is -`know', the type specifier comes from `comp-known-type-specifiers'. +This function returns a cons cell whose car is the function specifier, +and cdr is a symbol, either `inferred' or `declared'. If the symbol is +`inferred', the type specifier is automatically inferred from the code +itself by the native compiler; if it is `declared', the type specifier +comes from `comp-primitive-type-specifiers' or the function type declaration +itself. (fn FUNCTION)") (register-definition-prefixes "comp-common" '("comp-" "native-comp-")) @@ -5313,11 +5314,12 @@ Show in-buffer completion suggestions in a preview as you type. This mode automatically shows and updates the completion preview according to the text around point. -\\When the preview is visible, \\[completion-preview-insert] -accepts the completion suggestion, +\\When the preview is visible, \\[completion-preview-insert] accepts the +completion suggestion, \\[completion-preview-complete] completes up to +the longest common prefix of all completion candidates, \\[completion-preview-next-candidate] cycles forward to the next -completion suggestion, and \\[completion-preview-prev-candidate] -cycles backward. +completion suggestion, and \\[completion-preview-prev-candidate] cycles +backward. This is a minor mode. If called interactively, toggle the `Completion-Preview mode' mode. If the prefix argument is positive, @@ -5362,7 +5364,7 @@ Completion-Preview mode. minor mode is used in. (fn &optional ARG)" t) -(defvar global-completion-preview-modes '((not minibuffer-mode special-mode) t) "\ +(defvar global-completion-preview-modes '((not archive-mode calc-mode compilation-mode diff-mode dired-mode image-mode minibuffer-mode minibuffer-inactive-mode org-agenda-mode special-mode wdired-mode) t) "\ Which major modes `completion-preview-mode' is switched on in. This variable can be either t (all major modes), nil (no major modes), or a list of modes and (not modes) to switch use this minor mode or @@ -11974,9 +11976,10 @@ this are the `default' and `header-line' faces: they will both be scaled even if they have an explicit `:height' setting. See also the related command `global-text-scale-adjust'. Unlike -that command, which scales the font size with a increment, -`text-scale-adjust' scales the font size with a factor, -`text-scale-mode-step'. With a small `text-scale-mode-step' +that command, which scales the font size with a increment (and can +also optionally resize frames to keep the same number of lines and +characters per line), `text-scale-adjust' scales the font size with +a factor, `text-scale-mode-step'. With a small `text-scale-mode-step' factor, the two commands behave similarly. (fn INC)" t) @@ -15615,6 +15618,8 @@ See the `help-enable-symbol-autoload' variable for special handling of autoloaded functions. (fn FUNCTION)" t) +(autoload 'help-find-source "help-fns" "\ +Switch to a buffer visiting the source of what is being described in *Help*." t) (autoload 'describe-command "help-fns" "\ Display the full documentation of COMMAND (a symbol). When called from Lisp, COMMAND may also be a function object. @@ -18108,7 +18113,10 @@ A non-image major mode displays an image file as text.") (defvar imenu-sort-function nil "\ The function to use for sorting the index mouse-menu. -Affects only the mouse index menu. +Affects only the mouse index menu. If you want to change +the sorting order of completions, you can customize +the option `completion-category-overrides' and set +`display-sort-function' for the category `imenu'. Set this to nil if you don't want any sorting (faster). The items in the menu are then presented in the order they were found @@ -19417,6 +19425,9 @@ The symbol's function definition becomes the keyboard macro string. Such a \"function\" cannot be called from Lisp, but it is a valid editor command. (fn SYMBOL)" t) +(defalias 'kmacro-menu #'list-keyboard-macros) +(autoload 'list-keyboard-macros "kmacro" "\ +List the keyboard macros." t) (register-definition-prefixes "kmacro" '("kmacro-")) @@ -19550,11 +19561,6 @@ A major mode to edit GNU ld script files. (register-definition-prefixes "ldap" '("ldap-")) - -;;; Generated autoloads from gnus/legacy-gnus-agent.el - -(register-definition-prefixes "legacy-gnus-agent" '("gnus-agent-")) - ;;; Generated autoloads from textmodes/less-css-mode.el @@ -22204,6 +22210,11 @@ Start newsticker treeview." t) (register-definition-prefixes "nnagent" '("nnagent-")) + +;;; Generated autoloads from gnus/nnatom.el + +(register-definition-prefixes "nnatom" '("nnatom-")) + ;;; Generated autoloads from gnus/nnbabyl.el @@ -22246,6 +22257,11 @@ symbol in the alist. (register-definition-prefixes "nneething" '("nneething-")) + +;;; Generated autoloads from gnus/nnfeed.el + +(register-definition-prefixes "nnfeed" '("nnfeed-")) + ;;; Generated autoloads from gnus/nnfolder.el @@ -23845,7 +23861,13 @@ with \"-q\"). Even if the value is nil, you can type \\[package-initialize] to make installed packages available at any time, or you can -call (package-activate-all) in your init-file.") +call (package-activate-all) in your init-file. + +Note that this variable must be set to a non-default value in +your early-init file, as the variable's value is used before +loading the regular init file. Therefore, if you customize it +via Customize, you should save your customized setting into +your `early-init-file'.") (custom-autoload 'package-enable-at-startup "package" t) (defcustom package-user-dir (locate-user-emacs-file "elpa") "\ Directory containing the user's Emacs Lisp packages. @@ -24780,6 +24802,12 @@ Global menu used by PCL-CVS.") (register-definition-prefixes "pcvs-util" '("cvs-")) + +;;; Generated autoloads from progmodes/peg.el + +(push (purecopy '(peg 1 0 1)) package--builtin-versions) +(register-definition-prefixes "peg" '("bob" "bol" "bos" "bow" "define-peg-rule" "eob" "eol" "eos" "eow" "fail" "null" "peg" "with-peg-rules")) + ;;; Generated autoloads from progmodes/perl-mode.el @@ -24964,8 +24992,8 @@ disabled. (fn &optional ARG)" t) (autoload 'pixel-scroll-precision-scroll-down-page "pixel-scroll" "\ Scroll the current window down by DELTA pixels. -Note that this function doesn't work if DELTA is larger than -the height of the current window. +Note that this function doesn't work if DELTA is larger than or +equal to the text height of the current window in pixels. (fn DELTA)") (autoload 'pixel-scroll-precision-scroll-up-page "pixel-scroll" "\ @@ -25700,7 +25728,7 @@ Open profile FILENAME. ;;; Generated autoloads from progmodes/project.el -(push (purecopy '(project 0 10 0)) package--builtin-versions) +(push (purecopy '(project 0 11 0)) package--builtin-versions) (autoload 'project-current "project" "\ Return the project instance in DIRECTORY, defaulting to `default-directory'. @@ -27042,6 +27070,7 @@ usually more efficient than that of a simplified version: (cdr parens)))) (fn STRINGS &optional PAREN)") +(function-put 'regexp-opt 'function-type '(function (list &optional t) string)) (function-put 'regexp-opt 'pure 't) (function-put 'regexp-opt 'side-effect-free 't) (autoload 'regexp-opt-depth "regexp-opt" "\ @@ -28031,7 +28060,7 @@ disabled. Major mode for editing Rust, powered by tree-sitter. (fn)" t) -(register-definition-prefixes "rust-ts-mode" '("rust-ts-mode-")) +(register-definition-prefixes "rust-ts-mode" '("rust-ts-")) ;;; Generated autoloads from emacs-lisp/rx.el @@ -32487,7 +32516,7 @@ If IGNORE-COMMENT-OR-STRING is non-nil comments and strings are treated as white space. (fn &optional IGNORE-COMMENT-OR-STRING)") -(register-definition-prefixes "thingatpt" '("beginning-of-thing" "define-thing-chars" "end-of-thing" "filename" "form-at-point" "in-string-p" "sentence-at-point" "thing-at-point-" "word-at-point")) +(register-definition-prefixes "thingatpt" '("beginning-of-thing" "bounds-of-thing-at-point-" "define-thing-chars" "end-of-thing" "filename" "for" "in-string-p" "sentence-at-point" "thing-at-point-" "word-at-point")) ;;; Generated autoloads from thread.el @@ -33270,6 +33299,12 @@ the output buffer or changing the window configuration. (defalias 'trace-function 'trace-function-foreground) (register-definition-prefixes "trace" '("inhibit-trace" "trace-" "untrace-")) + +;;; Generated autoloads from emacs-lisp/track-changes.el + +(push (purecopy '(track-changes 1 2)) package--builtin-versions) +(register-definition-prefixes "track-changes" '("track-changes-" "with--track-changes")) + ;;; Generated autoloads from net/tramp.el @@ -33325,6 +33360,11 @@ Discard Tramp from loading remote files." (interactive) (ignore-errors (unload-f (register-definition-prefixes "tramp-adb" '("tramp-")) + +;;; Generated autoloads from net/tramp-androidsu.el + +(register-definition-prefixes "tramp-androidsu" '("tramp-")) + ;;; Generated autoloads from net/tramp-archive.el @@ -34390,29 +34430,6 @@ Return the nondirectory part of FILE, for a URL. (fn QUERY &optional DOWNCASE ALLOW-NEWLINES)") -(autoload 'url-build-query-string "url-util" "\ -Build a query-string. - -Given a QUERY in the form: - ((key1 val1) - (key2 val2) - (key3 val1 val2) - (key4) - (key5 \"\")) - -(This is the same format as produced by `url-parse-query-string') - -This will return a string -\"key1=val1&key2=val2&key3=val1&key3=val2&key4&key5\". Keys may -be strings or symbols; if they are symbols, the symbol name will -be used. - -When SEMICOLONS is given, the separator will be \";\". - -When KEEP-EMPTY is given, empty values will show as \"key=\" -instead of just \"key\" as in the example above. - -(fn QUERY &optional SEMICOLONS KEEP-EMPTY)") (autoload 'url-unhex-string "url-util" "\ Decode %XX sequences in a percent-encoded URL. If optional second argument ALLOW-NEWLINES is non-nil, then allow the @@ -34445,6 +34462,29 @@ normalization, if URL is already URI-encoded, this function should return it unchanged. (fn URL)") +(autoload 'url-build-query-string "url-util" "\ +Build a query-string. + +Given a QUERY in the form: + ((key1 val1) + (key2 val2) + (key3 val1 val2) + (key4) + (key5 \"\")) + +(This is the same format as produced by `url-parse-query-string') + +This will return a string +\"key1=val1&key2=val2&key3=val1&key3=val2&key4&key5\". Keys may +be strings or symbols; if they are symbols, the symbol name will +be used. + +When SEMICOLONS is given, the separator will be \";\". + +When KEEP-EMPTY is given, empty values will show as \"key=\" +instead of just \"key\" as in the example above. + +(fn QUERY &optional SEMICOLONS KEEP-EMPTY)") (autoload 'url-file-extension "url-util" "\ Return the filename extension of FNAME. If optional argument X is t, then return the basename @@ -35198,6 +35238,7 @@ Request editing the next VC shell command before execution. This is a prefix command. It affects only a VC command executed immediately after this one." t) (put 'vc-prepare-patches-separately 'safe-local-variable 'booleanp) + (put 'vc-default-patch-addressee 'safe-local-variable 'stringp) (autoload 'vc-prepare-patch "vc" "\ Compose an Email sending patches for REVISIONS to ADDRESSEE. If `vc-prepare-patches-separately' is nil, use SUBJECT as the @@ -37421,6 +37462,61 @@ Default value of MODIFIERS is `shift-super'. (fn &optional MODIFIERS)" t) (register-definition-prefixes "windmove" '("windmove-")) + +;;; Generated autoloads from window-tool-bar.el + +(push (purecopy '(window-tool-bar 0 2)) package--builtin-versions) +(autoload 'window-tool-bar-string "window-tool-bar" "\ +Return a (propertized) string for the tool bar. + +This is for when you want more customizations than +`window-tool-bar-mode' provides. Commonly added to the variable +`tab-line-format', `header-line-format', or `mode-line-format'") +(autoload 'window-tool-bar-mode "window-tool-bar" "\ +Toggle display of the tool bar in the tab line of the current buffer. + +This is a minor mode. If called interactively, toggle the +`Window-Tool-Bar mode' mode. If the prefix argument is positive, enable +the mode, and if it is zero or negative, disable the mode. + +If called from Lisp, toggle the mode if ARG is `toggle'. Enable the +mode if ARG is nil, omitted, or is a positive number. Disable the mode +if ARG is a negative number. + +To check whether the minor mode is enabled in the current buffer, +evaluate `window-tool-bar-mode'. + +The mode's hook is called both when the mode is enabled and when it is +disabled. + +(fn &optional ARG)" t) +(put 'global-window-tool-bar-mode 'globalized-minor-mode t) +(defvar global-window-tool-bar-mode nil "\ +Non-nil if Global Window-Tool-Bar mode is enabled. +See the `global-window-tool-bar-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `global-window-tool-bar-mode'.") +(custom-autoload 'global-window-tool-bar-mode "window-tool-bar" nil) +(autoload 'global-window-tool-bar-mode "window-tool-bar" "\ +Toggle Window-Tool-Bar mode in all buffers. +With prefix ARG, enable Global Window-Tool-Bar mode if ARG is +positive; otherwise, disable it. + +If called from Lisp, toggle the mode if ARG is `toggle'. +Enable the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +Window-Tool-Bar mode is enabled in all buffers where +`window-tool-bar--turn-on' would do it. + +See `window-tool-bar-mode' for more information on Window-Tool-Bar +mode. + +(fn &optional ARG)" t) +(register-definition-prefixes "window-tool-bar" '("window-tool-bar-")) + ;;; Generated autoloads from winner.el @@ -37652,7 +37748,7 @@ If LIMIT is non-nil, then do not consider characters beyond LIMIT. ;;; Generated autoloads from progmodes/xref.el -(push (purecopy '(xref 1 6 3)) package--builtin-versions) +(push (purecopy '(xref 1 7 0)) package--builtin-versions) (autoload 'xref-find-backend "xref") (define-obsolete-function-alias 'xref-pop-marker-stack #'xref-go-back "29.1") (autoload 'xref-go-back "xref" "\ @@ -37877,17 +37973,6 @@ run a specific program. The program must be a member of (fn &optional PGM)" t) (register-definition-prefixes "zone" '("zone-")) - - -;;; Generated autoloads from net/tramp-androidsu.el - -(register-definition-prefixes "tramp-androidsu" '("tramp-androidsu-")) - - -;;; Generated autoloads from progmodes/peg.el - -(push (purecopy '(peg 1 0 1)) package--builtin-versions) -(register-definition-prefixes "peg" '("bob" "bol" "bos" "bow" "define-peg-rule" "eob" "eol" "eos" "eow" "fail" "null" "peg" "with-peg-rules")) ;;; End of scraped data commit fd61cf3976c1c24b5a58923f6520f88db060cacd Author: Davide Pola Date: Wed May 29 17:34:38 2024 +0200 Rename comp-run.el and comp-cstr.el private functions * lisp/emacs-lisp/comp-run.el (native-compile-async-skip-p) (comp-async-runnings, comp-effective-async-max-jobs) (comp-accept-and-process-async-output, comp-run-async-workers) (comp-trampoline-search): rename using '--' separator convention for private functions. * lisp/emacs-lisp/comp-cstr.el (comp-cstr-copy, comp-cstrs-homogeneous, comp-split-pos-neg) (comp-normalize-valset, comp-union-valsets) (comp-intersection-valsets, comp-normalize-typeset) (comp-union-typesets, comp-intersect-two-typesets) (comp-intersect-typesets, comp-range-union) (comp-range-intersection, comp-range-negation, comp-cstr-add-2) (comp-cstr-sub-2, comp-cstr-union-homogeneous-no-range) (comp-cstr-union-homogeneous, comp-cstr-union-1-no-mem) (comp-cstr-union-1, comp-cstr-union-make) (comp-cstr-intersection-make): Likewise. diff --git a/lisp/emacs-lisp/comp-cstr.el b/lisp/emacs-lisp/comp-cstr.el index 0b34cf8098c..058fc522858 100644 --- a/lisp/emacs-lisp/comp-cstr.el +++ b/lisp/emacs-lisp/comp-cstr.el @@ -117,7 +117,7 @@ Integer values are handled in the `range' slot.") :documentation "Hash pred -> type.") (union-typesets-mem (make-hash-table :test #'equal) :type hash-table :documentation "Serve memoization for -`comp-union-typesets'.") +`comp--union-typesets'.") ;; TODO we should be able to just cons hash this. (common-supertype-mem (make-hash-table :test #'equal) :type hash-table :documentation "Serve memoization for @@ -127,10 +127,10 @@ Integer values are handled in the `range' slot.") `comp-cstr-ctxt-subtype-p-mem'.") (union-1-mem-no-range (make-hash-table :test #'equal) :type hash-table :documentation "Serve memoization for -`comp-cstr-union-1'.") +`comp--cstr-union-1'.") (union-1-mem-range (make-hash-table :test #'equal) :type hash-table :documentation "Serve memoization for -`comp-cstr-union-1'.") +`comp--cstr-union-1'.") (intersection-mem (make-hash-table :test #'equal) :type hash-table :documentation "Serve memoization for `intersection-mem'.")) @@ -158,7 +158,7 @@ defined types." `(comp-cstr-neg ,x))) ,@body)) -(defun comp-cstr-copy (cstr) +(defun comp--cstr-copy (cstr) "Return a deep copy of CSTR." (with-comp-cstr-accessors (make-comp-cstr :typeset (copy-sequence (typeset cstr)) @@ -190,7 +190,7 @@ defined types." (null (neg cstr)) (equal (valset cstr) '(nil))))) -(defun comp-cstrs-homogeneous (cstrs) +(defun comp--cstrs-homogeneous (cstrs) "Check if constraints CSTRS are all homogeneously negated or non-negated. Return `pos' if they are all positive, `neg' if they are all negated or nil otherwise." @@ -205,7 +205,7 @@ negated or nil otherwise." ((zerop n-neg) (cl-return 'pos)) ((zerop n-pos) (cl-return 'neg))))) -(defun comp-split-pos-neg (cstrs) +(defun comp--split-pos-neg (cstrs) "Split constraints CSTRS into non-negated and negated. Return them as multiple value." (cl-loop @@ -229,7 +229,7 @@ Return them as multiple value." ;;; Value handling. -(defun comp-normalize-valset (valset) +(defun comp--normalize-valset (valset) "Sort and remove duplicates from VALSET then return it." ;; Sort valset as much as possible (by type and by value for symbols ;; and strings) to increase cache hits. But refrain to use @@ -248,13 +248,13 @@ Return them as multiple value." (cl-sort values #'string<) values)))) -(defun comp-union-valsets (&rest valsets) +(defun comp--union-valsets (&rest valsets) "Union values present into VALSETS." - (comp-normalize-valset (cl-reduce #'cl-union valsets))) + (comp--normalize-valset (cl-reduce #'cl-union valsets))) -(defun comp-intersection-valsets (&rest valsets) +(defun comp--intersection-valsets (&rest valsets) "Union values present into VALSETS." - (comp-normalize-valset (cl-reduce #'cl-intersection valsets))) + (comp--normalize-valset (cl-reduce #'cl-intersection valsets))) ;;; Type handling. @@ -307,7 +307,7 @@ Return them as multiple value." (cl-return-from main 'restart))))) typeset)) -(defun comp-normalize-typeset (typeset) +(defun comp--normalize-typeset (typeset) "Sort TYPESET and return it." (cl-sort (comp--normalize-typeset0 (cl-remove-duplicates typeset)) #'comp--sym-lessp)) @@ -340,7 +340,7 @@ Return them as multiple value." (or (assq type (comp-cstr-ctxt-typeof-types comp-ctxt)) (error "Type %S missing from typeof-types!" type))) -(defun comp-union-typesets (&rest typesets) +(defun comp--union-typesets (&rest typesets) "Union types present into TYPESETS." (or (gethash typesets (comp-cstr-ctxt-union-typesets-mem comp-ctxt)) (puthash typesets @@ -357,10 +357,10 @@ Return them as multiple value." ;; the other types. unless (comp--intersection types res) do (push (car types) res) - finally return (comp-normalize-typeset res)) + finally return (comp--normalize-typeset res)) (comp-cstr-ctxt-union-typesets-mem comp-ctxt)))) -(defun comp-intersect-two-typesets (t1 t2) +(defun comp--intersect-two-typesets (t1 t2) "Intersect typesets T1 and T2." (with-comp-cstr-accessors (cl-loop @@ -374,13 +374,13 @@ Return them as multiple value." other-types) collect type)))) -(defun comp-intersect-typesets (&rest typesets) +(defun comp--intersect-typesets (&rest typesets) "Intersect types present into TYPESETS." (unless (cl-some #'null typesets) (if (length= typesets 1) (car typesets) - (comp-normalize-typeset - (cl-reduce #'comp-intersect-two-typesets typesets))))) + (comp--normalize-typeset + (cl-reduce #'comp--intersect-two-typesets typesets))))) ;;; Integer range handling @@ -430,7 +430,7 @@ Return them as multiple value." "Greater entry in RANGE." (cdar (last range))) -(defun comp-range-union (&rest ranges) +(defun comp--range-union (&rest ranges) "Combine integer intervals RANGES by union set operation." (cl-loop with all-ranges = (apply #'append ranges) @@ -456,7 +456,7 @@ Return them as multiple value." (cl-decf nest) finally return (reverse res))) -(defun comp-range-intersection (&rest ranges) +(defun comp--range-intersection (&rest ranges) "Combine integer intervals RANGES by intersecting." (cl-loop with all-ranges = (apply #'append ranges) @@ -488,7 +488,7 @@ Return them as multiple value." (cl-decf nest) finally return (reverse res))) -(defun comp-range-negation (range) +(defun comp--range-negation (range) "Negate range RANGE." (if (null range) '((- . +)) @@ -514,15 +514,15 @@ Return them as multiple value." '(float)) (valset dst) () (range dst) (if (range old-dst) - (comp-range-intersection (range old-dst) + (comp--range-intersection (range old-dst) ext-range) ext-range) (neg dst) nil) (comp-cstr-shallow-copy dst old-dst)))) (defmacro comp-cstr-set-range-for-arithm (dst src1 src2 &rest range-body) - ;; Prevent some code duplication for `comp-cstr-add-2' - ;; `comp-cstr-sub-2'. + ;; Prevent some code duplication for `comp--cstr-add-2' + ;; `comp--cstr-sub-2'. (declare (debug (range-body)) (indent defun)) `(with-comp-cstr-accessors @@ -541,12 +541,12 @@ Return them as multiple value." '(float)) (range ,dst) ,@range-body)))))) -(defun comp-cstr-add-2 (dst src1 src2) +(defun comp--cstr-add-2 (dst src1 src2) "Sum SRC1 and SRC2 into DST." (comp-cstr-set-range-for-arithm dst src1 src2 `((,(comp-range-+ l1 l2) . ,(comp-range-+ h1 h2))))) -(defun comp-cstr-sub-2 (dst src1 src2) +(defun comp--cstr-sub-2 (dst src1 src2) "Subtract SRC1 and SRC2 into DST." (comp-cstr-set-range-for-arithm dst src1 src2 (let ((l (comp-range-- l1 h2)) @@ -558,17 +558,17 @@ Return them as multiple value." ;;; Union specific code. -(defun comp-cstr-union-homogeneous-no-range (dst &rest srcs) +(defun comp--cstr-union-homogeneous-no-range (dst &rest srcs) "As `comp-cstr-union' but excluding the irange component. All SRCS constraints must be homogeneously negated or non-negated." ;; Type propagation. (setf (comp-cstr-typeset dst) - (apply #'comp-union-typesets (mapcar #'comp-cstr-typeset srcs))) + (apply #'comp--union-typesets (mapcar #'comp-cstr-typeset srcs))) ;; Value propagation. (setf (comp-cstr-valset dst) - (comp-normalize-valset + (comp--normalize-valset (cl-loop with values = (mapcar #'comp-cstr-valset srcs) ;; TODO sort. @@ -583,12 +583,12 @@ All SRCS constraints must be homogeneously negated or non-negated." dst) -(defun comp-cstr-union-homogeneous (range dst &rest srcs) +(defun comp--cstr-union-homogeneous (range dst &rest srcs) "Combine SRCS by union set operation setting the result in DST. Do range propagation when RANGE is non-nil. All SRCS constraints must be homogeneously negated or non-negated. DST is returned." - (apply #'comp-cstr-union-homogeneous-no-range dst srcs) + (apply #'comp--cstr-union-homogeneous-no-range dst srcs) ;; Range propagation. (setf (comp-cstr-neg dst) (when srcs @@ -599,15 +599,15 @@ DST is returned." (comp-subtype-p 'integer x)) (comp-cstr-typeset dst)) (if range - (apply #'comp-range-union + (apply #'comp--range-union (mapcar #'comp-cstr-range srcs)) '((- . +))))) dst) -(cl-defun comp-cstr-union-1-no-mem (range &rest srcs) +(cl-defun comp--cstr-union-1-no-mem (range &rest srcs) "Combine SRCS by union set operation setting the result in DST. Do range propagation when RANGE is non-nil. -Non memoized version of `comp-cstr-union-1'. +Non memoized version of `comp--cstr-union-1'. DST is returned." (with-comp-cstr-accessors (let ((dst (make-comp-cstr))) @@ -616,22 +616,22 @@ DST is returned." (valset dst) () (range dst) () (neg dst) nil) - (cl-return-from comp-cstr-union-1-no-mem dst))) + (cl-return-from comp--cstr-union-1-no-mem dst))) ;; Check first if we are in the simple case of all input non-negate ;; or negated so we don't have to cons. - (when-let ((res (comp-cstrs-homogeneous srcs))) - (apply #'comp-cstr-union-homogeneous range dst srcs) - (cl-return-from comp-cstr-union-1-no-mem dst)) + (when-let ((res (comp--cstrs-homogeneous srcs))) + (apply #'comp--cstr-union-homogeneous range dst srcs) + (cl-return-from comp--cstr-union-1-no-mem dst)) ;; Some are negated and some are not - (cl-multiple-value-bind (positives negatives) (comp-split-pos-neg srcs) - (let* ((pos (apply #'comp-cstr-union-homogeneous range + (cl-multiple-value-bind (positives negatives) (comp--split-pos-neg srcs) + (let* ((pos (apply #'comp--cstr-union-homogeneous range (make-comp-cstr) positives)) ;; We'll always use neg as result as this is almost ;; always necessary for describing open intervals ;; resulting from negated constraints. - (neg (apply #'comp-cstr-union-homogeneous range + (neg (apply #'comp--cstr-union-homogeneous range (make-comp-cstr :neg t) negatives))) ;; Type propagation. (when (and (typeset pos) @@ -662,7 +662,7 @@ DST is returned." (typeset neg))) (comp-cstr-shallow-copy dst pos) (setf (neg dst) nil) - (cl-return-from comp-cstr-union-1-no-mem dst)) + (cl-return-from comp--cstr-union-1-no-mem dst)) ;; Verify disjoint condition between positive types and ;; negative types coming from values, in case give-up. @@ -680,7 +680,7 @@ DST is returned." ;; Value propagation. (cond ((and (valset pos) (valset neg) - (equal (comp-union-valsets (valset pos) (valset neg)) + (equal (comp--union-valsets (valset pos) (valset neg)) (valset pos))) ;; Pos is a superset of neg. (give-up)) @@ -703,9 +703,9 @@ DST is returned." (equal (range pos) (range neg))) (give-up) (setf (range neg) - (comp-range-negation - (comp-range-union - (comp-range-negation (range neg)) + (comp--range-negation + (comp--range-union + (comp--range-negation (range neg)) (range pos)))))) (comp-cstr-shallow-copy dst (if (comp-cstr-empty-p neg) @@ -721,7 +721,7 @@ DST is returned." dst))) -(defun comp-cstr-union-1 (range dst &rest srcs) +(defun comp--cstr-union-1 (range dst &rest srcs) "Combine SRCS by union set operation setting the result in DST. Do range propagation when RANGE is non-nil. DST is returned." @@ -731,8 +731,8 @@ DST is returned." (comp-cstr-ctxt-union-1-mem-no-range comp-ctxt))) (res (or (gethash srcs mem-h) (puthash - (mapcar #'comp-cstr-copy srcs) - (apply #'comp-cstr-union-1-no-mem range srcs) + (mapcar #'comp--cstr-copy srcs) + (apply #'comp--cstr-union-1-no-mem range srcs) mem-h)))) (comp-cstr-shallow-copy dst res) res))) @@ -754,12 +754,12 @@ DST is returned." ;; Type propagation. (setf (typeset dst) - (apply #'comp-intersect-typesets + (apply #'comp--intersect-typesets (mapcar #'comp-cstr-typeset srcs))) ;; Value propagation. (setf (valset dst) - (comp-normalize-valset + (comp--normalize-valset (cl-loop for src in srcs append @@ -782,7 +782,7 @@ DST is returned." (unless (cl-some (lambda (type) (comp-subtype-p 'integer type)) (typeset dst)) - (apply #'comp-range-intersection + (apply #'comp--range-intersection (cl-loop for src in srcs ;; Collect effective ranges. @@ -805,14 +805,14 @@ Non memoized version of `comp-cstr-intersection-no-mem'." (range dst) () (neg dst) nil) (cl-return-from comp-cstr-intersection-no-mem dst))) - (when-let ((res (comp-cstrs-homogeneous srcs))) + (when-let ((res (comp--cstrs-homogeneous srcs))) (if (eq res 'neg) - (apply #'comp-cstr-union-homogeneous t dst srcs) + (apply #'comp--cstr-union-homogeneous t dst srcs) (apply #'comp-cstr-intersection-homogeneous dst srcs)) (cl-return-from comp-cstr-intersection-no-mem dst)) ;; Some are negated and some are not - (cl-multiple-value-bind (positives negatives) (comp-split-pos-neg srcs) + (cl-multiple-value-bind (positives negatives) (comp--split-pos-neg srcs) (let* ((pos (apply #'comp-cstr-intersection-homogeneous (make-comp-cstr) positives)) (neg (apply #'comp-cstr-intersection-homogeneous @@ -860,8 +860,8 @@ Non memoized version of `comp-cstr-intersection-no-mem'." do (setf found t)))) (setf (range pos) - (comp-range-intersection (range pos) - (comp-range-negation (range neg))) + (comp--range-intersection (range pos) + (comp--range-negation (range neg))) (valset pos) (cl-set-difference (valset pos) (valset neg))) @@ -1074,30 +1074,30 @@ SRC can be either a comp-cstr or an integer." (defun comp-cstr-add (dst srcs) "Sum SRCS into DST." - (comp-cstr-add-2 dst (cl-first srcs) (cl-second srcs)) + (comp--cstr-add-2 dst (cl-first srcs) (cl-second srcs)) (cl-loop for src in (nthcdr 2 srcs) - do (comp-cstr-add-2 dst dst src))) + do (comp--cstr-add-2 dst dst src))) (defun comp-cstr-sub (dst srcs) "Subtract SRCS into DST." - (comp-cstr-sub-2 dst (cl-first srcs) (cl-second srcs)) + (comp--cstr-sub-2 dst (cl-first srcs) (cl-second srcs)) (cl-loop for src in (nthcdr 2 srcs) - do (comp-cstr-sub-2 dst dst src))) + do (comp--cstr-sub-2 dst dst src))) (defun comp-cstr-union-no-range (dst &rest srcs) "Combine SRCS by union set operation setting the result in DST. Do not propagate the range component. DST is returned." - (apply #'comp-cstr-union-1 nil dst srcs)) + (apply #'comp--cstr-union-1 nil dst srcs)) (defun comp-cstr-union (dst &rest srcs) "Combine SRCS by union set operation setting the result in DST. DST is returned." - (apply #'comp-cstr-union-1 t dst srcs)) + (apply #'comp--cstr-union-1 t dst srcs)) -(defun comp-cstr-union-make (&rest srcs) +(defun comp--cstr-union-make (&rest srcs) "Combine SRCS by union set operation and return a new constraint." (apply #'comp-cstr-union (make-comp-cstr) srcs)) @@ -1108,7 +1108,7 @@ DST is returned." (let* ((mem-h (comp-cstr-ctxt-intersection-mem comp-ctxt)) (res (or (gethash srcs mem-h) (puthash - (mapcar #'comp-cstr-copy srcs) + (mapcar #'comp--cstr-copy srcs) (apply #'comp-cstr-intersection-no-mem srcs) mem-h)))) (comp-cstr-shallow-copy dst res) @@ -1134,7 +1134,7 @@ DST is returned." do (push v strip-values) (push (cl-type-of v) strip-types)) (when strip-values - (setf (typeset dst) (comp-union-typesets (typeset dst) strip-types) + (setf (typeset dst) (comp--union-typesets (typeset dst) strip-types) (valset dst) (cl-set-difference (valset dst) strip-values))) (cl-loop for (l . h) in (range dst) when (or (bignump l) (bignump h)) @@ -1142,7 +1142,7 @@ DST is returned." (cl-return)))) dst)) -(defun comp-cstr-intersection-make (&rest srcs) +(defun comp--cstr-intersection-make (&rest srcs) "Combine SRCS by intersection set operation and return a new constraint." (apply #'comp-cstr-intersection (make-comp-cstr) srcs)) @@ -1210,10 +1210,10 @@ FN non-nil indicates we are parsing a function lambda list." ((pred atom) (comp--type-to-cstr type-spec)) (`(or . ,rest) - (apply #'comp-cstr-union-make + (apply #'comp--cstr-union-make (mapcar #'comp-type-spec-to-cstr rest))) (`(and . ,rest) - (apply #'comp-cstr-intersection-make + (apply #'comp--cstr-intersection-make (mapcar #'comp-type-spec-to-cstr rest))) (`(not ,cstr) (comp-cstr-negation-make (comp-type-spec-to-cstr cstr))) @@ -1227,7 +1227,7 @@ FN non-nil indicates we are parsing a function lambda list." ;; No float range support :/ (comp--type-to-cstr 'float)) (`(member . ,rest) - (apply #'comp-cstr-union-make (mapcar #'comp--value-to-cstr rest))) + (apply #'comp--cstr-union-make (mapcar #'comp--value-to-cstr rest))) (`(function ,args ,ret) (make-comp-cstr-f :args (mapcar (lambda (x) diff --git a/lisp/emacs-lisp/comp-run.el b/lisp/emacs-lisp/comp-run.el index f159c5b1911..8e08eca7442 100644 --- a/lisp/emacs-lisp/comp-run.el +++ b/lisp/emacs-lisp/comp-run.el @@ -138,7 +138,7 @@ if `confirm-kill-processes' is non-nil." (declare-function comp-el-to-eln-filename "comp.c") (declare-function native-elisp-load "comp.c") -(defun native-compile-async-skip-p (file load selector) +(defun native--compile-async-skip-p (file load selector) "Return non-nil if FILE's compilation should be skipped. LOAD and SELECTOR work as described in `native--compile-async'." @@ -164,7 +164,7 @@ LOAD and SELECTOR work as described in `native--compile-async'." (defvar comp-async-compilations (make-hash-table :test #'equal) "Hash table file-name -> async compilation process.") -(defun comp-async-runnings () +(defun comp--async-runnings () "Return the number of async compilations currently running. This function has the side effect of cleaning-up finished processes from `comp-async-compilations'" @@ -178,7 +178,7 @@ processes from `comp-async-compilations'" (hash-table-count comp-async-compilations)) (defvar comp-num-cpus nil) -(defun comp-effective-async-max-jobs () +(defun comp--effective-async-max-jobs () "Compute the effective number of async jobs." (if (zerop native-comp-async-jobs-number) (or comp-num-cpus @@ -190,7 +190,7 @@ processes from `comp-async-compilations'" (make-variable-buffer-local 'comp-last-scanned-async-output) ;; From warnings.el (defvar warning-suppress-types) -(defun comp-accept-and-process-async-output (process) +(defun comp--accept-and-process-async-output (process) "Accept PROCESS output and check for diagnostic messages." (if native-comp-async-report-warnings-errors (let ((warning-suppress-types @@ -218,14 +218,14 @@ processes from `comp-async-compilations'" (defconst comp-valid-source-re (rx ".el" (? ".gz") eos) "Regexp to match filename of valid input source files.") -(defun comp-run-async-workers () +(defun comp--run-async-workers () "Start compiling files from `comp-files-queue' asynchronously. When compilation is finished, run `native-comp-async-all-done-hook' and display a message." (cl-assert (null comp-no-spawn)) (if (or comp-files-queue - (> (comp-async-runnings) 0)) - (unless (>= (comp-async-runnings) (comp-effective-async-max-jobs)) + (> (comp--async-runnings) 0)) + (unless (>= (comp--async-runnings) (comp--effective-async-max-jobs)) (cl-loop for (source-file . load) = (pop comp-files-queue) while source-file @@ -312,7 +312,7 @@ display a message." (run-hook-with-args 'native-comp-async-cu-done-functions source-file) - (comp-accept-and-process-async-output process) + (comp--accept-and-process-async-output process) (ignore-errors (delete-file temp-file)) (let ((eln-file (comp-el-to-eln-filename source-file1))) @@ -322,10 +322,10 @@ display a message." (file-exists-p eln-file)) (native-elisp-load eln-file (eq load1 'late)))) - (comp-run-async-workers)) + (comp--run-async-workers)) :noquery (not native-comp-async-query-on-exit)))) (puthash source-file process comp-async-compilations)) - when (>= (comp-async-runnings) (comp-effective-async-max-jobs)) + when (>= (comp--async-runnings) (comp--effective-async-max-jobs)) do (cl-return))) ;; No files left to compile and all processes finished. (run-hooks 'native-comp-async-all-done-hook) @@ -348,7 +348,7 @@ display a message." "List of primitives we want to warn about in case of redefinition. This are essential for the trampoline machinery to work properly.") -(defun comp-trampoline-search (subr-name) +(defun comp--trampoline-search (subr-name) "Search a trampoline file for SUBR-NAME. Return the trampoline if found or nil otherwise." (cl-loop @@ -371,7 +371,7 @@ Return the trampoline if found or nil otherwise." (memq subr-name native-comp-never-optimize-functions) (gethash subr-name comp-installed-trampolines-h)) (cl-assert (subr-primitive-p subr)) - (when-let ((trampoline (or (comp-trampoline-search subr-name) + (when-let ((trampoline (or (comp--trampoline-search subr-name) (comp-trampoline-compile subr-name)))) (comp--install-trampoline subr-name trampoline))))) @@ -437,7 +437,7 @@ bytecode definition was not changed in the meantime)." else collect i))) - (unless (native-compile-async-skip-p file load selector) + (unless (native--compile-async-skip-p file load selector) (let* ((out-filename (comp-el-to-eln-filename file)) (out-dir (file-name-directory out-filename))) (unless (file-exists-p out-dir) @@ -449,11 +449,11 @@ bytecode definition was not changed in the meantime)." (display-warning 'comp (format "No write access for %s skipping." out-filename))))))) - ;; Perhaps nothing passed `native-compile-async-skip-p'? + ;; Perhaps nothing passed `native--compile-async-skip-p'? (when (and added-something ;; Don't start if there's one already running. - (zerop (comp-async-runnings))) - (comp-run-async-workers)))) + (zerop (comp--async-runnings))) + (comp--run-async-workers)))) ;;;###autoload (defun native-compile-async (files &optional recursively load selector) commit 063b67325b4e00a31c1178b340529a94e0de1c4e Author: Michael Albinus Date: Wed May 29 17:11:49 2024 +0200 Obey tramp-histfile-override in remote direct async processes * doc/emacs/custom.texi (Connection Variables): Mention `permanent-local' symbol property. * lisp/net/tramp-sh.el (tramp-histfile-override): Add ;;;###tramp-autoload cookie and `permanent-local' symbol property. * lisp/net/tramp.el (tramp-handle-make-process): Obey `tramp-histfile-override'. (Bug#71049) diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index 4725af0ee5f..0d695032d77 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -1607,6 +1607,13 @@ matches all buffers with a remote default directory. and setting these profiles to criteria which could match in parallel. It is unspecified which variable value is used then. + Be also careful when setting connection-local variables in a buffer, +which changes its major mode afterwards. Because all buffer-local +variables will be killed when changing the major mode, the +connection-local variable's value would be lost. You can prevent this +by setting the respective variable's @code{permanent-local} symbol +property to non-@code{nil}. + @node Key Bindings @section Customizing Key Bindings @cindex key bindings diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index c079455a444..615f9219448 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -64,6 +64,7 @@ If it is nil, out-of-the-band copy will be used without a check." :group 'tramp :type '(choice (const nil) integer)) +;;;###tramp-autoload (defcustom tramp-histfile-override "~/.tramp_history" "When invoking a shell, override the HISTFILE with this value. When setting to a string, it redirects the shell history to that @@ -80,6 +81,8 @@ the default storage location, e.g. \"$HOME/.sh_history\"." (const :tag "Unset HISTFILE" t) (string :tag "Redirect to a file"))) +(put 'tramp-histfile-override 'permanent-local t) + ;; ksh on OpenBSD 4.5 requires that $PS1 contains a `#' character for ;; root users. It uses the `$' character for other users. In order ;; to guarantee a proper prompt, we use "#$ " for the prompt. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 9385b023392..b2442f4538c 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -4962,6 +4962,18 @@ should be set conmnection-local.") (string-join (tramp-get-remote-path v) ":"))) (setenv-internal env "PATH" remote-path 'keep) env)) + ;; Add HISTFILE if indicated. + (env (if-let ((sh-file-name-handler-p)) + (cond + ((stringp tramp-histfile-override) + (setenv-internal env "HISTFILE" tramp-histfile-override 'keep)) + (tramp-histfile-override + (setq env (setenv-internal env "HISTFILE" "''" 'keep)) + (setq env (setenv-internal env "HISTSIZE" "0" 'keep)) + (setenv-internal env "HISTFILESIZE" "0" 'keep)) + (t env)) + env)) + ;; Add INSIDE_EMACS. (env (setenv-internal env "INSIDE_EMACS" (tramp-inside-emacs) 'keep)) (env (mapcar #'tramp-shell-quote-argument (delq nil env))) commit 4e836407ce3a2140725c7ddc2cedc3478d34a479 Author: Eli Zaretskii Date: Wed May 29 17:56:18 2024 +0300 ; * src/w32.c (sys_open): Set errno to EISDIR if opening a directory. diff --git a/src/w32.c b/src/w32.c index a1b34e4103b..6d0b178e978 100644 --- a/src/w32.c +++ b/src/w32.c @@ -4652,6 +4652,14 @@ sys_open (const char * path, int oflag, int mode) res = _wopen (mpath_w, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode); if (res < 0) res = _wopen (mpath_w, oflag | _O_NOINHERIT, mode); + if (res < 0 && errno == EACCES) + { + DWORD attributes = GetFileAttributesW (mpath_w); + + if (attributes != INVALID_FILE_ATTRIBUTES + && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + errno = EISDIR; + } } else { @@ -4662,6 +4670,14 @@ sys_open (const char * path, int oflag, int mode) res = _open (mpath_a, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode); if (res < 0) res = _open (mpath_a, oflag | _O_NOINHERIT, mode); + if (res < 0 && errno == EACCES) + { + DWORD attributes = GetFileAttributesA (mpath_a); + + if (attributes != INVALID_FILE_ATTRIBUTES + && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + errno = EISDIR; + } } return res; commit 8f618711d13fb82b10b67a12f47736e01ec4b92b Author: Eli Zaretskii Date: Wed May 29 17:41:01 2024 +0300 Fix error message from 'insert-file-contents' * src/fileio.c (Finsert_file_contents): Consider directories to be "regular" files. (Bug#71258) diff --git a/src/fileio.c b/src/fileio.c index 960a3b21dc0..9955f83e625 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4188,7 +4188,7 @@ by calling `format-decode', which see. */) named pipes, and it's probably just not worth it. So we should at least signal an error. */ - if (!S_ISREG (st.st_mode)) + if (!(S_ISREG (st.st_mode) || S_ISDIR (st.st_mode))) { regular = false; commit 608e9a5806f643876246ee9dceac381589a4fa2a Author: Andrea Corallo Date: Wed May 29 16:30:16 2024 +0200 Add some documentation for 'help-find-source' * doc/emacs/help.texi (Name Help): Add 'help-find-source' entry. * etc/NEWS: Likewise. diff --git a/doc/emacs/help.texi b/doc/emacs/help.texi index d60310456ff..6bd2d53b3b1 100644 --- a/doc/emacs/help.texi +++ b/doc/emacs/help.texi @@ -367,6 +367,11 @@ variable, or a face. If the symbol has more than one definition, like it has both definition as a function and as a variable, this command will show the documentation of all of them, one after the other. +@kindex C-h 4 s +@findex help-find-source + @kbd{C-h 4 s} (@code{help-find-source}) switch to a buffer visiting +the source definition of what is being described in the help buffer. + @vindex completions-detailed If the @code{completions-detailed} user option is non-@code{nil}, some commands provide details about the possible values when diff --git a/etc/NEWS b/etc/NEWS index 805962488d8..6cdf6afbcea 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -158,6 +158,11 @@ number has been bumped to 2048 bits. ** Help ++++ +*** New command 'help-find-source'. +Switch to a buffer visiting the source of what is being described in +"*Help*". It is bound to 'C-h 4 s' globally. + *** 'describe-function' shows function inferred type when available. For native compiled Lisp functions 'describe-function' prints (after the signature) the automatically inferred function type as well. commit c04eccd667f425e9b253e8178130c7e00c659df9 Author: Andrea Corallo Date: Tue May 28 21:06:21 2024 +0200 Add 'help-find-source' and bind it to C-h 4 s * lisp/help-fns.el (help-find-source): New function. * lisp/help.el (help-map): Bind 'help-find-source' to C-h z. (help-for-help): Add 'help-find-source'. diff --git a/lisp/help-fns.el b/lisp/help-fns.el index f2257cb9398..1ffe1b16588 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el @@ -298,6 +298,15 @@ handling of autoloaded functions." ;; Return the text we displayed. (buffer-string)))))) +;;;###autoload +(defun help-find-source () + "Switch to a buffer visiting the source of what is being described in *Help*." + (interactive) + (if-let ((help-buffer (get-buffer "*Help*"))) + (with-current-buffer help-buffer + (help-view-source)) + (error "No *Help* buffer found"))) + ;;;###autoload (defun describe-command (command) "Display the full documentation of COMMAND (a symbol). diff --git a/lisp/help.el b/lisp/help.el index 616a45328fd..2feb178ff6c 100644 --- a/lisp/help.el +++ b/lisp/help.el @@ -114,6 +114,7 @@ buffer.") "R" #'info-display-manual "s" #'describe-syntax "t" #'help-with-tutorial + "4 s" #'help-find-source "v" #'describe-variable "w" #'where-is "x" #'describe-command @@ -418,6 +419,7 @@ Do not call this in the scope of `with-help-window'." "Search documentation of functions, variables, and other items") ("describe-command" "Show help for command") ("describe-function" "Show help for function") + ("help-find-source" "Show the source for what's being described in *Help*") ("describe-variable" "Show help for variable") ("describe-symbol" "Show help for function or variable")) ("Manuals" commit a9af70849d2d9ad2b6d2b81ba4ef4d4342bff6d8 Author: Eshel Yaron Date: Wed May 29 11:49:15 2024 +0200 Add a couple of minibuffer completion tests Add tests for regressions that followed commit ff3f17ca3cd. See discussion here: https://lists.gnu.org/archive/html/emacs-devel/2024-05/msg00701.html * test/lisp/minibuffer-tests.el (completion-cycle) (minibuffer-next-completion): New tests. diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el index c4a7de9e51f..df36bce4634 100644 --- a/test/lisp/minibuffer-tests.el +++ b/test/lisp/minibuffer-tests.el @@ -630,5 +630,18 @@ (previous-line-completion 7) (should (equal "aa1" (get-text-property (point) 'completion--string)))))) +(ert-deftest completion-cycle () + (completing-read-with-minibuffer-setup '("aaa" "bbb" "ccc") + (let ((completion-cycle-threshold t)) + (execute-kbd-macro (kbd "TAB TAB TAB")) + (should (equal (minibuffer-contents) "ccc"))))) + +(ert-deftest minibuffer-next-completion () + (let ((default-directory (ert-resource-directory))) + (completing-read-with-minibuffer-setup #'read-file-name-internal + (insert "d/") + (execute-kbd-macro (kbd "M- M- M-")) + (should (equal "data/minibuffer-test-cttq$$tion" (minibuffer-contents)))))) + (provide 'minibuffer-tests) ;;; minibuffer-tests.el ends here commit 39d2f402528cd3c8be1ffd1b59d036ce3cd213fa Author: Po Lu Date: Wed May 29 17:16:47 2024 +0800 ; * src/sfntfont.c (sfnt_parse_style): Fix typo. diff --git a/src/sfntfont.c b/src/sfntfont.c index 79bc251abe4..cb94a13642e 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -563,7 +563,7 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) for (x = 0; x < SBYTES (desc->adstyle); ++x) { c = SREF (desc->adstyle, x); - if (c == '-' || c == '*' || c == '?' && c == '"') + if (c == '-' || c == '*' || c == '?' || c == '"') SSET (desc->adstyle, x, ' '); } commit b8d880e059252541be0017523fc1ffa2251bb542 Author: Po Lu Date: Wed May 29 17:11:11 2024 +0800 Fix unsafe usage of string data on Android * src/dired.c (open_directory): Reload name after calling maybe_quit, which might invoke GC. diff --git a/src/dired.c b/src/dired.c index 37a9cad992f..884d5df2410 100644 --- a/src/dired.c +++ b/src/dired.c @@ -126,6 +126,10 @@ open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp) else if (errno == EINTR) { maybe_quit (); + + /* Reload the address of DIRNAME's data, as it might have been + relocated by GC. */ + name = SSDATA (dirname); goto again; } #endif