文本編輯器是程序員日常工作生活中不可缺少的重要工具。優秀的文本編輯器可以解放勞動力、提高生產率。在程序員的世界裏效率是一件重要的事情。因此,文本編輯器之爭也就成爲大家茶餘飯後的談資(如同程序設計語言一樣)。我向來是一個自由主義者。誰好誰壞這種事情,所持的觀點和角度不同,有時候很難分辨。程序員和文本編輯器之間的關係,應該有點像談戀愛,蘿蔔青菜各有所愛。
Emacs是編輯器排行榜中爭論較爲突出的一位,支持者聲稱Emacs是世界上最好用的編輯器(因爲配置和擴展的能力實在太強,它確實不僅僅是個文本編輯器,還是IDE、日曆/日程/約會管理器、郵件客戶端、Web瀏覽器、命令終端、圖片瀏覽、音樂播放、視頻編輯等,甚至曾有人把它比成操作系統.....)。反對者也能列舉它諸多不是,將其與神經病、抓狂相聯繫(比如太多的快捷鍵綁定把手指頭都搞抽筋,比如太古老,比如配置起來太複雜,比如啓動的速度可以趕超操作系統等等)。其實採納兩方觀點,才能把Emacs看得更清楚。扯遠了,回到正題。
話說我和Emacs之間的過往,不嚴格的說從大學時代就已經開始。那個時候僅僅以爲它是古董又蹩腳的東西,21世紀的“新新軟件工程師”還有誰會使用它。所以是隻聞其名,未見其身。大學畢業工作三年後,一個偶然的機會,我踏進了Emacs的世界(一是因爲踏進了lisp世界,二是出於對古董級物品難以遏制的好奇心。)。瞭解了lisp之後,發現原來Emacs的配置和開發並不是很恐怖,也體會到這個所謂的無所不能的神器的震撼,“Emacs只不過是僞裝成編輯器的操作系統”這句話真的一點也不假。
古董級的東西還能活在現今的世界中,一定有它的原因。“Emacs is a refugee from the long dead culture of Lisp Machines”
以上扯的更遠了,再回到正題。
既然本篇的題爲神器的養成,那麼還是要談談我在使用Emacs後的一些心得(這纔是正題)。對於像我這樣用慣了新一代編輯器(主要在微軟平臺上)的“新新軟件工程師”,腦海中形成了固化的概念,傻子式、所見即所得、圖形界面形式的編輯器很好用很給力。這個概念說的通用些“因爲學習曲線短所以認爲很好用很給力”。但如果把這樣的概念應用到Emacs上,不合適!Emacs是瘋子式(天才式)、所按即所得、提供複雜編輯功能的編輯器。Emacs的概念說的通用些“它不想你一來就上手。學習曲線必須是相當的陡峭。開始使用的時候一點力也不給你。但它關心的是你學成之後,無所不能。”
如果把學習和使用編輯器比作登山。那普通的編輯器不過是個丘陵,你登的容易登不高。Emacs是珠穆朗瑪峯,一般人不輕易去登它,二般人只有兩個結果,要麼登頂要麼犧牲。
想要登珠峯,除了練就一身好體魄,還有有堅持不懈地決心。所以這應該是Emacs使用者應該持有的態度吧。
光有態度恐怕還不行,還需要方法。那麼什麼方法呢,其實很簡單。只要記住幾個非常重要快捷鍵,一個非常重要的文件,還有若干網站,這就是登山裝備了。說的確切點,
快捷鍵是Ctrl-h t, Ctrl-h i, Ctrl-h f, Ctrl-h v, Ctrl-h d, Ctrl-h b, Ctrl-h n等Ctrl-h作爲前綴的幫助快捷鍵。
文件是大名鼎鼎的dot emacs文件(.emacs配置文件)
網站是http://www.emacswiki.org/,http://emacser.com/about.htm或者直接google好了。
最後的最後,貼出我初養成的神器Emacs的dot emacs。以待後日成爲大牛時作個參考。
(require 'ido)
;;使用CVS版本,因爲Emacs 23中已經有內欠的CEDET了,所以必須明確load-file (load-file "~/.emacs.d/contrib/cedet/common/cedet.el") (require 'eassist) ;;所有擴展包都在.emacs.d的contrib目錄下 (let ((default-directory "~/.emacs.d/")) (normal-top-level-add-subdirs-to-load-path)) (require 'color-theme) (require 'maxframe) (require 'traverselisp) (require 'session) (require 'auto-complete-config) (require 'yasnippet) (require 'ecb-autoloads)
(require 'org) (require 'muse-mode) (require 'muse-publish) (require 'muse-html) (require 'emacs-wiki) (require 'slime) (require 'cc-mode) ;; 還有一些擴展包包含在emacs-goodies-el中,用的是debian系統 (add-to-list 'load-path "/usr/share/emacs/site-lisp/emacs-goodies-el/" t) (require 'tabbar) (require 'highlight-current-line) (require 'pack-windows) ;;;;;;;;;;;;;;;很多適合我的配置;;;;;;;;;;;;;;;;;;;; ;;Custom Setting (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(auto-image-file-mode t) '(calendar-mark-diary-entries-flag t) '(calendar-mark-holidays-flag t) '(calendar-week-start-day 1) '(column-number-mode t) '(desktop-base-file-name ".emacs.d/.desktop") '(desktop-base-lock-name ".emacs.d/.desktop.lock") '(desktop-save-mode t) '(display-time-24hr-format t) '(display-time-day-and-date t) '(display-time-format "%A %B %d %T %Z") '(display-time-interval 1) '(display-time-mail-function nil) '(display-time-mode t) '(display-time-world-list (quote (("PST8PDT" "Seattle") ("EST5EDT" "New York") ("GMT0BST" "London") ("CET-1CDT" "Paris") ("IST-5:30" "Bangalore") ("JST-9" "Tokyo") ("CST-8" "BeiJing")))) '(display-time-world-time-format "%A %B %d %T %Z") '(display-time-world-timer-second 1) '(ecb-options-version "2.40") '(ede-project-placeholder-cache-file "~/.emacs.d/.projects.ede") '(gdb-many-windows nil) '(highlight-current-line-globally t nil (highlight-current-line)) '(highlight-current-line-whole-line t) '(holiday-hebrew-holidays nil) '(holiday-islamic-holidays nil) '(holiday-solar-holidays nil) '(ido-create-new-buffer (quote always)) '(ido-enable-last-directory-history nil) '(ido-max-work-directory-list 0) '(ido-max-work-file-list 0) '(ido-mode (quote buffer) nil (ido)) '(ido-record-commands nil) '(image-load-path (quote ("~/.emacs.d/" "/usr/local/share/emacs/23.2/etc/images/" data-directory load-path ""))) '(inhibit-startup-buffer-menu t) '(inhibit-startup-echo-area-message "nixie") '(inhibit-startup-screen t) '(initial-buffer-choice nil) '(initial-scratch-message nil) '(kill-do-not-save-duplicates t) '(kill-ring-max 500) '(legacy-style-world-list (quote (("PST8PDT" "Seattle") ("EST5EDT" "New York") ("GMT0BST" "London") ("CET-1CDT" "Paris") ("IST-5:30" "Bangalore") ("JST-9" "Tokyo") ("CST-8" "BeiJing")))) '(make-backup-files nil) '(max-lisp-eval-depth 1048576) '(max-specpdl-size 1048576) '(save-interprogram-paste-before-kill t) '(scroll-conservatively 100000) '(scroll-margin 3) '(scroll-step 1) '(semantic-c-dependency-system-include-path (quote ("/usr/include" "/usr/local/include/opencv"))) '(semantic-idle-scheduler-idle-time 0.5) '(show-paren-mode t) '(tabbar-background-color "yellow") '(tabbar-mode t nil (tabbar)) '(uniquify-buffer-name-style (quote forward) nil (uniquify)) '(x-select-enable-clipboard t) '(yank-pop-change-selection t) '(yas/trigger-key (kbd "C-x C-y")) '(zoneinfo-style-world-list (quote (("America/Los_Angeles" "Seattle") ("America/New_York" "New York") ("Europe/London" "London") ("Europe/Paris" "Paris") ("Asia/Calcutta" "Bangalore") ("Asia/Tokyo" "Tokyo") ("Asia/BeiJing" "BeiJing"))))) (custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(highlight-current-line-face ((t (:background "yellow" :foreground "black" :inverse-video t :weight bold)))) '(tabbar-default ((((class color grayscale) (background dark)) (:inherit variable-pitch :background "gray50" :foreground "grey75"))))) (put 'upcase-region 'disabled nil) ;;;;;;;;;;;;;;;;;;;擴展功能的一些初始化;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Color-Theme (eval-after-load "color-theme" '(progn (color-theme-initialize) (color-theme-calm-forest))) ;;Auto-complete (add-to-list 'ac-dictionary-directories "~/.emacs.d/contrib/auto-complete/dict/") (ac-config-default) ;;Yasnippet (yas/initialize) (yas/load-directory "~/.emacs.d/contrib/yasnippet/snippets") ;; Exempted from auto-complete-yasnippet.el (defun ac-yasnippet-candidate-1 (table) (let ((hashtab (yas/snippet-table-hash table)) (parent (yas/snippet-table-parent table)) candidates) (maphash (lambda (key value) (push key candidates)) hashtab) (setq candidates (all-completions ac-prefix (nreverse candidates))) (if parent (setq candidates (append candidates (ac-yasnippet-candidate-1 parent)))) candidates)) (defun ac-yasnippet-candidate () (let ((table (yas/snippet-table major-mode))) (if table (ac-yasnippet-candidate-1 table)))) (defface ac-yasnippet-candidate-face '((t (:background "sandybrown" :foreground "black"))) "Face for yasnippet candidate.") (defface ac-yasnippet-selection-face '((t (:background "coral3" :foreground "white"))) "Face for the yasnippet selected candidate.") (defvar ac-source-yasnippet '((candidates . ac-yasnippet-candidate) (action . yas/expand) (limit . 3) (candidate-face . ac-yasnippet-candidate-face) (selection-face . ac-yasnippet-selection-face)) "Source for Yasnippet.") ;; CEDET (global-ede-mode 1) (semantic-load-enable-minimum-features) (semantic-load-enable-code-helpers) ;;(semantic-load-enable-guady-code-helpers) ;;(semantic-load-enable-excessive-code-helpers) (semantic-load-enable-semantic-debugging-helpers) (global-semantic-highlight-func-mode 1) (if (fboundp #'which-func-mode) (add-hook 'semantic-init-hook (lambda () (which-func-mode 1)))) (global-srecode-minor-mode 1) (when (and (require 'semantic-tag-folding nil 'noerror)) (global-semantic-tag-folding-mode 1) (global-set-key (kbd "C-?") 'global-semantic-tag-folding-mode) (define-key semantic-tag-folding-mode-map (kbd "C-c -") 'semantic-tag-folding-fold-block) (define-key semantic-tag-folding-mode-map (kbd "C-c =") 'semantic-tag-folding-show-block) (define-key semantic-tag-folding-mode-map (kbd "C-c M--") 'semantic-tag-folding-fold-all) (define-key semantic-tag-folding-mode-map (kbd "C-c M-=") 'semantic-tag-folding-show-all)) (enable-visual-studio-bookmarks) ;; Slime (setq slime-lisp-implementations `((sbcl ("~/.emacs.d/contrib/sbcl/src/runtime/sbcl" "--core" ,(file-truename "~/.emacs.d/contrib/sbcl/output/sbcl.core")) :env ,(list (format "SBCL_HOME=%s" (file-truename "~/.emacs.d/contrib/sbcl/contrib/")))))) (slime-setup '(slime-fancy slime-repl slime-scratch slime-editing-commands)) ;;;;;;;;;;;;;;;;;;適合我的設置;;;;;;;;;;;;;;;;;;;;;;;;;;; (global-set-key (kbd "C-x 9") 'maximize-frame);;這個沒有ubuntu下面系統默認的alt+f10好用 ;; Enable IDO Mode (ido-mode 1) ;;管理諸多Buffer實在很煩,索性用一個鍵刪除之。開始不習慣,刪除一個字母的時候老按delete,結果...... (global-set-key (kbd "<delete>") 'kill-buffer-and-window) ;;把所有不想要的Buffer都一個鍵刪了去。 (defun kill-other-buffers (&rest buffers-not-to-kill) "Kill buffers not listed in arguements. If the arguements are nil, all buffers except current buffer will be killed" (interactive) (let ((buffers-all (buffer-list)) (buffers-not-to-kill (or buffers-not-to-kill (list (current-buffer)))) (kill-buffer-query-functions nil)) (mapc 'kill-buffer (remove-if (lambda (buffer) (memq buffer buffers-not-to-kill)) buffers-all)))) (global-set-key (kbd "S-<delete>") (lambda () (interactive) (kill-other-buffers (current-buffer) (get-buffer "*scratch*") (get-buffer "*Messages*")) (call-interactively 'delete-other-window))) ;; 列出buffer的時候,最好跳到list中,讓我選擇。 (global-set-key (kbd "C-x C-b") (lambda () (interactive) (call-interactively 'list-buffers) (call-interactively 'other-window))) ;;Save Session when exit (add-hook 'after-init-hook 'session-initialize) ;; Toggle Visual Line Mode (global-set-key (kbd "C-x M-T") (lambda nil (interactive) (if visual-line-mode (visual-line-mode 0)) (setq word-wrap nil) (if truncate-lines (toggle-truncate-lines -1) (toggle-truncate-lines 1)))) (global-set-key (kbd "C-`") (lambda () (interactive) (if (or (not (fboundp 'calendar-exit)) (null (calendar-exit))) (calendar)))) ;; 從emacen.com那裏抄來,忒好用了 (defun isearch-current-word-forward (&optional regexp-p no-recursive-edit) (interactive "P/np") (isearch-mode t (not (null regexp-p)) nil (not no-recursive-edit)) (isearch-yank-string (current-word))) (defun isearch-current-word-backward (&optional regexp-p no-recursive-edit) (interactive "P/np") (isearch-mode nil (not (null regexp-p)) nil (not no-recursive-edit)) (isearch-yank-string (current-word))) (global-set-key (kbd "C-*") 'isearch-current-word-forward) (global-set-key (kbd "C-#") 'isearch-current-word-backward) (define-key isearch-mode-map (kbd "C-*") 'isearch-repeat-forward) (define-key isearch-mode-map (kbd "C-#") 'isearch-repeat-backward) ;;;;;;;;;;;;;;;我實在習慣了Visual Studio了,一時還改不過來;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;C/C++ FN keys (defun define-c-mode-fn-key-debug () (define-key c-mode-base-map (kbd "<f5>") 'gud-go) (define-key c-mode-base-map (kbd "<f10>") 'gud-step) (define-key c-mode-base-map (kbd "<f11>") 'gud-next) (define-key c-mode-base-map (kbd "S-<f10>") 'gud-finish) (define-key c-mode-base-map (kbd "<f9>") 'gud-break) (define-key c-mode-base-map (kbd "S-<f9>") 'gud-remove) (define-key gud-mode-map (kbd "<f5>") 'gud-go) (define-key gud-mode-map (kbd "<f10>") 'gud-step) (define-key gud-mode-map (kbd "<f11>") 'gud-next) (define-key gud-mode-map (kbd "S-<f10>") 'gud-finish) (define-key gud-mode-map (kbd "<f9>") 'gud-break)) (defun define-c-mode-fn-key-edit () ;;Code Reference (define-key c-mode-base-map (kbd "<f12>") (lambda () (interactive) (if (semantic-tag-of-class-p (semantic-current-tag) 'include) (progn (push-mark) (when (fboundp 'push-tag-mark) (push-tag-mark)))) (semantic-ia-fast-jump (point)))) (define-key c-mode-base-map (kbd "S-<f12>") (lambda () (interactive) (if (ring-empty-p (oref semantic-mru-bookmark-ring ring)) (error "Semantic Bookmark Ring is 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)))) (define-key c-mode-base-map (kbd "M-<f12>") 'eassist-switch-h-cpp) (define-key c-mode-base-map (kbd "M-S-<f12>") 'semantic-analyze-proto-impl-toggle) (define-key c-mode-base-map (kbd "<f8>") (lambda () (interactive) (call-interactively 'ecb-minor-mode))) ;;Compile and Debug (define-key c-mode-base-map (kbd "<f6>") (lambda () (interactive) (compile "make -k"))) (define-key c-mode-base-map (kbd "<f5>") (lambda () (interactive) (call-interactively 'gdb))) (define-key c-mode-base-map (kbd "S-<f5>") (lambda () (interactive) (call-interactively 'gdb) (call-interactively 'gdb-many-windows))) (define-key c-mode-base-map (kbd "M-<f5>") (lambda () (interactive) (compile "make -k") (call-interactively 'gdb))) (define-key c-mode-base-map (kbd "M-S-<f5>") (lambda () (interactive) (compile "make -k") (call-interactively 'gdb) (call-interactively 'gdb-many-windows)))) ;; Close compilation buffer if successful (add-to-list 'compilation-finish-functions (lambda (buffer string) (when (and (string= (buffer-name buffer) "*compilation*") (not (string-match "exited abnormally" string))) (run-at-time 2.5 nil 'delete-windows-on buffer)))) ;; 當gdb/gud結束的時候,最好回到代碼窗口,或者回到*scratch*好了 (defun kill-buffers-when-gdb-quit () "Close gdb buffer when gdb quit. This function is invoked when gdb mode is enabled with gdb-mode-hook" (let ((process (get-buffer-process (current-buffer)))) (when (processp process) (set-process-sentinel process (lambda (proc change) (when (string-match "//(finished//|exited//|killed//)" change) (condition-case nil (progn (gdb-many-windows -1) (delete-other-windows (get-buffer-window (switch-to-buffer (condition-case nil (gud-find-file gdb-main-file) (error "*Scratch*"))))) (define-c-mode-fn-key-edit) (gud-reset) (gdb-reset) (kill-buffer (process-buffer proc))) (error nil)))))))) ;; (defun gud-kill () ;; "kill gdb process" ;; (interactive) ;; (with-current-buffer gud-comint-buffer (comint-skip-input)) ;; (set-process-query-on-exit-flag (get-buffer-process gud-comint-buffer) nil) ;; (kill-buffer gud-comint-buffer)) (mapc (lambda (mode-hook) (add-hook mode-hook (lambda () (kill-buffers-when-gdb-quit) (define-c-mode-fn-key-debug)))) (list 'gdb-mode-hook 'gud-mode-hook)) (mapc (lambda (mode-hook) (add-hook mode-hook (lambda () (progn (linum-mode 1) (define-c-mode-fn-key-edit) (tool-bar-add-item "gud" 'gdb 'gdb :visible '(memq major-mode '(c++-mode c-mode))) (tool-bar-add-item "compile" 'compile 'compile :visible '(memq major-mode '(c++-mode c-mode))))))) (list 'c-mode-hook 'c++-mode-hook)) ;; C/C++ Comment Kill (fset 'kill-c-comment (lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ("/223////|///*/273" 0 "%d")) arg))) (define-key c-mode-base-map (kbd "C-x M-K") 'kill-c-comment) (fset 'kill-c-blank-line (lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ("/223^[[:space:]]*$" 0 "%d")) arg))) (define-key c-mode-base-map (kbd "C-x C-K") 'kill-c-blank-line)