我的emacs配置文档介绍

Table of Contents

1. 总体框架

1.1. 简介

本文档汇总并总结我所使用的emacs配置的基本用法,并会进行不间断地更新。

由于我希望在多个平台上使用他,其中,又因受限于论文写作、画图、QQ、微信等工作流的需求,本emacs配置不得不支持windows系统上的使用。

由于在服务器端写代码的需求,需要维持在linux终端上的使用。由于在笔记本电脑上使用linux的需求,还需要维持linux桌面端的使用。因而总体上需要以下几部分:

  1. 在windows系统上,主要功能需求包括和两部分;
  2. 在linux服务器上,具有良好的python语法支持;
  3. 在windows系统和linux系统下,最好拥有以下支持:
    1. 邮件处理
    2. C++、C语法支持;
    3. 各种编程语言的开箱即用;

针对于以上问题,目前所采用的折中解决方案是:采用win10系统+ WSL2,所有的emacs配置均建立在linux系统上进行配置,同时为wsl2撰写一些函数,以提供更加方便的系统交互。 通过以上方式,我整合了我的工作流,实现上述需求:

  1. 通过在wsl2上(目前采用ubuntu20.04)通过脚本安装二进制软件,将emacs作为前端,实现emacs中的各项基本功能;
  2. 通过tramp,一个ssh软件,来自动管理连接远程服务器;
  3. 通过执行powershell命令,来更好地辅助wsl和windows系统之间的交流;

同时,我还为emacs的使用准备了两种环境:

  1. TUI(Text User Inferface),通过在windows的官方应用市场下载terminal,可以直接建立对wsl的访问。我常常在terminal中启动emacs,这占据了我工作中的绝大多数时间。比起GUI环境,terminal有以下优点:
    1. 速度更快,我的emacs很需要高效率,正常用着用着,就会开很多buffer,如果还打开lsp这种服务,那么就更复杂了;
    2. tui看起来更好看;
    3. tui更加纯粹,因为绝大多数时候,我都是在coding;
  2. GUI环境,win11可以支持WSL下的原生GUI,不过很可惜,我工位的电脑在硬件上无法支持win11,所以我只能使用win10,而在win10中,我采用mobaxterm,一个开源的ssh连接软件,来进行GUI映射。 mobaxterm自带x11,在终端键入emacs,就可以产生一个gui版本的emacs了。使用GUI的方式有以下优点:
    1. 支持图片,如此在写博客时,可以内置现实图片;在写latex时,可以试试查看产生的pdf(tui不行);在python科研绘图时,也可以直接display得到对应的结果;
    2. 鼠标使用。gui支持使用鼠标,这在拼写检查、绘图等事情上有一定用途;
    3. 更多的theme。有一些theme还是gui比较好看
    4. 字体,略过不谈;
    5. 没有快捷键缺损的问题。在tui使用时,为emacs定义的快捷键,可能会与终端的快捷键发生冲突。而我至今没找到怎么把terminal中所有的快捷键都关掉……

1.2. 文件结构

我所采用的是最经典的emacs配置结构。

我们知道,emacs通过访问 \~/emacs.d/init.el 来执行emacs配置的加载。而init文件,则通过顺序加载各个子文件,来实现对整个emacs的加载。

整体的一个tree,可以通过下述代码所述:

├── auto-insert
├── auto-save-list
├── collected
├── doc
│   ├── images
│   └── utils-in-windows
├── elpa
├── eshell
├── imdict
├── lisp
├── netease-cloud-music
├── other-files
│   ├── easy-collections
│   │   └── img
│   ├── netease-cloud-music.el
│   └── org-static-blog
├── pyim
│   └── dcache
├── quelpa
│   └── melpa
├── request
├── rime
│   ├── build
│   ├── luna_pinyin.userdb
│   │   └── lost
│   ├── luna_pinyin.userdb.old
│   │   └── lost
│   ├── terra_pinyin.userdb
│   └── terra_pinyin.userdb.old
│       └── lost
├── site-lisp
│   └── emacs-application-framework
│       ├── app
│       │   └── airshare
│       ├── core
│       │   └── js
│       ├── extension
│       └── img
├── snippets
│   ├── c++-mode
│   ├── c-mode
│   ├── emacs-lisp-mode
│   ├── latex-mode
│   ├── org-mode
│   ├── python-mode
│   ├── rust-mode
│   ├── sh-mode
│   └── web-mode
├── software
├── transient
└── url

