Obsah
1. Jazyk Go a aplikace s vlastním příkazovým řádkem
2. Čtení příkazů s využitím funkce Scanln ze standardního balíčku fmt
3. Čtení ze standardního vstupu pomocí metody Reader.ReadString
5. Knihovna prompt_toolkit pro tvorbu aplikací s interaktivní smyčkou REPL v jazyku Python
6. Balíček go-prompt pro aplikace naprogramované v jazyku Go
7. Instalace balíčku go-prompt
8. Základní klávesové zkratky používané knihovnou go-prompt
9. Přečtení řádku s příkazy funkcí Input
10. Plnohodnotný příkazový řádek s vlastní historií příkazů
11. Zajištění automatického doplňování příkazů na základě statické tabulky
12. Kontextová nápověda na základě části zapsaného příkazu
13. Přidání nápovědy k jednotlivým příkazům
14. Rozsáhlejší příklad se všemi klíčovými slovy BASICu
15. Pokročilejší vyhledání vhodných příkazů na základě fuzzy filtru
16. Kontextová nápověda u příkazů, které se skládají ze dvou slov
17. Implementace kontextové nápovědy víceslovních příkazů
18. Úplný zdrojový kód příkladu s víceslovními příkazy
19. Repositář s demonstračními příklady
1. Jazyk Go a aplikace s vlastním příkazovým řádkem
Již mnohokrát jsme si v seriálu o programovacím jazyku Go řekli, že se tento jazyk primárně používá pro tvorbu síťových utilit, mikroslužeb či dokonce ucelených webových aplikací. Je to ostatně logické, protože právě v těchto oblastech se využijí prakticky všechny užitečné vlastnosti tohoto programovacího jazyka, zejména pak podpora pro práci s gorutinami, komunikace mezi gorutinami s využitím kanálů a v neposlední řadě taktéž možnost přeložit nástroj naprogramovaný v jazyku Go do jediného binárního souboru (pro určenou platformu), který nevyžaduje prakticky žádnou instalaci (na rozdíl od aplikací v některých jiných jazycích, v nichž je nutné zajistit buď kompatibilní dynamicky linkované knihovny či dokonce celý runtime daného programovacího jazyka).
To však samozřejmě neznamená, že by se jazyk Go nemohl využívat i v dalších oblastech. Sice se (s poměrně velkou pravděpodobností) prozatím neprosadí například pro tvorbu her, ovšem naproti tomu nalezneme velké množství nástrojů naprogramovaných právě v jazyce Go, které se ovládají interaktivně příkazy zadávanými z příkazového řádku (command line).
Tyto aplikace mohou pracovat dvěma způsoby:
- Příkazy se specifikují vždy při spouštění aplikace/nástroje. V takovém případě aplikace/nástroj daný příkaz provede, vypíše případný výstup či chyby a ukončí se. Příkladem mohou být prakticky všechny základní nástroje Linuxu (či obecně Unixu): cp, ls atd., dále například git a z aplikací naprogramovaných v jazyku Go pak nástroj oc použitý pro práci s OpenShiftem. Pokud jsou takové aplikace naprogramované v Go, nepotřebují kromě zpracování příkazové řádky žádnou další podpůrnou knihovnu.
- Ve druhém typu aplikací se používá vlastní interaktivní smyčka REPL (Read-Eval-Print Loop), tj. aplikace se spustí, vypíše tzv. výzvu (prompt) uživateli, akceptuje zadané příkazy, nějakým způsobem je vykoná a opět vypíše výzvu. Zde je již většinou nutné investovat více času na přípravu prostředí aplikace, protože dnes uživatelé (po právu) vyžadují, aby nástroj s vlastní interaktivní smyčkou REPL podporoval historii příkazů, vyhledávání v historii, obarvení vstupů, podporu pro automatické doplňování příkazů atd. atd. A právě tímto typem aplikací, jejichž typickým zástupcem je samotný BASH či jakýkoli jiný shell, se dnes budeme zabývat.

Obrázek 1: Příkladem aplikace s interaktivní smyčkou REPL je IPython.
Pro aplikace s interaktivní smyčkou REPL programované v jazyku Go vzniklo několik knihoven, které nabízí některé vyžadované funkce. Jedná se například o tyto knihovny:
- go-readline
https://github.com/fiorix/go-readline - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui
2. Čtení příkazů s využitím funkce Scanln ze standardního balíčku fmt
Podívejme se nejprve na některé prostředky, které nám pro čtení textu z příkazového řádku nabízí standardní balíčky programovacího jazyka Go. Nutno říci, že se jedná o základní funkce, od nichž nelze očekávat uživatelskou přívětivost – například nebudou fungovat ani příkazy pro pohyb kurzoru v rámci vstupního řádku, o neexistenci historie příkazů či kontextové nápovědy ani nemluvě. Jednou z funkcí, které nalezneme v základní knihovně jazyka Go, je funkce pojmenovaná Scanln z balíčku fmt, viz též https://golang.org/pkg/fmt/#Scanln:
func Scanln(a ...interface{}) (n int, err error)
Funkce Scanln se pokusí načíst celý textový řádek (ukončený Enterem) ze standardního vstupu a následně v tomto řádku nalezne jednotlivé elementy, které jsou od sebe odděleny mezerou. Tyto elementy uloží do proměnných, jejichž reference jsou předány v seznamu parametrů. Navíc funkce Scanln vrátí počet skutečně přečtených elementů a popř. i objekt reprezentující vstupně-výstupní chybu, která může při práci nastat. Můžeme tedy například psát:
var s string n, err := fmt.Scanln(&s) if n != 1 || err != nil { println("Error reading login") }
Vidíme, že tato funkce nejenom že načítá text ze standardního vstupu, ale navíc se snaží text nějakým způsobem „rozkouskovat“ a následně částečně zpracovat. V některých případech se může jednat o očekávané chování, ovšem mnoho aplikací vyžaduje větší kontrolu nad zapisovaným textem, takže v těchto aplikacích nebude možné Scanln použít.
Podívejme se nyní na zdrojový kód dnešního prvního demonstračního příkladu, který si po svém spuštění vyžádá zadání jména a hesla, která se pokusí načíst do proměnných login a password:
package main import "fmt" func main() { var login string var password string print("Login: ") n, err := fmt.Scanln(&login) if n != 1 || err != nil { println("Error reading login") } print("Password: ") n, err = fmt.Scanln(&password) if n != 1 || err != nil { println("Error reading password") } println(login) println(password) }
Sami si vyzkoušejte chování aplikace ve chvíli, kdy se na řádek zadá více slov oddělených mezerou či naopak pokud se nezadá žádný text (nebo dokonce jen mezery).

