Balík fancyvrb, popsaný v předchozím článku, dovede ledacos, ale pořád je to „jen“ opisování zdrojového kódu s přidanou hodnotou. Pokročilejší variantu pro sazbu zdrojových kódů představuje balík listings, který byl vytvořen právě pro tento účel. Analyzuje zdrojový kód, vyhledává a zvýrazňuje v něm syntaktické entity, jako jsou klíčová slova, identifikátory a podobně.
Aby to fungovalo, potřebuje vědět, v jakém jazyce je kód zapsán. Podporovaných jazyků je kolem stovky, jejich sortiment najdete v dokumentaci. Toto nastavení bývá globální, takže tentokrát začneme společnými parametry. U každého konkrétního vloženého kódu je lze samozřejmě doplnit či změnit podle potřeby.
Parametry mají opět podobu název=hodnota a jsou oddělovány čárkami, stejně jako u fancyvrb a řady novějších balíků. Jejich výchozí nastavení má na starosti příkaz \lstset. Záhlaví dokumentu proto typicky obsahuje něco jako:
\usepackage{listings}
\lstset{
language=java,
showstringspaces=false,
}
Příkaz \lstset se dá používat opakovaně. Pozdější definice se přidají k předchozím, případně je přepíší.
Pro vlastní sazbu kódu balík nabízí prostředí lstlisting, kterým kód obklopíte, nebo příkaz \lstinputlisting{ soubor} pro vložení kódu ze souboru. Pro krátké ukázky zařazené do textu je k dispozici příkaz \lstinline, který se chová podobně jako \verb. Znak, který následuje bezprostředně za ním, ohraničuje zepředu a zezadu vložený kód, například \lstinline#if (x<0)#.
Základy máme, můžeme vysázet první kód:
\begin{lstlisting}
if (city.getName() != null) {
name = city.getName();
} else {
name="Neznámé město";
}
\end{lstlisting}
Když si pozorně prohlédnete výsledek, můžete si všimnout tří věcí:
- Jsou zvýrazněna klíčová slova jazyka Java.
- Vypadá vážně hnusně.
- Písmeno „ě“ vycestovalo na začátek slova.
Šeredný vzhled je způsoben tím, že ve výchozím nastavení se snaží dodržovat sloupce znaků ze zdrojového textu. Vlastně provádí neproporcionální sazbu s proporcionálním písmem. To nemůže dopadnout dobře. Šířka sloupců je nastavena podle širokých znaků, mnohé jsou ale užší a sazba je řídká a nepravidelná. Srovnejte například sousední slova „name“ a „city“ na druhém řádku. Třešničkou na dortu je, že ani tak nejsou všechny znaky prvních dvou řádků přesně pod sebou.
Existují dvě řešení. Pokud je pro vás zarovnání sloupců důležité, přejděte na neproporcionální písmo. K tomu lze využít parametr basicstyle, který nastaví výchozí formátování. Přidáme-li basicstyle=\ttfamily, bude výsledek vypadat takto:
Podle mého názoru lepší variantou je rezignovat na zarovnání sloupců. U kódu je většinou důležité jen odsazení levého okraje, které vyznačuje vnořování bloků. Na pozicích dalších znaků spíš nezáleží. Práci se sloupci řídí parametr columns , jehož výchozí ohyzdnou hodnotou je fixed . Doporučuji přejít na fullflexible , která dodržuje levé odsazení, ale obsah řádku vysází typograficky optimálně. V nabídce jsou ještěflexible a spaceflexible . Sází správně slova a přidáváním mezer mezi ně se snaží alespoň částečně zachovat svislé uspořádání originálu.
S columns=fullflexible vypadá ukázkový kód mnohem lépe:
Zbývá problém s „ě“. Znaky mimo sadu Latin 1 se chovají divně. Řešení bohužel není jednoduché a závisí na implementaci TeXu. Pokud používáte moderní implementace (XeTeX nebo LuaTeX) se vstupem v kódování UTF-8, je nejvhodnější přidat kódy znaků, které má považovat za běžná písmena, do makra \lst@DefEC . Tento přístup doporučila Ulrike Fischer na StackExchange.
Jestliže dosud používáte starší implementaci, například pdfTeX, měli byste se podívat do kalendáře, akceptovat, že už máme 21. století, a přejít na některou z předchozího odstavce. Připravujete se totiž o řadu laskomin, jako je používání systémových písem, písem ve formátu OpenType, automatický zákaz rozdělení řádku za jednopísmennými spojkami a předložkami, který pro češtinu zajistí balík polyglossia, a mnohé další.
Trváte-li na svém pdfTeXu, můžete využít mechanismus automatického nahrazování, kterým balík listings disponuje. Řídí jej parametr literate, jemuž zadáte trojice: co nahradit, čím to nahradit a jaká je délka nahrazovaného řetězce. Tímto způsobem se dají problémové znaky převést na příkazy LaTeXu a odstranit tak jejich podivné chování. Například nahrazení pro „ě“ vypadá takto: {ě}{{\v{e}}}1.
Připravili jsme pro vás minimalistické balíky, které načtou balík listings a provedou potřebné konfigurační změny. Můžete je použít místo listings:
- listings-cz pro XeLaTeX a LuaLaTeX,
- listings-cz-oldschool pro pdfLaTeX.
Uvést balík listings do chodu nám dalo trochu práce, teď už to ale bude odsýpat. Řada parametrů je podobných až shodných s fancyvrb. Začněme však u těch, které opis vstupu z principu mít nemůže. Dá se totiž nastavit, jak mají vypadat jednotlivé lexikální kategorie. K dispozici jsou tyto parametry:
keywordstyle– klíčová slova,identifierstyle– identifikátory,stringstyle– řetězce znaků,commentstyle– komentáře.
Výchozí tučné písmo pro klíčová slova mi připadá vhodné, u identifikátorů dávám přednost kurzívě a komentáře odliším barvou. Po přidání
\lstset{
identifierstyle=\itshape,
commentstyle=\color{olive},
}
vypadá výsledek takto:
Pokud by se v analýze jazyka objevily nějaké nesrovnalosti, lze do seznamu klíčových slov přidat další pomocímorekeywords={raz,dva,tři} či odebrat stávající parametrem deletekeywords={pět,šest} . Parametrů, které řídí interpretaci jazyka, je celá řada. U podporovaných jazyků byste je neměli potřebovat, ale můžete se rozšoupnout a definovat si nějaký nepodporovaný.
Chcete-li vysázet jen část kódu, určete pomocí firstline, na kterém řádku má sazba začít, a/nebo pomocí lastline, kde má skončit. K dispozici je i linerange, kde zadáváte obě hranice najednou: linerange={8-15}. V jednom linerange může být i několik rozmezí, ale smysluplné využití konstrukcí typu linerange={8-15,35-41} bude hodně vzácné.
Parametrem gobble odstraníte úvodní odsazení. Hodnotou je počet znaků, které má odebrat ze začátku každého řádku, například gobble=4.
Jistě jste si domysleli, že parametrem showstringspaces řídíte, zda se mají či nemají názorně zobrazovat mezery v řetězcích znaků. Implicitně je zapnutý, mám ve zvyku jej vypínat. Kromě něj je k dispozici ještě showspaces pro zobrazení všech mezer a showtabs pro tabulátory.
Číslování řádků zajistí parametr numbers s hodnotami left, right a none podle toho, na které straně chcete čísla mít. stepnumber=5 způsobí, že číslovat se bude jen kažý 5. řádek, a numbersep=1em určuje velikost mezery mezi čísly řádků a kódem. Tentokrát máme k dispozici i numberstyle pro nastavení vzhledu čísel řádků: numberstyle=\footnotesize\color{olive}.
Ke stanovení počátečního čísla slouží parametr firstnumber. Hodnotou může být konkrétní číslo nebo klíčové slovo last, které naváže v číslování na předchozí listing. U složitějších případů by se vám mohl hodit parametr name. Ukázky kódu se stejnou hodnotou name patří k sobě a představují úseky společného kódu. Neřeknete-li pomocí firstnumber jinak, bude číslování jejich řádků vždy navazovat na předchozí listing se stejnou hodnotou name. Lze tak prezentovat složitější kód po kouskách a prokládat jej například ukázkami datových či konfiguračních souborů.
Na rámečcích se můžete vyřádit. Základem je parametr frame, kterému můžete přiřadit jednu z připravených hodnot:
none– žádný, výchozí hodnota,single– rámeček kolem kódu,leftline– svislá čára vlevo,topline– vodorovná čára nad kódem,bottomline– vodorovná čára pod kódem,lines– vodorovná čára nad a pod kódem,shadowbox– rámeček kolem kódu se „stínem“, tohle prosím nepoužívejte.
Kromě toho můžete parametru frame přiřadit kombinaci malých a velkých písmen trbl, kterými nastavíte rámeček nahoře, vpravo, dole a vlevo. Malé písmeno znamená, že na příslušné straně má být jednoduchý rámeček, velké vytvoří rámeček dvojitý. Například frame=Ltb vytvoří dvojitý rámeček po levé straně a jednoduchý nahoře a dole.
Z přehršle parametrů pro úpravu vzhledu rámečku vyberme třeba framerule=0.5pt pro tloušťku čáry, rulecolor=\color{teal} pro její barvu, rulesep=1.5ex pro šířku mezery mezi kódem a rámečkem. Tu lze nastavovat i individuálně pro jednotlivé strany. Pokud například chcete oddálit rámeček na levé straně, aby čísla řádků byla uvnitř rámečku, použijte něco jako framexleftmargin=2em.
Pro popisek jsou k dispozici dva parametry. title je minimalistický a jednoduše zobrazí svůj obsah. Naproti tomu caption připomíná titulky obrázků či tabulek. Přidá ke svému obsahu úvodní „Listing N:“, kde N je automaticky vygenerované číslo. Parametr captionpos s hodnotou t nebo b řídí, zda popisek bude nahoře nebo dole.
Používáte-li caption, můžete příkazem \lstlistoflistings vygenerovat seznam takto titulkovaných kódů, podobně jako standardní \listoffigures vysází seznam obrázků. Kódu, který nemá být zařazen do seznamu, přidejte parametr nolol. Návěští pro křížové odkazy vytvoříte parametrem label.
Jestliže vám nevyhovuje výchozí název „Listing“, můžete jej změnit předefinováním příkazu \lstlistingname. Pomocí \lstlistingnamestyle pak nařídíte, jak má vypadat. Název seznamu kódů změníte redefinicí příkazu \lstlistlistingname.
K nastavení základní barvy kódu lze využít výše zmiňovaný parametr basicstyle. Tentokrát máme navíc k dispozici i backgroundcolor, kterým lze určit barvu pozadí celého bloku.
Pojďme si vyzkoušet nové hračky. Nastavíme obecný styl, nadefinujeme si příkaz pro vložení kódu z externího souboru a o kus dál jej zavoláme.
\lstset{
language=java,
columns=fullflexible,
numbers=left,
numberstyle=\footnotesize\color{olive},
frame=Ltb,
framerule=1pt,
rulecolor=\color{black!30},
backgroundcolor=\color{black!7},
identifierstyle=\itshape,
commentstyle=\color{olive},
showstringspaces=false,
}
\renewcommand{\lstlistlistingname}{Seznam zdrojových kódů}
\renewcommand{\lstlistingname}{Kód}
\renewcommand{\lstlistingnamestyle}{\bfseries}
\newcommand{\ukazka}[1]{\lstinputlisting[caption=#1]{#1}}
...
\ukazka{mesta.java}
Chcete-li v kódu něco zvýraznit, abyste přitáhli čtenářovu pozornost, máte dvě základní možnosti. Dá se to udělat zvenčí bez zásahu do vlastního kódu. Slouží k tomu parametry emph , který obsahuje seznam zvýrazněných identifikátorů, aemphstyle pro nastavení stylu zvýraznění. Když přidám
emph={city,getName},
emphstyle=\color{purple}\itshape,
budou všechny výskyty identifikátorů city a getName vysázeny tmavě červenou barvou.
Druhou možností je vložit přímo do kódu příkazy LaTeXu, které se mají provést. Slouží k tomu parametr escapechar . Jeho hodnotou je znak, kterým zahájíte a ukončíte takto vložený kód. Dokumentace to nezmiňuje, ale nelze použít úplně každý, například A s háčkem nefungovalo. Odhaduji, že se v kódování UTF-8 musí vejít do jednoho bajtu. Zvolil jsem escapechar=Ý , u kterého je také krajně nepravděpodobné, že by se v kódu vyskytlo.
Řekněme, že bych chtěl zvýraznit tmavě červenou barvou celou podmínku. První řádek kódu bych proto upravil na
if (Ý\color{purple}\emph{city.getName}() != \textbf{null}Ý) {
Vložení LaTeXových příkazů vyřadí formátování balíku, takže kurzívu pro identifikátor a tučné písmo pro klíčové slovo musím zajistit vlastními silami. Moc nedivočte. Pokud například použijete prostředí, musí začínat i končit ve stejném vloženém kódu. Autor také doporučuje vyhýbat se ve vloženém kódu příkazům balíku listings.
Opět si můžete vytvářet vlastní „listingová“ prostředí, když třeba potřebujete do dokumentu zařadit ukázky dvou různých jazyků, nebo chcete vizuálně odlišovat více různých typů ukázek. Slouží k tomu příkaz: \lstnewenvironment{ název}{ zahájení}{ ukončení}.
Podobá se standardnímu \newenvironment z LaTeXu. V parametrech uvedete jeho název, příkazy, které se mají provést na začátku prostředí (typicky \lstset), a příkazy prováděné na jeho konci. V této základní podobě ale neumožňuje volby. Obvykle si chceme zachovat možnost upravit parametry kódu. V tom případě je třeba přidat za název prostředí nepovinné argumenty s počtem argumentů prostředí (obvykle 1) a výchozí hodnotou. Řekněme, že bych v dokumentu kromě Javy potřeboval ještě ukázky XSLT. Definoval bych si prostředí
\lstnewenvironment{xslt}[1][]{\lstset{
language=xslt,
basicstyle=\color{teal},
#1}}{}
Má jeden parametr pro případné další nastavení vzhledu kódu. Je nepovinný a výchozí hodnota je prázdná, což zajistí druhá dvojice hranatých závorek. Použití by pak vypadalo třeba takto:
\begin{xslt}[numbers=none,emph={match}]
<xsl:template match="cenik">
<table>
<xsl:apply-templates>
<xsl:sort select="nazev"/>
</xsl:apply-templates>
</table>
</xsl:template>
\end{xslt}
Vynechal jsem ještě několik desítek dalších parametrů, konfigurovatelnost balíku listings je opravdu impozantní. Najdete je v jeho rozsáhlé dokumentaci.
Příště seriál o sazbě zdrojových kódů uzavřeme balíkem minted.
(Autorem obrázků je Pavel Satrapa.)







