Skip to content

代码风格和编辑器

Formatter 和 Linter

Formatters: Tools that quickly check and reformat your code for stylistic consistency without changing the runtime behavior of the code.

Linters: Tools that detect not just stylistic inconsistency but also potential logical bugs, and often suggest code fixes.

代码风格很重要,但无需为此花费太多时间。现在越来越多的 editor/linter/formatter 做到了开箱即用,试图尽可能减少用户的烦恼(比如 Prettier 提出 stop all the on-going debates over styles)。直接使用它们管理项目即可,这些自动化插件有:

  • Prettier:
    • 广泛用于 Web 开发:HTML、CSS、JavaScript。
    • 仅适用于基础 Markdown:会破坏 MkDocs Admonition 等块。
  • markdownlint:
    • 无需配置即与 MkDocs 等扩展语法兼容。
Mkdocs Material 一般需要额外配置

一般 Markdown 插件遵循 CommonMark,将下级列表与上级列表缩进对齐(一般为 2 空格缩进)。而 MkDocs 仅能识别 4 空格缩进,需要额外配置。

对于 C、C++、Python 等热门语言,几乎所有编辑器都提供了一定程度的格式化功能,无需额外安装插件。

EditorConfig

EditorConfig 为不同编辑器提供了统一的配置文件。对公共项目来说,使用 EditorConfig 是保持基本代码格式统一的最便捷的方式。

能够无缝支持 EditorConfig 的有:GitHub、GitLab、IntelliJ IDEA、PyCharm、Vim、Visual Studio 等。VSCode 需要安装插件。

我的配置文件
# Located at the project root
root = true

# Base settings for most files
[*]
charset = utf-8
end_of_line = lf

# Linux Kernel Style
[*.{c,h,cpp}]
insert_final_newline = true
indent_style = tab
indent_size = 8
trim_trailing_whitespace = true

# General 4-space files
[*.{py,json}]
indent_style = space
indent_size = 4

# General 2-space files
[*.{yaml,clang-format}]
indent_style = space
indent_size = 2

[*.md]
indent_style = space
indent_size = 4

[Makefile]
indent_style = tab

VSCode

VSCode 配置文件分为用户设置和工作区设置。

我的用户配置文件
{
  //-------------Theme--------------
  "window.autoDetectColorScheme": true,
  "workbench.preferredLightColorTheme": "Default High Contrast Light",
  "workbench.preferredDarkColorTheme": "Default High Contrast",
  // "latex-workshop.view.pdf.invert": 1, // dark mode pdf
  //------------Terminal------------
  "terminal.integrated.defaultProfile.linux": "fish",
  "terminal.integrated.defaultProfile.windows": "PowerShell",
  "terminal.integrated.defaultProfile.osx": "zsh", // for powerlevel theme
  "terminal.integrated.fontFamily": "MesloLGM Nerd Font, MesloLGS NF, monospace",
  //-------------Editor--------------
  "editor.fontSize": 14,
  "editor.fontLigatures": false,
  "files.autoSave": "afterDelay",
  "editor.stickyScroll.enabled": true,
  "editor.unicodeHighlight.nonBasicASCII": false,
  "editor.lineNumbers": "relative",
  "editor.wordWrap": "on",
  "editor.formatOnType": true,
  "editor.formatOnSave": true,
  "editor.formatOnSaveMode": "modificationsIfAvailable",
  "editor.formatOnPaste": true,
  "editor.rulers": [72, 90],
  "editor.minimap.autohide": true,
  "rewrap.autoWrap.enabled": true,
  "rewrap.doubleSentenceSpacing": false,
  "rewrap.reformat": true,
  "rewrap.wholeComment": true,
  "window.titleBarStyle": "custom", // linux gnome
  "window.menuBarVisibility": "toggle",
  "explorer.confirmDragAndDrop": true,
  "explorer.confirmDelete": true,
  //---------Formatter---------------
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[markdown]": {
    "rewrap.autoWrap.enabled": false,
    "editor.defaultFormatter": "DavidAnson.vscode-markdownlint",
    "editor.acceptSuggestionOnEnter": "off",
    "editor.snippetSuggestions": "top",
    "editor.quickSuggestions": {
      "other": "on",
      "comments": "off",
      "strings": "off"
    }
  },
  "markdownlint.config": {
    "MD046": false, // mkdocs material admonition
    "MD007": { "indent": 4 }, // mkdocs material list
    "MD033": false, // inline HTML
    "MD010": { "ignore_code_languages": ["makefile"] } // Makefile
  },
  "markdown.extension.list.indentationSize": "inherit", // MkDocs
  "[latex]": {
    "editor.defaultFormatter": "James-Yu.latex-workshop"
  },
  //-------------Extension------------
  //Vim
  "vim.handleKeys": {
    "<C-a>": false, // selection
    "<C-k>": false, // vscode combination
    "<C-w>": false, // close tab
    "<C-`>": false // terminal
  },
  "vim.insertModeKeyBindings": [
    {
      "before": ["j", "j"],
      "after": ["<Esc>"]
    }
  ],
  //--------Miscellaneous------------
  "update.showReleaseNotes": false,
}

值得一提的是,VSCode 内置了简单浏览器,实时预览 MkDocs 等十分便捷。打开命令面板输入 simple browser 即可打开内置浏览器。

Vim

Vim 现今大概不会被用作主力编辑器,但其操作模式仍然广泛地影响着所有编辑器。支持 Vim 模式的场景有:

  • 几乎所有编辑器
  • Overleaf

