手把手教你学会 Emacs 定制

时间:2022-10-12 19:11:57

Table of Contents

  • 1 前言
  • 2 配置Emacs
    • 2.1 设置界面
    • 2.2 全屏以及最大化
    • 2.3 设置周边
    • 2.4 显示时间设置
    • 2.5 设置日历
    • 2.6 设置符合个人的操作习惯
    • 2.7 其它小功能
    • 2.8 绑定键设置
    • 2.9 C/C++/JAVA编程模式

1 前言

说实话,emacs的迷人之处就是具有非常强的自主配置能力,你可以完全根据自己的个人习惯来设置它。就像我在下面罗列的那些lisp代码,你可以 尽情的改变F1,F2。。等等键所对应的功能,也可以在编辑C/C++/java时使用自动补全等等非常人性化的功能。或许这就是emacs作为一个 “神”一样的编辑器所存在的理由。如果你非常喜欢折腾,那emacs绝对是你的首选编辑器。

每一次emacs启动的时候,它将加载一个名为~/.emacs的Lisp代码文件,对该文件进行编辑是一种常用的定制emacs的方式。我不会讲 lisp的具体使用方法,只是罗列出常用的对emacs进行配置的lisp代码,当你熟悉这些代码的时候,再去深入学习lisp,或许效果更好。

下面是一个典型的Lisp语句:

(setq column-number-mode t) 
(setq line-number-mode t)

它的意思是显示行列号。其中,括号表示这是一个lisp函数调用,圆括号内第一个符号setq为emacs配置文件中常用的的函数,它为某个全局变 量赋值。该函数第一个参数为要设置的变量名,第二个数则是该变量的值。下面讲到的每一行代码,你都可以直接拿到~/.emacs中使用。当你想要了解某一 行代码的作用时,可以把它复制粘贴到~/.emacs中,保存然后重启emacs就可以看到效果了。 

小技巧:当你编辑~/.emacs配置文件的时候,每一次要看到效果都必须重新启动
emacs,非常麻烦。你可以用emacs打开.emacs文件并加入你想要的lisp代码后,
使用C-x C-s保存,接着在minibuffer输入:M-x eval-buffer,它会立即生效。
你可以试验一下,非常方便和简单。

2 配置Emacs

 

2.1 设置界面

 

其实这个有更好的解决办法,因为你可以使用theme插件,里面包含了几十种常用的主题。但是这里仅仅介绍一些最基本的配置方案,后续我会介绍相应的主题配置方法。

 

;;设置字体颜色为黑色,默认为黑色 (set-foreground-color "grey") ;;设置背景为黑色,默认为白色,你也可以*设置其它颜色 (set-background-color "black") ;;设置光标的颜色为金黄,默认为黑色 (set-cursor-color "gold1") ;;设置鼠标的颜色 (set-mouse-color "gold1") ;; 设置语法高亮显示的背景和主题,区域选择的背景和主题,二次选择的背景和选择 (set-face-foreground 'highlight "white") (set-face-background 'highlight "blue") (set-face-foreground 'region "cyan") (set-face-background 'region "blue") (set-face-foreground 'secondary-selection "skyblue") (set-face-background 'secondary-selection "darkblue") 


 

2.2 全屏以及最大化

 
;; 实现全屏效果,快捷键为f11 (global-set-key [f11] 'my-fullscreen) (defun my-fullscreen () (interactive) (x-send-client-message nil 0 nil "_NET_WM_STATE" 32 '(2 "_NET_WM_STATE_FULLSCREEN" 0)) ) ;; 最大化 (defun my-maximized () (interactive) (x-send-client-message nil 0 nil "_NET_WM_STATE" 32 '(2 "_NET_WM_STATE_MAXIMIZED_HORZ" 0)) (x-send-client-message nil 0 nil "_NET_WM_STATE" 32 '(2 "_NET_WM_STATE_MAXIMIZED_VERT" 0)) ) ;; 启动emacs时窗口最大化 (my-maximized) 


 

