Obsah
1. Použití Language Server Protocolu v textovém editoru Vim
2. Základní informace o projektu LSP
3. Některé starší projekty založené na podobném principu
4. LSP v Eclipse, Visual Studiu Code, Eclipse Che a Atom IDE
6. Kooperace mezi Python Language Serverem a Vimem
7. Využití nových technologií Vimu 8 v pluginu vim-lsp
8. Konfigurace pluginu vim-lsp
9. Dostupné příkazy poskytované pluginem vim-lsp
10. Popis nejdůležitějších příkazů
12. Technologie Omni Completion a další možnosti doplňování textu ve Vimu
13. Nastavení funkce Omni Completion pro plugin vim-lsp
14. Konfigurace klávesové zkratky pro vyvolání omni completion
15. Nastavení automatického doplňování volbou completeopt
16. Přijdou další protokoly – tentokrát pro ladění aplikací?
18. Ukázka konfiguračního souboru .vimrc připraveného pro Python
19. Popis jednotlivých částí konfiguračního souboru
1. Použití Language Server Protocolu v textovém editoru Vim
Language Server Protocol je otevřený standard navržený takovým způsobem, aby umožňoval komunikaci mezi textovými editory popř. mezi integrovanými vývojovými prostředími (IDE) na jedné straně a různými typy programátorských nástrojů na straně druhé. Mezi nástroje, které je díky existenci LSP možné z editoru/IDE použít, mohou patřit zejména různé lintery, statické analyzátory kódu, programy pro kontrolu stylu zápisu programů, nástroje umožňující refaktoring zdrojového kódu, teoreticky i profilery atd. Nesmíme samozřejmě zapomenout na dnes již všemi programátory očekávané nástroje pro automatické doplňování jmen funkcí, metod, objektů atd., „inteligentní“ vyhledávání ve zdrojovém kódu, doskoky na definici funkce, objektu nebo proměnné apod. Všechny tyto nástroje mohou komunikovat s editorem/IDE přímo (pokud obsahují podporu pro LSP), nebo je možné využít nějaký obecnější nástroj, který je většinou nazývaný Language Server a který podporuje větší množství funkcí (typicky vyhledání definic, refaktoring a automatické doplňování; samozřejmě v závislosti na konkrétní implementaci).
Obrázek 1: Textový editor Emacs ve funkci integrovaného vývojového prostředí.
2. Základní informace o projektu LSP
Samotný protokol je založen na formátu JSON, přesněji řečeno na protokolu JSON-RPC. Při použití LSP je textový editor či IDE považován za klienta a language server pochopitelně vystupuje v roli serveru, který klientovi poskytuje tzv. „language service“. Mezi klientem a serverem probíhá komunikace s využitím tří typů zpráv:
- Požadavek (request) je posílán klientem na server. Server musí na tento požadavek odpovědět. Používá se protokol JSON-RPC.
- Odpověď (response) serveru na požadavek klienta. Klient vždy dokáže spárovat svůj požadavek s odpovědí serveru (to je základ JSON-RPC).
- Oznamovací zprávy (notification) může posílat jak klient, tak i server, přičemž se na tyto zprávy nemusí posílat odpověď. Klient (editor) může například serveru ohlásit, že uživatel posunul kurzor na další funkci, ale prozatím od něj neočekává žádnou další službu.
Příklad komunikace (na levé straně je klient/editor, na straně pravé language server):
textDocument/didOpen -> textDocument/didChange -> <- textDocument/publishDiagnostics <- textDocument/didClose
Konkrétní tvar dotazu může vypadat takto:
{'method': 'textDocument/hover', 'jsonrpc': '2.0', 'id': 4, 'params': {'textDocument': {'uri': 'file:///home/ptisnovs/t.py'}, 'position': {'character': 0, 'line': 21}}}
Jeden typ notifikace vrácené serverem klientovi:
textDocument/publishDiagnostics {'uri': 'file:///home/ptisnovs/t.py', 'diagnostics': [{'source': 'pycodestyle', 'range': {'start': {'line': 3, 'character': 19}, 'end': {'line': 3, 'character': 21}}, 'message': 'W291 trailing whitespace', 'code': 'W291', 'severity': 2}]}
3. Některé starší projekty založené na podobném principu
Myšlenka, na které je language server protocol postaven, samozřejmě není nijak nová ani převratná, protože se podobný princip využíval již dříve, například v textových editorech Vim a Emacs, které takto mohly komunikovat s nástroji běžícími na pozadí (daemons), viz též https://github.com/Microsoft/language-server-protocol/wiki/Protocol-History. Příkladem může být integrace nástroje Jedi do Vimu, o níž jsme se nedávno zmínili v článku Knihovna Jedi: doplňování kódu a statická analýza kódu v Pythonu.
Obrázek 2: Dnes již historické IDE Turbo Pascalu určené pouze pro jediný jazyk. Prakticky všechny nástroje jsou nedílnou součástí tohoto IDE (s výjimkou externích nástrojů grep a Turbo Debugger).
4. LSP v Eclipse, Visual Studiu Code, Eclipse Che a Atom IDE
Protokol LSP se v současnosti již běžně používá. Podporu pro něj nalezneme především ve Visual Studiu Code, což je ovšem pochopitelné, protože myšlenka LSP vznikla právě v Microsoftu pro potřeby tohoto dnes populárního integrovaného vývojového prostředí. Nesmíme ovšem zapomenout ani na klasické Eclipse, v němž je možné LSP používat díky pluginu Eclipse LSP4E. Na LSP je taktéž postavena velká část funkcionality webového integrovaného vývojového prostředí Eclipse Che. Další aplikací, která je založena na použití LSP, je Atom IDE, což je ve skutečnosti známý textový editor Atom doplněný o několik pluginů, především pak o plugin Atom Language Server Protocol Client.
Obrázek 3: Editor Atom použitý jako jednoduché integrované vývojové prostředí.
5. Python Language Server
V dalších kapitolách si ukážeme, jakým způsobem je možné integrovat language server protocol do Vimu s využitím pluginu vim-lsp. Aby bylo možné použít reálné příklady, použijeme jednu konkrétní implementaci LSP určenou pro programovací jazyk Python. Tato implementace se jmenuje jednoduše Python Language Server, kráceně PYLS či jen pyls. Instalace Python Language Serveru je jednoduchá. Postačuje použít nástroj pip nebo pip3 a provést instalaci lokálně pro právě přihlášeného uživatele:
$ pip3 install --user python-language-server
Průběh instalace závisí na tom, zda již máte nainstalovány všechny závislé balíčky či nikoli. V mém případě jsem již některé balíčky měl nainstalované (minimálně od doby psaní článku o Jedi), takže instalace byla prakticky okamžitá:
Collecting python-language-server Downloading https://files.pythonhosted.org/packages/9f/1d/2817b5dc2dd77f897410a11c1c9e2a6d96b3273c53d4219dd9edab7882af/python-language-server-0.21.2.tar.gz (51kB) Requirement already satisfied: future>=0.14.0 in ./.local/lib/python3.6/site-packages (from python-language-server) Requirement already satisfied: jedi>=0.12 in ./.local/lib/python3.6/site-packages (from python-language-server) Requirement already satisfied: python-jsonrpc-server in ./.local/lib/python3.6/site-packages (from python-language-server) Requirement already satisfied: pluggy in /usr/lib/python3.6/site-packages (from python-language-server) Requirement already satisfied: parso>=0.3.0 in ./.local/lib/python3.6/site-packages (from jedi>=0.12->python-language-server) Installing collected packages: python-language-server Running setup.py install for python-language-server: started Running setup.py install for python-language-server: finished with status 'done' Successfully installed python-language-server-0.21.2
Pro velmi rychlou kontrolu, zda instalace proběhla v pořádku, si můžete Python Language Server spustit a zjistit, zda příkaz vůbec existuje popř. zda při spuštění nedojde k nějaké chybě:
$ pyls
Prozatím můžeme server ukončit pomocí Ctrl+C, protože dále popisovaný plugin vim-lsp si server dokáže spustit sám automaticky.
6. Kooperace mezi Python Language Serverem a Vimem
V tomto okamžiku máme nainstalován jak editor Vim (doufejme), tak i Python Language Server. Zbývá nám tedy jen „maličkost“ – propojit tyto dva nástroje a začít používat všechny funkce Python Language Serveru přímo z Vimu. K tomuto účelu je možné použít plugin vim-lsp, který naleznete na GitHubu, konkrétně na adrese https://github.com/prabirshrestha/vim-lsp. Instalaci je možné provést různými způsoby (například využít různé správce balíčků), ovšem lze zůstat u konzervativní metody a rozbalit všechny soubory přímo do adresáře ~./vim. Stejným způsobem (rozbalením popř. prostým naklonováním) je nutné nainstalovat i závislý plugin async.vim.
V podadresáři ~/.vim by měla být vytvořena následující adresářová struktura (samozřejmě zde můžeme mít uloženy další pluginy, ostatně i já zde mám nainstalovány například slovníky):
. ├── autoload │ ├── async │ │ └── job.vim │ ├── lsp │ │ ├── capabilities.vim │ │ ├── client.vim │ │ ├── omni.vim │ │ ├── ui │ │ │ ├── vim │ │ │ │ ├── diagnostics │ │ │ │ │ └── echo.vim │ │ │ │ ├── diagnostics.vim │ │ │ │ ├── hover.vim │ │ │ │ ├── output.vim │ │ │ │ ├── signs.vim │ │ │ │ └── utils.vim │ │ │ └── vim.vim │ │ ├── utils │ │ │ └── step.vim │ │ └── utils.vim │ └── lsp.vim ├── doc │ └── vim-lsp.txt ├── ftplugin │ └── lsp-hover.vim ├── plugin │ └── lsp.vim ├── pythonx ├── spell │ ├── cs.utf-8.add │ ├── cs.utf-8.add.spl │ ├── cs.utf-8.spl │ ├── en.utf-8.add │ └── en.utf-8.add.spl └── syntax
Po spuštění Vimu a napsání:
:Lsp<Tab>
by se měly vypsat všechny nové příkazy začínající prefixem „Lsp“.
Následuje dokončení instalace, zejména indexace nápovědy. Spusťte textový editor Vim a zadejte příkaz:
:helptags ~/.vim/doc
7. Využití nových technologií Vimu 8 v pluginu vim-lsp
Na tomto místě je vhodné upozornit na to, že plugin vim-lsp vyžaduje pro svou korektní činnost Vim 8. Ve starších verzích nebude modul fungovat. Je tomu tak z toho prostého důvodu, že (jak již víme) se pro komunikaci mezi LSP klientem a LSP serverem používá JSON-RPC a funkce pro serializaci a deserializaci dat do JSONu nalezneme až ve skriptovacím engine Vimu 8. Pravděpodobně by bylo možné si tyto funkce dopsat i pro Vim 6 nebo Vim 7, ovšem výhodnější je update editoru.
Jen pro krátké připomenutí informací z článku o novinkách ve Vimu 8: do skriptovacího engine Vimu byly přidány čtyři nové funkce, které slouží pro převod datových struktur VimScriptu do JSON formátu a zpět. Proč je vlastně tato novinka tak důležitá? Souvisí totiž s další novou technologií, konkrétně s úlohami (jobs) a kanály (channels). Úlohy umožňují přesně definovaným způsobem vytvářet pluginy (i externí pluginy) s tím, že tyto pluginy mohou běžet asynchronně, tj. částečně nezávisle na samotném Vimu. Důležité je, že pluginy s Vimem komunikují právě přes JSON formát, což je pěkně navržené řešení, protože JSON je dnes podporovaný v prakticky všech relevantních programovacích jazycích, v nichž se externí pluginy většinou píšou (typicky se jedná o Python, ale lze použít i jazyk Lua, JavaScript/TypeScript apod.).
Skriptovací engine Vimu nabízí programátorům dvě funkce určené pro převod datových struktur do formátu JSON (encode) a dvě funkce určené pro parsování JSON dat do interních struktur VimScriptu (decode). Dvě varianty jsou implementovány z toho prostého důvodu, že někdy je zapotřebí, aby byly klíče objektů (či slovníků) reprezentovány řetězci a jinde se může jednat o identifikátory bez uvozovek (záleží na konkrétní situaci, ne vždy jsou totiž klíče současně i platnými identifikátory):
Funkce | Stručný popis |
---|---|
json_encode(výraz) | převod výrazu do JSON formátu, který se vrátí ve formě řetězce |
json_decode(řetězec) | opak předchozí funkce, parsování řetězce s daty uloženými v JSON formátu do interních datových struktur VimScriptu |
js_encode(výraz) | podobné funkci json_encode(), ovšem klíče nejsou umístěny v uvozovkách |
js_decode(řetězec) | podobné funkci json_decode(), ovšem při parsingu se nevyžaduje, aby byly klíče umístěny v uvozovkách |
V pluginu vim-lsp nalezneme funkce json_encode a json_decode.
8. Konfigurace pluginu vim-lsp
Plugin vim-lsp by se měl inicializovat automaticky, protože jeho část je umístěna v podadresáři ~.vim/autoload. Nápovědu jsme též již inicializovali, takže by mělo být možné zadat příkaz:
:help vim-lsp
Tento příkaz by měl zobrazit stránku s popisem všech příkazů poskytovaných pluginem:
vim-lsp ================================================================================ CONTENTS vim-lsp-contents Introduction vim-lsp-introduction Install vim-lsp-install Language Servers vim-lsp-language-servers Configure vim-lsp-configure-source Wiki vim-lsp-configure-wiki Options vim-lsp-options g:lsp_auto_enable g:lsp_auto_enable g:lsp_preview_keep_focus g:lsp_preview_keep_focus Functions vim-lsp-functions enable vim-lsp-enable disable vim-lsp-disable register_server vim-lsp-register_server stop_server vim-lsp-stop_server Commands vim-lsp-commands
Dále je nutné funkcí (ne příkazem!) lsp#register_server zaregistrovat jednotlivé jazykové servery, které chcete použít. Této funkci se předává jméno serveru, příkaz pro jeho spuštění a taktéž seznam jazyků, pro které je tento server určen. Následující skript zajistí načtení Python Language Serveru. Vložte tento skript do svého .vimrc, kdekoli za příkaz :set nocompatible:
if (executable('pyls')) au User lsp_setup call lsp#register_server({ \ 'name': 'pyls', \ 'cmd': {server_info->['pyls']}, \ 'whitelist': ['python'] \ }) endif
Pokud budete chtít sledovat logy produkované jazykovým serverem, použijte mírně modifikovaný příkaz:
if (executable('pyls')) au User lsp_setup call lsp#register_server({ \ 'name': 'pyls', \ 'cmd': {server_info->['pyls', '-vv', '--log-file', '/tmp/pyls.txt']}, \ 'whitelist': ['python'] \ }) endif
Po znovuspuštění Vimu se můžete přesvědčit o to, jestli byl jazykový server spuštěn či nikoli:
:LspStatus
s odpovědí:
pyls: running
nebo:
pyls: not running
Pokud server běží, lze o něm získat další informace:
:echo lsp#get_server_info('pyls') {'cmd': function('<lambda>1'), 'name': 'pyls', 'whitelist': ['python']}
Samozřejmě se můžeme přesvědčit i na dalším terminálu, zda byl jazykový server spuštěn:
$ ps -ax |grep pyls 20787 ? Ssl 0:01 /usr/bin/python3 /home/ptisnovs/.local/bin/pyls 20800 pts/3 S+ 0:00 grep pyls
9. Dostupné příkazy poskytované pluginem vim-lsp
Všechny nové příkazy, které jsou poskytované pluginem vim-lsp, jsou i se stručným popisem vypsány v následující tabulce:
# | Příkaz | Stručný popis příkazu |
---|---|---|
1 | LspDocumentDiagnostics | spuštění diagnostiky dokumentu, viz další kapitolu |
2 | LspDefinition | doskok na definici objektu, jehož jméno se nachází pod kurzorem |
3 | LspDocumentFormat | zformátování dokumentu (není vždy implementováno) |
4 | LspDocumentRangeFormat | zformátování části dokumentu |
5 | LspDocumentSymbol | vypíše všechny symboly nalezené v aktuálně editovaném modulu |
6 | LspHover | otevře nové okno s podrobnějšími informacemi o objektu, který se nalézá pod kurzorem |
7 | LspNextError | přechod na následující detekovanou chybu |
8 | LspPreviousError | přechod na předcházející detekovanou chybu |
9 | LspImplementation | nalezne všechny implementace rozhraní (záleží na jazyku) |
10 | LspReferences | nalezne všechny reference (volání/definice funkcí, metod atd.) |
11 | LspRename | symbol (a jeho další výskyty) pod kurzorem se přejmenují |
12 | LspTypeDefinition | doskok na definici typu (záleží na jazyku) |
13 | LspWorkspaceSymbol | vyhledání symbolu |
14 | LspStatus | stav jazykového serveru (běží/neběží) |
10. Popis nejdůležitějších příkazů
Jedním z nejužitečnějších příkazů je :LspHover. Tento příkaz otevře nové okno s podrobnějšími informacemi o objektu, který se nalézá pod kurzorem. Toto informační okno lze zavřít klávesovou zkratkou Ctrl+W Ctrl+Z.
Dalším důležitým příkazem je :LspDocumentDiagnostic. Ten slouží pro zobrazení výsledků produkovaných linterem a taktéž nástrojem pro kontrolu dokumentačních řetězců. Výsledky se zobrazí v takzvaném Quickfix oknu, v němž každý řádek odpovídá jednomu problému. Výběrem řádku a stiskem klávesy Enter se otevře ta část dokumentu, která byla označena jako potenciálně problematická.
Příkaz :LspDocumentSymbol vypíše všechny symboly (funkce, metody, třídy, proměnné) nalezené v aktuálně editovaném modulu. Výsledky se opět zobrazí v takzvaném Quickfix oknu, v němž každý řádek odpovídá jednomu nalezenému symbolu.
A konečně příkaz :LspRename slouží pro přejmenování symbolu pod kurzorem (a jeho dalších výskytů). Po zadání tohoto příkazu se Vim zeptá na nové jméno, které následně použije. V případě Pythonu vyžaduje opravenou verzi.
11. Ukázka použití
Podívejme se nyní na způsob použití některých výše zmíněných příkazů. Otevřeme si ve Vimu jednoduchý skript naprogramovaný v Pythonu:
def hello_world(): print("Hello world") def hex(): """Foobar""" print("HEX") hex(40) hello_world
Následuje několik screenshotů ukazujících základní možnosti pluginu vim-lsp společně s Python Language Serverem:
Obrázek 4: Nápověda k pluginu.
Obrázek 5: Ruční zavolání příkazu LspHover.
Obrázek 6: Výsledkem je pomocné okno s informacemi o objektu „hello_world“.
Obrázek 7: Napíšeme prefix „he“ a zadáme příkaz LspDocumentSymbol.
Obrázek 8: Výsledkem je výpis symbolů začínajících na tento prefix. Seznam je otevřen v Quickfix okně.
Obrázek 9: Klasické chování omnicompletion. Povšimněte si dynamické nápovědy v horním okně.
Obrázek 10: Klasické chování omnicompletion. Povšimněte si dynamické nápovědy v horním okně.
Obrázek 11: Vylepšení omnicompletion o specifikaci typu objektu.
Obrázek 12: Nápověda se samozřejmě zobrazí i k funkcím se základní knihovny (zde funkce print).
12. Technologie Omni Completion a další možnosti doplňování textu ve Vimu
Velmi užitečnou vlastností textového editoru Vim, kterou se dnes budeme v souvislosti s LSP zabývat, je technologie nazvaná „omni completion“ (někdy se setkáme i s jednoslovným zápisem „omnicompletion“). Tato technologie, která se ve Vimu objevila až v jeho sedmé verzi, rozšiřuje možnosti automatického doplňování kódu (či obecně textu) o externí nástroje. Připomeňme si, že Vim nabízí ve vkládacím a přepisovacím režimu klávesovou zkratku Ctrl+P (previous) pro nalezení a doplnění slova nacházejícího se před kurzorem a taktéž zkratku Ctrl+N (next), která slouží ke stejnému účelu, ovšem hledá slovo pro doplnění v textu za kurzorem (pokud je k dispozici více možností, zobrazí se v kontextovém menu). V praxi tedy postačuje napsat jen začátek slova a stlačit Ctrl+P nebo Ctrl+N. Rozsah vyhledávání se specifikuje volbou complete popsanou zde a samozřejmě i ve vestavěné nápovědě.
Obrázek 13: Doplňování kódu nebo libovolného textu pomocí příkazů CTRL+P a CTRL+N zavolaných v režimu zápisu (insert mode).
Ovšem možnosti automatického doplňování kódu jsou ve skutečnosti daleko větší a textový editor Vim pro ně dokonce nabízí samostatný režim vyvolávaný z vkládacího či přepisovacího režimu klávesovou zkratkou Ctrl+X (právě z tohoto důvodu se tento režim nazývá ^X-mode nebo též CTRL-X mode). Po stlačení této klávesové zkratky se v příkazové řádce objeví řádkové menu s příkazy platnými pro režim doplňování:
Obrázek 14: Menu s dostupnými klávesovými zkratkami platnými v režimu CTRL-X.
Všechny dostupné příkazy jsou vypsány v tabulce níže:
# | Příkaz | Význam |
---|---|---|
1 | Ctrl+X Ctrl+L | nalezení a doplnění celého (shodného) řádku, užitečné především v případě editace některých typů konfiguračních souborů |
2 | Ctrl+X Ctrl+N | doplnění slova, které se nalézá v aktuálně editovaném souboru |
3 | Ctrl+X Ctrl+K | podobné Ctrl+N, ovšem slova se hledají v souborech specifikovaných pomocí konfiguračního parametru dictionary (jedná se o běžný textový soubor se seznamem slov) |
4 | Ctrl+X Ctrl+T | podobné Ctrl+T, ovšem slova se hledají v souborech specifikovaných pomocí konfiguračního parametru thesaurus |
5 | Ctrl+X Ctrl+I | podobné Ctrl+N, ovšem prohledávají se i všechny vkládané (included) soubory |
6 | Ctrl+X Ctrl+] | vyhledávání v seznamu značek |
7 | Ctrl+X Ctrl+F | doplnění názvu souboru a/nebo cesty, postačuje například zadat text ~/ za nímž následuje klávesová zkratka Ctrl+X Ctrl+F a zobrazí se výpis souborů v domácím adresáři |
8 | Ctrl+X Ctrl+D | vyhledání definice makra a doplnění jména tohoto makra |
9 | Ctrl+X Ctrl+U | zavolání funkce zadané v konfiguračním parametru completefunc, které se předá právě editovaný text |
10 | Ctrl+X Ctrl+O | vyvolání funkce omni completion popsané v následující kapitole (dostupné od Vimu 7) |
Obrázek 15: Doplnění názvu souboru v pracovním adresáři pomocí příkazu CTRL+X CTRL+F.
13. Nastavení funkce Omni Completion pro plugin vim-lsp
V předchozí kapitole jsme si řekli, že s využitím klávesové zkratky Ctrl+X Ctrl+O lze ve vkládacím a přepisovacím režimu zavolat technologii „omni completion“. Tuto technologii lze využít pro (pseudo)inteligentní doplňování textů založeném na analýze zdrojových kódů. Podobnou funkci můžeme najít v nejrůznějších integrovaných vývojových prostředích (Eclipse, Netbeans, Visual Studio, Visual Studio Code, nověji například i v Atomu), v nichž lze doplňovat například jména funkcí a metod, názvy prvků ve strukturách či uniích, atributů objektů, metod objektů či tříd, jmen balíčků atd. Plugin vim-lsp tuto funkci samozřejmě nabízí; její jméno je lsp#complete. Aby byla tato funkce skutečně použita po stisku Ctrl+X Ctrl+O je nutné tuto funkci přiřadit konfigurační volbě omnifunc.
Implicitně tato funkce není specifikována vůbec, o čemž se můžete velmi snadno přesvědčit při spuštění „prázdného“ Vimu:
:set omnifunc? omnifunc=
Naopak při editaci tohoto článku (v HTML) je funkce nastavena, a to konkrétně standardním filetype pluginem html.vim:
:set omnifunc? omnifunc=htmlcomplete#CompleteTags
V případě, že chceme použít možnosti nabízené pluginem vim-lsp, můžeme omnifunc nastavit globálně (pro celý Vim). To se provede jednoduše příkazem:
:set omnifunc=lsp#complete
Popř. lze stejnou funkci nastavit pouze lokálně pro právě aktivní buffer:
:setlocal omnifunc=lsp#complete
Samozřejmě nemusíme toto nastavení stále provádět ručně po otevření každého zdrojového souboru, ale můžeme do konfiguračního souboru .vimrc přidat příkaz, který se automaticky zavolá při otevření souboru s koncovkou *.py (Python):
augroup __python__ au! au BufRead,BufNewFile *.py setlocal omnifunc=lsp#complete augroup END
14. Konfigurace klávesové zkratky pro vyvolání omni completion
Výše uvedená konfigurace sice teoreticky postačuje po prakticky plnohodnotné využití možností pluginu vim-lsp, ovšem skalní uživatelé Vimu pravděpodobně očekávají nějaká vylepšení, která by Vim přiblížila k moderním IDE. Týká se to mj. i klávesové zkratky použité pro vyvolání funkce omni completion, protože Ctrl+X Ctrl+O je spíše „emacsovina“ :-) Řešení samozřejmě existuje několik. Pokud se ve zdrojových textech nikde nepoužívá tabulátor (záleží samozřejmě na zvolené „štábní kultuře“ a taktéž i na zvyklostech platící pro daný programovací jazyk), může pomoci následující mapování umožňující, aby se místo Ctrl+X Ctrl+O mohla stlačit pouze klávesa Tab:
:imap <Tab> <C-X><C-O>
V případě, že používáte GVim nebo KVim, lze namísto klávesy Tab použít například i klávesovou zkratku Ctrl+Space:
:imap <C-Space> <C-X><C-O>
Na terminálech sice většinou není klávesová zkratka Ctrl+Space korektně rozpoznána, ale můžete se pokusit zjistit, zda terminál namísto Ctrl+Space nerozpozná Ctrl+@, tj. přesněji řečeno, zda obě klávesové zkratky nejsou rozpoznány jako shodný kód. Pokud tomu tak je, můžete mapování změnit na:
:imap <C-@> <C-X><C-O>
a používat Ctrl+Space i v terminálu.
15. Nastavení automatického doplňování volbou completeopt
Chování automatického doplňování je řízeno volbou completeopt. Implicitní nastavení této volby získáme takto:
:set completeopt? completeopt=menu,preview
Hodnota menu povoluje zobrazení menu s nabídkami doplnění, ovšem pouze v případě, že existují dvě či více variant. Hodnota preview říká, že se k samotnému doplňovanému textu přidají například informace o souboru, kde byl nalezen atd. Existují ovšem i další volby, zejména menuone pro zobrazení menu i v případě, kdy byla nalezena jen jediná shoda a hodnota longest řídicí zobrazení položek textu, který odpovídá zadanému prefixu (nevybere se žádná možnost doplnění, uživatel však může výběr postupně zužovat klávesou Ctrl+L nebo rozšiřovat prostým zápisem dalších znaků, což je pro mnoho uživatelů přirozenější chování):
:set completeopt=longest,menuone
Dále je možné upravit chování klávesy Enter. Interně se totiž při automatickém doplňování může editor nacházet v jednom ze tří stavů a v každém stavu se Enter chová poněkud odlišně (někdy vkládá konec řádky, což je nepříjemné). Toto chování zakážeme následujícím příkazem, po jehož zápisu do .vimrc se Enter bude chovat vždy stejně (nikdy neodřádkuje):
:inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
16. Přijdou další protokoly – tentokrát pro ladění aplikací?
Po přečtení předchozích kapitol možná čtenáře napadlo, že existence LSP je sice skvělá a užitečná věc pro samotné psaní programového kódu, pro hledání chyb, formátování kódu, refaktorizaci, statickou analýzu atd., ovšem jedna podstatná věc v protokolu chybí. Jedná se samozřejmě o možnosti ladění aplikací. Bylo by pravděpodobně velmi užitečné mít k dispozici jeden protokol, který by umožňoval spustit libovolný debugger (například GDB nebo LLDB, debugger Pythonu atd. atd.) a ovládat tento debugger jedním standardizovaným způsobem.
Obrázek 16: Příklad komunikace aplikace s GUI (zde Nemiver) s GNU Debuggerem.
Snahy o vytvoření takového protokolu samozřejmě nejsou nijak nové, protože podobný přístup nalezneme například v již zmíněném GNU Debuggeru, který lze spustit a ovládat z jiné aplikace (IDE) a navíc obsahuje i možnost ladění aplikací běžících na jiném počítači přes GDB Remote Serial Protocol. Novější přístup nabízí VSCode Debug protokol, s jehož základy se seznámíme v sedmnácté kapitole.
Obrázek 17: Klasické rozhraní GNU Debuggeru ovládaného z příkazového řádku.
Obrázek 18: GNU Debugger ovládaný z aplikace KDbg.
17. VSCode Debug Protocol
Jedním z navrhovaných univerzálních protokolů určených pro komunikaci mezi textovým editorem či IDE na jedné straně a debuggerem na straně druhé, je VSCode Debug Protocol. Jedná se vlastně o obdobu LSP, v níž se ovšem namísto Language Serveru komunikuje s takzvaným Debug Adapterem, který již může pracovat přímo se zvoleným debuggerem. Podporované debuggery (nebo podobně pracující nástroje) naleznete na této adrese. Zajímavé je, že mezi debuggery nalezneme i Z80 Debugger s typickou ikonou.
18. Ukázka konfiguračního souboru .vimrc připraveného pro Python
Podívejme se nyní na to, jak by mohl vypadat konfigurační soubor .vimrc připravený pro práci s Pythonem, přičemž jediným externím pluginem, který použijeme, bude právě modul vim-lsp. Snažil jsem se o minimalizaci počtu konfiguračních voleb v tomto souboru. Jednotlivé části konfiguračního souboru budou popsány v navazující kapitole:
" .vimrc by Tisnik " zakladni volby set nocompatible " rezim nekompatibilni s vi, pouzivejte jako prvni volbu set encoding=utf-8 " interni format znaku ve Vimu (neovlivnuje nacitani a ukladani) set novisualbell " pri chybe se nepipa, ale zablika obrazovka set hlsearch " zvyrazneni vysledku hledani set incsearch " zvyrazneni pri hledani set showmatch " bude se zobrazovat prislusna druha zavorka set showmode " bude se zobrazovat rezim cinnosti (-- INSERT --, -- REPLACE --, -- VISUAL -- ...) set showcmd " bude se zobrazovat prave zadavany prikaz (3dd ...) set ruler " bude se zobrazovat pravitko s pozici kurzoru set shiftwidth=4 " pocet mezer pri posunu bloku pomoci << a >> set expandtab " expanze znaků Tab na mezery set tabstop=4 " pocet mezer odpovidajicich znaku Tab set bs=2 " backspace maze vse " adresare pro docasne soubory, zalohy atd. set backupdir=~/temp,. " adresar pro ulozeni zaloznich souboru set directory=~/temp,. " adresar pro swapovaci soubor set viminfo='20,\"50,n~/temp/_viminfo " nastaveni pro Python augroup __python__ au! au BufRead,BufNewFile *.py map <F1> :LspStatus<cr> au BufRead,BufNewFile *.py map <F2> :!python2 %<cr> au BufRead,BufNewFile *.py map <F3> :!python3 %<cr> au BufRead,BufNewFile *.py map <F4> :LspDocumentDiagnostics<cr> au BufRead,BufNewFile *.py map <F5> :LspHover<cr> au BufRead,BufNewFile *.py map <F6> :LspDocumentSymbol<cr> au BufRead,BufNewFile *.py map <F8> :!pep8 %<cr> au BufRead,BufNewFile *.py map <F9> :!pydocstyle ./%<cr> au BufRead,BufNewFile *.py map <F10> :!radon cc ./%<cr> au BufRead,BufNewFile *.py map <F11> :!pudb3 %<cr> au BufRead,BufNewFile *.py map <F12> <C-W><C-Z> au BufRead,BufNewFile *.py imap <F12> <C-O><C-W><C-Z> au BufRead,BufNewFile *.py imap <Tab> <C-X><C-O> au BufRead,BufNewFile *.py highlight OverLength ctermbg=yellow ctermfg=white guibg=#592929 au BufRead,BufNewFile *.py match OverLength /\%99v.\+/ au BufRead,BufNewFile *.py setlocal omnifunc=lsp#complete augroup END " spusteni Python Language Serveru if (executable('pyls')) au User lsp_setup call lsp#register_server({ \ 'name': 'pyls', \ 'cmd': {server_info->['pyls']}, \ 'whitelist': ['python'] \ }) endif
19. Popis jednotlivých částí konfiguračního souboru
První část souboru .vimrc obsahuje obecná nastavení, z nichž se Pythonu týká především nastavení chování znaku Tab. Podle PEP 8 se nemají znaky Tab ve zdrojových kódech používat, takže nastavíme expanzi Tabů na mezery a určíme odsazení bloků o čtyři znaky (pro posun bloků doleva a doprava budou sloužit příkazy << a >>, popř. jen < a > ve chvíli, kdy je blok vybrán vizuálně):
set shiftwidth=4 " pocet mezer pri posunu bloku pomoci << a >> set expandtab " expanze znaků Tab na mezery set tabstop=4 " pocet mezer odpovidajicich znaku Tab
Dále nastavíme několik klávesových zkratek pro vyvolání externích příkazů. První dvě zkratky jsou mnemotechnické:
Režim | Klávesa | Funkce |
---|---|---|
normální | F2 | spuštění aktuálně editovaného souboru Pythonem 2 |
normální | F3 | spuštění aktuálně editovaného souboru Pythonem 3 |
normální | F8 | kontrola aktuálně editovaného souboru nástrojem pep8 |
normální | F9 | kontrola zápisu dokumentačních řetězců (existence, styl) |
normální | F10 | změření cyklomatické složitosti nástrojem radon |
normální | F11 | spuštění aktuálně editovaného souboru debuggerem pydb |
au BufRead,BufNewFile *.py map <F2> :!python2 %<cr> au BufRead,BufNewFile *.py map <F3> :!python3 %<cr> au BufRead,BufNewFile *.py map <F8> :!pep8 %<cr> au BufRead,BufNewFile *.py map <F9> :!pydocstyle ./%<cr> au BufRead,BufNewFile *.py map <F10> :!radon cc ./%<cr> au BufRead,BufNewFile *.py map <F11> :!pudb3 %<cr>
Třetí skupina klávesových zkratek volá funkce pluginu vim-lsp, o nichž jsme se zmínili výše:
Režim | Klávesa | Funkce |
---|---|---|
normální | F1 | LspStatus – stav LSP, připojení k serveru atd. |
normální | F4 | LspDocumentDiagnostics – analýza a nalezení případných chyb ve zdrojovém kódu |
normální | F5 | LspHover – informace o symbolu, na němž je kurzor |
normální | F6 | LspDocumentSymbol – nalezení symbolů začínajících daným prefixem |
au BufRead,BufNewFile *.py map <F1> :LspStatus<cr> au BufRead,BufNewFile *.py map <F4> :LspDocumentDiagnostics<cr> au BufRead,BufNewFile *.py map <F5> :LspHover<cr> au BufRead,BufNewFile *.py map <F6> :LspDocumentSymbol<cr>
Poslední skupina kláves obsahuje mapování související s pomocnými okny i s výše popsanou funkcí omni completion:
Režim | Klávesa | Funkce |
---|---|---|
normální | F12 | uzavření náhledového okna vytvořeného některými funkcemi LSP |
vkládací | F12 | uzavření náhledového okna vytvořeného některými funkcemi LSP |
vkládací | Tab | zavolání funkce omni completion |
au BufRead,BufNewFile *.py map <F12> <C-W><C-Z> au BufRead,BufNewFile *.py imap <F12> <C-O><C-W><C-Z> au BufRead,BufNewFile *.py imap <Tab> <C-X><C-O>
V předposlední části skriptu provádíme dvě operace. První z nich se zvýraznění těch částí textu na řádku, které přesahují 100 znaků. Samozřejmě si můžete tuto hodnotu změnit a použít například konzervativnějších 80 znaků atd. (opět podle nastavené „štábní kultury“). Druhá operace nastavuje funkci volanou při omni completion:
au BufRead,BufNewFile *.py highlight OverLength ctermbg=yellow ctermfg=white guibg=#592929 au BufRead,BufNewFile *.py match OverLength /\%99v.\+/ au BufRead,BufNewFile *.py setlocal omnifunc=lsp#complete
Poslední část skriptu spustí Python Language Server, samozřejmě s testem, zda vůbec existuje spustitelný soubor pyls:
if (executable('pyls')) au User lsp_setup call lsp#register_server({ \ 'name': 'pyls', \ 'cmd': {server_info->['pyls']}, \ 'whitelist': ['python'] \ }) endif
20. Odkazy na Internetu
- Langserver.org
https://langserver.org/ - Language Server Protocol
https://microsoft.github.io/language-server-protocol/ - Language Server Protocol Specification
https://microsoft.github.io/language-server-protocol/specification - Implementations Language Servers
https://microsoft.github.io/language-server-protocol/implementors/servers - JSON-RPC 2.0 Specification
https://www.jsonrpc.org/specification - Why You Should Know the Language Server Protocol
https://tomassetti.me/what-is-the-language-server-protocol/ - Language Server Protocol: A Language Server For DOT With Visual Studio Code
https://tomassetti.me/language-server-dot-visual-studio/ - Python Language Server
https://github.com/palantir/python-language-server - Jedi – an awesome autocompletion/static analysis library for Python
https://github.com/davidhalter/jedi - What is lsp
https://www.reddit.com/r/vim/comments/7lnhrt/which_lsp_plugin_should_i_use/ - Vim-lsp
https://github.com/prabirshrestha/vim-lsp - Using LSP & clangd in Vim
https://jonasdevlieghere.com/vim-lsp-clangd/ - Seriál Textový editor Vim jako IDE
https://www.root.cz/serialy/textovy-editor-vim-jako-ide/ - Seriál VIM na plný výkon
https://www.root.cz/serialy/vim-na-plny-vykon/ - What about a Common Debug Protocol?
https://kichwacoders.com/2017/07/28/what-about-a-common-debug-protocol/ - Example – Debug Adapter
https://code.visualstudio.com/docs/extensions/example-debuggers - Konfigurační volba complete
http://vimdoc.sourceforge.net/htmldoc/options.html#‚complete‘ - Konfigurační volba completeopt
http://vimdoc.sourceforge.net/htmldoc/options.html#‚completeopt‘ - Make Vim completion popup menu work just like in an IDE
http://vim.wikia.com/wiki/Make_Vim_completion_popup_menu_work_just_like_in_an_IDE - Novinky ve Vimu 7.0
http://vimdoc.sourceforge.net/htmldoc/version7.html#new-omni-completion - IndentTab
https://github.com/vim-scripts/IndentTab - Smart-Tabs
https://github.com/vim-scripts/Smart-Tabs - Omni completion
http://vim.wikia.com/wiki/Omni_completion - Smart mapping for tab completion
http://vim.wikia.com/wiki/Smart_mapping_for_tab_completion - Make Vim completion popup menu work just like in an IDE
http://vim.wikia.com/wiki/Make_Vim_completion_popup_menu_work_just_like_in_an_IDE - The LLDB Debugger
https://lldb.llvm.org/ - GDB: The GNU Project Debugger
https://www.gnu.org/software/gdb/ - gdbgui 0.7.8.3: browser-based gdb frontend using Flask and JavaScript to visually debug C, C++, Go, or Rust
https://pypi.python.org/pypi/gdbgui - Repositář projektu gdbgui
https://github.com/cs01/gdbgui - gdbgui – examples
https://github.com/cs01/gdbgui/tree/master/examples - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Visual Debugging with DDD
http://www.drdobbs.com/tools/visual-debugging-with-ddd/184404519 - Pydb – Extended Python Debugger
http://bashdb.sourceforge.net/pydb/ - Debugging
http://janus.uclan.ac.uk/pagray/labs/debug.htm - Insight
http://www.sourceware.org/insight/ - Using Language Servers to Edit Code in the Eclipse IDE
https://www.eclipse.org/community/eclipse_newsletter/2017/may/article3.php - Eclipse LSP4E
https://projects.eclipse.org/projects/technology.lsp4e - Textový editor Vim 8 (nejenom) ve Fedoře
https://mojefedora.cz/textovy-editor-vim-8-nejenom-ve-fedore/ - Textový editor Vim 8 – změny ve skriptovacím engine Vimu
https://mojefedora.cz/textovy-editor-vim-8-zmeny-ve-skriptovacim-engine-vimu/ - Textový editor Vim 8 – dokončení popisu novinek ve skriptovacím engine
https://mojefedora.cz/textovy-editor-vim-8-dokonceni-popisu-novinek-ve-skriptovacim-engine/ - Sample code illustrating the VS Code extension API
https://github.com/Microsoft/vscode-extension-samples - PEP 8: Tabs or Spaces?
https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces - Eclipse Next-Generation IDE
http://www.eclipse.org/che/ - Atom IDE
https://ide.atom.io/ - Atom Language Server Protocol Client
https://github.com/atom/atom-languageclient - Atom: moderní textový editor
https://www.root.cz/clanky/atom-moderni-textovy-editor/