其中,other-files是我用来存储那些不需要进行更新,或者自己所写的包的地址;lisp是我的配置地址,剩下的都比较明显,就不再进行介绍了。

2. 所实现功能

2.1. prog related

2.1.1. find definition & application

2.1.2. auto insert and my snippets

2.1.3. details for some progs

2.2. version control

我通过magit去做版本控制,这个东西对新手比较友好。1

我没有为git设置快捷键,因为使用不是很多, git有很多低级功能,如add,commit, push,pull等操作,就不赘述了。

比较常见的进阶问题是关于diff的,此处先不赘述,后面专门写文章介绍。

2.3. take notes

我的笔记通过org mode进行记录,之后发表到我自己的blog上。发表到博客有利于我自己查看。

关于如何使用ORG mode,这应该不是一个问题。我主要对orgmode进行了三个方面的自定义:

2.3.1. insert codes

相当于写了一点点的语法糖,我把它绑定在了“C-c s i”上,代表 src,input。

函数如下:

;; 设置org快速插入源代码
(defun org-insert-src-block (src-code-type)
  "Insert a `SRC-CODE-TYPE' type source code block in org-mode."
  (interactive
   (let ((src-code-types
          '("emacs-lisp" "python" "C" "sh" "java" "js" "clojure" "C++" "css"
            "calc" "asymptote" "dot" "gnuplot" "ledger" "lilypond" "mscgen"
            "octave" "oz" "plantuml" "R" "sass" "screen" "sql" "awk" "ditaa"
            "haskell" "latex" "lisp" "matlab" "ocaml" "org" "perl" "ruby"
            "scheme" "sqlite")))
     (list (ido-completing-read "Source code type: " src-code-types))))
  (progn
    (newline-and-indent)
    (insert (format "#+BEGIN_SRC %s\n" src-code-type))
    (newline-and-indent)
    (insert "#+END_SRC\n")
    (previous-line 2)
    (org-edit-src-code)))