2.3 设置周边

 
;;显示行列号,它显示在minibuffer上面那个杠上 (setq column-number-mode t) (setq line-number-mode t) ;;设置滚动栏在窗口右侧,默认是在左侧 (customize-set-variable 'scroll-bar-mode' right) ;;取消滚动栏 (set-scroll-bar-mode nil) ;;取消工具栏 (tool-bar-mode nil) ;; 禁用菜单栏,F10 开启关闭菜单 (menu-bar-mode nil) ;;设置标题栏为buffer的内容 (setq frame-title-format "emacs@%b") ;; 设置启动时窗口的长宽,下面为90*35 (setq initial-frame-alist '((width . 90) (height . 35))) ;;关闭出错时的提示声 (setq visible-bell t) ;;关闭emacs启动时的画面 (setq inhibit-startup-message t) ;;关闭gnus启动时的画面 (setq gnus-inhibit-startup-message t) ;;允许屏幕左移 (put 'scroll-left 'disabled nil) ;;允许屏幕右移 (put 'scroll-right 'disabled nil) 


 

2.4 显示时间设置

;;启用时间显示设置,在minibuffer上面的那个杠上 (display-time-mode 1) ;;时间使用24小时制 (setq display-time-24hr-format t) ;;时间显示包括日期和具体时间 (setq display-time-day-and-date t) 


 

2.5 设置日历

 

说实话,日历这个我平时不常用。一般有org模式来做日程管理,关于org-mode,后面我会详细介绍,它是一个强大的日程管理插件,非常优秀(前提是你学会使用)。

 

;;设置日历的一些颜色 (setq calendar-load-hook '(lambda () (set-face-foreground 'diary-face "skyblue") (set-face-background 'holiday-face "slate blue") (set-face-foreground 'holiday-face "white"))) ;; 设置阴历显示,在 calendar 上用 pC 显示阴历 (setq chinese-calendar-celestial-stem ["甲" "乙" "丙" "丁" "戊" "己" "庚" "辛" "壬" "癸"]) (setq chinese-calendar-terrestrial-branch ["子" "丑" "寅" "卯" "辰" "巳" "戊" "未" "申" "酉" "戌" "亥"]) ;; 设置 calendar 的显示 (setq calendar-remove-frame-by-deleting t) ;;设置星期一为每周的第一天 (setq calendar-week-start-day 1) ;; 去掉不关心的节日,设定自己在意的节日,在 calendar 上用 h 显示节日 ;; 在这里,可以把家人还有朋友的生日写上 (setq christian-holidays nil) (setq hebrew-holidays nil) (setq islamic-holidays nil) (setq solar-holidays nil) (setq general-holidays '((holiday-fixed 1 1 "元旦") (holiday-fixed 2 14 "情人节") (holiday-fixed 3 14 "白色情人节") (holiday-fixed 4 1 "愚人节") (holiday-fixed 5 1 "劳动节") (holiday-float 5 0 2 "母亲节") (holiday-fixed 6 1 "儿童节") (holiday-float 6 0 3 "父亲节") (holiday-fixed 7 1 "建党节") (holiday-fixed 8 1 "建军节") (holiday-fixed 9 10 "教师节") (holiday-fixed 10 1 "国庆节") (holiday-fixed 12 25 "圣诞节"))) 


 

2.6 设置符合个人的操作习惯

 
;;设置打开文件的缺省路径,这里audio,我的主要工作目录就是在这里 ;;默认的路径为“~/” (setq default-directory "~/audio") ;;设置M-/作为标志位,默认C-@来setmark,C-@不太好用 (global-set-key (kbd "M-/") 'set-mark-command) ;;改变Emacs要你回答yes的行为。按y或空格键表示yes,n表示no。 (fset 'yes-or-no-p 'y-or-n-p) ;;禁止在鼠标点击的地方插入剪贴板内容 (setq mouse-yank-at-point t) ;;所有模式中都自动填充 (setq-default auto-fill-function 'do-auto-fill) ;;设置默认的文档宽度 (setq default-fill-column 60) ;;不用 TAB 字符来indent, 这会引起很多奇怪的错误。 ;;编辑 Makefile 的时候也不用担心,因为 makefile-mode 会把 TAB 键设置成真正的 TAB 字符,并且加亮显示的 (setq-default indent-tabs-mode nil) (setq default-tab-width 8) (setq tab-stop-list ()) ;;防止页面滚动时跳动 ;;scroll-margin 3 可以在靠近屏幕边沿3行时就开始滚动 ;;scroll-step 1 设置每次都是一行一行向下翻,更有连贯性。 (setq scroll-step 1 scroll-margin 3 scroll-conservatively 10000) ;;设置缺省主模式是text,,并进入auto-fill次模式. ;;而不是基本模式fundamental-mode (setq default-major-mode 'text-mode) (add-hook 'text-mode-hook 'turn-on-auto-fill) ;;打开括号匹配显示模式 (setq show-paren-mode t) ;;括号匹配时可以高亮显示另外一边的括号,但光标不会烦人的跳到另一个括号处。 (setq show-paren-style 'parenthesis) ;;让 Emacs 可以直接打开和显示图片。 (setq auto-image-file-mode t) ;; 不自动保存模式 (setq auto-save-mode nil) ;;允许emacs和外部其他程序的粘贴 (setq x-select-enable-clipboard t) ;; 自动的在文件末增加一新行 (setq require-final-newline t) ;; 当光标在行尾上下移动的时候,始终保持在行尾。 (setq track-eol t) ;; 设置时间戳,标识出最后一次保存文件的时间。 (setq time-stamp-active t) (setq time-stamp-warn-inactive t) (setq time-stamp-format "%:y-%02m-%02d %3a %02H:%02M:%02S chunyu") ;Emacs的插件一般都是一个以el为后缀名的文件, 把这个文件下载下来后放到一个目录, 比如~/emacs/lisps, 然后执行下面这两条语句: (add-to-list 'load-path "~/emacs/lisps") (require 'pluginname) ;;把C-j绑定到”到达指定行上”(goto-line) (global-set-key (kbd "C-j") 'goto-line) ;;对c++-mode进行快捷键定制, 用define-key即可 ;;define-key第一个参数map一般为mode后面直接加”-map” (define-key c++-mode-map (kbd "C-j") 'goto-line) ;; 设置默认tab宽度为2 (setq tab-width 2 indent-tabs-mode t c-basic-offset 2) ;; 回车缩进 (global-set-key "\C-m" 'newline-and-indent) (global-set-key (kbd "C-<return>") 'newline) 


 

2.7 其它小功能

 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;; 实现不设置标志就能复制 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;复制单词 (defun copy-word (&optional arg) "Copy words at point into kill-ring" (interactive "P") (copy-thing 'backward-word 'forward-word arg) (paste-to-mark arg)) ;;绑定键 (global-set-key (kbd "C-c w") (quote copy-word)) ;;复制一行 (defun copy-line (&optional arg) "Save current line into Kill-Ring without mark the line " (interactive "P") (copy-thing 'beginning-of-line 'end-of-line arg) (paste-to-mark arg)) ;;绑定键 (global-set-key (kbd "C-c l") (quote copy-line)) ;;复制一段 (defun copy-paragraph (&optional arg) "Copy paragraphes at point" (interactive "P") (copy-thing 'backward-paragraph 'forward-paragraph arg) (paste-to-mark arg)) ;;绑定键 (global-set-key (kbd "C-c p")(quote copy-paragraph)) ;;复制字符串 (defun beginning-of-string(&optional arg) " " (re-search-backward "[ \t]" (line-beginning-position) 3 1) (if (looking-at "[\t ]") (goto-char (+ (point) 1)) ) ) (defun end-of-string(&optional arg) " " (re-search-forward "[ \t]" (line-end-position) 3 arg) (if (looking-back "[\t ]") (goto-char (- (point) 1)) ) ) (defun thing-copy-string-to-mark(&optional arg) " Try to copy a string and paste it to the mark When used in shell-mode, it will paste string on shell prompt by default " (interactive "P") (copy-thing 'beginning-of-string 'end-of-string arg) (paste-to-mark arg)) ;;绑定键 (global-set-key (kbd "C-c s") (quote thing-copy-string-to-mark)) (defun beginning-of-parenthesis(&optional arg) " " (re-search-backward "[[<(?\"]" (line-beginning-position) 3 1) (if (looking-at "[[<(?\"]") (goto-char (+ (point) 1)) ) ) (defun end-of-parenthesis(&optional arg) " " (re-search-forward "[]>)?\"]" (line-end-position) 3 arg) (if (looking-back "[]>)?\"]") (goto-char (- (point) 1)) ) ) (defun thing-copy-parenthesis-to-mark(&optional arg) " Try to copy a parenthesis and paste it to the mark When used in shell-mode, it will paste parenthesis on shell prompt by default " (interactive "P") (copy-thing 'beginning-of-parenthesis 'end-of-parenthesis arg) (paste-to-mark arg)) ;;绑定键 (global-set-key (kbd "C-c a") (quote thing-copy-parenthesis-to-mark)) ;;所有以上几个函数,都依赖于下面这三个基础函数,由他们三个负责完成实际的工作。 (defun get-point (symbol &optional arg) "get the point" (funcall symbol arg) (point)) (defun copy-thing (begin-of-thing end-of-thing &optional arg) "copy thing between beg & end into kill ring" (let ((beg (get-point begin-of-thing 1)) (end (get-point end-of-thing arg))) (copy-region-as-kill beg end))) (defun paste-to-mark(&optional arg) "Paste things to mark, or to the prompt in shell-mode" (let ((pasteMe (lambda() (if (string= "shell-mode" major-mode) (progn (comint-next-prompt 25535) (yank)) (progn (goto-char (mark)) (yank) ))))) (if arg (if (= arg 1) nil (funcall pasteMe)) (funcall pasteMe)) )) 


 

2.8 绑定键设置

 
;;绑定键的设置最好放在配置文件的最后,以免被后面的配置文件覆盖 ;;设置F1打开帮助文档 (global-set-key [f1] 'info) ;;设置F2为恢复键 (global-set-key [f2] 'undo) ;;设置F3关闭当前buffer (global-set-key [f3] 'kill-this-buffer) ;;设置F4打开一个终端,可以绑定:eshell,shell,terminal-emulator (global-set-key [f4] 'term) ;;C-f5 设置编译命令 ;;f5 保存所有文件然后编译当前窗口文件 (defun du-onekey-compile () "Save buffers and start compile" (interactive) (save-some-buffers t) (switch-to-buffer-other-window "*compilation*") (compile compile-command)) (global-set-key [C-f5] 'compile) (global-set-key [f5] 'du-onekey-compile) ;;F6设置为在Emacs中调用gdb (global-set-key [f6] 'gdb) ;;可以显示所有目录以及文件 (setq speedbar-show-unknown-files t) ;;设置f9调用speedbar命令 ;;使用 n 和 p 可以上下移动, ;; + 展开目录或文件进行浏览,- 收缩,RET 访问目录或文件,g 更新 speedbar。 (global-set-key [f9] 'speedbar) ;;设置F10打开/关闭菜单 (global-set-key [f10] 'menu-bar-mode) ;; 设置F11快捷键指定Emacs 的日历系统 (global-set-key [f11] 'calendar) ;; 设置F12 快速察看日程安排 (global-set-key [f12] 'list-bookmarks) ;; "C-,"设为屏幕左移命令 (global-set-key (kbd "C-,") 'scroll-left) ;; "C-."设为屏幕右移命令 (global-set-key (kbd "C-.") 'scroll-right) 


 

2.9 C/C++/JAVA编程模式

 
;; C语言模式 (add-hook 'c-mode-hook '(lambda () ;;把c语言风格设置为k&r风格 (c-set-style "k&r") ;; 此模式下,当按Backspace时会删除最多的空格 (c-toggle-hungry-state) ;; TAB键的宽度设置为4 (setq c-basic-offset 4) ;; 在菜单中加入当前Buffer的函数索引 (imenu-add-menubar-index) ;; 在状态条上显示当前光标在哪个函数体内部 (which-function-mode))) ;; C++语言模式 (add-hook 'c++-mode-hook '(lambda() ;;把C++语言风格设置为stroustrup风格 (c-set-style "stroustrup") (c-toggle-auto-state) (c-toggle-hungry-state) (setq c-basic-offset 4) (imenu-add-menubar-index) (which-function-mode))) ;; JAVA语言模式 (add-hook 'java-mode-hook '(lambda() (c-set-style "java") (c-toggle-auto-state) (c-toggle-hungry-state) (setq c-basic-offset 4) (imenu-add-menubar-index) (which-function-mode))) ;;能把一个代码块缩起来,需要的时候再展开 ;; M-x hs-minor-mode ;; C-c @ ESC C-s show all ;; C-c @ ESC C-h hide all ;; C-c @ C-s show block ;; C-c @ C-h hide block ;; C-c @ C-c toggle hide/show (load-library "hideshow") (add-hook 'c-mode-hook 'hs-minor-mode) (add-hook 'c++-mode-hook 'hs-minor-mode) (add-hook 'java-mode-hook 'hs-minor-mode) (add-hook 'perl-mode-hook 'hs-minor-mode) (add-hook 'php-mode-hook 'hs-minor-mode) (add-hook 'emacs-lisp-mode-hook 'hs-minor-mode) ;;自动补全右边的部分.包括<>, "", '', (), //, [] , {}等等。 (defun my-c-mode-auto-pair () (interactive) (make-local-variable 'skeleton-pair-alist) (setq skeleton-pair-alist '( ;;自动补全<> (?\< ? _ ">") ;;自动补全// (?` ?` _ "/") ;;自动补全() (?\( ? _ " )") ;;自动补全[] (?\[ ? _ " ]") ;;设自动补全"" (?\" ? _ "\"") ;;自动补全'' (?\' ? _ "'") ;;自动补全{} (?{ \n > _ \n ?} >))) (setq skeleton-pair t) (local-set-key (kbd "<") 'skeleton-pair-insert-maybe) (local-set-key (kbd "/") 'skeleton-pair-insert-maybe) (local-set-key (kbd "(") 'skeleton-pair-insert-maybe) (local-set-key (kbd "/") 'skeleton-pair-insert-maybe) (local-set-key (kbd "'") 'skeleton-pair-insert-maybe) (local-set-key (kbd "\"") 'skeleton-pair-insert-maybe) (local-set-key (kbd "[") 'skeleton-pair-insert-maybe) (local-set-key (kbd "{") 'skeleton-pair-insert-maybe)) (add-hook 'c-mode-hook 'my-c-mode-auto-pair) (add-hook 'c++-mode-hook 'my-c-mode-auto-pair)