Obrázek 2: Zadání jména a hesla v interaktivní aplikaci používající čtení ze standardního vstupu.
Na dalším obrázku je ukázáno, co se stane ve chvíli, kdy uživatel stiskne šipku doleva ve chvíli, kdy chce upravit již zapsaný text. Aplikace se evidentně nechová příliš uživatelsky přívětivě:

Obrázek 3: Po stisku klávesy „šipka doleva“ se ve skutečnosti do textu vloží sekvence znaků poslaná terminálem.
3. Čtení ze standardního vstupu pomocí metody Reader.ReadString
V případě, že nám z nějakého důvodu nevyhovuje chování výše popsané funkce Scanln a budeme potřebovat více kontroly nad textem načítaným ze standardního vstupu, je možné použít metodu ReadString, která je dostupná pro objekty (resp. přesněji řečeno datové struktury) typu Reader, viz též https://golang.org/pkg/bufio/#Reader.ReadString. Hlavička této metody vypadá následovně:
func (b *Reader) ReadString(delim byte) (string, error)
Povšimněte si toho, že funkce akceptuje parametr delim, do kterého se zapisuje kód ASCII znaku (ne Unicode!) sloužící jako oddělovač či ukončovač záznamu, ve skutečnosti se totiž na vstupu pracuje se sekvencí bajtů a nikoli Unikódových znaků. V případě, že budeme chtít přečíst celý textový řádek, bude tímto oddělovačem pochopitelně znak pro konec řádku:
reader.ReadString('\n')
Metoda ReadString vrací jak přečtený text (ve formě běžného řetězce, nyní již v Unicode), tak i případnou hodnotu s popisem chyby. Správně by se tedy mělo každé načtení ze standardního vstupu testovat, a to například následujícím způsobem:
reader := bufio.NewReader(os.Stdin) login, err := reader.ReadString('\n') if err != nil { println("Error reading login") }
Opět se samozřejmě podíváme na zdrojový kód demonstračního příkladu, který načte dva řetězce ze standardního vstupu:
package main import ( "bufio" "os" ) func main() { reader := bufio.NewReader(os.Stdin) print("Login: ") login, err := reader.ReadString('\n') if err != nil { println("Error reading login") } print("Password: ") password, err := reader.ReadString('\n') if err != nil { println("Error reading password") } println(login) println(password) }
Samozřejmě ani v tomto případě nemůžeme očekávat žádné pokročilejší funkce – posuny kurzoru šipkami, přesuny textu, historii příkazů atd. Ve chvíli, kdy je tato funkcionalita vyžadována, se musíme uchýlit k externím knihovnám popř. aplikaci „obalit“ příkazem rlwrap.