Vim 本身最常见的使用场景是在服务器等终端环境,终端本身就有很多美化选项,因此无需追求 Vim 的美化,作一些简单的配置即可:

.vimrc
set number
set relativenumber
set textwidth=80
set hls
imap jj <ESC>

启动时带 -u NONE -N 将不加载 .vimrc 并禁用所有内置插件。

下面列出一些非常好用的 Vim 操作备忘:

Common

  • 移动
^ 
5gg H M L zz zb zt
{ } % 
<C-e> <C-y> <C-b> <C-f> <C-d> <C-u> (backward, forward, down, up)
gj gk
E 单词结尾(单词可包含符号),其实就是下一个空格前

Vim 自带 matchit 插件,可以开启它,使 % 能够在开闭标签、语句结构前后跳转:

runtime macros/matchit.vim
  • 查找
fx Fx
*
  • 重复
; ,
& 重复替换
@x 重复 q 记录的修改
  • 帮助
:h operator

学会结合操作符和查找命令

  • 对象:Vim 可以理解文本中的标签、括号等对象。

一般 a 代表 around,i 代表 inside。

aw ap it

常见的表示对象的符号:

) } ] > ' " ` t
w(word) W s(sentence) p(paragrah)

用例:

daw ciw
  • 重复两次操作符一般表示在本行执行。
  • 调换字符的技巧:xp ddp

Normal

  • >{motion} 缩进
  • cwn.n.n.n.n.
  • 复合命令:
    • C = c$
    • S = ^C
    • I = ^i
    • D = d$
  • 算术:<C-a> <C-x>
  • 待决命令:
    • g~ gu gU
    • ga
  • 缩进:< > =

Insert

  • 同 Shell 的几个按键:<C-w> <C-u>
  • <C-o>
  • 寄存器:<C-r><C-p>

专用寄存器

  • 复制 "0
  • 黑洞 "_
  • 系统剪贴板 "+
  • 选择 "*
  • 上次插入文本 ".

用例:

"0p

Visual

v V <C-v>

  • o
  • 多使用 . 获得即时反馈
  • gv 重选
  • Vr-

Replace

R gR 虚拟替换

Select

<C-g>

Command

:h ex-cmd-index
:w 
:t(co, copy) :m(move)
:[range]join 
:[rang]normal{commands}
:shell 启动一个会话
:read !{cmd} 将命令输出重定向到缓冲区
:write !{cmd} 作为标准输入
:[range]!{cmd} 过滤缓冲区内容

Range:

% 所有行
+n
pattern:/<html>/,/<\/html>/
visual

Address:

0 1 $
. %(1,$)

例子:

:'<,'>t0
:'<,'>m$
:'<,'>normal A;
:%normal i// 执行指定的普通模式命令前,光标会先移动到行起始处

文件

分屏可以用 tmux 部分解决,但掌握 Vim 内置的文件管理还是能提高不少效率。

有几个概念:当前可见 %、轮换文件 #

:ls
:bnext :bprev :bfirst :blast 
:buffer N :buffer {bufname} 只需包含唯一标识字符即可
<C-^>
:edit! 重新载入文件 :edit {filename}
<C-w>s <C-w>v :sp[lit] {filename} :vsp[lit] {filename}
<C-w>w <C-w>h <C-w>j <C-w>k <C-w>l 
:clo[se] <C-w>c :on[ly] <C-w>o
<C-w>= [N]<C-w>_ [N]<C-w>|
  • 标签页和缓冲区并不一一对应
:tabe[dit] {filename}
<C-w>T 当前窗口创建新标签页
:tabc[lose] :tabo[nly]
:tabn[ext] {N} {N}gt
:tabp[revious]gT
:tabmove [N] 默认到结尾
  • 设置 path,使用 netrw 插件管理文件
:set path+=app/** 匹配所有子目录
:find {filename}
:edit .
:Explore :Sexplore :Vexplore
:h netrw
<C-g> 查看文件状态
  • 以超级用户权限保存文件:
:w !sudo tee % >/dev/null

这里的 % 展开为当前文件完整路径。

代码风格

代码风格因编程语言和个人经验的差异极大。

CodeAesthetic 频道介绍了一些较高抽象层次的代码组织方法,在大型项目中十分有用。

C

进行 C 编程时我偏向于遵守内核代码风格,符合偏向底层的思维。有几个要点:

  • 缩进
    • 使用 Tab,长度设置等价于 8 个空格。
  • 断行
    • 80 字符断行,特殊情况(grep 等)除外。
  • 命名
    • 本地变量尽量短:cntusr()
    • 全局变量具有描述性:count_active_users()
  • 不使用 typedef
  • 函数
    • 一个函数的功能保持在一个高中生能理解的范围。
    • 本地变量在 5-10 个左右。
    • 使用 goto 集中处理函数退出的过程。
  • 注释
    • 代码是显然的,不需要解释的。函数内不写注释。
    • Doxygen 风格,描述代码做了什么。

C++

名字

名字的描述性与作用域的大小成正比。在简短的函数中使用单字符命名没有问题,但在类中显然太模糊了。

  • 类型名、别名(typedefusing)、枚举:混合大小写 struct UrlTableProperties
  • 变量名:a_local_variable
    • 类成员:下划线后缀 a_class_data_member_
    • 常量:k 前缀、混合大小写 kDaysInAWeek
  • 函数名:混合大小写
  • 宏名:大写、下划线 MY_MACRO_THAT_SCARES_SMALL_CHILDREN_AND_ADULTS_ALIKE