LaTeX中Python代碼的語法高亮
本文中,“{}”中的字母為LaTeX或Python的包名,只有“Pygments”是Python的包,其他都是LaTeX的包。
LaTeX提供了功能豐富的包(package),以實現某些特定的排版功能。其中,{listings}包和{minted}包主要用於文檔中程序代碼語法的高亮顯示,支持常見的編程語言。
- {listings}基於內置或用戶提供的關鍵字高亮程序代碼語法,高亮的內容受限於關鍵詞的數量,代碼的着色功能也是有限。
- {minted}使用Pymgent高亮程序代碼,提供了更多的高亮內容,基本類似在Jupyter Notebook或Jupyter Qtconsole中的代碼高亮。
使用{listings}包
{listings}的使用簡潔明了:
- 在導言區加載包,並設置包選項。
- 在文檔區使用
\begin{lstlisting}\end{lstlisting}
包圍代碼。 - 編譯文檔,PDF文件中,代碼高亮顯示。
可以根據自己的需要,通過\lstset
設置列表參數,或定義自己的列表新環境。如果沒有特殊的需求,使用{listings}已經足夠。如下使用{listings}高亮Python代碼。
例 1
% !TeX program = xelatex
\documentclass{ctexart}
\usepackage{xcolor} %高亮使用的顏色
\definecolor{commentcolor}{RGB}{85,139,78}
\definecolor{stringcolor}{RGB}{206,145,108}
\definecolor{keywordcolor}{RGB}{34,34,250}
\definecolor{backcolor}{RGB}{220,220,220}
\usepackage{accsupp}
\newcommand{\emptyaccsupp}[1]{\BeginAccSupp{ActualText={}}#1\EndAccSupp{}}
\usepackage{listings}
\lstset{ %高亮代碼設置
language=python, %Python語法高亮
linewidth=0.9\linewidth, %列表list寬度
%basicstyle=\ttfamily, %tt無法顯示空格
commentstyle=\color{commentcolor}, %注釋顏色
keywordstyle=\color{keywordcolor}, %關鍵詞顏色
stringstyle=\color{stringcolor}, %字符串顏色
%showspaces=true, %顯示空格
numbers=left, %行數顯示在左側
numberstyle=\tiny\emptyaccsupp, %行數數字格式
numbersep=5pt, %數字間隔
frame=single, %加框
framerule=0pt, %不划線
escapeinside=@@, %逃逸標志
emptylines=1, %
xleftmargin=3em, %list左邊距
backgroundcolor=\color{backcolor}, %列表背景色
tabsize=4, %制表符長度為4個字符
gobble=4 %忽略每行代碼前4個字符
}
\begin{document}
這是Python語法高亮
\begin{lstlisting}
def prtmotto(n):
for i in range(0,n):
print("lift is short, i use Python!")
\end{lstlisting}
\end{document}
}
使用{minted}包
{minted}包使用Python庫(包)Pygments進行語法着色,所以首先必須安裝Python,並安裝Pygments庫,同時使Python處於Windows系統路徑。
{minted}的代碼語法着色十分具有吸引力,但它沒有{listings}好使用——簡單的直接使用例外。當要在一個環境中包含minted環境,經常會帶來許多問題。如果要定義自己的minted環境,比較方便的形式是使用{tcolorbox}包。
{mdframed}和{tcolorbox}都是定制框(box)的包,前者要自己hacker,后者的已有功能十分豐富,且定制功能強大。{tcolorbox}提供了minted
庫,通過如下形式之一使用:
\usepackage[minted]{tcolorbox}
。自動加載{minted}包- 通過
\tcbuselibrary
加載\usepackage{tcolorbox} \tcbuselibrary{minted} %加載{minted}包
如果要定義新環境或命令,{tcolorbox}提供了如下命令:
\tcbset
設置所有tcolorbox的參數。tcolorbox
和tcblisting
環境建立box或列表list。\newtcblisting
或\newtcbinputlisting
創建新的列表環境(tcblisting)或宏。\newtcolorbox
或\newtcbox
創建新的tcolorbox環境或宏。\DeclareTCBListing
或\NewTCBListing
創建有一個可選參數的列表(還是要2個以上的參數)。需要加載{xparse,listings},但是{minted}應該在{listings}的后面,否則會覆蓋minted選項的設置。\usepackage[xparse,listings,minted]{tcolorbox} 或 \tcbuselibrary{xparse,listings,minted}
- 加載{xparse}后,選項中可以使用邏輯選項(對應xparse中的宏):
IfNoValueTF
、IfValueTF
、IfBooleanTF
,注意用法。 - 結合{minted}的
\newminted
命令,可以方便定制基於{minted}的語法高亮。 - {minted}內部使用了{fancyvrb},要定義新的minted環境,必須包含命令
\VerbatimEnvironment
,如下定義了cpp代碼環境(minted.pdf,P30):\newminted{cpp}{gobble=2,linenos} \newenvironment{env} {\VerbatimEnvironment\begin{cppcode}} {\end{cppcode}}
\newtcblisting
、\DeclareTCBListing
等創建的列表不需要如此!
編譯過程注意以下幾點(以xelatex編譯為例):
- {minted}將制表符轉義為形如
^^T
的符號。在xelatex的編譯選項中加入-8bit
可避免此問題。 - 由於要調用Python的Pygments庫,xelatex的編譯選項中應加入
-shell-escape
。 - 在VScode中,即使文件第一行指定了xelatex編譯,但是仍使用recipes中的
latexmk
(這道菜的做法和xelatex這道菜的做法不同!),因此要專門選擇recipes中的xelatex
編譯。盡量配置快捷鍵,見 VScode 配置為 LaTeX IDE
例 2
% !TEX program = xelatex
\documentclass{ctexart}
\usepackage[breakable,minted]{tcolorbox}
%tcolorbox設置:列表無邊框、無邊距、可跨頁、位於頁面中心、90%的正文寬度
\newtcblisting{pylistings}[1][]{
frame empty,size=minimal,breakable,center,width=0.9\linewidth,
listing only,
minted language=python,
minted options={ %自動移除前置空白、制表為4字符、mathhe和||內的不處理
autogobble,tabsize=4,mathescape,escapeinside=||,
linenos,numberblanklines=false,numbersep=2pt,#1}}
\begin{document}
\begin{pylistings}[showspaces]
|$\sum_{x}^{2}$| |不處理|
def prtmotto(n): # 帶一個參數參數
for i in range(0,n):
print("lift is short, i use Python!")
prtmotto(2)
\end{pylistings}
\begin{pylistings}[]
print("如果沒有參數,環境開始后也要帶一個空的中括號")
\end{pylistings}
\end{document}
例2定義的列表環境包含了一個參數,方便調整某些代碼的格式。使用中要注意以下2點:
- 即使沒有參數,環境后也要有對中括號“[]”,否則會報錯(tcolorbox.pdf中的有些例子也使如此處理的)。
- 參數以逗號分割,因此不能同時傳遞2個參數,如[showspace,numbers=none]會出現錯誤!
例 3
例3使用了\DeclareTCBListing
命令,要先加載{xparse,listings},放於{minted}之前。
為方便使用,定義了一個tcb樣式pycode
,就是常用選項的集合,使用方法也與選項相同。相同的選項參數,后面的設置會覆蓋前面的!所以“minted options”選項移動到新列表的定義中,通過有無“*”判斷是否使用新添加的選項。
pylistings
是定義的新列表環境,一個可選參數和一個s參數,就是“*”號。使用了“*”就表示要添加額外的列表控制選項(主要控制空格的顯示與否)。否則,就按照基本樣式定制列表顯示。和前一個例子的使用不同:如果沒有添加新選項,環境后無需加中括號“[]”;否則,將新選項放在中括號中,前面加“*”並放於環境名后。
% !TEX program = xelatex
\documentclass{ctexart}
\usepackage[breakable,xparse,listings,minted]{tcolorbox}
\tcbset{pycode/.style={
frame empty,size=minimal,breakable,center,width=0.9\linewidth,
listing only,
minted language=python}}
\DeclareTCBListing{pylistings}{s o}{pycode,
IfBooleanTF={#1}
{minted options={ %自動移除前置空白、制表為4字符、mathhe和||內的不處理
autogobble,tabsize=4,mathescape,escapeinside=||,
linenos,numberblanklines=false,numbersep=2pt,#2}}
{minted options={ %自動移除前置空白、制表為4字符、mathhe和||內的不處理
autogobble,tabsize=4,mathescape,escapeinside=||,
linenos,numberblanklines=false,numbersep=2pt}}}
\begin{document}
\begin{pylistings}
|$\sum_{x}^{2}$| #不處理
def prtmotto(n):
for i in range(0,n):
print("lift is short, i use Python!")
\end{pylistings}
\begin{pylistings}*[]
|$\sum_{x}^{2}$| #注意和上一個比較!
def prtmotto(n):
\end{pylistings}
\begin{pylistings}*[showspaces]
# test
def prtmotto(n):
for i in range(0,n):
print("lift is short, i use Python!")
\end{pylistings}
\end{document}
問題:
- "||"中的字符轉義后,前后空一格
- 環境名后無參數,且代碼第一行為注釋語句時,注釋語句會被當成參數而報錯。仍舊能編譯成功,但多出來一個"#"。可傳遞一個空參數,即
{pylisting}*[]
。
可參考的處理方案
如果一個文檔中包含了集中不同的程序語言,要高亮顯示,可以參考如下方法:
\newcommand{\mynewminted}[3]{%
\newminted[#1]{#2}{#3}%
\tcbset{myminted/#1/.style={minted language=#2,minted options={#3}}}}
\mynewminted{mypylb}{python}{autogobble,mathescape,escapeinside=||,
linenos,numberblanklines=false,numbersep=2pt}
\newtcblisting{pylistings}[2][]{listing only,myminted/#2,#1}
%2 %用tcolorbox, breakable選項可跨頁
\newminted[mypyc]{python}{autogobble,mathescape,escapeinside=||}
\newenvironment{mypy}[2][]
{%
\def\mypyenvironment{#1}%save the environments
\VerbatimEnvironment%
\begin{tcolorbox}[#2]%
\begin{\mypyenvironment}}%
{%
\end{\mypyenvironment}%
\end{tcolorbox}%
}
%% 用法
\begin{pylistings}{mypylb}
def fun(): #fanhui
return
\end{pylistings}
\begin{mypy}{mypyc}{}
def fun(): #fanhui
return
\end{mypy}
minipage不能跨頁(page break),不用考慮。如果代碼的第一行使用了“||”貌似有問題,看前面的例子。
{xparse}包
{xparse}的\NewDocumentCommand
或\NewDocumentEnvironment
的使用中,參數(arguments)分為兩類:
- 強制性(mandatory)參數(找不到會報錯),分別用字母“m,r,R,v,b”表示,在環境名后以大括號“{}”包圍實參。
- 可選(optional)參數,分別用字母“o,d,O,D,s,t,e,E”表示,在環境名后以中括號“[]”包圍實參。
- 大寫的字母一般要求提供一個默認值,如
{s O{test} m}
。 - 不是必須有強制性參數!強制性是參數設置的屬性。可以設置2個可選參數。
- 如果要定義的參數不多,比較方便的是在其他參數前加s參數(BooleanTrue),后跟其他參數。測試中只用一個o參數會報錯,但又不想用強制參數,所以前面加s,需要了加個星號后跟參數,不需要的話什么都不做。比較方便。
代碼使用Jupyter Notebook樣式
- 在Jupyter Notebook或QTconsole中輸入代碼,並保存為LaTeX文件。
- 從中找到“In :”和“Out:”的顏色。
- LaTeX文件中:定義顏色;代碼前加“In :”或“Out:”,並添加顏色。
- 也可以之間在Jupyter Notebook中輸入為Markdown,導出為LaTeX,然后調整樣式。
- 這是看到:Mckinney2017、VanderPlas2017后想到的。
后記
學生實訓,臨時起意,給學生講Python基礎。先是用{listings},后來不滿意。直接用{minted},又想控制部分代碼的空格顯示。用了{tcolorbox}后,還是想很好的控制空格顯示,發現用網上的方法和自定義函數並不能令自己滿意。看{tcolorbox}文檔,接觸{xparse}中的參數控制,形成了最后一個例子的結果。
記錄上面過程,比較辛苦,但總歸是有了兩種以上的方法可供選擇,這應該是好事!
參考文章
https://tex.stackexchange.com/questions/124657/combine-minted-and-tcolorbox