Obrázek 4: Vstup textu s nabodeníčky je sice možný, ovšem posun kurzoru šipkami již nikoli. Navíc se načítá i samotný znak pro konec řádku, který však není nijak problematické v případě potřeby odstranit.
4. Knihovna GNU Readline
Pro vylepšení příkazové řádky o další vlastnosti se v mnoha aplikacích (zejména těch naprogramovaných v jazycích C a C++) používá známá knihovna GNU Readline, která dokáže zajistit například:
- Možnost použití kurzorových šipek pro pohyb na řádku se zapisovaným vstupním textem.
- Příkazy do určité míry emulující textové editory Emacs či Vi (v Emacs režimu například Ctrl+A pro přechod na začátek řádku, Ctrl+W pro smazání slova atd.)
- Doplňování příkazů (vstupních dat).
- Vyhledávání v historii již zadaných příkazů (vstupních dat). Tuto funkcionalitu je možné zajistit i externě s využitím knihovnyrlwrap.
Knihovnu GNU Readline samozřejmě můžeme použít i v jazyce Go, ovšem v žádném případě se nejedná o ideální řešení, protože se přidává závislost na externích knihovnách a nástrojích, což například může dělat potíže při tvorbě distribučních souborů s vyvíjenou aplikací či nástrojem. Dále popsaný balíček go-prompt tuto závislost odstraňuje, protože je kompletně naprogramován v Go. Existuje i několik dalších balíčků, jejichž cílem je reimplementovat funkcionalitu GNU Readline v čistém Go (viz též úvodní kapitolu, pozor však, že jeden ze zmíněných balíčků je wrapper právě nad GNU Readline).
5. Knihovna prompt_toolkit pro tvorbu aplikací s interaktivní smyčkou REPL v jazyku Python
Pro aplikace s příkazovým řádkem vytvářené v programovacím jazyku Python je určena velmi užitečná knihovna, která se jmenuje prompt_toolkit (viz https://github.com/prompt-toolkit/python-prompt-toolkit). Název této knihovny sice naznačuje, že slouží pouze pro implementaci vstupního (či příkazového) řádku do aplikací, to však ve skutečnosti není vše. Tato knihovna například umožňuje využít víceřádkový vstupní text, dovoluje použití myši (kromě implicitní funkce myši v terminálu pro operace výběru textu s následným copy & paste) a dokonce obsahuje sadu základních prvků uživatelského rozhraní (takzvaných widgetů), mezi něž patří například toolbary, menu, checkboxy, tlačítka, či dialogy. Díky tomu lze tuto knihovnu použít i pro tvorbu aplikací s plnohodnotným textovým uživatelským rozhraním (ne nepodobným starodávnému TurboVision).

Obrázek 5: Pohled na textové uživatelské rozhraní debuggeru pudb.
6. Balíček go-prompt pro aplikace naprogramované v jazyku Go
Výše zmíněnou knihovnou prompt_toolkit se inspirovali autoři knihovny go-prompt, která je určena pro použití v aplikacích naprogramovaných v jazyku Go. Jedná se o knihovnu přenositelnou na různé operační systémy (pochopitelně včetně všech tří nejpoužívanějších desktopových systémů) a aplikace, které tuto knihovnu používají, by mělo být možné provozovat na prakticky všech emulátorech terminálu (otestováno především s XTermem). Tato knihovna uživatelům nabízí následující funkce:
- Plnohodnotnou editaci na příkazovém řádku, samozřejmě včetně možnosti přesunu kurzoru s využitím příkazů Ctrl+znak, specializovaných kláves Home, End atd.
- Mazání textu před kurzorem, za kurzorem, smazání slova apod.
- Automatické doplňování příkazů na základě tabulky, kterou je možné dynamicky měnit.
- Kontextovou nápovědu s dostupnými příkazy, a to včetně popisu jednotlivých příkazů.
- Historii již zapsaných příkazů.
V navazujících kapitolách si ukážeme základy práce s touto knihovnou při tvorbě aplikací s plnohodnotným interaktivním příkazovým řádkem, který je stále jedním z nejdokonalejších nástrojů určených pro komunikaci mezi člověkem a počítačem [1].
7. Instalace balíčku go-prompt
Balíček go-prompt nainstalujeme naprosto stejným způsobem, jako jakýkoli jiný externí balíček určený pro programovací jazyk Go. Pro instalaci použijeme příkaz go get, a to v následující podobě:
$ go get
Po chvíli by se měly stáhnout všechny potřebné závislé balíčky a knihovna go-prompt by se měla přeložit. Obsah adresáře, na který ukazuje proměnná GOPATH by měl obsahovat mj. i následující podadresáře:
. ├── bin ├── pkg │ └── linux_amd64 │ └── github.com │ └── c-bata └── src └── github.com ├── c-bata │ └── go-prompt │ ├── completer │ ├── _example │ │ ├── exec-command │ │ ├── http-prompt │ │ ├── live-prefix │ │ └── simple-echo │ │ └── cjk-cyrillic │ ├── internal │ │ ├── bisect │ │ ├── debug │ │ ├── strings │ │ └── term │ └── _tools │ ├── complete_file │ ├── sigwinch │ └── vt100_debug ├── mattn │ └── go-runewidth └── pkg └── term └── termios
Samozřejmě je ihned po instalaci dostupná i nápověda k balíčku:
$ godoc go-prompt PACKAGE DOCUMENTATION package prompt import "go-prompt" ... ... ...
či přímo nápověda k nějakému datovému typu:
$ godoc go-prompt Prompt type Prompt struct { ASCIICodeBindings []ASCIICodeBind // contains filtered or unexported fields } Prompt is core struct of go-prompt. func New(executor Executor, completer Completer, opts ...Option) *Prompt New returns a Prompt with powerful auto-completion. func (p *Prompt) Input() string Input just returns user input text. func (p *Prompt) Run() Run starts prompt.
8. Základní klávesové zkratky používané knihovnou go-prompt
V knihovně go-prompt jsou deklarovány některé základní klávesové zkratky, které se snaží být do určité míry kompatibilní jak s BASHem (v režimu Emacs), tak i přímo s textovým editorem Emacs. Ovšem všechny zkratky jsou ve skutečnosti plně konfigurovatelné, takže je možné vytvořit si vlastní sadu příkazů – nutno ovšem říci, že pouze nemodálních (režim Vi/Vimu není a pravděpodobně ani nebude podporován).
Příkazy pro přesuny kurzoru
Základní příkazy pro přesun kurzoru používají kombinaci Ctrl+znak. V případě, že je terminál správně nakonfigurován, měly by fungovat i kurzorové šipky doleva/doprava a navíc i klávesy Home a End (se zřejmou funkcí):
Klávesa | Význam |
---|---|
Ctrl+B | přesun kurzoru na předchozí znak |
šipka doleva | přesun kurzoru na předchozí znak |
Ctrl+F | přesun kurzoru na další znak |
šipka doprava | přesun kurzoru na další znak |
Ctrl+A | přesun kurzoru na začátek řádku |
Home | přesun kurzoru na začátek řádku |
Ctrl+E | přesun kurzoru na konec řádku |
End | přesun kurzoru na konec řádku |
Mazání textu, práce s kill ringem
Pro přesun části textu v rámci editovaného řádku se například v BASHi používá takzvaný kill ring, do něhož se smazaný text uloží. Pro vložení takto smazaného textu do jiné oblasti se používá operace nazvaná yank (odpovídá paste). V případě knihovny go-prompt jsou v současné verzi implementovány pouze příkazy pro smazání části textu, nikoli však příkaz yank pro jeho vložení na jiné místo (tato operace je však plánována do dalších verzí, popravdě ji není příliš složité doimplementovat):
Klávesa | Význam |
---|---|
Ctrl+D | smaže jeden znak na pozici kurzoru (pokud je ovšem na řádku nějaký obsah, jinak typicky ukončí vstup či celou aplikaci) |
Delete | smaže jeden znak na pozici kurzoru |
Ctrl+H | smaže jeden znak před kurzorem |
Backspace | smaže jeden znak před kurzorem |
Ctrl+W | smaže předchozí slovo |
Ctrl+K | smaže text od pozice kurzoru do konce řádku |
Ctrl+U | smaže text od začátku řádku do pozice kurzoru |
Práce s historií dříve zadaných příkazů
Uživatelé mají k dispozici i historii zapsaných příkazů. K jejich vyvolání slouží několik klávesových zkratek vypsaných v následující tabulce. Prozatím zde však nenajdete zkratky pro vyhledávání v historii příkazů; tuto funkcionalitu je však možné v případě potřeby doprogramovat a pravděpodobně se s ní setkáme v další verzi knihovny go-prompt:
Klávesa | Význam |
---|---|
Ctrl+P | průchod historií – předchozí text/příkaz |
šipka nahoru | průchod historií – předchozí text/příkaz |
Ctrl+N | průchod historií – následující text/příkaz |
šipka dolů | průchod historií – předchozí text/příkaz |
9. Přečtení řádku s příkazy funkcí Input
Nyní si ukážeme základní funkcionalitu, kterou nám knihovna go-prompt nabízí. Bude se jednat o nejjednodušší možnou aplikaci, která pouze přečte dva textové údaje z příkazového řádku. Aplikace je napsána takovým způsobem, že jsou podporovány všechny výše zmíněné editační příkazy, ovšem nikoli historie příkazového řádku (nemáme totiž žádný objekt, který by historii držel). Základem je použití funkce Input:
login := prompt.Input("Login: ", completer) password := prompt.Input("Password: ", completer)
Této funkci je nutné předat referenci na takzvaný completer, s jehož významem se setkáme v navazujících kapitolách.
Zdrojový kód příkladu vypadá následovně:
package main import "github.com/c-bata/go-prompt" func completer(in prompt.Document) []prompt.Suggest { return []prompt.Suggest{} } func main() { login := prompt.Input("Login: ", completer) password := prompt.Input("Password: ", completer) println(login) println(password) }

Obrázek 6: Povšimněte si, že se stiskem klávesy Ctrl+A či Home můžeme bez problémů přesunout na začátek vstupního řádku. Všechny ostatní editační příkazy budou taktéž funkční.
10. Plnohodnotný příkazový řádek s vlastní historií příkazů
Aby bylo možné použít příkazový řádek s vlastní historií příkazů, je možné aplikaci poněkud upravit, a to takovým způsobem, že se reakce na příkazy zadané uživatelem přesune do samostatné funkce. Tato funkce se většinou nazývá executor, i když nám samozřejmě nic nebrání použít odlišné jméno:
func executor(t string) { println(t) }
Doplníme i funkci se jménem completer, která bude vracet prázdnou tabulku s návrhy na doplnění celých slov s příkazy:
func completer(in prompt.Document) []prompt.Suggest { return []prompt.Suggest{} }
Nyní již můžeme zkonstruovat objekt (datovou strukturu) typu Prompt a spustit interní smyčku, která bude jednotlivé příkazy načítat a pro každý příkaz zavolá výše zmíněnou callback funkci executor:
p := prompt.New(executor, completer) p.Run()

Obrázek 7: Příkazový řádek začíná výzvou (prompt). Ukončení aplikace lze docílit stiskem klávesy Ctrl+D.
Úplný kód příkladu, který implementuje plnohodnotný příkazový řádek i s historií, je dostupný na adrese https://github.com/tisnik/go-root/blob/master/article20/04_prompt.go:
package main import "github.com/c-bata/go-prompt" func executor(t string) { println(t) } func completer(in prompt.Document) []prompt.Suggest { return []prompt.Suggest{} } func main() { p := prompt.New(executor, completer) p.Run() }
11. Zajištění automatického doplňování příkazů na základě statické tabulky
Jednou z velmi užitečných funkcí nabízených knihovnou go-prompt je automatické doplňování příkazů, a to bez toho, aby uživatel musel použít nějakou specializovanou klávesovou zkratku – příkazy se nabízí automaticky přímo při psaní. Taktéž se přímo při psaní zobrazuje tabulka s dostupnými příkazy. Interně je tato vlastnost zajištěna neustálým voláním callback funkce completer, které se předá hodnota typu Document obsahující informace jak o zapsaném textu, tak i o pozici kurzoru v rámci tohoto textu. V tom nejjednodušším případě může callback funkce completer vracet vždy stejnou tabulku s příkazy, nezávisle na tom, co uživatel zapisuje. Informace o dostupných příkazech jsou reprezentovány hodnotami typu Suggest:
func completer(in prompt.Document) []prompt.Suggest { return []prompt.Suggest{ {Text: "help"}, {Text: "exit"}, {Text: "quit"}, } }
Aby byl příklad úplný, doplníme i kód funkce executor, který bude jednoduchý – na každý podporovaný příkaz nějakým způsobem zareagujeme. Aplikace se buď ukončí po příkazech „exit“ a „quit“, nebo se zobrazí nápověda popř. informace o tom, že se jedná o neznámý příkaz:
func executor(t string) { switch t { case "exit": fallthrough case "quit": os.Exit(0) case "help": println("HELP:\nexit\nquit") default: println("Nothing happens") } }
Nyní se podívejme na chování aplikace:

Obrázek 8: Po spuštění se pouze očekává příkaz, žádná nápověda se nevypíše.

Obrázek 9: Vrácením kurzoru se zobrazí tabulka se všemi dostupnými příkazy.

Obrázek 10: Nápověda prozatím není kontextová, ovšem klávesou Tab lze příkaz doplnit.

Obrázek 11: Doplněný příkaz se zobrazí odlišnou barvou.
Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/go-root/blob/master/article20/05_basic_completer.go:
package main import ( "github.com/c-bata/go-prompt" "os" ) func executor(t string) { switch t { case "exit": fallthrough case "quit": os.Exit(0) case "help": println("HELP:\nexit\nquit") default: println("Nothing happens") } } func completer(in prompt.Document) []prompt.Suggest { return []prompt.Suggest{ {Text: "help"}, {Text: "exit"}, {Text: "quit"}, } } func main() { p := prompt.New(executor, completer) p.Run() }
12. Kontextová nápověda na základě části zapsaného příkazu
Předchozí příklad pochopitelně nebyl dokonalý, protože nezávisle na vstupu zapsaném uživatelem nabízel stále stejné příkazy. Ovšem díky funkci FilterHasPrefix je možné z tabulky se všemi dostupnými příkazy vyfiltrovat pouze ty, které začínají zadaným řetězcem. Povšimněte si, jakým způsobem se získá text umístěný před kurzorem (to je jeden z důvodů, proč se funkci completer nepředává pouhý řetězec, ale objekt typu Document:
func completer(in prompt.Document) []prompt.Suggest { s := []prompt.Suggest{ {Text: "help"}, {Text: "exit"}, {Text: "quit"}, } return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true) }
Po této nepatrné úpravě se chování aplikace radikálně změní, protože bude možné použít plnohodnotné doplňování, kontextovou nápovědu atd.:

Obrázek 12: Stiskem q a klávesy Tab se automaticky doplní příkaz „quit“.

Obrázek 13: Tabulka je nyní již kontextová, protože nabízí jen relevantní příkazy.
Opět se podívejme na úplný zdrojový kód tohoto příkladu:
package main import ( "github.com/c-bata/go-prompt" "os" ) func executor(t string) { switch t { case "exit": fallthrough case "quit": println("Quitting") os.Exit(0) case "help": println("HELP:\nexit\nquit") default: println("Nothing happens") } } func completer(in prompt.Document) []prompt.Suggest { s := []prompt.Suggest{ {Text: "help"}, {Text: "exit"}, {Text: "quit"}, } return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true) } func main() { p := prompt.New(executor, completer) p.Run() }
13. Přidání nápovědy k jednotlivým příkazům
Rozšířením datové struktury Suggest o položku Description je možné zajistit zobrazení nápovědy k jednotlivým příkazům, které jsou nabízeny v kontextovém okně. Úprava zdrojového kódu je snadná:
s := []prompt.Suggest{ {Text: "help", Description: "show help with all commands"}, {Text: "exit", Description: "quit the application"}, {Text: "quit", Description: "quit the application"}, }
Výsledek by měl vypadat takto:

Obrázek 14: Zobrazení všech příkazů i s nápovědou.
Zdrojový kód takto upraveného demonstračního příkladu naleznete na adrese https://github.com/tisnik/go-root/blob/master/article20/07_completion_description.go:
package main import ( "github.com/c-bata/go-prompt" "os" ) func executor(t string) { switch t { case "exit": fallthrough case "quit": os.Exit(0) case "help": println("HELP:\nexit\nquit") default: println("Nothing happens") } return } func completer(in prompt.Document) []prompt.Suggest { s := []prompt.Suggest{ {Text: "help", Description: "show help with all commands"}, {Text: "exit", Description: "quit the application"}, {Text: "quit", Description: "quit the application"}, } return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true) } func main() { p := prompt.New(executor, completer) p.Run() }
14. Rozsáhlejší příklad se všemi klíčovými slovy BASICu
Nyní již máme k dispozici všechny informace nutné pro vytvoření rozsáhlejšího příkladu, který uživatelům bude na příkazovém řádku nabízet všechny příkazy, funkce a klíčová slova Atari BASICu. Jak seznam příkazů, tak i jejich popis byl získán z Wikipedie. Tento demonstrační příklad by se měl chovat následovně:

