虽然只剩一个月就2021年了,但仍然有很多人使用着非常原始的工具书写LaTeX(包括一个星期前的我在内),那么我就不废话直奔主题,请使用现代化的工具——VS Code。
起源
就在前不久,由于意外获得了一块1T的固态硬盘,这使得我有足够的空间在笔记本上装了个Ubuntu双系统,然后在折腾各种工具之余,我也开始测试在Ubuntu上使用什么编辑器写起LaTeX来最舒服。
最先安装的自然是TeXstudio,这家伙比在win10上看起来舒服了不少,随后我顺便测试了一下VS Code,然后我流泪了,我深刻地理解了什么叫做 a modern approach.
配置
如何安装LaTeX就不废话了,但要注意两点:
- 下载 TeXlive 而不是 CTeX
- 下载
texlive-full
如何在VS Code中配置 LaTeX呢?
分为以下几部分:
安装 LaTeX Workshop
在插件商店中搜索 LaTeX Workshop
,安装即可。
打开插件设置
这里有两种方法,一个是在 设置-拓展-LaTeX
中,瞪大眼睛一个个查看选项并勾选。
另一种是在 settings.json
中设置,只需 Ctrl+Shift+P 打开命令面板,然后输入 Open Settings(json)
,如果你的VScode不巧抽风了,Ctrl+P+输入>
,之后再输入 Open Settings(json)
也一样。
复制配置代码
"latex-workshop.latex.recipes": [
{
"name": "XeLaTeX",
"tools": [
"xelatex"
]
},
{
"name": "PDFLaTeX",
"tools": [
"pdflatex"
]
},
{
"name": "xelatex -> bibtex -> xelatex*2",
"tools": [
"xelatex",
"bibtex",
"xelatex",
"xelatex"
]
},
// {
// "name": "latexmk 🔃",
// "tools": [
// "latexmk"
// ]
// },
// {
// "name": "latexmk (latexmkrc)",
// "tools": [
// "latexmk_rconly"
// ]
// },
// {
// "name": "latexmk (lualatex)",
// "tools": [
// "lualatexmk"
// ]
// },
{
"name": "pdflatex ➞ bibtex ➞ pdflatex * 2",
"tools": [
"pdflatex",
"bibtex",
"pdflatex",
"pdflatex"
]
}
// {
// "name": "Compile Rnw files",
// "tools": [
// "rnw2tex",
// "latexmk"
// ]
// },
// {
// "name": "Compile Jnw files",
// "tools": [
// "jnw2tex",
// "latexmk"
// ]
// },
// {
// "name": "tectonic",
// "tools": [
// "tectonic"
// ]
// }
],
"latex-workshop.latex.tools": [
{
"name": "xelatex",
"command": "xelatex",
"args": [
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
"-shell-escape",
"%DOCFILE%"
],
"env": {}
},
{
"name": "latexmk",
"command": "latexmk",
"args": [
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
"-pdf",
"-outdir=%OUTDIR%",
"%DOC%"
],
"env": {}
},
{
"name": "lualatexmk",
"command": "latexmk",
"args": [
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
"-lualatex",
"-outdir=%OUTDIR%",
"%DOC%"
],
"env": {}
},
{
"name": "latexmk_rconly",
"command": "latexmk",
"args": [
"%DOC%"
],
"env": {}
},
{
"name": "pdflatex",
"command": "pdflatex",
"args": [
"-synctex=1",
"-interaction=nonstopmode",
"-file-line-error",
"%DOC%"
],
"env": {}
},
{
"name": "bibtex",
"command": "bibtex",
"args": [
"%DOCFILE%"
],
"env": {}
},
{
"name": "rnw2tex",
"command": "Rscript",
"args": [
"-e",
"knitr::opts_knit$set(concordance = TRUE); knitr::knit('%DOCFILE_EXT%')"
],
"env": {}
},
{
"name": "jnw2tex",
"command": "julia",
"args": [
"-e",
"using Weave; weave(\"%DOC_EXT%\", doctype=\"tex\")"
],
"env": {}
},
{
"name": "jnw2texmintex",
"command": "julia",
"args": [
"-e",
"using Weave; weave(\"%DOC_EXT%\", doctype=\"texminted\")"
],
"env": {}
},
{
"name": "tectonic",
"command": "tectonic",
"args": [
"--synctex",
"--keep-logs",
"%DOC%.tex"
],
"env": {}
}
],
这是对应你的编译流程,一般来说只需通过第一个 XeLaTeX
就够了,但如果你插入了参考文献,就得使用第三个流程 xelatex -> bibtex -> xelatex*2
,这对于老手来说应该不陌生了。
"latex-workshop.message.error.show": false,
"latex-workshop.message.warning.show": false,
"latex-workshop.intellisense.package.enabled": true,
"latex-workshop.latex.autoClean.run": "onFailed",
"latex-workshop.latex.recipe.default": "lastUsed",
"latex-workshop.showContextMenu": true,
前两个是关闭错误和警告消息提示,不然会一直在右下角弹窗,非常烦人,一般都是自己从状态栏看报错信息。
双向同步
接下来是稍微复杂一点的配置——双向同步,也就是你能从当前代码跳转到PDF对应行,也能从PDF所在行跳转到对应代码处。
"latex-workshop.view.pdf.viewer": "external",
"latex-workshop.view.pdf.external.synctex.command": "C:\\Program Files\\SumatraPDF\\SumatraPDF.exe",
"latex-workshop.view.pdf.external.viewer.command": "C:\\Program Files\\SumatraPDF\\SumatraPDF.exe",
"latex-workshop.view.pdf.invert": 1,
"latex-workshop.view.pdf.external.synctex.args": [
"-forward-search",
"%TEX%",
"%LINE%",
"-reuse-instance",
"-inverse-search",
"\"C:\\Users\\Theigrams\\AppData\\Local\\Programs\\Microsoft VS Code\\code.exe\" \"C:\\Users\\Theigrams\\AppData\\Local\\Programs\\Microsoft VS Code\\resources\\app\\out\\cli.js\" -r -g \"%f:%l\"",
"%PDF%"
],
第一行是设置使用外置PDF浏览器,VSCode内置的会比较卡。
第二三行是 SumatraPDF.exe
所在的路径。
第5-13行自己对照着修改VScode和SumatraPDF的路径即可。
然后记得打开SumatraPDF-设置-高级选项
,找到 InverseSearchCmdLine
那一行修改,记得换成自己的路径。
InverseSearchCmdLine = "C:\Users\Theigrams\AppData\Local\Programs\Microsoft VS Code\code.exe" "C:\Users\Theigrams\AppData\Local\Programs\Microsoft VS Code\resources\app\out\cli.js" -r -g "%f:%l"
A modern approach
至此,你已经可以开始用VScode编写LaTeX文档了,但如果你看过这篇文章1700页数学笔记火了!全程敲代码,速度飞快易搜索,硬核小哥教你上手LaTeX+Vim - 知乎 (zhihu.com),大概你就能明白快速使用LaTeX的核心就是Snippets。
使用Snippets
请点进 Snippets · James-Yu/LaTeX-Workshop Wiki (github.com),详细观看里面有哪些Snippets,
核心Snippets有如下:
Prefix | Environment name |
---|---|
BEQ |
equation |
BAL |
align |
BIT |
itemize |
BEN |
enumerate |
Prefix | Sectioning level |
---|---|
SCH | chapter |
SSE | section |
SSS | subsection |
SS2 | subsubsection |
Prefix | Shortcut | Command |
---|---|---|
MRM |
ctrl+m, ctrl+r | \mathrm{${1}} |
MBF |
ctrl+m, ctrl+s | \mathsf{${1}} |
MBF |
ctrl+m, ctrl+b | \mathbf{${1}} |
MBB |
ctrl+m, ctrl+shift+b | \mathbb{${1}} |
MCA |
ctrl+m, ctrl+c | \mathcal{${1}} |
MIT |
ctrl+m, ctrl+i | \mathit{${1}} |
MTT |
ctrl+m, ctrl+t | \mathtt{${1}} |
注意使用snippet时,如果有多个空,在填写完第一个之后按Tab
就能自动跳到第二个空。
自制Snippets
workshop自带的这些Snippets是远远不够的,而且众口难调,不如自己配置Snippets来得爽快。
打开设置-用户代码片段-latex.json
,然后你就可以模仿样例输入完成自己的Snippets.
例如,我们可以自制一个插入图片的Snippet:
"Input a figure": {
"prefix": "fig",
"body": [
"\\begin{figure}[htp]",
"\t\\centering",
"\t\\includegraphics[width=0.7linewidth]{fig/$1}",
"\t\\caption{$2}",
"\t\\label{fig:$3}",
"\\end{figure}",
"$0"
],
"description": "Input a figure"
},
直接输入 fig
就能看到Snippet提示:
注意生成的代码中有三个细微的竖线,也就是上面代码中 $1,$2,$3
的地方,我们可以填完内容后用 Tab
跳到下一个。
我们可以继续制作一个三线表的snippet:
"three horizontal lines": {
"prefix": "3linetable",
"body": [
"\\begin{table}[htbp]",
"\t\\centering\n\t\\caption{$1}",
"\t\\label{tab:$2}",
"\t\\begin{tabular}{ccc}",
"\t\t\\toprule",
"\t\t姓名 & 学号 & 性别 \\\\\\\\ \\midrule",
"\t\tSteve Jobs & 001 & Male \\\\\\\\",
"\t\tBill Gates & 002 & Female \\\\\\\\ \\bottomrule",
"\t\\end{tabular}",
"\\end{table}\n"
],
}
注意,由于 \
的转移符非常特殊,要生成换行符号 \\
,我们得使用 \\\\\\\\
来完成。
插入图片
虽然我们在上面设置了插入图片的snippet,但还是得自己把图片放到文件夹下,一点也不智能。
所以我们得使用modern一点的方法:
首先进入拓展商店,安装插件 Paste Image
,它的作用就是自动帮你把剪切板中的图片放到设定目录中,并生成特定代码。
然后是配置,还是打开 settings.json
,将以下代码复制进去:
"pasteImage.defaultName": "f-H-m-s",
"pasteImage.path": "${currentFileDir}/fig",
"pasteImage.filePathConfirmInputBoxMode": "onlyName",
"pasteImage.showFilePathConfirmInputBox": true,
"pasteImage.insertPattern": "\\begin{figure}[htp]\n\t\\centering\n\t\\includegraphics[width=0.7\\linewidth]{fig/${imageFileName}}\n\t\\caption{${imageFileNameWithoutExt}}\n\t\\label{fig:${imageFileNameWithoutExt}}\n\\end{figure}\n",
第2行是设置图片保存路径,我设置的是存在当前目录下的 fig
文件夹中,
第4行是指保存之前弹出一个框,让你修改文件名
第5行是插入后生成代码
流程大致如下:
- 截图,或将图片复制到剪切板
Ctrl+Alt+V
(如果没反应,大概率是快捷键冲突了,可以自行把其他屏蔽)- 在弹出的box中修改名称
- 自动插入代码
设置快捷键
对于一些非常高频的操作,snippet也显得不够便捷,这时我们就需要自定义快捷键。
例如对于行内数学公式环境,一般使用 Ctrl+M 就能自动生成 $ $
,并且光标还在中间。
这时我们打开 设置-键盘快捷方式
,打开keybindings.json
复制如下代码即可:
{
"key": "ctrl+m",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"snippet": "$ $1 $ $0"
}
},
第6行中,第1,3个 $
是用于产生数学公式环境,$1
是指待会儿光标移到此处,待输入文件,$0
是指输完后按 Tap
可以调到公式环境外面去。
但是,有的时候我们还有这种需求:选中一段文本,按 Ctrl+M 希望能在其两端插入 $
符号,这时只需稍微修改一点即可满足需求:
{
"key": "ctrl+m",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"snippet": "$ ${TM_SELECTED_TEXT}$1 $ $0"
}
},
自用模板
最后贴一个自用模板,定理样式来源于 HaoyunQin 的 LaTeX Snippets
\documentclass[a4paper]{article} \usepackage[UTF8]{ctex} %中文 \usepackage{amsmath,amsfonts,amssymb,amsthm,mathrsfs,bm} \usepackage{setspace} %行间距 \usepackage{geometry} %页边距 \geometry{left=3.18cm,right=3.18cm,top=2.54cm,bottom=2.54cm} %\usepackage{pifont} %带圈数字 \ding{172} \usepackage{graphicx} %插图 \includegraphics[width=1cm]{picture.jpg} \usepackage{subfigure} \usepackage{mdframed} %\usepackage{wrapfig} \usepackage[ colorlinks=true, citecolor=blue, linkcolor=red, anchorcolor=green, urlcolor=blue, bookmarksopen=true,bookmarksnumbered=true]{hyperref} \usepackage{enumitem} \usepackage[square,numbers,sort&compress]{natbib} \usepackage{tcolorbox} \tcbuselibrary{most} \newenvironment{myitemize}{\begin{itemize}}{\end{itemize}} \tcolorboxenvironment{myitemize} {blanker, before skip = 6pt, after skip = 6pt, borderline west = {3mm}{0pt}{red}} \usepackage{array} \usepackage{float} \usepackage{booktabs} %\toprule \midrule \bottomrule \usepackage{multicol} \usepackage{minted} \usepackage{tabularx} %%%%%%%%%%%%%%%%%%%%%%%%%% Define some useful colors %%%%%%%%%%%%%%%%%%%%%%%%%% \definecolor{ocre}{RGB}{243,102,25} \definecolor{mygray}{RGB}{243,243,244} \definecolor{deepGreen}{RGB}{26,111,0} \definecolor{shallowGreen}{RGB}{235,255,255} \definecolor{deepBlue}{RGB}{61,124,222} \definecolor{shallowBlue}{RGB}{235,249,255} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newtheoremstyle{mytheoremstyle}{3pt}{3pt}{\normalfont}{0cm}{\rmfamily\bfseries}{}{\newline}{{\color{black}\thmname{#1}~\thmnumber{#2}}\thmnote{\,--\,#3}} \newtheoremstyle{myproblemstyle}{3pt}{3pt}{\normalfont}{0cm}{\rmfamily\bfseries}{}{\newline}{{\color{black}\thmname{#1}~\thmnumber{#2}}\thmnote{\,--\,#3}} \theoremstyle{mytheoremstyle} \newmdtheoremenv[linewidth=1pt,backgroundcolor=shallowGreen,linecolor=deepGreen,leftmargin=0pt,innerleftmargin=20pt, innerrightmargin=20pt,]{theorem}{Theorem}[section] \theoremstyle{mytheoremstyle} \newmdtheoremenv[linewidth=1pt,backgroundcolor=shallowBlue,linecolor=deepBlue,leftmargin=0pt,innerleftmargin=20pt, innerrightmargin=20pt,]{definition}{Definition}[section] \theoremstyle{myproblemstyle} \newmdtheoremenv[linecolor=black,leftmargin=0pt,innerleftmargin=10pt,innerrightmargin=10pt,]{problem}{Problem}[section] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % \usepackage{listings} \usepackage{fontspec} \usepackage{xcolor} \newcommand{\univname}{\href{https://www.buaa.edu.cn}{Beihang Universite}} \newcommand{\depname}{\href{http://math.buaa.edu.cn}{School of Mathematical Sciences}} \newcommand{\faculty}{\href{http://lmib.buaa.edu.cn}{MOE Key Laboratory of Mathematics Informatics and Behavioral Semantics(LMIB)}} \newcommand{\horrule}[1]{\rule{\linewidth}{#1}} \newcommand{\entit}{Geometric Deep Learning} \newcommand{\cntit}{基于图和流形上的深度学习} \renewcommand{\theFancyVerbLine}{\sffamily \textcolor[rgb]{0.5,0.5,1.0}{\scriptsize \oldstylenums{\arabic{FancyVerbLine}}}} \newminted{ruby}{mathescape,linenos,breaklines,breakanywhere,style=xcode,frame=single} \newminted{python}{texcl=true,mathescape,linenos,breaklines,breakanywhere,style=colorful,frame=single} \definecolor{darkblue}{rgb}{0.0,0.0,0.3} \newtcbox{\mybox}[1][red] {on line, arc = 3pt, colback = #1!10!white, colframe = #1!50!black, boxsep = 0pt, left = 1pt, right = 1pt, top =2.5pt, bottom = 1pt, boxrule = 0.8pt, bottomrule = 0.8pt, toprule = 0.8pt} \def\cdg#1{\mybox[green]{\mintinline{ruby}{#1}}} % text style \def\cdr#1{\mybox[red]{\mintinline{ruby}{#1}}} %\def\cdr#1{\mintinline{ruby}{#1}} %%%%%%%%%%%%%%%%%%%%%%%%%% main text %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{document} \begin{titlepage} \begin{center} \horrule{0.5pt} \\[0.4cm] \vspace{-1.5ex}\textcolor{darkblue} { \zihao{-1} \bfseries \entit \\ \vspace{0.4cm}} \textcolor{darkblue}{\zihao{3} \bfseries \cntit} \\[0.1cm]\horrule{2pt} \vspace{-2ex} \end{center} \vspace{1cm} \begin{center} \textsc{\LARGE \univname\\ \large \depname \\ \unskip\strut} \vspace{-1.5ex} \\ \end{center} \vspace{3cm} \begin{table}[h] \zihao{4} \centering \begin{tabular}{p{3cm}<{\raggedleft} p{6cm}<{\centering}} 姓名: & {\fangsong 某某} \\ \specialrule{0em}{2pt}{2pt} 学号: & {\fangsong 123456} \\ \specialrule{0em}{2pt}{2pt} 导师: & {\fangsong 某某} \\ \specialrule{0em}{2pt}{2pt} 邮箱: & \url{theigrams@buaa.edu.cn} \\ \end{tabular} \end{table} \vspace{3cm} \begin{center} \zihao{4} \today \end{center} \end{titlepage} \newpage \begin{spacing}{1.5} \zihao{4} \thispagestyle{empty} \tableofcontents \setcounter{page}{1} \thispagestyle{empty} \end{spacing} \newpage \zihao{-4} \section{Basics of graph theory} \subsection{Graph Laplacian} \begin{definition}[非标准化图Laplace算子(Unnormalized Laplacian)] 定义了一个作用于 $ f $ 上的算子 $ \Delta :L^2(\mathcal{V})\rightarrow L^2(\mathcal{V}) $ ,用于刻画 $ f $ 与其局部加权平均值之间的差异. \[\begin{aligned} (\Delta f)_{i} & =\sum_{j:(i, j) \in \mathcal{E}} w_{i j}\left(f_{i}-f_{j}\right) \\ & =f_{i} \sum_{j:(i, j) \in \mathcal{E}}w_{i j}-\sum_{j:(i, j) \in \mathcal{E}}w_{i j} f_{j} \end{aligned} \] \end{definition} \begin{problem}[ $ \Delta $ 定义在 $ f $ 上 ] 注意:这里的$ \Delta $ 定义是在 $ f $ 上 ,但是由于 $ f_i $ 不是函数,因此不能写成 $ \Delta f_i $ 的形式,只能写成 $ (\Delta f)_i $. \end{problem} $ \Delta$算子 可以用一个半正定的 $ n\times n $ 矩阵 $\boldsymbol{\Delta } =\mathbf{D}-\mathbf{W} $ 代替 ,其中 $ \mathbf{W}=\left( w_{ij} \right) $ 是一个邻接矩阵(adjacency matrix), $ \mathbf{D}=\mathrm{diag}\left( \sum_{j\ne i}{w_{ij}} \right) $ 是对角阵(相当于把$ \mathbf{W}$的每一行加到对角元上). \begin{equation} \mathbf{Wf}=\left[ \begin{array}{c} \vdots \\ \sum_j{w_{ij}f_j} \\ \vdots \\ \end{array} \right] ,\qquad \mathbf{Df}=\left[ \begin{array}{c} \vdots \\ \sum_j{w_{ij}f_i} \\ \vdots \\ \end{array} \right] \end{equation} \begin{equation} \mathbf{\Delta f}=\left[ \begin{array}{c} \vdots \\ \left( \Delta f \right) _i \\ \vdots \\ \end{array} \right] =\left[ \begin{array}{c} \vdots \\ \sum_{j:(i,j)\in \mathcal{E}}{w_{ij}\left( f_i-f_j \right)} \\ \vdots \\ \end{array} \right] =\mathbf{Df}-\mathbf{Wf}=\left( \mathbf{D}-\mathbf{W} \right) \mathbf{f} \end{equation} \begin{definition}[Dirichlet energy of $ f $ ] \[\|f\|_{\mathcal{G}}^{2}=\frac{1}{2} \sum_{i j=1}^{n} w_{i j}\left(f_{i}-f_{j}\right)^{2}=\mathbf{f}^{\top} \boldsymbol{\Delta} \mathbf{f}\] \end{definition} \begin{pythoncode} import torch as tf a=1 \end{pythoncode} \cdr{redbox test} and \cdg{greenbox test} \end{document}