让你的Emacs像VS一样方便

Emacs是强大的文本编辑器之一,尤其是进行程序设计,人们常说:“程序员可以分为三种:使用Emacs的,使用Vim的,以及其他”,可见Emacs在程序员中间的地位。不多废话,直接进入正题。本文介绍了在Ubuntu环境下使用Emacs进行C/C++进行开发的配置方法,通过两天的折腾,配置了一些基本的功能:代码提示/补齐、代码折叠、代码缩进和代码跳转等功能,后续的配置还会继续更新本文章,但仅此三项已经大大提高了开发效率。

配置方法

本文配置是基于Emacs24进行配置的,但配置原理上也支持Emacs23。配置主要用到两个Emacs扩展:CEDET和Auto-Complete。CEDET用于向Auto-Complete提供语义分析,从而向auto-complete提供代码提示支撑。配置方法如下:

  1. 基本概念:

    Emacs在启动的时候会默认加载home目录下的.emacs配置文件,此文件是隐藏文件,如果不存在可手动建立(命令:touch .emacs),建立此文件之后可以使用gedit进行编辑(gedit .emacs)。然后配置信息都写在此文件内。
  2. 开启CEDET

    首先需要开启CEDET,配置如下:
    ;;set the mode enabled
    (setq semantic-default-submodes '(global-semanticdb-minor-mode
                                      global-semantic-idle-scheduler-mode
                                      global-semantic-idle-summary-mode
                                      global-semantic-idle-completions-mode
                                      global-semantic-decoration-mode
                                      global-semantic-highlight-func-mode
                                      global-semantic-stickyfunc-mode
                                      global-semantic-mru-bookmark-mode
                                      global-semantic-tag-folding-mode))
    ;;enable cedet
    (semantic-mode 1)
    	
    在CEDET1.1及以后版本中,通过设置默认语义子模式(semantic-default-submodes)来配置需要开启的模式,然后只需要(semantic-mode 1)一句话即可开启CEDET。CEDET支持的模式包括:
    CEDET模式列表
    模式名称模式作用
    global-semanticdb-minor-mode开启Semanticdb支持。Semanticdb用于存储已经分析出的语义结果
    global-semantic-mru-bookmark-mode开启自动书签存储,从而可以使用代码跳转
    global-cedet-m3-minor-mode激活CEDET右键菜单(还没发先有啥用)
    global-semantic-highlight-func-mode高亮方法、类等的第一行
    global-semantic-stickyfunc-mode当前编辑的方法上滚到可视区域外时,将方法名显示在最顶端
    global-semantic-decoration-mode激活样式
    global-semantic-idle-local-symbol-highlight-mode高亮所有当前选中变量使用的位置
    global-semantic-idle-scheduler-mode空闲的时候对源码自动进行语义分析
    global-semantic-idle-completions-mode在空闲的时候显示可能的命名补全(CEDET自己的命名补全显示在已输入部分后面,显示为淡灰色)
    global-semantic-idle-summary-mode在空闲时显示当前位置的信息
    global-semantic-show-unmatched-syntax-mode显示当前没有进行语义处理的元素
    global-semantic-show-parser-state-mode显示当前语义处理器的状态
    global-semantic-highlight-edits-mode高亮还没有进行语义处理的元素




  3. 配置代码提示/补齐

    代码提示需要配合auto-complete进行,虽然CEDET也有代码提示功能(参见CEDET配置参考文章),但CEDET官方推荐的是使用auto-complete进行代码提示和补齐。

    使用代码提示首先要开启CEDET代码补齐功能:
    ;;开启代码补齐功能
    (require 'semantic/ia)
    ;;添加C++头文件
    (require 'semantic/bovine/gcc)
    

    然后,将CEDET语义分析添加到ac-sources变量中,这是由于auto-complete使用ac-sources进行代码提示和补齐,配置如下:
    ;;添加ac-sources
    (defun my-c-mode-cedet-hook ()
      (add-to-list 'ac-sources 'ac-source-gtags)
      (add-to-list 'ac-sources 'ac-source-semantic))
    (add-hook 'c-mode-common-hook 'my-c-mode-cedet-hook)

    最后,需要配置auto-complete。由于auto-complete不是Emacs标准的一部分,因此需要手动下载而配置。首先去auto-complete官网Auto Complete Mode下载最新版本的auto-complete(本文是基于1.3.1),然后解压放在~/home/.emacs.d文件夹下(.emacs.d如果不存在则手动建立),并将文件夹名称改为"auto-complete"(如果不是)。然后配置加载auto-complete如下:
    (add-to-list 'load-path "~/.emacs.d/auto-complete")
    (require 'auto-complete-config)
    (add-to-list 'ac-dictionary-directories "~/.emacs.d/auto-complete/dict")
    (ac-config-default)
    ;;ignore case
    (setq ac-ignore-case t)

    最后一行配置了auto-complete代码提示忽略大小写,还有其他一些配置,例如设置提示框颜色等等,可以参考auto-complete配置参考文章。最后一行配置了忽略大小写

  4. 代码跳转

    代码跳转时程序设计中很常用的一个功能,CEDET可以通过配置支持代码跳转功能。配置如下:
    (global-set-key [f2] 'semantic-ia-fast-jump);;jump
    (global-set-key [S-f2];;jump back:shift+f2
                    (lambda ()
                      (interactive)
                      (if (ring-empty-p (oref semantic-mru-bookmark-ring ring))
                          (error "Semantic Bookmark ring is currently empty"))
                      (let* ((ring (oref semantic-mru-bookmark-ring ring))
                             (alist (semantic-mrub-ring-to-assoc-list ring))
                             (first (cdr (car alist))))
                        (if (semantic-equivalent-tag-p (oref first tag)
                                                       (semantic-current-tag))
                            (setq first (cdr (car (cdr alist)))))
                        (semantic-mrub-switch-tags first))))
    上面代码配置了使用f2进行代码跳转,然后使用shift+f2进行代码跳回(默认是使用\C-x B),另外需要注意的是使用代码跳转需要开启模式。此外如果代码无法跳回可在跳转配置前加入如下配置(参考点击打开链接):
    (defadvice push-mark (around semantic-mru-bookmark activate)
      "Push a mark at LOCATION with NOMSG and ACTIVATE passed to `push-mark'.
    If `semantic-mru-bookmark-mode' is active, also push a tag onto
    the mru bookmark stack."
      (semantic-mrub-push semantic-mru-bookmark-ring
                          (point)
                          'mark)
      ad-do-it)


  5. 代码缩进

    代码缩进功能没有深入研究,就参考了GNU Emacs官方文档上的示例代码设置了一下(参见点击打开链接),代码如下:
    ;;-------------indent setting start 
    ;; Make a non-standard key binding.  We can put this in
    ;; c-mode-base-map because c-mode-map, c++-mode-map, and so on,
    ;; inherit from it.
    (defun my-c-initialization-hook ()
      (define-key c-mode-base-map "\C-m" 'c-context-line-break))
    (add-hook 'c-initialization-hook 'my-c-initialization-hook)
    
    
    ;; offset customizations not in my-c-style
    ;; This will take precedence over any setting of the syntactic symbol
    ;; made by a style.
    (setq c-offsets-alist '((member-init-intro . ++)))
    
    
    ;; Create my personal style.
    (defconst my-c-style
      '((c-tab-always-indent        . t)
        (c-comment-only-line-offset . 4)
        (c-hanging-braces-alist     . ((substatement-open after)
                                       (brace-list-open)))
        (c-hanging-colons-alist     . ((member-init-intro before)
                                       (inher-intro)
                                       (case-label after)
                                       (label after)
                                       (access-label after)))
        (c-cleanup-list             . (scope-operator
                                       empty-defun-braces
                                       defun-close-semi))
        (c-offsets-alist            . ((arglist-close . c-lineup-arglist)
                                       (substatement-open . 0)
                                       (case-label        . 4)
                                       (block-open        . 0)
                                       (knr-argdecl-intro . -)))
        (c-echo-syntactic-information-p . t))
      "My C Programming Style")
    (c-add-style "PERSONAL" my-c-style)
    
    
    ;; Customizations for all modes in CC Mode.
    (defun my-c-mode-common-hook ()
      ;; set my personal style for the current buffer
      (c-set-style "PERSONAL")
      ;; other customizations
      (setq tab-width 8
            ;; this will make sure spaces are used instead of tabs
            indent-tabs-mode nil)
    ;; we like auto-newline, but not hungry-delete
      (c-toggle-auto-newline 1))
    (add-hook 'c-mode-common-hook 'my-c-mode-common-hook)
    ;;----------indent setting end


  6. 代码折叠

    代码折叠功能使用了Emacs自带的hs-minor-mode,但是由于默认快捷键使用起来太不方便,所以进行了自定义为C-c ?快捷键,配置代码如下
    ;;universal code folding
    (defun toggle-selective-display (column)
          (interactive "P")
          (set-selective-display
           (or column
               (unless selective-display
                 (1+ (current-column))))))
    (defun toggle-hiding (column)
          (interactive "P")
          (if hs-minor-mode
              (if (condition-case nil
                      (hs-toggle-hiding)
                    (error t))
                  (hs-show-all))
            (toggle-selective-display column)))
    (load-library "hideshow")
    (global-set-key (kbd "C-?") 'toggle-hiding)
    (global-set-key (kbd "C-`") 'toggle-selective-display)
    (add-hook 'c-mode-common-hook   'hs-minor-mode)
        (add-hook 'emacs-lisp-mode-hook 'hs-minor-mode)
        (add-hook 'java-mode-hook       'hs-minor-mode)
        (add-hook 'lisp-mode-hook       'hs-minor-mode)
        (add-hook 'perl-mode-hook       'hs-minor-mode)
        (add-hook 'sh-mode-hook         'hs-minor-mode)
    ;;display overlay content in echo area or tooltip
    (defun display-code-line-counts (ov)
          (when (eq 'code (overlay-get ov 'hs))
            (overlay-put ov 'help-echo
                         (buffer-substring (overlay-start ov)
                          (overlay-end ov)))))
     
        (setq hs-set-up-overlay 'display-code-line-counts)


  7. 其他设置

    除了上述设置,还有一些其他有趣的设置,包括:
  • 其他快捷键

    其他快捷键设置除了代码跳转外,还可以定义其他快捷键,包括:
    (defun my-c-mode-cedet-key()
      (local-set-key "\C-cd" 'semantic-ia-show-doc);;显示方法、类等的文档
      (local-set-key "\C-cs" 'semantic-ia-show-summary);;显示方法、类等的概述
      (local-set-key "\C-cc" 'semantic-ia-describe-class);;列举类的方法变量等
      (local-set-key "\C-ct" 'semantic-analyze-proto-impl-toggle);;在.h和.cpp文件之间进行跳转切换
      (local-set-key "\C-ci" 'semantic-decoration-include-visit);;进入include中的文件
    )
    (add-hook 'c-mode-common-hook 'my-c-mode-cedet-key)


  • 显示行号

(global-linum-mode 1)

  • Emacs24及以上版本主题设置

M-x list-faces-display

  • 隐藏工具栏

(tool-bar-mode -1)

  • Yasnippe代码模板

    yasnippe提供了代码模板功能,但我个人习惯了自己写书写,而且配置很麻烦,所以就没有深入弄,基本配置代码如下:
    (add-to-list 'load-path "~/.emacs.d/yasnippet-0.6.1c/")
    (require 'yasnippet)
    (yas/initialize)
    (yas/load-directory "~/.emacs.d/yasnippet-0.6.1c/snippets")
    需要注意的是,yasnnipe需要自己下载安装,与auto-complete方法一致,放在对应路径下就行,本文放在了~/.emacs.d/yasnippet-0.6.1c下。具体配置方法参见yasnippet配置参考文章

配置完成后的效果如下图:

配置效果图

全部配置:

;;=====================================================================
;;Emacs settings begin
;;=====================================================================
;;newline and indent
(setq c-newline-and-indent 'newline-and-indent)
;;hide tool bar
(tool-bar-mode -1)
;; display column-number-mode
(global-linum-mode 1)
;;-------------indent setting start http://www.gnu.org/software/emacs/manual/html_mono/ccmode.html#Sample-_002eemacs-File
;; Make a non-standard key binding.  We can put this in
;; c-mode-base-map because c-mode-map, c++-mode-map, and so on,
;; inherit from it.
(defun my-c-initialization-hook ()
  (define-key c-mode-base-map "\C-m" 'c-context-line-break))
(add-hook 'c-initialization-hook 'my-c-initialization-hook)

;; offset customizations not in my-c-style
;; This will take precedence over any setting of the syntactic symbol
;; made by a style.
(setq c-offsets-alist '((member-init-intro . ++)))

;; Create my personal style.
(defconst my-c-style
  '((c-tab-always-indent        . t)
    (c-comment-only-line-offset . 4)
    (c-hanging-braces-alist     . ((substatement-open after)
                                   (brace-list-open)))
    (c-hanging-colons-alist     . ((member-init-intro before)
                                   (inher-intro)
                                   (case-label after)
                                   (label after)
                                   (access-label after)))
    (c-cleanup-list             . (scope-operator
                                   empty-defun-braces
                                   defun-close-semi))
    (c-offsets-alist            . ((arglist-close . c-lineup-arglist)
                                   (substatement-open . 0)
                                   (case-label        . 4)
                                   (block-open        . 0)
                                   (knr-argdecl-intro . -)))
    (c-echo-syntactic-information-p . t))
  "My C Programming Style")
(c-add-style "PERSONAL" my-c-style)

;; Customizations for all modes in CC Mode.
(defun my-c-mode-common-hook ()
  ;; set my personal style for the current buffer
  (c-set-style "PERSONAL")
  ;; other customizations
  (setq tab-width 8
        ;; this will make sure spaces are used instead of tabs
        indent-tabs-mode nil)
  ;; we like auto-newline, but not hungry-delete
  (c-toggle-auto-newline 1))
(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)
;;----------indent setting end
;;=====================================================================
;;emacs setting end
;;=====================================================================
;;=====================================================================
;;cedet begin
;;cedet(1.1) setting based on method descripted at
;;http://alexott.net/en/writings/emacs-devenv/EmacsCedet.html
;;=====================================================================
;;set the mode enabled
(setq semantic-default-submodes '(global-semanticdb-minor-mode
                                  global-semantic-idle-scheduler-mode
                                  global-semantic-idle-summary-mode
                                  global-semantic-idle-completions-mode
                                  global-semantic-decoration-mode
                                  global-semantic-highlight-func-mode
                                  global-semantic-stickyfunc-mode
                                  global-semantic-mru-bookmark-mode
                                  global-semantic-tag-folding-mode))
;;enable cedet
(semantic-mode 1)
;;enable advanced functionality for name completion
(require 'semantic/ia)
;;include standard C/C++ headers
(require 'semantic/bovine/gcc)
;;add ac-souces, ac-sources is used by auto-complete
(defun my-c-mode-cedet-hook ()
  (add-to-list 'ac-sources 'ac-source-gtags)
  (add-to-list 'ac-sources 'ac-source-semantic))
(add-hook 'c-mode-common-hook 'my-c-mode-cedet-hook)
;;solve the problem can not jump back according to http://blog.163.com/vic_kk/blog/static/494705242010726297405/#sec-3_4
(defadvice push-mark (around semantic-mru-bookmark activate)
  "Push a mark at LOCATION with NOMSG and ACTIVATE passed to `push-mark'.
If `semantic-mru-bookmark-mode' is active, also push a tag onto
the mru bookmark stack."
  (semantic-mrub-push semantic-mru-bookmark-ring
                      (point)
                      'mark)
  ad-do-it)
;;add c-mode key set
(global-set-key [f1] 'speedbar)
(global-set-key [f2] 'semantic-ia-fast-jump);;jump
(global-set-key [S-f2];;jump back:shift+f2
                (lambda ()
                  (interactive)
                  (if (ring-empty-p (oref semantic-mru-bookmark-ring ring))
                      (error "Semantic Bookmark ring is currently empty"))
                  (let* ((ring (oref semantic-mru-bookmark-ring ring))
                         (alist (semantic-mrub-ring-to-assoc-list ring))
                         (first (cdr (car alist))))
                    (if (semantic-equivalent-tag-p (oref first tag)
                                                   (semantic-current-tag))
                        (setq first (cdr (car (cdr alist)))))
                    (semantic-mrub-switch-tags first))))
(defun my-c-mode-cedet-key()
  (local-set-key "\C-cd" 'semantic-ia-show-doc)
  (local-set-key "\C-cs" 'semantic-ia-show-summary)
  (local-set-key "\C-cc" 'semantic-ia-describe-class)
  (local-set-key "\C-ct" 'semantic-analyze-proto-impl-toggle)
  (local-set-key "\C-ci" 'semantic-decoration-include-visit)
)
(add-hook 'c-mode-common-hook 'my-c-mode-cedet-key);
;;=====================================================================
;;cedet end
;;=====================================================================
;;=====================================================================
;;auto-complete begin
;;auto-complete(1.3.1) setting based on method descripted at
;;http://cx4a.org/software/auto-complete/manual.html
;;=====================================================================
(add-to-list 'load-path "~/.emacs.d/auto-complete")
(require 'auto-complete-config)
(add-to-list 'ac-dictionary-directories "~/.emacs.d/auto-complete/dict")
(ac-config-default)
;;ignore case
(setq ac-ignore-case t)
;;=====================================================================
;;auto-complete end
;;=====================================================================
;;=====================================================================
;;yasnippet begin
;;yasnippet(0.6.1c) setting based on method descripted at
;;http://capitaomorte.github.io/yasnippet/#id3
;;=====================================================================
(add-to-list 'load-path "~/.emacs.d/yasnippet-0.6.1c/")
(require 'yasnippet)
(yas/initialize)
(yas/load-directory "~/.emacs.d/yasnippet-0.6.1c/snippets")
;;=====================================================================
;;yasnippet end
;;=====================================================================
;;=====================================================================
;;hs-minor-mode setting according to http://www.emacswiki.org/emacs/HideShow
;;=====================================================================
;;universal code folding
(defun toggle-selective-display (column)
      (interactive "P")
      (set-selective-display
       (or column
           (unless selective-display
             (1+ (current-column))))))
(defun toggle-hiding (column)
      (interactive "P")
      (if hs-minor-mode
          (if (condition-case nil
                  (hs-toggle-hiding)
                (error t))
              (hs-show-all))
        (toggle-selective-display column)))
(load-library "hideshow")
(global-set-key (kbd "C-?") 'toggle-hiding)
(global-set-key (kbd "C-`") 'toggle-selective-display)
(add-hook 'c-mode-common-hook   'hs-minor-mode)
    (add-hook 'emacs-lisp-mode-hook 'hs-minor-mode)
    (add-hook 'java-mode-hook       'hs-minor-mode)
    (add-hook 'lisp-mode-hook       'hs-minor-mode)
    (add-hook 'perl-mode-hook       'hs-minor-mode)
    (add-hook 'sh-mode-hook         'hs-minor-mode)
;;display overlay content in echo area or tooltip
(defun display-code-line-counts (ov)
      (when (eq 'code (overlay-get ov 'hs))
        (overlay-put ov 'help-echo
                     (buffer-substring (overlay-start ov)
                      (overlay-end ov)))))
 
    (setq hs-set-up-overlay 'display-code-line-counts)
;;=====================================================================
;;hs-minor-mode end
;;=====================================================================

有用的链接

CEDET配置参考文章


auto-complete配置参考文章


yasnippet配置参考文章


代码折叠配置参考文章


EmacsWiki


配置CEDET浏览和编辑C++


GNU Emacs官方文档

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值