Obrázek 15: Nabídka některých příkazů Atari BASICu.
V ostatních ohledech se tento příklad neliší od demonstračního kódu, s nímž jsme se seznámili v předchozí kapitole, takže se podívejme na jeho výpis:
package main import ( "github.com/c-bata/go-prompt" "os" ) func executor(t string) { switch t { case "exit": fallthrough case "quit": os.Exit(0) default: println("READY") } return } func completer(in prompt.Document) []prompt.Suggest { s := []prompt.Suggest{ {Text: "ABS", Description: "Returns the absolute value of a number"}, {Text: "ADR", Description: "Returns the address in memory of a variable (mostly used for machine code routines stored in variables)"}, {Text: "AND", Description: "Logical conjunction"}, {Text: "ASC", Description: "Returns the ATASCII value of a character"}, {Text: "ATN", Description: "Returns the arctangent of a number"}, {Text: "BYE", Description: "Transfers control to the internal Self Test program"}, {Text: "CHR", Description: "Returns a character given an ATASCII value"}, {Text: "CLOAD", Description: "Loads from cassette tape a tokenized program that was saved with CSAVE"}, {Text: "CLOG", Description: "Returns the common logarithm of a number"}, {Text: "CLOSE", Description: "Terminates pending transfers (flush) and closes an I/O channel"}, {Text: "CLR", Description: "Clears variables' memory and program stack"}, {Text: "COLOR", Description: "Chooses which logical color to draw in"}, {Text: "COM", Description: "Implementation of MS Basic's COMMON was cancelled. Recognized but the code for DIM is executed instead"}, {Text: "CONT", Description: "Resumes execution of a program after a STOP at the next line number (see STOP)"}, {Text: "COS", Description: "Returns the cosine of a number"}, {Text: "CSAVE", Description: "Saves to cassette tape a program in tokenized form (see CLOAD)"}, {Text: "DATA", Description: "Stores data in lists of numeric or string values"}, {Text: "DEG", Description: "Switches trigonometric functions to compute in degrees (radians is the default mode) (see RAD)"}, {Text: "DIM", Description: "Defines the size of a string or array (see COM)"}, {Text: "DOS", Description: "Transfers control to the Disk Operating System (DOS); if DOS was not loaded, same as BYE"}, {Text: "DRAWTO", Description: "Draws a line to given coordinates"}, {Text: "END", Description: "Finishes execution of the program, closes open I/O channels and stops any sound"}, {Text: "ENTER", Description: "Loads and merges into memory a plain text program from an external device, usually from cassette tape or disk (see LIST)"}, {Text: "EXP", Description: "Exponential function"}, {Text: "FOR", Description: "Starts a for loop"}, {Text: "FRE", Description: "Returns the amount of free memory in bytes"}, {Text: "GET", Description: "Reads one byte from an I/O channel (see PUT)"}, {Text: "GOSUB", Description: "Jumps to a subroutine at a given line in the program, placing the return address on the stack (see POP and RETURN)"}, {Text: "GOTO", Description: "and GO TO Jumps to a given line in the program. GOTO can be omitted in IF ... THEN GOTO ..."}, {Text: "GRAPHICS", Description: "Sets the graphics mode"}, {Text: "IF", Description: "Executes code depending on whether a condition is true or not"}, {Text: "INPUT", Description: "Retrieves a stream of text from an I/O channel; usually data from keyboard (default), cassette tape or disk"}, {Text: "INT", Description: "Returns the floor of a number"}, {Text: "LEN", Description: "Returns the length of a string"}, {Text: "LET", Description: "Assigns a value to a variable. LET can be omitted"}, {Text: "LIST", Description: "Lists (all or part of) the program to screen (default), printer, disk, cassette tape, or any other external device (see ENTER)"}, {Text: "LOAD", Description: "Loads a tokenized program from an external device; usually a cassette tape or disk (see SAVE)"}, {Text: "LOCATE", Description: "Stores the logical color or ATASCII character at given coordinates"}, {Text: "LOG", Description: "Returns the natural logarithm of a number"}, {Text: "LPRINT", Description: "Prints text to a printer device (same result can be achieved with OPEN, PRINT and CLOSE statements)"}, {Text: "NEW", Description: "Erases the program and all the variables from memory; automatically executed before a LOAD or CLOAD"}, {Text: "NEXT", Description: "Continues the next iteration of a FOR loop"}, {Text: "NOT", Description: "Logical negation"}, {Text: "NOTE", Description: "Returns the current position on an I/O channel"}, {Text: "ON", Description: "A computed goto - performs a jump based on the value of an expression"}, {Text: "OPEN", Description: "Initialises an I/O channel"}, {Text: "OR", Description: "Logical disjunction"}, {Text: "PADDLE", Description: "Returns the position of a paddle controller"}, {Text: "PEEK", Description: "Returns the value at an address in memory"}, {Text: "PLOT", Description: "Draws a point at given coordinates"}, {Text: "POINT", Description: "Sets the current position on an I/O channel"}, {Text: "POKE", Description: "Sets a value at an address in memory"}, {Text: "POP", Description: "Removes a subroutine return address from the stack (see GOSUB and RETURN)"}, {Text: "POSITION", Description: "Sets the position of the graphics cursor"}, {Text: "PRINT", Description: "and ? Writes text to an I/O channel; usually to screen (default), printer, cassette tape or disk (see LPRINT and INPUT)"}, {Text: "PTRIG", Description: "Indicates whether a paddle trigger is pressed or not"}, {Text: "PUT", Description: "Writes one byte to an I/O channel (see GET)"}, {Text: "RAD", Description: "Switches trigonometric functions to compute in radians (see DEG)"}, {Text: "READ", Description: "Reads data from a DATA statement"}, {Text: "REM", Description: "Marks a comment in a program"}, {Text: "RESTORE", Description: "Sets the position of where to read data from a DATA statement"}, {Text: "RETURN", Description: "Ends a subroutine, effectively branching to the line immediately following the calling GOSUB (see GOSUB and POP)"}, {Text: "RND", Description: "Returns a pseudorandom number"}, {Text: "RUN", Description: "Starts execution of a program, optionally loading it from an external device (see LOAD)"}, {Text: "SAVE", Description: "Writes a tokenized program to an external device; usually a cassette tape or disk (see LOAD)"}, {Text: "SETCOLOR", Description: "Maps a logical color to a physical color"}, {Text: "SGN", Description: "Returns the signum of a number"}, {Text: "SIN", Description: "Returns the sine of a number"}, {Text: "SOUND", Description: "Starts or stops playing a tone on a sound channel (see END)"}, {Text: "SQR", Description: "Returns the square root of a number"}, {Text: "STATUS", Description: "Returns the status of an I/O channel"}, {Text: "STEP", Description: "Indicates the increment used in a FOR loop"}, {Text: "STICK", Description: "Returns a joystick position"}, {Text: "STOP", Description: "Stops the program, allowing later resumption (see CONT)"}, {Text: "STRIG", Description: "Indicates whether a joystick trigger is pressed or not"}, {Text: "STR", Description: "Converts a number to string form"}, {Text: "THEN", Description: "Indicates the statements to execute if the condition is true in an IF statement"}, {Text: "TO", Description: "Indicates the limiting condition in a FOR statement"}, {Text: "TRAP", Description: "Sets to jump to a given program line if an error occurs (TRAP 40000 cancels this order)"}, {Text: "USR", Description: "Calls a machine code routine, optionally with parameters"}, {Text: "VAL", Description: "Returns the numeric value of a string"}, {Text: "XIO", Description: "General-purpose I/O routine (from Fill screen to Rename file to Format disk instructions) "}, {Text: "exit", Description: "Quit the application"}, {Text: "quit", Description: "Quit the application"}, } if in.GetWordBeforeCursor() == "" { return nil } else { return prompt.FilterHasPrefix(s, in.GetWordBeforeCursor(), true) } } func main() { p := prompt.New(executor, completer) p.Run() }
15. Pokročilejší vyhledání vhodných příkazů na základě fuzzy filtru
V mnoha případech je výhodnější změnit způsob výběru nabízených příkazů. Namísto seznamu příkazů, které začínají určitým řetězcem lze použít takzvaný fuzzy filtr, který vrátí všechny příkazy obsahující zadanou sekvenci znaků (v daném pořadí). Například pro řetězec „PN“ se mohou vrátit příkazy PRINT, LPRINT, POINT a POSITION, ale i OPEN apod. Úprava funkce completer je snadná – pouze zavoláme funkci FilterFuzzy namísto funkce FilterHasPrefix:
func completer(in prompt.Document) []prompt.Suggest { if in.GetWordBeforeCursor() == "" { return nil } else { return prompt.FilterFuzzy(suggestions, in.GetWordBeforeCursor(), true) } }

