从零开始配置 Spacemacs

在我看来,在文本编辑领域,没有比 Vim 更高效的操作方式了。在文档存储和知识管理相 关的软件使用上面,没有比 Org-Mode 更牛逼的存在了。

之前一直很纠结,既放不下 Vim 的高效编辑方式,又不放下 Emacs 里的 Org-mode。直到 遇到了 Spacemacs, 完美的结合了两者的优点:

Spacemacs = Vim + Emacs

这篇文章,就记录一下自己是怎么从零开始配置一个符合自己使用习惯的 Spacemacs。

1 安装

  1. 安装 Emacs
  2. Clone Spacemacs
git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d

clone 完成后,会多两个东西,

  • ~/.emacs.d/
  • ~/.spacemacs

2 配置

安装完成后,对于常规的配置,可以放置到 ~/.spacemacs 文件中,就像 Vim 和 Zsh 一样。 但 Spacemacs 提供了 layer 概念,极大方便了插件的管理.

下面分为两部分来配置, 首先把一些简单的配置放置到 ~/.spacemacs 文件中:

2.1 .spacemacs

  • layers

    dotspacemacs-configuration-layers 变量中增加你想配置的 layer(Spacemacs 出厂 自带的或者是你后续自己创建的)

    dotspacemacs-configuration-layers
    '(
      helm
      auto-completion
      better-defaults
      emacs-lisp
      git
      org
      )
    
  • themes

    可以从 这里 挑选你喜欢的主题,把名字放到配置的第一个即可。

    dotspacemacs-themes '(monokai
                          molokai
                          niflheim)
    
  • fullscreen

    dotspacemacs-fullscreen-at-startup t
    

在 dotspacemacs/user-config 函数中配置:

(defun dotspacemacs/user-config ()
  "Configuration function for user code.
This function is called at the very end of Spacemacs initialization after
layers configuration.
This is the place where most of your configurations should be done. Unless it is
explicitly specified that a variable should be set before a package is loaded,
you should place your code here."

  ;;;;;;;;;;;;;;;;;;;;;;;
  ;; org-mode 相关设置 ;;
  ;;;;;;;;;;;;;;;;;;;;;;;

  ;; Since version 0.104, spacemacs uses the org version from the org ELPA
  ;; repository instead of the one shipped with emacs. Then, any org related
  ;; code should not be loaded before dotspacemacs/user-config, otherwise both
  ;; versions will be loaded and will conflict. Because of autoloading, calling
  ;; to org functions will trigger the loading up of the org shipped with emacs
  ;; which will induce conflicts. One way to avoid conflict is to wrap your org
  ;; config code in a with-eval-after-load block like this:
  (with-eval-after-load 'org
    ;; 设置 todo keywords
    (setq org-todo-keywords
          '((sequence "TODO" "HAND" "|" "DONE")))

    ;; 调试好久的颜色,效果超赞!todo keywords 增加背景色
    (setf org-todo-keyword-faces '(("TODO" . (:foreground "white" :background "#95A5A6"   :weight bold))
                                   ("HAND" . (:foreground "white" :background "#2E8B57"  :weight bold))
                                   ("DONE" . (:foreground "white" :background "#3498DB" :weight bold))))

    ;; 设置 bullet list
    (setq org-bullets-bullet-list '("☰" "☷" "☯" "☭"))

    ;; 打开 org-indent mode
    (setq org-startup-indented t)

    (setq org-agenda-include-diary nil)
    (setq org-agenda-files (list  "/Users/ljg/Library/Mobile Documents/com~apple~CloudDocs/org/gtd.org"
                                  "/Users/ljg/Library/Mobile Documents/com~apple~CloudDocs/org/gtd.org_archive"))

    (setq org-default-notes-file "/Users/ljg/Library/Mobile Documents/com~apple~CloudDocs/org/gtd.org")
    (setq org-refile-targets '("~/Library/Mobile Documents/com~apple~CloudDocs/org/gtd.org" :maxlevel . 3))
    )

  ;;;;;;;;;;;;;;
  ;; 显示相关 ;;
  ;;;;;;;;;;;;;;

  ;; 在状态栏显示时间
  (display-time-mode 1)

  ;; 折叠时不再显示「...」
  (setq org-ellipsis "▼")

  ;; inline image 不用展示实际大小,可以自定义大小显示
  (setq org-image-actual-width nil)

  (global-hl-line-mode -1)

  ;; more useful frame title, that show either a file or a
  ;; buffer name (if the buffer isn't visiting a file)
  (setq frame-title-format
        '("" " 為學日益, 為道日損 - "
          (:eval (if (buffer-file-name)
                     (abbreviate-file-name (buffer-file-name)) "%b"))))
  ;;;;;;;;;;;;;;
  ;; 导出相关 ;;
  ;;;;;;;;;;;;;;

  ;; 安装 XeLaTeX 是另外一个故事了..
  (setq Tex-command-default "XeLaTeX")


  ;;;;;;;;;;;;;;
  ;; 其它杂项 ;;
  ;;;;;;;;;;;;;;

  (setq default-major-mode 'org-mode)

  (prefer-coding-system 'utf-8)
  (set-default-coding-systems 'utf-8)
  (setq default-buffer-file-coding-system 'utf-8)

  ;; Agenda clock report parameters
  (setq org-agenda-clockreport-parameter-plist
        '(:link t :maxlevel 6 :fileskip0 t :compact t :narrow 60 :score 0))

  (setq org-hierarchical-todo-statistics nil)

  ;; stop emacs asking for confirmation when eval source code
  (setq org-confirm-babel-evaluate nil)

  ;; active Org-babel languages
  (org-babel-do-load-languages
   'org-babel-load-languages
   '(;; other Babel languages
     (plantuml . t)))

  (setq org-plantuml-jar-path
        (expand-file-name "~/lib/plantuml.jar"))
  )

2.2 private layer

首先把自己 layer 中想要的 packages 给列好, 并设置相应的启动配置项:

(defconst lijigang-packages
  '(org-page
    cnfonts
    swiper
    wttrin
    beacon)
)

(defun lijigang/init-org-page()
  "Initialize org-page to publish blog."
  (use-package org-page
    :ensure t
    :config (progn
          (setq op/site-main-title "為學日益, 為道日損")
          (setq op/personal-github-link "https://github.com/lijigang")
          (setq op/repository-directory "~/lijigang")
          (setq op/site-domain "http://lijigang.github.io/")
          (setq op/theme-root-directory "~/.emacs.d/elpa/org-page-20170806.1924/themes")
          (setq op/theme 'ljg)
          (setq op/highlight-render 'js)
          (setq op/category-config-alist
            '(("blog"
              :show-meta t
              :show-comment t
              :uri-generator op/generate-uri
              :uri-template "/blog/%t/"
              :sort-by :date     ;; how to sort the posts
              :category-index nil) ;; generate category index or not
              ("index"
              :show-meta nil
              :show-comment nil
              :uri-generator op/generate-uri
              :uri-template "/"
              :sort-by :date
              :category-index nil)
              ("about"
              :show-meta nil
              :show-comment nil
              :uri-generator op/generate-uri
              :uri-template "/about/"
              :sort-by :date
              :category-index nil)))
          (bind-key "C-c M-p" 'op/do-publication-and-preview-site)))
    )

(defun lijigang/init-cnfonts()
  "Initialize cnfonts"
  (use-package cnfonts
    :init
    (cnfonts-enable)
    (cnfonts-set-spacemacs-fallback-fonts)))

(defun lijigang/init-swiper()
  "Initialize swiper"
  (use-package swiper
    :init
    (define-key global-map (kbd "C-s") 'swiper)))


(defun lijigang/init-wttrin()
  (use-package wttrin
    :ensure t
    :commands (wttrin)
    :init
    (setq wttrin-default-cities '("Beijing"
                                  "Bristol")))
  )

(defun lijigang/init-beacon()
  "Initialize beacon"
  (use-package beacon
    :init
    (beacon-mode 1)
    (setq beacon-color "#666600")))

;;; packages.el ends here

然后把自己定义的一些函数放到 funcs.el 文件:

(defun make-progress (width percent has-number?)
  (let* ((done (/ percent 100.0))
         (done-width (floor (* width done))))
    (concat
     "["
     (make-string done-width ?/)
     (make-string (- width done-width) ? )
     "]"
     (if has-number? (concat " " (number-to-string percent) "%"))
     )))

(defun insert-day-progress ()
  (interactive)
  (let* ((today (time-to-day-in-year (current-time)))
         (percent (floor (* 100 (/ today 365.0)))))
    (insert (make-progress 30 percent t))
    ))

;; latex 支持中文
(require 'ox)
(require 'ox-html)
(require 'ox-publish)

(add-to-list 'org-latex-classes '("pdf" "\\documentclass[fontset = mac]{ctexart}
[NO-DEFAULT-PACKAGES]
\\usepackage[colorlinks,linkcolor=black,anchorcolor=black,
             citecolor=black]{hyperref}
\\usepackage[top=3truecm,bottom=2.5truecm,
            left=1.1truecm,right=1.1truecm,
            bindingoffset=1.0truecm,
            headsep=1.6truecm,
            footskip=1.5truecm,
            headheight=15pt    % 标准中没有要求页眉的高度,这里设置成
                               % 15pt 了
           ]{geometry}
"
                  ("\\section{%s}" . "\\section*{%s}")
                  ("\\subsection{%s}" . "\\subsection*{%s}")
                  ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                  ("\\paragraph{%s}" . "\\paragraph*{%s}")
                  ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

(setq org-latex-default-class "pdf")

(setq org-latex-pdf-process
      '(
        "xelatex -interaction nonstopmode -output-directory %o %f"
        "xelatex -interaction nonstopmode -output-directory %o %f"
        "xelatex -interaction nonstopmode -output-directory %o %f"
        "rm -fr %b.out %b.log %b.tex auto"
        ))

(defun peng-use-xelatex ()
  (interactive)
  (let* ((tempfile
      (file-name-base))) (progn (shell-command (concat "rm -rf " tempfile
                               ".bbl " tempfile ".blg " tempfile ".out " tempfile ".log " tempfile
                               ".aux " tempfile ".toc" tempfile ".pdf"))
                    (compile (concat "xelatex "
                             (concat tempfile ".tex")
                             (concat ";rm -rf " tempfile ".bbl " tempfile
                                 ".blg " tempfile ".out " tempfile ".log " tempfile ".aux " tempfile
".toc" ";open " tempfile ".pdf"))))))

之后把自己使用的一些快捷键设置放到 keybindings.el :

(evil-leader/set-key "oc" 'org-capture)
(evil-leader/set-key "oa" 'org-agenda)
(evil-leader/set-key "ol" 'org-store-link)
(evil-leader/set-key "el" 'eval-print-last-sexp)
(evil-leader/set-key "od" 'org-archive-subtree)

(evil-leader/set-key "oip" 'org-set-property)
(evil-leader/set-key "oil" 'org-insert-link)
(evil-leader/set-key "ois" 'org-time-stamp)
(evil-leader/set-key "oid" 'org-insert-drawer)
(evil-leader/set-key "oif" 'org-footnote-action)

(evil-leader/set-key "oog" (lambda () (interactive) (find-file "~/Library/Mobile Documents/com~apple~CloudDocs/org/gtd.org")))
(evil-leader/set-key "ool" (lambda () (interactive) (find-file "/Users/ljg/.spacemacs.d/layers/lijigang/keybindings.el")))

(evil-leader/set-key "oit" 'insert-day-progress)

(global-set-key (kbd "C--") 'org-table-insert-hline)

(global-set-key
 (kbd "C-c p")
 (defhydra hydra-blog (:color blue :hint nil)
   "
    blog  _n_: new post                        _l_: publish last commit
          _r_: reset & publish all             _p_: publish interactively
          _t_: reset & publish to /tmp/blog    _e_: new-repository
          "
   ("n" op/new-post)
   ("r" (progn
          (setq op/item-cache nil)
          (op/do-publication t nil nil t t)))
   ("t" (progn
          (setq op/item-cache nil)
          (op/do-publication t "/tmp/blog" nil t nil)))
   ("l" (op/do-publication nil "HEAD~1" nil t t))
   ("p" op/do-publication)
   ("e" op/new-repository)))