;; (message "--------------------begin--------------------")
;;将其加载在快捷键上
(add-hook 'org-mode-hook
          '(lambda ()
             ;; ;; C-TAB for expanding
             ;; (local-set-key (kbd "C-<tab>")
             ;; 'yas/expand-from-trigger-key)
             ;; keybinding for editing source code blocks
             (local-set-key (kbd "C-c s e")
             'org-edit-src-code)
             ;; keybinding for inserting code blocks
             (local-set-key (kbd "C-c s i") 'org-insert-src-block )))

对的,我上面的这段代码,就是用这个配置插入的……

2.3.2. 运行代码

使用如下配置:

;;code running
(org-babel-do-load-languages
     'org-babel-load-languages
     '((emacs-lisp . t)
       (ditaa . t)
       (python . t)
       ;;(sh . t)
       (latex . t)
       (plantuml . t)
       (R . t)))

(setq org-plantuml-jar-path
     (expand-file-name "~/.emacs.d/software/plantuml.jar"))


;;code执行免应答(Eval code without confirm)
(setq org-confirm-babel-evaluate nil)

值得注意的是,我还对plantuml特殊照顾。这是为了画UML图的jar包。我用它是为了能够在某些时候画一些简单的图示。

比如下面的代码:

src plantuml :file ./img/202201071111.png
(a)-->(b)
(b)-->(c)
(c)--> (d)
(d)--> (a)

#+end_src

当我使用“C-c C-c”时,就可以自动插入代码进去了.

testuml.png

当然,正常的编程语言都可以理解,只要你有解释器。比如 python:

def helloword():
    return "helloword"

return helloword()
# return 1+1

screenshot_20220107_214119.png

2.3.3. 插入图片

这个是21世纪记笔记的硬需求。由于我是用的wsl,所以在拥有mac这种电脑之前,我需要解决的一个核心问题是:如何将windows里复制的图片进行粘贴。我把这个流程抽象的很简单,主要是借助powershell。

这个流程是,我通过C-M-a (qq的截图键) 和 M-a (微信的截图键) 选取我希望截图的部分,然后通过SPC-s-i,插入我截取完成的那部分图片,之后,对应的图片会被保存在当前文件所处地区的imgs文件夹中,并基于插入时间产生一个文件。

screenshot_20220107_215239.png

比如上面这张图片,就是我去那个文件夹下,打开了一个目录之后而得到的。

这一部分的配置如下:

(defun my-yank-image-from-win-clipboard-through-powershell()
  "to simplify the logic, use c:/Users/Public as temporary directoy, and move it into current directoy"
  (interactive)
  (let* ((powershell "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe")
         (file-name (format-time-string "screenshot_%Y%m%d_%H%M%S.png"))
         ;; (file-path-powershell (concat "c:/Users/\$env:USERNAME/" file-name))
         (file-path-wsl (concat "./images/" file-name))
         )
    ;; (shell-command (concat powershell " -command \"(Get-Clipboard -Format Image).Save(\\\"C:/Users/\\$env:USERNAME/" file-name "\\\")\""))
    (shell-command (concat powershell " -command \"(Get-Clipboard -Format Image).Save(\\\"C:/Users/Public/" file-name "\\\")\""))
    (rename-file (concat "/mnt/c/Users/Public/" file-name) file-path-wsl)
    (insert (concat "[[file:" file-path-wsl "]]"))
    (message "insert DONE.")
    ))

其基本原理是:将复制的图片保存在本地,然后移动到目标文件夹下,并在当前光标下插入。

如果你在windows下用emacs,我这里有一套完整的方案,通过C-M-Y便可以开启截图! 配置如下:

;; "C:\Program Files\IrfanView\i_view64.exe"
(defun my-screen-capture ()
  "Take a screenshot into a unique-named file in the current buffer file
   directory and insert a link to this file."
  (interactive)
  (lower-frame)
  (let ((capture-name (concat
                       (format-time-string "%Y%m%d%H%M%S") ".png"))
        (capture-save-path (concat
                            (file-name-directory buffer-file-name) "images/")))
    (setq capture-file (concat capture-save-path capture-name))
    (if *is-windows*
        ((setq command (concat "\"C://Program Files//IrfanView//i_view64.exe\"  /capture=4 /dpi=(300,300) /convert="
                               (replace-regexp-in-string "/" "\\\\" capture-file)))
         (shell-command command))
      (call-process-shell-command "scrot" nil nil nil nil "-s" capture-file)
      )

    (insert (concat
             "[[file:./images/" capture-name "]]")))
  )

(define-key org-mode-map (kbd "C-M-Y") 'my-screen-capture)

除此之外还有一些别的办法,但是似乎对WSL没用,没用所以我就用不到。

(setq-default org-download-heading-lvl nil)
(setq-default org-download-image-dir "./img") ;; 把图片保存在 org 文档所在目录的 img 子目录下

(add-hook 'org-mode-hook
          #'org-download-enable)

;; 设置插入图片的快捷键
;; (after-load 'org-download
  (define-key org-mode-map (kbd "C-c C-x s") 'org-download-screenshot) ;; 插入截图
(define-key org-mode-map (kbd "C-c C-x y") 'org-download-yank)

2.3.4. 格式导出

这个是org mode的默认功能,我对它做了增强,主要是确定模板和支持中文。

默认命令是C-c C-e,比如对于这个文件,我可以通过该命令得到导出页面。

screenshot_20220107_220217.png

对于上述命令,我用到的最多的,是通过org导出word相关格式,html格式,和latex支持的PDF格式。除此之外还有epub游戏。

其中最需要配置的是latex支持的PDF格式,配置方式如下:

;; 使用xelatex,配合当前org文件最开始的配置来正常输出中文
  ;; 这类笔记基本不可能是全英文,所以就安心用xelatex算了
  (setq org-latex-pdf-process '("xelatex -file-line-error -interaction nonstopmode %f"
                                "bibtex %b"
                                "xelatex -file-line-error -interaction nonstopmode %f"
                                "xelatex -file-line-error -interaction nonstopmode %f"))

  ;; 生成PDF后清理辅助文件
  ;; https://answer-id.com/53623039
  (setq org-latex-logfiles-extensions
    (quote ("lof" "lot" "tex" "tex~" "aux"
      "idx" "log" "out" "toc" "nav"
      "snm" "vrb" "dvi" "fdb_latexmk"
      "blg" "brf" "fls" "entoc" "ps"
      "spl" "bbl" "xdv")))

  ;; 图片默认宽度
  (setq org-image-actual-width '(300))

  (setq org-export-with-sub-superscripts nil)

  ;; 不要自动创建备份文件
  (setq make-backup-files nil)

  ;; elegantpaper.cls
  ;; https://github.com/ElegantLaTeX/ElegantPaper/blob/master/elegantpaper.cls
  (with-eval-after-load 'ox-latex
  ;; http://orgmode.org/worg/org-faq.html#using-xelatex-for-pdf-export
  ;; latexmk runs pdflatex/xelatex (whatever is specified) multiple times
  ;; automatically to resolve the cross-references.
  (setq org-latex-pdf-process '("latexmk -xelatex -quiet -shell-escape -f %f"))
  (setq org-latex-listings t)
  (add-to-list 'org-latex-classes
                '("elegantpaper"
                  "\\documentclass[lang=cn]{elegantpaper}
                  [NO-DEFAULT-PACKAGES]
                  [PACKAGES]
                  [EXTRA]"
                  ("\\section{%s}" . "\\section*{%s}")
                  ("\\subsection{%s}" . "\\subsection*{%s}")
                  ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                  ("\\paragraph{%s}" . "\\paragraph*{%s}")
                  ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
  )

这个东西需要配置开头文件,通过使用 #+latex_class: elegantpaper 的前缀。

其中的elegantpaper,是一个latex中的sty文件。

2.3.5. 发布博客

通过org-publish进行绑定,其配置如下,其中敏感信息一竟被删除:

(require 'ox-publish)
(setq org-publish-project-alist
      '(
         ("blog-notes"
            :base-directory "~/liangzid.github.io/notes/"
             :base-extension "org"
             :publishing-directory "~/liangzid.github.io/"
             :recursive t
             :htmlized-source t
             :html-extension "html"
             :body-only t
             :table-of-contents t
             :publishing-function org-html-publish-to-html
             :headline-levels 5
             :auto-preamble t
             :section-numbers t
             :author "LiangZi"
             :email "liangzid@stu.xjtu.edu.cn"
             ;; :exclude-tags "noshow"
             :auto-sitemap t                  ; 自动生成 sitemap.org 文件
             :sitemap-filename "sitemap.org"  ; ... call it sitemap.org (it's the default)...
             :sitemap-title "Sitemap"         ; ... with title 'Sitemap'.
             :sitemap-sort-files anti-chronologically
             :sitemap-file-entry-format "%d %t"
             :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"https://liangzid.github.io/notes/css/worg.css\"/>"

         )
         ("blog-static"
             :base-directory "~/liangzid.github.io/notes/"
             :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
             :publishing-directory "~/liangzid.github.io/"
             :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"https://liangzid.github.io/notes/css/worg.css\"/>"
             :recursive t
             :publishing-function org-publish-attachment
          )
         ("blog" :components ("blog-notes" "blog-static"))

        ;; 把各部分的配置文件写到这里面来

        ))

这里的css配置问题,我还没有解决。

2.4. ssh, with tramp

我之前使用vscode来连接远程服务器写代码,但是vscode比较麻烦,常常需要连接多次,多次输入密码。 除此之外,我面对vscode等工具会产生一种畏惧心态——因为太复杂了,没有emacs简洁简单。

后来,我转向了终端,即在服务器上安装emacs,然后ssh远程过去,然后在终端打开emacs使用。 后来发现,其实emacs竟然也自带ssh工具,这就是tramp。

我其实挺喜欢tramp的,这个东西使用起来很简单,并且记录一次密码之后,就可以永远快速进入了!

假如说断网了,也就是我和服务器失去了连接,没关系,我在本地的buffer上也会有一份副本。当出现多个写而产生的冲突时,会有一个冲突报警。

我主要有若干个实验室的服务器帐号,分别属于不同的ip上(当然,必须是服务器内网访问),所以我写了一个简单的函数,这样我只需要运行SPC-服务器序号,就可以访问他们了!

函数写的十分简单,下面仅以其中一个为例:

(defun ssh-connect-41 ()
  (interactive)
  (counsel-find-file "/ssh:username@XXX.XXX.XXX.XXX:/home/username/liangzi_need_smile")
  )

关于快捷键绑定,后面会统一介绍。

2.5. listen to music

是的,还可以听音乐。不过我没有这个习惯。现在的音乐多不合我的耳,我也不喜欢版权文化,因此我已经两年多不怎么听歌了。

可以通过我的仓库中other-files中 netease-cloud-music.el 文件夹获取听音乐功能。此处不赘述。

2.6. play games

略。

2.7. English related

我的英语需求,主要包括:在阅读别人的文档时,提供查单词功能;在自己进行写作时,能够提供一些单词辅助,以及拼写检查。

2.7.1. 查单词

我是用的是 youdao-dictionary 这个package,我把它绑定在快捷键C-c y上。我发现这个package很好用,支持当前position单词的翻译,以及对选中文本序列的翻译。

配置较为简单:

(use-package youdao-dictionary
  :config
  (global-set-key (kbd "C-c y") 'youdao-dictionary-search-at-point+))

2.7.2. 英语自动补全

使用lazycat的english helper,由于这个软件早就停止更新了,所以我也放在了other-files里面。 配置如下:

(require 'company-english-helper)
(setq company-english-helper-active-p nil)
(global-set-key (kbd "C-c e") 'toggle-company-english-helper)

不要忘记load-path

2.7.3. 英语拼写检查

当我写论文时,这个功能很重要。

我主要使用了两种check工具,一个是ispell,一个是flyspell。我不是特别了解这儿这的区别和联系,但是感觉flyspell更加复杂一点,同时我喜欢的是简单的东西。首先介绍ispell。ispell显然,也是linux下某拼写检查工具aspell的emacs前端,直接M-x运行即可。

和大多数GNU工具一样,其使用很简单,当你打开之后,它便会一个个的让你审阅那些单词,并为每一个单词提供可替换的选项。通过这种方式,就可以实现全文的一个拼写检查了。

另一个工具是flyspell,据说功能更加强大,当你打开这个mode之后,他会将所有有问题的word下标发红。我一直没学明白怎么进行最基本的correct,所以这个工具游戏地方 使用我就不得而知了。

2.8. 输入法

emacs也自带了一些输入法,来应对一些极端情况。比如我现在使用的wsl,如果我通过X11打开,那么就需要这个子系统配置输入法,否则就无法输入中文,在这种情况下,windows系统默认的输入法是无法使用的。

我为我的emacs配置提供了两种输入法,一种是pyim,这种输入法是elisp实现的,所以不需要额外安装任何软件,可靠性很强。但是确定就是,输入法不太好用……另一种方式是,安装不需要折腾的输入法后端于linux中,并在emacs中提供对应的前端。我目前主要使用这一种方案,也就是狗哥所给出的rime前端。我的配置参考的是lazycat的博客,如下:

(use-package rime
  :init
  (setq rime-posframe-properties
        (list :background-color "#333333"
            :foreground-color "#dcdccc"
            :internal-border-width 10))
  (setq default-input-method "rime"
        rime-show-candidate 'posframe)
  :custom
  (default-input-method "rime"))

效果如下图所示

screenshot_20220108_104821.png

3. 附录:键位表

3.1. evil相关的leader键位表

我直接把evil部分的设置拉过来,并进行简单的解释。

;;; 以下所有的键位,leader键都是空格。

 ;; manager for file and buffer.
 "xs" 'save-buffer  
 "xf" 'find-file
 "xr" 'recentf-open-files
 "xb" 'switch-to-buffer
 "xk" 'kill-buffer
 "xc" 'save-buffers-kill-terminal
 "bb" (lambda () (interactive) (switch-to-buffer nil)) ;; switch to last buffer.
 "jj" 'scroll-other-window
 "kk" 'scroll-other-window-up
 "x0" 'delete-window
 "x1" 'delete-other-windows
 "x2" 'split-window-below
 "x3" 'split-window-right

 "hf" 'counsel-describe-function 
 "hv" 'counsel-describe-variable
 "hk" 'describe-key
 "hm" 'describe-mode

 ;; code navigation.
 "sh" 'highlight-symbol
 "sr" 'highlight-symbol-query-replace
 "sn" 'highlight-symbol-nav-mode ;; 使用M-n,p进行上下导航

 "sy" 'wsl-copy-region-to-clipboard ;; 将wsl中emacs的选中区域复制到系统clipboard中 
 "sd" 'wsl-cut-region-to-clipboard ;; 同上,不过是剪切

 "si" 'my-yank-image-from-win-clipboard-through-powershell ;; image 级别的yank

 ;; shell relevant
 "sh" 'shell
 "sc" 'shell-command

 ;; "gd" 'evil-goto-definition
 "gd" 'xref-find-definitions-other-window

 ;; python debug
 "dd" 'pdb 
 "db" 'gud-break
 "dr" 'gud-remove
 "dc" 'gud-cont
 "dn" 'gud-next
 "ds" 'gud-step
 "di" 'gud-stepi
 "dp" 'gud-print
 "de" 'gud-finish

 ;; git relevant
 ;; "gs" 'git-add-commit-push-lz ;; git synroize.

 ;; ssh connect
 "41" 'ssh-connect-41
 "42" 'ssh-connect-42
 "45" 'ssh-connect-45

 ;; manager for english query.
 "cy" 'youdao-dictionary
 "te" 'toggle-company-english-helper

 ;; manager for other frequency.
 "ac" 'ispell-buffer
 "fs" 'flyspell-mode

 ;; for orgmode application
 "ii" 'my-screen-capture
 "ih" 'fastinsert-org-head
 "ic" 'org-insert-src-block

key info
C-c y youdao-dictionary
C-c p projectile-command-map
C-c e english helper
C-\\ comment-line
M-0 M-9 window switch
ESC, C-d EVIL ESC
<SPC> EVIL LEADER
C-s search files
C-c C-r rebegin after break
C-h f help of function
C-h v help of variable
C-x C-r recent open file
C-h C-k find function on key
C-0,-,= screen big,small and so on
ac aspell check buffer
fs flyspell
   

3.2. snippets

太多了,还是我自己去看并。在snippets目录下。。。

Footnotes:

1

如果对git不理解,可以参考this article.


Author: Zi Liang (liangzid@stu.xjtu.edu.cn) Create Date: Mon Jan 3 20:36:40 2022 Last modified: 2024-03-09 Sat 20:56 Creator: Emacs 28.1 (Org mode 9.5.2)