Obrázek 16: Všech šest příkazů, které jsou zobrazeny v kontextové nápovědě, skutečně obsahuje znaky „C“ a „L“ v uvedeném pořadí.
Následuje výpis zdrojového kódu tohoto příkladu:
package main import ( "github.com/c-bata/go-prompt" "os" ) var suggestions = []prompt.Suggest{ {Text: "ABS", Description: "Returns the absolute value of a number"}, {Text: "ADR", Description: "Returns the address in memory of a variable (mostly used for machine code routines stored in variables)"}, {Text: "AND", Description: "Logical conjunction"}, {Text: "ASC", Description: "Returns the ATASCII value of a character"}, {Text: "ATN", Description: "Returns the arctangent of a number"}, {Text: "BYE", Description: "Transfers control to the internal Self Test program"}, {Text: "CHR", Description: "Returns a character given an ATASCII value"}, {Text: "CLOAD", Description: "Loads from cassette tape a tokenized program that was saved with CSAVE"}, {Text: "CLOG", Description: "Returns the common logarithm of a number"}, {Text: "CLOSE", Description: "Terminates pending transfers (flush) and closes an I/O channel"}, {Text: "CLR", Description: "Clears variables' memory and program stack"}, {Text: "COLOR", Description: "Chooses which logical color to draw in"}, {Text: "COM", Description: "Implementation of MS Basic's COMMON was cancelled. Recognized but the code for DIM is executed instead"}, {Text: "CONT", Description: "Resumes execution of a program after a STOP at the next line number (see STOP)"}, {Text: "COS", Description: "Returns the cosine of a number"}, {Text: "CSAVE", Description: "Saves to cassette tape a program in tokenized form (see CLOAD)"}, {Text: "DATA", Description: "Stores data in lists of numeric or string values"}, {Text: "DEG", Description: "Switches trigonometric functions to compute in degrees (radians is the default mode) (see RAD)"}, {Text: "DIM", Description: "Defines the size of a string or array (see COM)"}, {Text: "DOS", Description: "Transfers control to the Disk Operating System (DOS); if DOS was not loaded, same as BYE"}, {Text: "DRAWTO", Description: "Draws a line to given coordinates"}, {Text: "END", Description: "Finishes execution of the program, closes open I/O channels and stops any sound"}, {Text: "ENTER", Description: "Loads and merges into memory a plain text program from an external device, usually from cassette tape or disk (see LIST)"}, {Text: "EXP", Description: "Exponential function"}, {Text: "FOR", Description: "Starts a for loop"}, {Text: "FRE", Description: "Returns the amount of free memory in bytes"}, {Text: "GET", Description: "Reads one byte from an I/O channel (see PUT)"}, {Text: "GOSUB", Description: "Jumps to a subroutine at a given line in the program, placing the return address on the stack (see POP and RETURN)"}, {Text: "GOTO", Description: "and GO TO Jumps to a given line in the program. GOTO can be omitted in IF ... THEN GOTO ..."}, {Text: "GRAPHICS", Description: "Sets the graphics mode"}, {Text: "IF", Description: "Executes code depending on whether a condition is true or not"}, {Text: "INPUT", Description: "Retrieves a stream of text from an I/O channel; usually data from keyboard (default), cassette tape or disk"}, {Text: "INT", Description: "Returns the floor of a number"}, {Text: "LEN", Description: "Returns the length of a string"}, {Text: "LET", Description: "Assigns a value to a variable. LET can be omitted"}, {Text: "LIST", Description: "Lists (all or part of) the program to screen (default), printer, disk, cassette tape, or any other external device (see ENTER)"}, {Text: "LOAD", Description: "Loads a tokenized program from an external device; usually a cassette tape or disk (see SAVE)"}, {Text: "LOCATE", Description: "Stores the logical color or ATASCII character at given coordinates"}, {Text: "LOG", Description: "Returns the natural logarithm of a number"}, {Text: "LPRINT", Description: "Prints text to a printer device (same result can be achieved with OPEN, PRINT and CLOSE statements)"}, {Text: "NEW", Description: "Erases the program and all the variables from memory; automatically executed before a LOAD or CLOAD"}, {Text: "NEXT", Description: "Continues the next iteration of a FOR loop"}, {Text: "NOT", Description: "Logical negation"}, {Text: "NOTE", Description: "Returns the current position on an I/O channel"}, {Text: "ON", Description: "A computed goto - performs a jump based on the value of an expression"}, {Text: "OPEN", Description: "Initialises an I/O channel"}, {Text: "OR", Description: "Logical disjunction"}, {Text: "PADDLE", Description: "Returns the position of a paddle controller"}, {Text: "PEEK", Description: "Returns the value at an address in memory"}, {Text: "PLOT", Description: "Draws a point at given coordinates"}, {Text: "POINT", Description: "Sets the current position on an I/O channel"}, {Text: "POKE", Description: "Sets a value at an address in memory"}, {Text: "POP", Description: "Removes a subroutine return address from the stack (see GOSUB and RETURN)"}, {Text: "POSITION", Description: "Sets the position of the graphics cursor"}, {Text: "PRINT", Description: "and ? Writes text to an I/O channel; usually to screen (default), printer, cassette tape or disk (see LPRINT and INPUT)"}, {Text: "PTRIG", Description: "Indicates whether a paddle trigger is pressed or not"}, {Text: "PUT", Description: "Writes one byte to an I/O channel (see GET)"}, {Text: "RAD", Description: "Switches trigonometric functions to compute in radians (see DEG)"}, {Text: "READ", Description: "Reads data from a DATA statement"}, {Text: "REM", Description: "Marks a comment in a program"}, {Text: "RESTORE", Description: "Sets the position of where to read data from a DATA statement"}, {Text: "RETURN", Description: "Ends a subroutine, effectively branching to the line immediately following the calling GOSUB (see GOSUB and POP)"}, {Text: "RND", Description: "Returns a pseudorandom number"}, {Text: "RUN", Description: "Starts execution of a program, optionally loading it from an external device (see LOAD)"}, {Text: "SAVE", Description: "Writes a tokenized program to an external device; usually a cassette tape or disk (see LOAD)"}, {Text: "SETCOLOR", Description: "Maps a logical color to a physical color"}, {Text: "SGN", Description: "Returns the signum of a number"}, {Text: "SIN", Description: "Returns the sine of a number"}, {Text: "SOUND", Description: "Starts or stops playing a tone on a sound channel (see END)"}, {Text: "SQR", Description: "Returns the square root of a number"}, {Text: "STATUS", Description: "Returns the status of an I/O channel"}, {Text: "STEP", Description: "Indicates the increment used in a FOR loop"}, {Text: "STICK", Description: "Returns a joystick position"}, {Text: "STOP", Description: "Stops the program, allowing later resumption (see CONT)"}, {Text: "STRIG", Description: "Indicates whether a joystick trigger is pressed or not"}, {Text: "STR", Description: "Converts a number to string form"}, {Text: "THEN", Description: "Indicates the statements to execute if the condition is true in an IF statement"}, {Text: "TO", Description: "Indicates the limiting condition in a FOR statement"}, {Text: "TRAP", Description: "Sets to jump to a given program line if an error occurs (TRAP 40000 cancels this order)"}, {Text: "USR", Description: "Calls a machine code routine, optionally with parameters"}, {Text: "VAL", Description: "Returns the numeric value of a string"}, {Text: "XIO", Description: "General-purpose I/O routine (from Fill screen to Rename file to Format disk instructions) "}, {Text: "exit", Description: "Quit the application"}, {Text: "quit", Description: "Quit the application"}, } func executor(t string) { switch t { case "exit": fallthrough case "quit": os.Exit(0) default: println("Nothing happens") } return } func completer(in prompt.Document) []prompt.Suggest { if in.GetWordBeforeCursor() == "" { return nil } else { return prompt.FilterFuzzy(suggestions, in.GetWordBeforeCursor(), true) } } func main() { p := prompt.New(executor, completer) p.Run() }
16. Kontextová nápověda u příkazů, které se skládají ze dvou slov
Často se setkáme s požadavkem, aby program umožňoval zadání příkazů, které se skládají ze dvou či dokonce z většího množství slov. I v takovém případě samozřejmě můžeme použít funkce z knihovny go-prompt, ovšem budeme muset provést několik úprav ve funkci completer. Nejprve si však ukažme, jak by mělo vypadat očekávané chování aplikace:

Obrázek 17: Nápověda prvních slov příkazu.

Obrázek 18: Nápověda dalšího slova dvouslovního příkazu.

Obrázek 19: Nápověda dalšího slova odlišného dvouslovního příkazu.
Vidíme, že kontextová nápověda ke druhému slovu závisí na tom, jakým slovem příkaz začíná.
17. Implementace kontextové nápovědy víceslovních příkazů
Jedna z možných implementací „inteligentní“ kontextové nápovědy pro víceslovní příkazy spočívá v tom, že ve funkci completer nejprve rozložíme řetězec zadaný uživatelem na jednotlivá slova:
blocks := strings.Split(in.TextBeforeCursor(), " ")
Dále si vytvoříme několik tabulek příkazů. První tabulka bude obsahovat pouze první slova příkazu:
s := []prompt.Suggest{ {Text: "help", Description: "show help with all commands"}, {Text: "user", Description: "add/delete user"}, {Text: "ls", Description: "list users/storages/computers"}, {Text: "list", Description: "list users/storages/computers"}, {Text: "exit", Description: "quit the application"}, {Text: "quit", Description: "quit the application"}, }
Další tabulky pak pouze druhá slova příkazů:
user_s := []prompt.Suggest{ {Text: "add", Description: "add new user"}, {Text: "assign", Description: "assign a role to user"}, {Text: "del", Description: "delete user"}, } list_s := []prompt.Suggest{... empty_s := []prompt.Suggest{}
Následně implementujeme primitivní logiku pro rozhodnutí, kterou tabulku s nabídkou příkazů vrátit a použít – zda seznam prvních slov či nějaký ze seznamů slov druhých. Ve větvi default se vrací prázdná tabulka pro jednoslovní či pro neznámé příkazy:
if len(blocks) == 2 { switch blocks[0] { case "user": return prompt.FilterHasPrefix(user_s, blocks[1], true) case "ls": fallthrough case "list": return prompt.FilterHasPrefix(list_s, blocks[1], true) default: return empty_s } } return prompt.FilterHasPrefix(s, blocks[0], true)
18. Úplný zdrojový kód příkladu s víceslovními příkazy
Úplný zdrojový kód příkladu s víceslovními příkazy nalezneme na adrese https://github.com/tisnik/go-root/blob/master/article20/10_two_word_commands.go:
package main import ( "github.com/c-bata/go-prompt" "os" "strings" ) func executor(t string) { switch t { case "exit": fallthrough case "quit": os.Exit(0) case "help": println("HELP:\nexit\nquit") case "user add": println("Adding user") case "user del": println("Deleting user") default: println("Nothing happens") } return } func completer(in prompt.Document) []prompt.Suggest { blocks := strings.Split(in.TextBeforeCursor(), " ") s := []prompt.Suggest{ {Text: "help", Description: "show help with all commands"}, {Text: "user", Description: "add/delete user"}, {Text: "ls", Description: "list users/storages/computers"}, {Text: "list", Description: "list users/storages/computers"}, {Text: "exit", Description: "quit the application"}, {Text: "quit", Description: "quit the application"}, } user_s := []prompt.Suggest{ {Text: "add", Description: "add new user"}, {Text: "assign", Description: "assign a role to user"}, {Text: "del", Description: "delete user"}, } list_s := []prompt.Suggest{ {Text: "users", Description: "show list of all users"}, {Text: "logs", Description: "show list of all logs"}, {Text: "storages", Description: "show list of all storages"}, {Text: "computers", Description: "show list of all computers"}, } empty_s := []prompt.Suggest{} if len(blocks) == 2 { switch blocks[0] { case "user": return prompt.FilterHasPrefix(user_s, blocks[1], true) case "ls": fallthrough case "list": return prompt.FilterHasPrefix(list_s, blocks[1], true) default: return empty_s } } return prompt.FilterHasPrefix(s, blocks[0], true) } func main() { p := prompt.New(executor, completer) p.Run() }
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně dva megabajty), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - Package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation