Emacs折腾日记(二十八)——代码片段

Emacs折腾日记(二十八)——代码片段

编码文章call10242025-07-19 22:12:035A+A-

在前面的章节中,介绍了 vertico 体系的补全,已经实现了在各个buffer中的补全功能,但是作为程序员,使用Emacs主要用来编程,对于编程来说上述的补全体系仍然不够完整,我们还需要基于lsp的补全以及基于代码片段模板的补全,这里先介绍代码片段。

在之前写vim的配置时已经介绍过代码片段的概念以及基于vim 的配置,感兴趣的读者可以自行在我往期的博客中寻找,这里就不对它进行详细的介绍了。直接来使用它。

在Emacs中,我们使用插件 yasnippet 来支持这个功能

(use-package yasnippet
  :ensure t
  :hook ((after-init . yas-reload-all)
	 ((prog-mode LaTeX-mode org-mode) . yas-minor-mode))
  :config
  (setq yas-snippet-dirs
	'("~/.emacs.d/snippets")))

我们通过上面的代码就能把它启用起来,yas-reload-all 表示在Emacs启动时重新加载所有已启动的代码片段;yas-minor-mode代表启动代码片段补全的功能。但是我们做了限制,只在编写代码、LaTeX和org-mode 文件时启动。如果各位读者希望在markdown 中也启动的话,也可以加上 markdown-mode 的选项。但是既然使用了Emacs,当然要用比 markdown 更加强大的 org-mode

现在Emacs已经支持了代码片段,它的快捷键与vim中的一致都是使用tab 来进行补全。那么接下来就是通过代码片段文件来提高我们编程的效率了。添加代码片段文件我们可以使用已经写好的,也可以自己根据自己的习惯来定制。

下载通用的代码片段

官方虽然提供了一些代码片段,但是数量较少,使用起来并不那么方便,我们可以下载网络上的一些公共库来丰富我们的代码片段。这里我介绍一下 Doom Emacs Snippets Library。它是专门为doom emacs 用户打造的代码片段库,使用者不少。其他的代码片段库的安装与使用与它类似,读者可以依葫芦画瓢来安装。

首先找到官方的代码仓库,然后将它克隆下来,我将它放到 ~/.emacs.d/emacs-snippets 目录中。

然后通过

(use-package doom-snippets
  :ensure nil
  :load-path "~/.emacs.d/doom-snippets"
  :after yasnippet)

来加载它。之后需要 Yasnippet 插件能找到这个目录,所以我们修改一下上面的配置

(use-package yasnippet
  :ensure t
  :hook ((after-init . yas-reload-all)
	 ((prog-mode LaTeX-mode org-mode) . yas-minor-mode))
  :config
  (setq yas-snippet-dirs
	'("~/.emacs.d/snippets"
	  "~/.emacs.d/doom-snippets"))) ;; 添加一行代码片段的加载路径

此时再编写代码发现多了一些代码片段

自定义代码片段

与 doom emacs snippets library 代码片段类似,我们也需要按照具体的mode来组织代码片段的文件目录。每一个关键字一个文件

例如

~/.emacs.d/snippets/
├── python-mode/
│   ├── for       ; for 循环模板
│   └── class     ; 类定义模板
└── c++-mode/
    └── foreach    ; for-each 循环模板

有了这些准备的前提条件,我们可以自行创建自己的代码片段了。

在emacs 中执行 yas-new-snippet 可以打开一个新的buffer,并显示代码片段的模板

# -*- mode: snippet -*-
# name: 
# key: 
# --

name后面跟着的是代码片段的名称,在补全时会以补全项显示这个名称;key代表关键字,表示用哪个缩写来触发。例如我们定义一个 prt 来展开成 printf 语句,那么就可以写成 name:prt key: prt

尝试生成第一个代码片段

后面的写具体展开的代码,例如我们编写一个自动生成hello world的模板

# -*- mode: snippet -*-
# name: create-hello
# key:  hello
# --

#include <stdio.h>

int main(int argc, char* argv[])
{
    printf("hello world\n");
    return 0;
}

在编写完成之后使用快捷键 C-c C-c 来保存,Emacs在保存时会让你输入对应的 mode 并且给出需要保存的位置。当我们修改完适用的 mode 和 保存的位置之后,我们发现它在对应位置生成了文件。

重启Emacs或者 使用命令 yas-reload-all 来重新加载所有的代码片段。之后输入hello就可以看到它为我们生成了 c 版本的 hello world 程序

占位符

我们希望的代码大体上不变但是关键地方由我们自己来填充,这个时候可以使用占位符。

占位符的格式为 ${} 大括号中填入数字序号。或者使用 ${1: default} 来表示这个位置默认填写default字符。

下面是一个具体的例子

# -*- mode: snippet -*-
# name: qt-class
# key: qcls
# --

class ${1} : public ${2:QObject}
{
    Q_OBJECT
public:
    ${1}(${2}* parent = nullptr);
    ${1}(const ${1}& other);
    ${1}& operator=(const ${1}& other);
    ~${1}();

signals:

slots:

protected:

}

上面我们定义了一个快速生成继承自 QObject 类的子类的模板,上面有两个占位符,第一个占位符表示名称,它没有默认值,一旦确定了它的也就是类型,它会自动生成类的无参构造、拷贝构造、赋值运算符重载和析构函数的定义。第二个占位符指出了它的父类,它的默认值是 QObject 类,它的效果如下

使用elisp代码生成动态信息

我们可以使用 `` 来包裹elisp代码,生成动态信息。

一般在C/C++ 代码的头文件中会包含一些注释信息,用来说明文件的用途包含的接口信息等等。我们希望生成这么一段注释的代码片段

# -*- mode: snippet -*-
# name: Custom File Header
# key: hdoc
# --

/**
 * @file `(file-name-nondirectory (buffer-file-name))`
 * @bref ${1:brief description}
 * @details ${2:detailed explanation}
 * @date `(format-time-string "%Y-%m-%d")`
 * @commit history:
 * \t v${3:1.0}: ${4:Initial version}
 */
$0

上面我们使用 elisp代码 (file-name-nondirectory (buffer-file-name)) 来获取对应的文件名,通过 (format-time-string "%Y-%m-%d") 来获取时间。并且最后使用 $0 来将光标定位到这个位置,这个位置将会在我们使用tab处理过所有tab之后自动跳转。

本文到此就结束了。本文主要介绍了在Emacs中使用代码片段,并且介绍了编写代码片段的一些方式。有了这些在编写代码时会更加得心应手。

点击这里复制本文地址 以上内容由文彬编程网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

文彬编程网 © All Rights Reserved.  蜀ICP备2024111239号-4