Skip to content

coldnew/coldnew-emacs

Repository files navigation

coldnew’s emacs

https://img.shields.io/badge/license-GPL_3-green.svg?dummy https://travis-ci.org/coldnew/coldnew-emacs.svg?branch=master

Introduction

This is my emacs configuration, I use ONE single org-mode file to write my emacs config.

Install emacs

My emacs is running under Mac OSX and Gentoo Linux and I really like them, following are some record for how I installed my emacs.

Mac OSX

In Mac OSX, I use homebrew with homebrew-emacs-head, I always install latest version of emacs via following command:

brew tap daviderestivo/emacs-head
brew install  emacs-head@30  --with-cocoa --with-gnutls --with-ctags --with-mailutils --with-xwidgets --with-native-comp --with-native-full-aot --with-tree-sitter
ln -s /opt/homebrew/opt/emacs-head@30/Emacs.app /Applications

Gentoo Linux

Gentoo Linux is the best linux distrobution I ever used and it’s really easy to install latest apps.

USE="X gtk3 inotify xft imagemagick" emerge app-editors/emacs

Building with script

My emacs configuration already shipped an update-to-date version of emacs based on git submodule, if your system can build emacs from source you can use my scripts/build-emacs.sh script to build latested emacs.

./scripts/build-emacs.sh

Note that if your are building under Mac OSX, don’t forget to install xcode tools:

xcode-select --install

Install or testing this config

  • First use git to download whole repo
    git clone https://github.com/coldnew/coldnew-emacs.git
        
  • Then use git submodule to download the spacemacs
    git submodule update --init --recursive
        
  • To generate the init.el, jut type
    make
        
  • If you do not put this repo on ~/.emacs.d, you need to use following command to start emacs
    emacs -q -l ~/coldnew-emacs/init.el
        

Packages need to be installed in system (Optional)

Some extra packages need to be installed in the system manually. These packages are optional but can make this configuration work more nicely.

Mac OSX

In Mac OSX, I use homebrew to install opensource packages.

#brew install fasd
#brew install aspell --with-lang-en
<<install-in-macosx>>

Gentoo Linux

In Gentoo Linux, don’t forget to enable USE=emacs to make Gentoo auto install emacs-related packages.

#emerge app-shells/fasd
#emerge app-shells/aspell
<<install-in-gentoo>>

All platform

Some package like npm use the same rule to install in any platform, here list what I need to install.

<<install-in-all>>

Early Initialize

Since Emacs 27, we can add some setup in ~/.emacs.d/early-init.el , these setup will be loadded before emacs startup.

;; early-init.el --- -*- lexical-binding: t; -*-
<<early-init>>
(provide 'early-init)
;;; early-init.el ends here

Disable native-compilation for some packages

Adding regexes to `comp-deferred-compilation-deny-list’ to make some packages not build by native-compilation when enabled.

Here I just disable native-compile auto loads. There’s something wrong with them.

(with-eval-after-load 'comp
  (setq-default comp-deferred-compilation-deny-list '("\\(?:[^z-a]*-autoloads\\.el$\\)"))
  ;; This variable is obsolete since 29.1
  (setq-default native-comp-deferred-compilation-deny-list '("\\(?:[^z-a]*-autoloads\\.el$\\)"))
  ;; introduce in emacs  28.1
  (setq-default native-comp-jit-compilation-deny-list '("\\(?:[^z-a]*-autoloads\\.el$\\)")))

Prevent native-compilation warning window popup

native-compilation will asyn report warning or error info, it’s so annoying, set it to silent mode so these warning will just logging but not pop up the window.

(with-eval-after-load 'comp
  (setq native-comp-async-report-warnings-errors 'silent))

Defer garbage collection further back in the startup process

(setq gc-cons-threshold most-positive-fixnum)

Prevent the glimpse of un-styled Emacs by disabling these UI elements early.

(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

Prevent package.el to load packages prior to init.el loading

We plain to handle our package initialization in our configs, we must prevent Emacs from doing it at early stage.

(setq package-enable-at-startup nil)

Allow loading from the package cache

(when (>= emacs-major-version 27)
  (setq-default package-quickstart t))

Do not resize the frame at this early stage.

(setq frame-inhibit-implied-resize t)

Initialize Emacs

There are some configurations I need to put at the beginning of the emacs config. These configurations are derived from my original init.el file.

Use lexical binding

For some of my functions, and general coolness, lexical binding is a must. Without it, closures cannot be made for example.

This line needs to appear at the beginning of the file to work. Just to keep things looking nice I put it at the beginning of the file.

;; -*- lexical-binding: t -*-

Make Emacs-26 compatible with Emacs-25

Emacs 26 introduce new switch bytecode which not contains in emacs-25, disable it so I can test both emacs-25 and emacs-26.

(setq byte-compile-cond-use-jump-table nil)

Use Common Lisp Extension

Some of my function may need the Common Lisp Extension, let’s import libraries first.

(require 'cl-lib)                       ; built-in

Load extra builtin library

Add some extry buildin library I will use in my config file.

(require 'find-lisp)

Prevent load outdated .elc files

Since emacs 24.4, new option load-prefer-newer has been introduce, which make me never accidentally using outdated compiled files.

(setq load-prefer-newer t)

Setup user-emacs-directory variable

In this configuration, user-emacs-directory always refer to the emacs configuration’s init.el parent directory.

;; We set `user-emacs-directory' here so we can use command-line
;; switch different emacs configuration like following:
;;
;;    emacs -q -l ~/coldnew-spacemacs/init.el
(defconst user-emacs-directory
  (file-name-directory (or load-file-name (buffer-file-name)))
  "My emacs config directory.")

Setup user-cache-directory variable

Setup the cache directory to store some cache content.

(defconst user-cache-directory
  (file-name-as-directory (concat user-emacs-directory ".cache"))
  "My emacs storage area for persistent files.")
;; create the `user-cache-directory' if not exists
(make-directory user-cache-directory t)

Setup user-modules-directory variable

Setup the modules directory to store some submodules and extra packages.

(defconst user-modules-directory
  (file-name-as-directory (concat user-emacs-directory "modules"))
  "My emacs storage area for modules.")

Setup user-ramdisk-directory variable

I specify a ramdisk path to make my emacs can save som temporary file to it. The ramdisk path should be ~/ramdisk, if the directory not found, use /tmp as fallback.

(defconst user-ramdisk-directory
  (let ( (user-ramdisk                   ; ~/ramdisk/
          (concat (getenv "HOME") "/ramdisk/")))
    ;; if ~/ramdisk/ exist, use it
    (if (file-exists-p user-ramdisk)
        user-ramdisk
        ;; fallcack to system default ramdisk dir
        temporary-file-directory))
  "My ramdisk path in system.")

Setup Load path

The variable load-path lists all the directories where Emacs should look for emacs-lisp files.

Following are my method to add directories to load-path recursively, this function also create directory to prevent directory not exist.

If you don’t have any local elisp and all packages is maintain by cask or elpa or spacemacs, you can skip following code.

(eval-and-compile
  ;; Add directories to emacs's `load-path' recursively.
  ;; if path does not exist, try to create directory.
  (let* ((my-lisp-dir
          (list (concat user-emacs-directory "elpa/") ; package installed by package.el
                (concat user-emacs-directory "local-lisp/") ; local lisp I used
                (concat user-emacs-directory "styles/"))) ; themes I used
         (sys-lisp-dir (cl-ecase system-type ; add some system site-lisp to load-path
                         ((darwin)    '("/usr/local/share/emacs/site-lisp/"))
                         ((gnu/linux) '("/usr/share/emacs/site-lisp/"))
                         ((t) nil)))  ; FIXME: Add more platform support
         (lisp-dir (append my-lisp-dir sys-lisp-dir)))
    ;; my-lisp-dir should always exist, but sys-lisp-dir may not exist
    (dolist (lisp-path my-lisp-dir)
      (when (not (file-exists-p lisp-path))
        (make-directory lisp-path t)))
    (dolist (lisp-path lisp-dir)
      (when (file-exists-p lisp-path)
        (let* ((default-directory lisp-path))
          (setq load-path
                (append
                 (let ((load-path (copy-sequence load-path)))
                   (append
                    (copy-sequence (normal-top-level-add-to-load-path '(".")))
                    (normal-top-level-add-subdirs-to-load-path)))
                 load-path)))))))

Under Mac OSX use Command key as ALT

Under Mac OSX, I always bind Caps lock as Control key, and make the Command key as ALT key like I done in Linux.

The Option key will be setup as Super.

I also disable some keys like ⌘-h bypass to system in emacs-mac port.

(when (eq system-type 'darwin)
  (setq-default mac-option-modifier 'super)
  (setq-default mac-command-modifier 'meta))

Save custom-file as cache

Most of my config is written in this file, it’s no need to tracking the emacs’s custom-setting.

I move the file to cache-dir and make git ignore it.

(setq-default custom-file (concat user-cache-directory "custom.el"))
;; load custom-file only when file exist
(when (file-exists-p custom-file)
  (load-file custom-file))

Loading Modules

I think I’ll write some dynamic module extension, which will save to ~/.emacs.d/modules, use a helper script to help me load them.

;; load the `load-modules.el' file which help me load external modulept
(let ((script (concat user-modules-directory "load-modules.el")))
  (when (file-exists-p script)
    (load script)))

Startup emacs server

Only start server mode if I’m not root

(unless (string-equal "root" (getenv "USER"))
  (require 'server)
  (when (fboundp 'server-running-p)
    (unless (server-running-p) (server-start))))

Setup backup files

By default Emacs saves BackupFiles under the original name with a tilde ~ appended. Example: Editing README will result in README and README~ in the same directory.

This is primitive and boring.

I save my backup files to ~/.emacs.d/.cache/backup and since is always ignore by version control system, it’s a nice place to store backup files.

(let ((backup-dir (concat user-cache-directory "backup")))
  ;; Move backup file to `~/.emacs.d/.cache/backup'
  (setq backup-directory-alist `(("." . ,backup-dir)))
  ;; Makesure backup directory exist
  (when (not (file-exists-p backup-dir))
    (make-directory backup-dir t)))

Also setup some backup settings.

(setq delete-by-moving-to-trash nil)
(setq version-control t)
(setq kept-old-versions 10)
(setq kept-new-versions 20)
(setq delete-old-versions t)
(setq backup-by-copying t)

Disable some default stuff

Some emacs feature is not really useful

Turn-off Alarm Bell

(setq ring-bell-function #'ignore)

Clean scratch buffer messages

Leave me a clean **scratch** buffer and I’ll be more happy :)

(setq initial-scratch-message "")

Use visible bell instead of buzzer

(setq visible-bell t)

Ask for y or n, not yes or no

Emacs starts out asking for you to type yes or no with most important questions. Just let me use y or n with no RET required an I’m quite happy.

(defalias 'yes-or-no-p 'y-or-n-p)

Maximized window after emacs start

(modify-all-frames-parameters '((fullscreen . maximized)))

Personal Information

It’s useful to have some basic personal information.

(setq user-full-name "Yen-Chin, Lee")
(setq user-mail-address "coldnew.tw@gmail.com")

Some of my password stored in a secret file with gnupg encrypt, I add this function here so I can inject some emacs-command which hass password/account setting.

(defun my-load-secret ()
  "Load my secret setting include password... etc."
  (let ((secret "~/.secret.el.gpg"))
    (when (file-exists-p secret) (load-file secret))))

Package Management

The main package manager I use is emacs’s package.el, it’s really nice and easy to use.

Add my extra package list

melpa contains many community packages that not contribute to emacs’s elpa, we add it here.

(eval-and-compile
  (require 'package)			; built-in

  ;; melpa
  (add-to-list 'package-archives
               '("melpa" . "https://melpa.org/packages/") t)

  ;; For important compatibility libraries like cl-lib
  (when (< emacs-major-version 24)
    (add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))))

Initialize package.el

Before we start to use package.el, we need to initialize it first.

;; This must come before configurations of installed packages.
;; Don't delete this line. If you don't want it, just comment it out by adding a
;; semicolon to the start of the line. You may delete these explanatory
;; comments.
(eval-and-compile
  (when (< emacs-major-version 27)
    (package-initialize)))

Install use-package

The use-package macro allows you to isolate package configuration in your .emacs file in a way that is both performance-oriented and, well, tidy. I created it because I have over 80 packages that I use in Emacs, and things were getting difficult to manage. Yet with this utility my total load time is around 2 seconds, with no loss of functionality!

GitHub: https://github.com/jwiegley/use-package

NOTE:

use-package is integrated in emacs directly since emacs 29.1, it’s no need to install it manually anymore.

(eval-and-compile
  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))) ; Installed by packages.el

use-package now can only need once, just add it to compile time

(eval-when-compile (require 'use-package))

You can setup use-package-always-ensure variable to t if you want to make use-package auto install packages.

However, since we need to initialize spacemacs first, we need to setup this variable after spacemacs init.

;; make `use-package' auto install non-installed packages.
(when (require 'use-package-ensure)
  (setq use-package-always-ensure t))

Report details about loading and configuration.

(setq use-package-verbose t)

Install straight

straight.el is next-generation, purely functional package manager for the Emacs hacker. It can integrated with use-package and install some packages from fork.

GitHub: https://github.com/raxod502/straight.el

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 6))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

Install paradox

Project for modernizing Emacs’ Package Menu. With improved appearance, mode-line information. Github integration, customizability, asynchronous upgrading, and more.

GitHub: https://github.com/Malabarba/paradox

(use-package paradox
  :ensure t
  :commands (paradox-enable)
  :hook (after-init . paradox-enable)
  :init
  (setq paradox-execute-asynchronously t
        paradox-spinner-type 'progress-bar
        paradox-github-token t
        paradox-display-star-count nil))

Install some common libraries

Some common libraries I’ll use in my personal’s command or anything else.

(use-package f :ensure t)
(use-package s :ensure t)
(use-package dash :ensure t)
(use-package htmlize :ensure t)
(use-package async :ensure t)

Languages and Encodings

Add UTF8 at the front of the priority list for automatic detection.

(prefer-coding-system 'utf-8)

Set up multilingual environment to use UTF-8.

(set-language-environment "UTF-8")

Set default value of various coding systems to UTF-8.

(set-default-coding-systems 'utf-8)

Use C as locale for display time info (Actually it will display English).

(setq system-time-locale "C")

Built-in Packages

Some buildin packages need to add extra setups for my emacs setting.

Most of them are the cache file, I’ll move them to ~/.emacs.d/.cache directory.

abbrev

(eval-after-load 'bookmark
  '(progn
     (setq abbrev-file-name
           (concat user-cache-directory "abbrev_defs"))))

eshell

Move eshell cache dir to ~/.emacs.d/.cache/eshell

(eval-when-compile (defvar eshell-directory-name)) ; defined in esh-mode.el

(with-eval-after-load 'esh-mode
  (setq-default eshell-directory-name
                (concat user-cache-directory "eshell")))

(with-eval-after-load 'em-hist
  (setq-default eshell-history-file-name
                (expand-file-name "history" eshell-directory-name)))

bookmark

(eval-after-load 'bookmark
  '(progn
     (setq-default bookmark-default-file
                   (concat user-cache-directory "bookmarks"))))

idlwave

Major mode for editing IDL source files.

(eval-after-load 'idlwave
  '(progn
     (setq-default idlwave-config-directory
           (concat user-cache-directory "idlwave"))))

srecode

;; change srecode cache file path
(eval-after-load 'srecode
  '(progn
     (setq-default srecode-map-save-file
                   (concat user-cache-directory "srecode-map.el"))))

request

(eval-after-load 'request
  '(progn
     (setq-default request-storage-directory
                   (concat user-cache-directory "request"))))

nsm

(eval-after-load 'nsm
  '(progn
     (setq-default nsm-settings-file
                   (concat user-cache-directory "network-security.data"))))

url

(eval-after-load 'url
  '(progn
     (setq url-configuration-directory
           (file-name-as-directory
            (concat user-cache-directory "url")))))

startup

;; NOTE:
;; `auto-save-list-file-prefix' defined in startup.el, but
;; startup.el doesn't have provide pacage name (provide 'startup)
;;
(setq-default auto-save-list-file-prefix
              (cond ((eq system-type 'ms-dos)
                     ;; MS-DOS cannot have initial dot, and allows only 8.3 names
                     (file-name-as-directory
                      (concat user-cache-directory "auto-save.list/_saves-")))
                    (t
                     (file-name-as-directory
                      (concat user-cache-directory "auto-save-list/.saves-")))))

External Packages

Most of emacs packages do not need many configs or just provide commands/functions to use, I put them here.

discover-my-major

discover-my-major make you discover key bindings and their meaning for the current Emacs major mode.

GitHub: https://github.com/steckerhalter/discover-my-major

(use-package discover-my-major
  :defer 2	      ; Install pkg if not exist after 2 sec idle time
  :commands (discover-my-major))

doxymacs

Doxygen is a system for extracting documentation from source code. It supports a variety of programming languages, human languages and output formats. You can find it at http://www.doxygen.org.

doxymacs is emacs’s wrapper for Doxygen.

(when (require 'doxymacs nil 'noerror)
  (add-hook 'prog-mode-hook #'(lambda () (doxymacs-mode))))

Install System Packages

To use doxymacs we also need to install it in system manually.

Mac OSX

For Mac OSX, just type

brew install doxymacs

Gentoo Linux

If you use Gentoo Linux, you can use following command to install this package

emerge app-shells/doxymacs

esup

Benchmark Emacs Startup time without ever leaving your Emacs.

GitHub: https://github.com/jschaf/esup

(use-package esup
  :ensure t
  :commands (esup)
  :init
  ;; Work around a bug where esup tries to step into the byte-compiled
  ;; version of `cl-lib', and fails horribly.
  ;; see:
  ;; https://github.com/jschaf/esup/issues/54#issuecomment-651247749
  (setq esup-depth 0))

exec-path-from-shell

exec-path-from-shell is A GNU Emacs library to ensure environment variables inside Emacs look the same as in the user’s shell.

Ever find that a command works in your shell, but not in Emacs?

This happens a lot on OS X, where an Emacs instance started from the GUI inherits a default set of environment variables.

This library works solves this problem by copying important environment variables from the user’s shell: it works by asking your shell to print out the variables of interest, then copying them into the Emacs environment.

GitHub: https://github.com/purcell/exec-path-from-shell

(use-package exec-path-from-shell
  :ensure t
  :config
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

expand-region

Expand region increases the selected region by semantic units. Just keep pressing the key until it selects what you want.

GitHub: https://github.com/magnars/expand-region.el

(use-package expand-region
  :ensure t
  :bind (("M-v" . er/expand-region)))

fancy-narrow

Emacs package to immitate narrow-to-region with more eye-candy.

GitHub: https://github.com/Malabarba/fancy-narrow

(use-package fancy-narrow :ensure t)

focus

Focus provides focus-mode that dims the text of surrounding sections, similar to iA Writer’s Focus Mode.

GitHub: https://github.com/larstvei/Focus

(use-package focus :ensure t)

fontawesome

Emacs fontawesome utility.

GitHub: https://github.com/syohex/emacs-fontawesome

(use-package fontawesome :ensure t)

google-translate

This package allows to translate the strings using Google Translate service directly from GNU Emacs.

GitHub: https://github.com/atykhonov/google-translate

(use-package google-translate :ensure t)

goto-last-change

Move point through buffer-undo-list positions.

GitHub: https://github.com/camdez/goto-last-change.el

(use-package goto-last-change
  :ensure t)

howdoi

howdoi is a way to query Stack Overflow directly from the Emacs and get back the most upvoted answer to the first question that comes up for that query.

GitHub: https://github.com/atykhonov/emacs-howdoi

(use-package howdoi
  :defer 2
  :commands (howdoi-query howdoi-query-line-at-point))

hungry-delete

hungry-delete borrows hungry deletion from cc-mode, which will causes deletion to delete all whitespace in the direction you are deleting.

(use-package hungry-delete
  :ensure t
  :config
  (global-hungry-delete-mode))

iedit

iedit let you edit multiple regions in the same way simultaneously.

GitHub: https://github.com/victorhge/iedit

(use-package iedit
  :ensure t
  :bind (("C-;" . iedit-mode)))

manage-minor-mode

manage-minor-mode let you manage your minor-mode on the dedicated interface buffer.

(use-package manage-minor-mode :ensure t)

mwim

This Emacs package provides commands for moving to the beginning/end of code or line.

GitHub: https://github.com/alezost/mwim.el

(use-package mwim
  :ensure t
  :bind
  (("C-a" . mwim-beginning-of-code-or-line)
   ("C-e" . mwim-end-of-code-or-line)))

pangu-spacing

pangu-spcing is an minor-mode to auto add space between Chinese and English characters. Note that these white-space characters are not really added to the contents, it just like to do so.

GitHub: https://github.com/coldnew/pangu-spacing

(use-package pangu-spacing
  :ensure t
  :commands (global-pangu-spacing-mode)
  :config
  ;; start pangu-spacing globally
  (global-pangu-spacing-mode 1)
  ;; Always insert `real' space in org-mode.
  (add-hook 'org-mode-hook
            (lambda ()
              (set (make-local-variable 'pangu-spacing-real-insert-separtor) t))))

password-generator

password-generator provides simple functions to create passwords and insert them inside buffer immediately.

GitHub: https://github.com/zargener/emacs-password-genarator

(use-package password-generator :ensure t)

rainbow-mode

rainbow-mode s a minor mode for Emacs which displays strings representing colors with the color they represent as background.

(use-package rainbow-mode :ensure t)

smartparens

Smartparens is a minor mode for dealing with pairs in Emacs.

GitHub: https://github.com/Fuco1/smartparens

(use-package smartparens
  :ensure t
  :commands (smartparens-mode)
  :config
  (smartparens-mode 1))

sx

SX is a full featured Stack Exchange mode for GNU Emacs 24+. Using the official API, it provides a versatile experience for the Stack Exchange network within Emacs itself.

(use-package sx :ensure t)

tldr

tldr is a collection of simplified and community-driven man pages.

GitHub: https://github.com/kuanyui/tldr.el

(use-package tldr
  :defer 2
  :commands (tldr)
  :config
  (setq tldr-directory-path (concat user-cache-directory "tldr/"))
  (setq tldr-saved-zip-path (concat user-cache-directory "tldr-source.zip")))

url-shortener

This package can convert long url to tiny url and expand tiny url to long url ,support:

  • bit.ly
  • goo.gl
  • dwz.cn
  • 126.am

GitHub: https://github.com/yuyang0/url-shortener

(use-package url-shortener :ensure t)

verify-url

verify-url is a little tool that used to find out invalid urls in the buffer or region.

Use M-x verify-url to find invalid urls in current buffer.

After executed command, you can use verify-url/next-invalid-url to goto next invalid-url or verify-url/previous-invalid-url to goto previous one.

GitHub: https://github.com/lujun9972/verify-url

(use-package verify-url
  :defer 2
  :commands (verify-url))

visual-regexp

visual-regexp for Emacs is like replace-regexp, but with live visual feedback directly in the buffer. Check out visual-regexp-steroids if you want to use modern regular expressions instead of Emacs-style regular expressions.

GitHub: https://github.com/benma/visual-regexp.el

(use-package visual-regexp :ensure t)

webpaste

webpaste.el allows to paste whole buffers or parts of buffers to pastebin-like services. It supports more than one service and will failover if one service fails. More services can easily be added over time and prefered services can easily be configured.

Supported platform:

  • [X] ix.io
  • [X] dpaste.com
  • [X] sprunge.us
  • [X] dpaste.de
  • [ ] paste.pound-python.org
  • [ ] paste.debian.net
  • [ ] bpaste.net

GitHub: https://github.com/etu/webpaste.el

(use-package webpaste
  :ensure t)

which-key

which-key is a minor mode for Emacs that displays the key bindings following your currently entered incomplete command (a prefix) in a popup. For example, after enabling the minor mode if you enter C-x and wait for the default of 1 second the minibuffer will expand with all of the available key bindings that follow C-x (or as many as space allows given your settings). This includes prefixes like C-x 8 which are shown in a different face. Screenshots of what the popup will look like are included below. which-key started as a rewrite of gude-key-mode, but the feature sets have diverged to a certain extent.

GitHub: https://github.com/justbur/emacs-which-key

(use-package which-key
  :ensure t
  :commands (which-key-mode)
  :config
  (which-key-mode)
  ;; Reset to the default or customized value before adding our values in order
  ;; to make this initialization code idempotent.
  (custom-reevaluate-setting 'which-key-replacement-alist)
  ;; Use my own rules for better naming of functions
  (let ((desc
         ;; being higher in this list means the replacement is applied later
         '(("er/expand-region" . "expand region")
           ("evil-lisp-state-\\(.+\\)" . "\\1")
           ;; my own commands prefix with `my/'
           ("my/\\(.+\\)" . "\\1")
           )))
    (dolist (nd desc)
      ;; ensure the target matches the whole string
      (push (cons (cons nil (concat "\\`" (car nd) "\\'")) (cons nil (cdr nd)))
            which-key-replacement-alist))))

Interactive Commands

In emacs, we can use M-x to execute interactive commands, I implement some of them to make my emacs more easy to use.

All my commands starts with my/ prefix.

Buffers

Kill all buffers except scratch buffer

Sometimes I just want to kill all buffers, this command will kill all of them and make *scratch* buffer alone.

(defun my/nuke-all-buffers ()
  "Kill all buffers, leaving *scratch* only."
  (interactive)
  (mapcar (lambda (x) (kill-buffer x)) (buffer-list))
  (delete-other-windows))

Make emacs can always save buffers (even if file is not modified)

The default command save-buffer will not really save file when it untouched, use this command can let me force save file even if file is not modified.

(defun my/save-buffer-always ()
  "Save the buffer even if it is not modified."
  (interactive)
  (set-buffer-modified-p t)
  (save-buffer))

Abort minibuffer recursive edit

(defun my/minibuffer-keyboard-quit ()
  "Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
  (interactive)
  (if (and delete-selection-mode transient-mark-mode mark-active)
      (setq deactivate-mark t)
    (when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
    (abort-recursive-edit)))

Make buffer untabify

(defun my/untabify-buffer ()
  (interactive)
  (save-excursion
    (untabify (point-min) (point-max))))

Indent whole buffer

(defun my/indent-whole-buffer ()
  "Indent whole buffer."
  (interactive)
  (save-excursion
    (indent-region (point-min) (point-max))))

Remove buffers trailing whitespace and untabify

(defun my/cleanup-buffer ()
  "Perform a bunch of operations on the whitespace content of a buffer."
  (interactive)
  (save-excursion
    (delete-trailing-whitespace)
    (indent-region (point-min) (point-max))
    (untabify (point-min) (point-max))))

Replace the preceding sexp with its value

(defun my/eval-and-replace ()
  "Replace the preceding sexp with its value."
  (interactive)
  (backward-kill-sexp)
  (condition-case nil
      (prin1 (eval (read (current-kill 0)))
             (current-buffer))
    (error (message "Invalid expression")
           (insert (current-kill 0)))))

Quick folding source block

(defun my/quick-folding-source ()
  "Use emacs buildin easy to folding code."
  (interactive)
  (set-selective-display
   (if selective-display nil 1)))

Convert file format between DOS and UNIX

Some of my partner’s file are in DOS format, so I need command to convert buffer between dos format and unix format.

(defun my/dos2unix ()
  "Convert buffer file from dos file to unix file."
  (interactive)
  (set-buffer-file-coding-system 'undecided-unix 't) )
(defun my/unix2dos ()
  "Convert buffer file from unix file to dos file."
  (interactive)
  (set-buffer-file-coding-system 'undecided-dos 't))

Edit (Insert/Remove)

Insert U200B char

<U200B> character is a zero width space character which is nice to use under org-mode.

For more info, please see: suggestion for org-emphasis-regexp-components: *U*nited *N*ations

(defun my/insert-U200B-char ()
  "Insert <U200B> char, this character is nice use in org-mode."
  (interactive)
  (insert "\ufeff"))

Insert empty line after current line

(defun my/insert-empty-line ()
  "Insert an empty line after current line and position cursor on newline."
  (interactive)
  (move-end-of-line nil)
  (open-line 1)
  (forward-line 1))

Insert lorem ipsum

(defun my/insert-lorem ()
  "Insert a lorem ipsum."
  (interactive)
  (insert "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
          "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim"
          "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
          "aliquip ex ea commodo consequat. Duis aute irure dolor in "
          "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
          "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
          "culpa qui officia deserunt mollit anim id est laborum."))

Delete word

(defun my/delete-word (arg)
  "Delete characters forward until encountering the end of a word.
With argument, do this that many times."
  (interactive "p")
  (delete-region (point) (progn (forward-word arg) (point))))

Set mark or expand region

(defun my/set-mark-mode/rectangle-mark-mode ()
  "toggle between set-mark-command or rectangle-mark-mode"
  (interactive)
  (if (not mark-active)
     (call-interactively 'set-mark-command)
    (call-interactively 'rectangle-mark-mode)))

Copy and comments

(defun my/copy-and-comment ()
  "Copy region and comment it."
  (interactive)
  (kill-ring-save (region-beginning) (region-end))
  (comment-dwim nil))

File Handle

Reopen file as root

(defun my/file-reopen-as-root ()
  (interactive)
  (when buffer-file-name
    (find-alternate-file
     (concat "/sudo:root@localhost:"
             buffer-file-name))))

Delete current buffer file

(defun my/delete-current-buffer-file ()
  "Remove file connected to current buffer and kill buffer."
  (interactive)
  (let ((filename (buffer-file-name))
        (buffer (current-buffer)))
    (if (not (and filename (file-exists-p filename)))
        (ido-kill-buffer)
      (when (yes-or-no-p "Are you sure you want to remove this file? ")
        (delete-file filename)
        (kill-buffer buffer)
        (message "File '%s' successfully removed" filename)))))

Rename current Buffer and file

(defun my/rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " filename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

Add executable attribute to file

Actually this command is the same as chmod +x but it doesn’t use any shell command, it use emacs’s logior function to change file attribute.

I only make owener can has executable permission, not change it for gourp or others user.

(defun my/set-file-executable()
  "Add executable permissions on current file."
  (interactive)
  (when (buffer-file-name)
    (set-file-modes buffer-file-name
                    (logior (file-modes buffer-file-name) #o100))
    (message (concat "Made " buffer-file-name " executable"))))

Clone current file to new one

(defun my/clone-file-and-open (filename)
  "Clone the current buffer writing it into FILENAME and open it"
  (interactive "FClone to file: ")
  (save-restriction
    (widen)
    (write-region (point-min) (point-max) filename nil nil nil 'confirm))
  (find-file filename))

Show current buffer-file information

(defun my/file-info ()
  "Show current buffer information."
  (interactive)
  (if (buffer-file-name (current-buffer))
      (progn
        (let* ((file-name (buffer-file-name (current-buffer)))
               (f-attr (file-attributes file-name))
               (f-size (nth 7 f-attr))  ; ファイルサイズ
               (f-mode (nth 8 f-attr))  ; ファイル属性
               (mes1 (format "file path: %s\n" file-name))
               (mes2 (format "file size: %s byte\n" f-size))
               (mes3 (format "file type: %s" f-mode))
               (mess (concat mes1 mes2 mes3)))
          (message "%s" mess)))
    nil))

Debug

Eval emacs buffer until error

A really nice command help me to find error on elisp buffer.

(defun my/eval-buffer-until-error ()
  "Evaluate emacs buffer until error occured."
  (interactive)
  (goto-char (point-min))
  (while t (eval (read (current-buffer)))))

Display face found at the current point

(defun my/what-face (pos)
  "Display face found at the current point."
  (interactive "d")
  (let ((face (or (get-char-property (point) 'read-face-name)
                  (get-char-property (point) 'face))))
    (if face (message "Face: %s" face) (message "No face at %d" pos))))

Reload emacs init config

(defun my/reload-init ()
  "Reload init.el file"
  (interactive)
  (load-file user-init-file))

Window

Switch to other window or split it

If other window does not exist, split it, else switch to it.

(defun my/other-window-or-split ()
  "Switch to other window or split it."
  (interactive)
  (when (one-window-p)
    (split-window-horizontally))
  (other-window 1))

Swap left/right windows

If I have two windows at left/right position, this command can help me change left-window to right-window.

(defun my/swap-window-positions ()
  "*Swap the positions of this window and the next one."
  (interactive)
  (let ((other-window (next-window (selected-window) 'no-minibuf)))
    (let ((other-window-buffer (window-buffer other-window))
          (other-window-hscroll (window-hscroll other-window))
          (other-window-point (window-point other-window))
          (other-window-start (window-start other-window)))
      (set-window-buffer other-window (current-buffer))
      (set-window-hscroll other-window (window-hscroll (selected-window)))
      (set-window-point other-window (point))
      (set-window-start other-window (window-start (selected-window)))
      (set-window-buffer (selected-window) other-window-buffer)
      (set-window-hscroll (selected-window) other-window-hscroll)
      (set-window-point (selected-window) other-window-point)
      (set-window-start (selected-window) other-window-start))
    (select-window other-window)))

Styles

My own emacs, my own style :)

General Setups

Some emacs’s default UI feature setup.

Turn-off menu bar

The menu bar is one of the UI elements which work best with mouses.

(when (featurep 'menu-bar) (menu-bar-mode -1))

Turn-off tool bar

I never use the tool bar, it’s really no need.

(when (featurep 'tool-bar) (tool-bar-mode -1))

Turn-off blinking cursor

I hate the blinking cursor actually, it’s really annoying.

(blink-cursor-mode -1)

Turn-off scroll bar

Actually when you familier with emacs, you don’t need to use scroll-bar anymore.

(when (featurep 'scroll-bar) (scroll-bar-mode -1))

Turn-off startup screen

I don’t want to enter the startup screen after initialize emacs.

(setq inhibit-startup-screen t)

Theme

Before use emacs’s load-theme function, I advise it to make it fully unload previous theme before loading a new one.

;; Make `load-theme' fully unload previous theme before loading a new one.
(defadvice load-theme
    (before theme-dont-propagate activate)
  (mapc #'disable-theme custom-enabled-themes))

I always want to customize everything on my own, so I build my own emacs theme called night-coldnew and day-coldnew, you can find them at styles dir.

(require 'day-coldnew-theme)
(require 'night-coldnew-theme)
(load-theme 'night-coldnew t nil)  ; default use `night-coldnew-theme'

mode-line

(require 'coldnew-modeline-config)

;; (use-package spaceline :ensure t)

;; (use-package spaceline-all-the-icons
;;   :after spaceline
;;   :config
;;   (spaceline-all-the-icons-theme)
;;   ;;
;;   (setq spaceline-all-the-icons-separators-type 'slant))
;; (use-package spaceline :ensure t)

;; (use-package spaceline-all-the-icons
;;   :after spaceline
;;   :config
;;   (spaceline-all-the-icons-theme)
;;   ;;
;;   (setq spaceline-all-the-icons-separators-type 'slant))
(require 'org-timer)

(defface mode-line-read-only-face
  '((t (:foreground "#C82829" :bold t)))
  "face for mode-name-string in modeline."
  :group 'mode-line)

(defface mode-line-modified-face
  '((t (:inherit 'font-lock-function-name-face :bolt t)))
  "face for mode-name-string in modeline."
  :group'mode-lin)

(defface mode-line-mode-name-face
  '((t (:inherit font-lock-keyword-face)))
  "face for mode-name-string in modeline."
  :group 'mode-line)

(defface font-lock-escape-char-face
  '((((class color)) (:foreground "seagreen2")))
  "highlight c escapes char like vim"
  :group 'font-lock-faces)

(defun my/org-timer-modeline ()
  "Show `org-timer' info in my custom mode-line."
  (if (or org-timer-mode-line-timer
          org-timer-countdown-timer
          (org-at-item-timer-p)
          )
      ;; - `org-timer-value-string'
      ;; - `org-timer-set-mode-line'
      ;; - `org-timer-mode-line-string'
      (propertize
       (concat
        ;; (all-the-icons-material "alarm_on" :v-adjust 0.05)
        (format "%s" org-timer-mode-line-string))
       'face 'font-lock-function-name-face)
      ""))


(defun my/org-clock-modeline ()
  "Show org-clock info."
  (when org-clock-idle-timer
    ;; get [0:05] from `org-clock-get-clock-string'
    (propertize
     (concat
      ;; (all-the-icons-octicon "clock" :v-adjust 0.05)
      "clock: "
      (propertize " " 'face 'variable-pitch)
      (format "%s"
              (org-minutes-to-clocksum-string (org-clock-get-clocked-time))))
     'face 'font-lick-exit-face))

  ;; org-clock-today (show current org clock)
  ;; NOTE: this time is doubled on `org-clock'.
  ;; (:eval
  ;;  (when (and (org-clock-is-active) (active))
  ;;    (list
  ;;     (propertize (format" ⏰%s"org-clock-today-string)
  ;;                 'face'(:foreground"cyan")))
  ;;    ))
  )

;; update org-clock timer in mode-line after `org-clock-out-hook'.
;; fix org-clock timer does not disappear after clock out.
(add-hook 'org-clock-out-hook
          '(lambda ()
             ;; (org-clock-update-mode-line)
             (setq org-mode-line-string nil)
(force-mode-line-update)))


(defun mode-line-buffer-permissions ()
  "Get buffer-file permissions."
  (when (buffer-file-name)
    (format "-%04o-" (file-modes (buffer-file-name)))))

(defun mode-line-major-mode ()
  "Get major-mode name with << >>."
  (concat "<< " (propertize mode-name 'face 'mode-line-mode-name-face) " >>"))

(setq-default mode-line-format
              '((" "
                 mode-line-mule-info
                 ;; read-only or modified status
                 (:eval
                  (cond (buffer-read-only
                         (propertize "RO" 'face 'mode-line-read-only-face))
                        ((buffer-modified-p)
                         (propertize "**" 'face 'mode-line-modified-face))
                        (t "--")))
                 "   "
                 ;; (when (featurep 'evil)
                 ;;   (:eval (evil-mode-string)))
                 "   "
                 mode-line-buffer-identification
                 " " ;; (:eval (mode-line-buffer-permissions))
                 "   "
                 ;; major-mode name
                 (:eval (mode-line-major-mode))
                 "   "
                 (:eval (my/org-timer-modeline))
                 "   "
                 (:eval (my/org-clock-modeline))
                 "   "
                 ;; line and column
                 "("
                 (:eval (propertize "%02l" 'face 'font-lock-type-face))
                 ","
                 (:eval (propertize "%02c" 'face 'font-lock-type-face))
                 ")"

                 "   "
                 (vc-mode vc-mode)
                 "   "

                 ;; (org-timer-mode-line-timer
                 ;;  (:eval (format "<fc=%s><%s></fc>"
                 ;;                 (let ((time (abs (floor (org-timer-seconds)))))
                 ;;                   (cond
                 ;;                    ((< time 30) "#ef2929")
                 ;;                    ((< time 60) "#f57900")
                 ;;                    (t "#8cc4ff")))
                 ;;                 (substring (org-timer-value-string) 0 -1))))
                 "   "
                 ;; relative position, size of file
                 "["
                 (:eval (propertize "%p" 'face 'font-lock-constant-face)) ;; % above top
                 "/"
                 (:eval (propertize "%I" 'face 'font-lock-constant-face)) ;; size
                 "] "

                 )))

(force-mode-line-update)

Fonts

(defvar my/emacs-english-font "Monaco"
  "The font name of English.")

(defvar my/emacs-cjk-font "Hiragino Sans GB"
  "The font name for CJK.")

(defvar my/emacs-font-size-pair '(13 . 16)
  "Default font size pair for (english . chinese)")

(defun my/font-exist-p (fontname)
  "Test if this font is exist or not.
This function only work on GUI mode, on terminal it just
return nil since you can't set font for emacs on it."
  (if (or (not fontname) (string= fontname "") (not (display-graphic-p)))
      nil
    (if (not (x-list-fonts fontname))
        nil t)))

(defun my/set-font (english chinese size-pair)
  "Setup emacs English and Chinese font on x window-system."

  (if (my/font-exist-p english)
      (set-frame-font (format "%s:pixelsize=%d" english (car size-pair)) t))

  (if (my/font-exist-p chinese)
      (dolist (charset '(kana han cjk-misc bopomofo))
        (set-fontset-font (frame-parameter nil 'font) charset
                          (font-spec :family chinese :size (cdr size-pair))))))

;; Setup font size based on my/emacs-font-size-pair
(my/set-font my/emacs-english-font my/emacs-cjk-font my/emacs-font-size-pair)

(defvar my/emacs-font-size-pair-list
  '(( 5 .  6) (10 . 12)
    (13 . 16) (15 . 18) (17 . 20)
    (19 . 22) (20 . 24) (21 . 26)
    (24 . 28) (26 . 32) (28 . 34)
    (30 . 36) (34 . 40) (36 . 44))
  "This list is used to store matching (english . chinese) font-size.")

(defun my/emacs-step-font-size (step)
  "Increase/Decrease emacs's font size."
  (let ((scale-steps my/emacs-font-size-pair-list))
    (if (< step 0) (setq scale-steps (reverse scale-steps)))
    (setq my/emacs-font-size-pair
          (or (cadr (member my/emacs-font-size-pair scale-steps))
              my/emacs-font-size-pair))
    (when my/emacs-font-size-pair
      (message "emacs font size set to %.1f" (car my/emacs-font-size-pair))
      (my/set-font my/emacs-english-font my/emacs-cjk-font my/emacs-font-size-pair))))

(defun my/increase-emacs-font-size ()
  "Decrease emacs's font-size acording emacs-font-size-pair-list."
  (interactive) (my/emacs-step-font-size 1))

(defun my/decrease-emacs-font-size ()
  "Increase emacs's font-size acording emacs-font-size-pair-list."
  (interactive) (my/emacs-step-font-size -1))

Setup Keybinds

(bind-keys :map global-map
           ("C-=" . my/increase-emacs-font-size)
           ("C--" . my/decrease-emacs-font-size))

Minibuffer

(when (require 'minibuffer)                  ; buildin
  <<minibuffer-config>>)

Make cursor in minibufer use bar shape

;; only use `bar' type of cursor shape
(add-hook 'minibuffer-setup-hook #'(lambda () (setq cursor-type 'bar)))

Some helper function to let me insert quick in minibuffer

;; define some helper function to insert to minibuffer quickly
(defun my/minibuffer-insert (p)
  (kill-line 0) (insert p))

(defun my/minibuffer-switch-to-ramdisk ()
  "Insert ramdisk path according to system type"
  (interactive)
  (my/minibuffer-insert user-ramdisk-directory))

(defun my/minibuffer-switch-to-home ()
  "Insert $HOME path."
  (interactive)
  (my/minibuffer-insert (file-name-as-directory (getenv "HOME"))))

(defun my/minibuffer-switch-to-rootdir ()
  "Insert / path."
  (interactive)
  (my/minibuffer-insert "/"))

(defun my/minibuffer-switch-to-tramp ()
  "Insert /ssh:."
  (interactive)
  (my/minibuffer-insert "/ssh:"))

Save history of minibuffer :tangle no :noweb-ref minibuffer-config

When Savehist mode is enabled, minibuffer history is saved periodically and when exiting Emacs. When Savehist mode is enabled for the first time in an Emacs session, it loads the previous minibuffer history from ‘savehist-file’.

(use-package savehist
  :config
  (setq savehist-file (concat user-cache-directory "savehist.dat"))
  (savehist-mode 1))

Setup Keybindings

(bind-keys :map minibuffer-local-map
           ("C-w" . backward-kill-word)
           ("M-p" . previous-history-element)
           ("M-n" . next-history-element)
           ("C-g" . minibuffer-keyboard-quit)
           ("M-t" . my/minibuffer-switch-to-ramdisk)
           ("M-h" . my/minibuffer-switch-to-home)
           ("M-/" . my/minibuffer-switch-to-rootdir)
           ("M-s" . my/minibuffer-switch-to-tramp))

Vim Emulation

Though I am really familier with emacs, I still like some vim command.

(use-package evil
  :ensure t
  :config
  ;; enable evil-mode globally
  (evil-mode t)
  ;; some configs setup later
  <<evil-config>>
  ;; extra keybindings defined in `Keybinding' section
  <<evil-keybindings>>
  <<evil-ex-commands>>)

Setup default state to insert-state

;; default state set to insert-state
(setq evil-default-state 'insert)

Make insert-state the same as emacs-state

To me, vim’s insert state is useless, so I mapping all my emacs keybinding to insert-state.

First, bind all emacs-state key to insert state

(setcdr evil-insert-state-map nil)
(define-key evil-insert-state-map
  (read-kbd-macro evil-toggle-key) 'evil-emacs-state)

Make sure ESC key in insert-state will call evil-normal-state.

(define-key evil-insert-state-map [escape] 'evil-normal-state)

We also need to make all emacs-state buffer become to insert-state.

(dolist (m evil-emacs-state-modes)
  (add-to-list 'evil-insert-state-modes m))

evil-leader

Evil Leader provides the <leader> feature from Vim that provides an easy way to bind keys under a variable prefix key. For an experienced Emacs User it is nothing more than a convoluted key map, but for a Evil user coming from Vim it means an easier start.

GitHub: https://github.com/cofi/evil-leader

(use-package evil-leader
  :ensure t
  :after evil
  :config
  ;; enable evil-leader globally
  (global-evil-leader-mode)
  ;; extra keybindings defined in `Keybinding' section
  <<evil-leader-keybindings>> )

evil-surround

This package emulates surround.vim by Tim Pope. The functionality is wrapped into a minor mode.

GitHub: https://github.com/timcharper/evil-surround

(use-package evil-surround
  :ensure t
  :after evil
  :config
  (global-evil-surround-mode 1))

evil-quickscope

This package emulates quick_scope.vim by Brian Le. It highlights targets for evil-mode’s f,F,t,T keys, allowing for quick navigation within a line with no additional mappings.

GitHub: https://github.com/blorbx/evil-quickscope

(use-package evil-quickscope
  :ensure t
  :after evil
  :config
  (add-hook 'prog-mode-hook 'turn-on-evil-quickscope-always-mode))

evil-terminal-cursor-changer

evil-terminal-cursor-changer is changing cursor shape and color by evil state for evil-mode.

When running in terminal, It’s especially helpful to recognize evil’s state.

GitHub: https://github.com/7696122/evil-terminal-cursor-changer

(use-package evil-terminal-cursor-changer
  :ensure t
  :after evil
  :commands (evil-terminal-cursor-changer-activate)
  :config (evil-terminal-cursor-changer-activate))

Displays tildes in the fringe on empty lines a la Vi

GitHub: https://github.com/syohex/vi-tilde-fringe

(use-package vi-tilde-fringe
  :ensure t
  :if window-system
  :config
  (global-vi-tilde-fringe-mode))

Make terminal support evil’s cursor shape change

This package changing cursor shape and color by evil state for evil-mode.

Supported terminal: xterm, gnome-terminal, iTerm, konsole.

GitHub: https://github.com/7696122/evil-terminal-cursor-changer

(use-package evil-terminal-cursor-changer
  :ensure t
  :if (not (display-graphic-p))		; Only use this package when in terminal
  :config
  ;; cursor shape setting
  (setq evil-motion-state-cursor 'box)  ;
  (setq evil-visual-state-cursor 'box)  ;
  (setq evil-normal-state-cursor 'box)  ;
  (setq evil-insert-state-cursor 'bar)  ;
  (setq evil-emacs-state-cursor  'hbar) ; _
  ;; enable this package
  (evil-terminal-cursor-changer-activate))

Editor

Why emacs config has an editor section, doesn’t means emacs is not an editor ? Yes, Emacs is an OS :)

I put some editor/IDE relative functions and packages here.

Create minor-mode to controll all keybindings

(defvar my-editor-map (make-keymap))

(define-minor-mode my-editor-mode
  "My editor minor mode."
  :init-value t
  :keymap my-editor-map)

(define-globalized-minor-mode global-my-editor-mode
  my-editor-mode (lambda ()
                        (if (not (minibufferp (current-buffer)))
                            (my-editor-mode 1))))

;; Gloabal enable
(global-my-editor-mode t)

Keeping files in sync

By default, Emacs will not update the contents of open buffers when a file changes on disk. This is inconvenient when switching branches in Git - as you’d risk editing stale buffers.

This problem can be solved by:

(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)
(setq revert-without-query '(".*")) ;; disable revert query

Disable lock file

I don’t want emacs create some temporary file like .#-emacs-a08196, disable it.

;; https://www.emacswiki.org/emacs/LockFiles
(when (version<= "24.3" emacs-version)
  (setq create-lockfiles nil))

Add support for editorconfig

EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles. EditorConfig files are easily readable and they work nicely with version control systems.

see: http://editorconfig.org/

GitHub: https://github.com/editorconfig/editorconfig-emacs

(use-package editorconfig
  :ensure t
  :if (executable-find "editorconfig")
  :mode ("\\.editorconfig\\'" . conf-unix-mode)
  :commands editorconfig-mode
  :init
  (add-hook 'prog-mode-hook #'editorconfig-mode))

Install System Packages

To use editorconfig we also need to install it in system manually.

Mac OSX

For Mac OSX, just type

brew install editorconfig

Gentoo Linux

If you use Gentoo Linux, you can use following command to install this package

emerge app-text/editorconfig-core-c

En/Decrypt files by EasyPG

(require 'epa-file)			; part of emacs
;; Enable epa, so I can use gnupg in emacs to en/decrypt file
(epa-file-enable)
;; Control whether or not to pop up the key selection dialog.
(setq epa-file-select-keys 0)
;; Cache passphrase for symmetric encryption.
(setq epa-file-cache-passphrase-for-symmetric-encryption t)

Install gnupg to system

For Mac OSX, just type

brew install gpg

Most Linux already shipped with gnupg, if you use Gentoo Linux, you can use following command to install it

emerge app-crypt/gnupg

In my system, I always use gpg2 instead of gpg1.

Make pinentry show on mini-buffer

For me, when enter password on gpg encrypt file, I always want the password input prompt show on minibuffer, so I need to add more setup on the gnupg.

First, put this in ~/.gnupg/gpg-agent.conf

allow-emacs-pinentry
allow-loopback-pinentry

# (optional) setup timeout
pinentry-timeout 3

Then tell gpg-agent to load this configuration with gpgconf in a shell:

gpgconf --reload gpg-agent

In your emacs, put this epa setup:

(require 'epa)				; built-in
(setq epg-pinentry-mode 'loopback)

All things done, let’s start the pinentry server:

(use-package pinentry
  :ensure t
  :config
  ;; Start the Pinentry service
  (pinentry-start))

Remote file editing

(use-package tramp
  :ensure t
  :init
  (setq tramp-persistency-file-name (concat user-cache-directory "tramp"))
  :config
  (setq tramp-default-method "rsync"))

Line Numbers

In most case, I’ll make line numers display globally by linum.

(if (version<= "26.1" emacs-version)
    ;; emacs 26.1 has display-line-number-mode, which is written in C
    (progn
      (require 'display-line-numbers)
      ;; Only use line number in `prog-mode-hook'
      (add-hook 'prog-mode-hook #'display-line-numbers-mode))
    ;; for emacs version less than 26, use linum instead
    (use-package linum :ensure t :init (global-linum-mode 1)))

Disable line number in some mode, for example, since org-mode can has many lines, it’s not recommand to enable linum-mode.

I use linum-off to disable some mode.

;; disble some mode with linum
(use-package linum-off
  :ensure t
  :config
  (setq linum-disabled-mode-list
        '(eshell-mode shell-mode term-mode erc-mode compilation-mode
                      woman-mode w3m-mode calendar-mode org-mode)))

;; for emacs 26.1 or above, we use `display-line-number-mode' instead
(when (version<= "26.1" emacs-version)
  ;; NOTE: overwrite display-line-numbers--turn-on
  (defun display-line-numbers--turn-on ()
    "Turn on `display-line-numbers-mode'."
    (unless (or (minibufferp)
                ;; taken from linum.el
                (and (daemonp) (null (frame-parameter nil 'client)))
                ;; take code from `linum-off'
                (member major-mode linum-disabled-modes-list))
      (display-line-numbers-mode))))

Colorfy delimters

rainbow-delimiters is a “rainbow parentheses”-like mode which highlights delimiters such as parentheses, brackets or braces according to their depth. Each successive level is highlighted in a different color. This makes it easy to spot matching delimiters, orient yourself in the code, and tell which statements are at a given depth.

(use-package rainbow-delimiters
  :ensure t
  :config
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

Save recent file info

(use-package recentf
  :straight (:type built-in)
  :init (setq recentf-save-file (expand-file-name "recentf" user-cache-directory))
  :config
  (recentf-mode 1))

Highlight numbers

highlight-numbers is an Emacs minor mode that highlights numeric literals in source code.

GitHub: https://github.com/Fanael/highlight-numbers

(use-package highlight-numbers
  :ensure t
  :config
  ;; json-mode has it's own highlight numbers method
  (add-hook 'prog-mode-hook #'(lambda()
                               (if (not (derived-mode-p 'json-mode))
                                   (highlight-numbers-mode)))))

Highlight escape charset

GitHub: https://github.com/dgutov/highlight-escape-sequences

(use-package highlight-escape-sequences
  :ensure t
  :config
  ;; Make face the same as builtin face
  (put 'font-lock-regexp-grouping-backslash 'face-alias 'font-lock-builtin-face)
  ;; Enable globally
  (hes-mode 1))

Highlight FIXME, TODO

(defun font-lock-comment-annotations ()
  "Highlight a bunch of well known comment annotations.
This functions should be added to the hooks of major modes for programming."
  (font-lock-add-keywords
   nil
   '(("\\<\\(FIX\\(ME\\)?\\|BUG\\|HACK\\):" 1 font-lock-warning-face t)
     ("\\<\\(NOTE\\):" 1 'org-level-2 t)
     ("\\<\\(TODO\\):" 1 'org-todo t)
     ("\\<\\(DONE\\):" 1 'org-done t))
   ))

(add-hook 'prog-mode-hook 'font-lock-comment-annotations)

Show vertical lines to guide indentation

GitHub: https://github.com/zk-phi/indent-guide

(use-package indent-guide
  :ensure t
  :config
  ;; Only show indent-guide in idle-time.
  (setq indent-guide-delay 0.1))

Visualization of matching parens

(show-paren-mode 1)
(setq show-paren-delay 0)               ; no delay

Adapt to foreign indentation offsets

An Emacs minor mode that guesses the indentation offset originally used for creating source code files and transparently adjusts the corresponding settings in Emacs, making it more convenient to edit foreign files.

GitHub: https://github.com/jscheid/dtrt-indent

(use-package dtrt-indent
  :ensure t
  :config
  ;; enable dtrt-indent-mode globally
  (dtrt-indent-mode 1))

Intelligently call whitespace-cleanup on save

This Emacs library minor mode will intelligently call whitespace-cleanup before buffers are saved.

whitespace-cleanup is a handy function, but putting it in before-save-hook for every buffer is overkill, and causes messy diffs when editing third-party code that did not initially have clean whitespace.

Additionally, whitespace preferences are often project-specific, and it’s inconvenient to set up before-save-hook in a .dir-locals.el file.

whitespace-cleanup-mode is a minor mode which calls whitespace-cleanup before saving the current buffer, but only if the whitespace in the buffer was initially clean. It determines this by quickly checking to see if whitespace-cleanup would have any effect on the buffer.

GitHub: https://github.com/purcell/whitespace-cleanup-mode

(use-package whitespace-cleanup-mode
  :ensure t
  :config
  (add-hook 'prog-mode-hook 'whitespace-cleanup-mode))

Make Firefox can call Emacs to edit textarea

Want to edit Firefox’s textarea in emacs ?

First, you need to install Edit with Emacs on Firefox.

Then, in emacs, add following code to install

(use-package edit-server
  :ensure t
  :config
  (edit-server-start))

Setup TABS

(setq tab-always-indent 'complete)

Highlight symbols with overlay

(use-package symbol-overlay
  :ensure t
  :config
  (add-hook 'prog-mode-hook #'symbol-overlay-mode)
  ;; (global-set-key (kbd "M-i") 'symbol-overlay-put)
  (define-key symbol-overlay-map (kbd "p") 'symbol-overlay-jump-prev) ;; 次のシンボルへ
  (define-key symbol-overlay-map (kbd "n") 'symbol-overlay-jump-next) ;; 前のシンボルへ
  (define-key symbol-overlay-map (kbd "C-g") 'symbol-overlay-remove-all) ;; ハイライトキャンセル
  )

Scrolling

ref: https://www.reddit.com/r/emacs/comments/8sw3r0/finally_scrolling_over_large_images_with_pixel/

(setq mouse-wheel-scroll-amount '(1)) ; Distance in pixel-resolution to scroll each mouse wheel event.
(setq mouse-wheel-progressive-speed nil) ; Progressive speed is too fast for me.

Undo with undo-tree

Standard Emacs undo is kind of confusing. undo-tree replaces this with a simpler tree structure. It also allows us to visualize the tree directly.

GitHub: https://github.com/emacsmirror/undo-tree

(use-package undo-tree
  :ensure t
  :commands (global-undo-tree-mode)
  :config
  ;; Persistent undo-tree history across emacs sessions
  (let ((dir
         (file-name-as-directory (concat user-cache-directory "undo-tree"))))
    (setq undo-tree-history-directory-alist `(("." . ,dir))))
  ;; Make undo-tree save history
  (setq undo-tree-auto-save-history t)
  ;; global enable undo-tree
  (global-undo-tree-mode))

Buffer

Create scratch automatically

Sometimes I’ll clean the *scratch* buffer by kill it, add following function to let emacs re-create it automatically.

;; Create *scratch* automatically
(run-with-idle-timer 1 t
                     #'(lambda ()
                        (unless (get-buffer "*scratch*")
                          (with-current-buffer (get-buffer-create "*scratch*")
                            (lisp-interaction-mode)))))

Make buffer names unique

The library uniquify overrides Emacs’ default mechanism for making buffer names unique (using suffixes like <2>, <3> etc.) with a more sensible behaviour which use parts of the file names to make the buffer names distinguishable.

For instance, buffers visiting /tmp/Makefile and /projects/coldnew/Makefile would be named Makefile|tmp and Makefile|coldnew, respectively (instead of Makefile and Makefile<2>).

(use-package uniquify
  :ensure nil                           ; built-in
  :config
  ;; starting separator for buffer name components
  (setq uniquify-separator "")
  ;; rerationalize buffer names after a buffer has been killed.
  (setq uniquify-after-kill-buffer-p t)
  ;; ignore non file buffers
  (setq uniquify-ignore-buffers-re "^\\*"))

Bookmarks

Set up default registers

In emacs, there’s a jump-to-register (C-x r j) command which can quickly switch to the file you setup.

For more info, you can refer to: EmacsWiki: Registers

(dolist
    (r `(
	   ;; emacs's config.org
	   (?e (file . "~/.emacs.d/init.org"))
	   ;; tasks: todo
	   (?t (file . "~/Org/tasks/todo.org"))
	   ;; tasks: personal
	   (?p (file . "~/Org/tasks/personal.org"))
	   ;; tasks: work
	   (?w (file . "~/Org/tasks/work.org"))
	   ;; Offilce docs
	   (?W (file . "~/Org/Weintek/index.org"))
	   ;; My personal note
	   (?n (file . "~/Org/Note.org"))
	   ;; blogging ideas
	   (?b (file . "~/Org/blog.org"))
	   ;; Finance
	   (?f (file . "~/Org/finance/personal.org"))
	   ))
  (set-register (car r) (cadr r)))

Setup Keybinding

(bind-keys :map my-editor-map
           ;("C-x n" . bm-next)
           ;("C-x p" . bm-previous)
           ;("C-x ." . bm-toggle)
           )

Helm

(use-package helm
  :straight t
  :init
  <<helm-init>>
  :config
  <<helm-config>>)

Install System packages

Since helm integrate some external tool like gtags, cscope …etc. We need to install these tools to our system.

Mac OSX

It’s recommand to use homebrew to install the_silver_searcher under Mac OSX.

brew install global
brew install cscope

Gentoo Linux

If you use Gentoo Linux, you can use portage to install it.

emerge dev-util/cscope
emerge dev-util/global

Basic setup

Enable helm mode by default.

(add-hook 'after-init-hook #'helm-mode)
(add-hook 'after-init-hook #'helm-autoresize-mode)
(add-hook 'after-init-hook #'helm-adaptive-mode)
(add-hook 'after-init-hook #'helm-popup-tip-mode)

Use fuzzy match in helm

;; Use fuzzy match in helm
(setq helm-M-x-fuzzy-match t)
(setq helm-buffers-fuzzy-matching t)
(setq helm-recentf-fuzzy-match t)

Make helm can select anything even not match

;; make helm can select anything even not match
(setq helm-move-to-line-cycle-in-source nil)
(setq helm-ff-search-library-in-sexp t)
(setq helm-ff-file-name-history-use-recentf t)

helm-bm

GitHub: https://github.com/yasuyk/helm-bm

(use-package helm-bm :ensure t :after (helm))

helm-dash

This package uses Dash docsets inside emacs to browse documentation. Here’s an article explaining the basic usage of it.

It doesn’t require Dash app.

GitHub: https://github.com/areina/helm-dash

(use-package helm-dash :ensure t :after (helm))

helm-gtags

helm-gtags.el is GNU GLOBAL helm interface.

GitHub: https://github.com/syohex/emacs-helm-gtags

(use-package helm-gtags
  :ensure t
  :after (helm)
  :config
  (setq helm-gtags-ignore-case t)
  (setq helm-gtags-auto-update t)
  (setq helm-gtags-use-input-at-cursor t)
  (setq helm-gtags-pulse-at-cursor t)
  ;; add to following modes
  (add-hook 'c-mode-hook #'helm-gtags-mode)
  (add-hook 'c++-mode-hook #'helm-gtags-mode))

helm-swoop

List match lines to another buffer, which is able to squeeze by any words you input. At the same time, the original buffer’s cursor is jumping line to line according to moving up and down the line list.

GitHub: https://github.com/ShingoFukuyama/helm-swoop

(use-package helm-swoop :ensure t :after (helm))

helm-c-yasnippet

GitHub: https://github.com/emacs-jp/helm-c-yasnippet

(use-package helm-c-yasnippet
  :ensure t
  :after (helm yasnippet)
  :config
  (setq helm-yas-space-match-any-greedy t))

helm-smex

(use-package helm-smex :ensure t :after (helm))

Setup Helm Keybindings

(bind-keys :map helm-map
           ("TAB"   . helm-execute-persistent-action)
           ("<tab>" . helm-execute-persistent-action)
           ("C-w"   . backward-kill-word)
           ("M-t" . my/minibuffer-switch-to-ramdisk)
           ("M-h" . my/minibuffer-switch-to-home)
           ("M-/" . my/minibuffer-switch-to-rootdir)
           ("M-s" . my/minibuffer-switch-to-tramp)
           ("M-v" . my/minibuffer-switch-to-vm)
           ("M-c" . my/minibuffer-switch-to-cluster)
           ("C-z" . helm-select-action))

Org

(use-package org
  :ensure nil				; built-in
  :mode (("\\.org\\'" . org-mode)
         ("\\.org_archive\\'" . org-mode))
  :config
  (setq org-url-hexify-p nil)
  <<org-config>>)

Basic setup

;; fontify source code
(setq org-src-fontify-natively t)
;; Use current window when switch to source block
(setq org-src-window-setup 'current-window)
;; Disable prompting to evaluate babel blocks
(setq org-confirm-babel-evaluate nil)
;; Disable add validation link when export to HTML
(setq org-html-validation-link nil)

Indention setup

Always enable auto indent for org-mode.

(use-package org-indent
  :ensure nil				; build-in
  :after (org)
  :config
  ;; Enable `org-indent-mode' by default
  (add-hook 'org-mode-hook #'(lambda () (org-indent-mode t))))

Pretty Bullets

Show org-mode bullets as UTF-8 characters.

GitHub: https://github.com/sabof/org-bullets

(use-package org-bullets
  :ensure t
  :after (org)
  :config
  (add-hook 'org-mode-hook #'(lambda () (org-bullets-mode 1))))

Setup TODO keywords

(setq org-todo-keywords '((sequence "TODO(t)" "|" "DONE(d)")
                          (sequence "WAITING(w)" "|")
                          (sequence "|" "CANCELED(c)")))

Enable word-wrap

Call visual-line-mode to have soft-wrap.

(add-hook 'org-mode-hook #'visual-line-mode)

Disable electric-pair for org-mode

(add-hook 'org-mode-hook #'(lambda () (electric-pair-local-mode -1)))

Extend org-mode’s easy templates

org-mode make it easy to insert blocks by typing <s[TAB] …etc. I defined some extra easy-templates I need to use here.

;; NOTE:
;; After org-mode 9.2, we need to require `org-tempo' module
;; to make easy-template work
(when (not (version< (org-version) "9.2"))
  (require 'org-tempo))

(add-to-list 'org-structure-template-alist
	       '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist
	       '("S" . "src sh"))
(add-to-list 'org-structure-template-alist
	       '("p" . "src plantuml :file uml.png"))

Extend babel support languages

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (C . t)
   (ditaa . t)
   (dot . t)
   (js . t)
   (latex . t)
   (perl . t)
   (python . t)
   (ruby . t)
   ;; (sh . t)
   (shell . t)
   (plantuml . t)
   (R . t)
   (clojure . t)))
;; make dot work as graphviz-dot
(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))

Setup link abbreviations

Link abbreviations

An abbreviated link looks like

[[linkword:tag][description]]
(setq org-link-abbrev-alist
      '(("google" . "http://www.google.com/search?q=")      ; ex: [[google:hi emacs]]
        ("google-map" . "http://maps.google.com/maps?q=%s") ; ex: [[google-map:taiwan]]
        ("wiki" . "https://en.wikipedia.org/wiki/%s")       ; ex: [[wiki:emacs]]
        ))

Capture and Agenda

;; make agenda show on current window
(setq org-agenda-window-setup 'current-window)
;; highlight current in agenda
(add-hook 'org-agenda-mode-hook 'hl-line-mode)

;; Setup files for agenda
(setq org-directory "~/Org/tasks")
;; U all .org files in `org-directory'
(setq org-agenda-files
      (find-lisp-find-files org-directory "\.org$"))
;;
(setq org-default-notes-file (f-join org-directory "tasks" "TODO.org"))

;; Always use `C-g' to exit agenda
(add-hook 'org-agenda-mode-hook
          #'(lambda ()
             (local-set-key (kbd "C-g") 'org-agenda-exit)))

;; Use speed command to quick navigating
(setq org-use-speed-commands t)
;; Log timestamp when done
(setq org-log-done 'time)
(setq org-capture-templates
      '(("t" "TODO"     entry (file+headline "" "Tasks") "* TODO %?\n %i\n")
        ("n" "NOTE"     entry (file+headline "" "Tasks") "* NOTE %?\n %i\n %a")
        ("l" "Links"    entry (file+headline "" "Links") "* TODO %? :link:\nSCHEDULED: <%<%Y-%m-%d %a>>\n %i\n %a")
        ("j" "Journal"  entry (file+datetree "" "Journal") "* %?\nEntered on %U\n  %i\n  %a")
        ))

Deft for Note-taking

(use-package deft
  :ensure t
  :config
  ;; default use org-mode
  (setq deft-default-extension "org")
  ;; default directory set to ~/Org
  (setq deft-directory "~/Org")
  ;; Do not make deft automatically save file
  (setq deft-auto-save-interval 0)
  ;; Recursive search
  (setq deft-recursive t)

  ;; setup an minor-mode to quickly kill all deft buffers
  (define-minor-mode deft-note-mode "Deft notes" nil " Deft-Notes" nil)
  (setq deft-text-mode 'deft-note-mode)

  ;; Quickly kill deft buffers
  (defun my/kill-all-deft-notes ()
    (interactive)
    (save-excursion
      (let ((count 0))
        (dolist (buffer (buffer-list))
          (set-buffer buffer)
          (when (not (eq nil deft-note-mode))
            (setq count (1+ count))
            (kill-buffer buffer))))))

  ;; Enable/Disable defts
  (defun deft-or-close ()
    (interactive)
    (if (or (eq major-mode 'deft-mode) (not (eq nil deft-note-mode)))
        (progn (my/kill-all-deft-notes) (kill-buffer "*Deft*"))
        (deft)))

  (defun my/deft-practice ()
    "Use deft to quickly see my blog drafts."
    (interactive)
    (let ((deft-directory "~/Workspace/practice")
          (deft-extensions '("md" "org"))))
    (deft)))

Make spell-checking tool ignore some org-mode section

see: http://emacs.stackexchange.com/questions/450/intelligent-spell-checking-in-org-mode

(eval-after-load 'ispell
  '(progn
     (add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:"))
     (add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC"))
     ))

Crypt contents in org-mode

(use-package org-crypt
  :ensure nil
  :after (org)
  :config
  ;; Disable `auto-save-mode' for org-mode buffer prior to decrypting an entry.
  (setq org-crypt-disable-auto-save t)
  ;; Auto encrypt when save file
  (org-crypt-use-before-save-magic)
  ;; Encrypt with tagname: `secret'
  (setq org-crypt-tag-matcher "secret")
  ;; Prevent the `secret' tag inherit by child
  ;; (The child item still will be encrypt)
  (setq org-tags-exclude-from-inheritance (quote ("secret")))
  ;; Use my own password to encrypt
  (setq org-crypt-key nil))

Drag-and-drop image to your org-mode file

org-download will let you drag-and-drop image to your org-mode file easily.

GitHub: https://github.com/abo-abo/org-download

(use-package org-download
  :ensure t
  :config
  ;; add support to dired
  (add-hook 'dired-mode-hook 'org-download-enable))

Setup Org-mode Keybindings

(bind-keys :map org-mode-map
           ("M-p"   . org-previous-visible-heading)
           ("M-n"   . org-next-visible-heading)
           ("C-c a" . org-agenda)
           ("C-c c" . org-capture)
           ("C-c l" . org-store-link)
           ("C-c b" . org-metaleft)
           ("C-c f" . org-metaright)
           ("C-c p" . org-metaup)
           ("C-c n" . org-metadown)
           ("C-c i" . org-insert-link)
           ("C-c I" . org-toggle-inline-images)
           ("C-c %" . org-mark-ring-push)
           ("C-c &" . org-mark-ring-goto)
           ("C-c C-." . org-babel-remove-result-one-or-many))
(bind-keys :map org-src-mode-map
           ("C-c C-c" . org-edit-src-exit))

Programming Languages

PlantUML

GitHub: https://github.com/skuro/plantuml-mode

(use-package plantuml-mode
  :ensure t
  :mode ("\\.plantuml\\'")
  :init
  ;; FIXME: setup plantuml jar path
  ;; FIXME: add org-mode support
  )

Bison

Bison s a general-purpose parser generator that converts an annotated context-free grammar into a deterministic LR or generalized LR (GLR) parser employing LALR(1) parser tables. As an experimental feature, Bison can also generate IELR(1) or canonical LR(1) parser tables. Once you are proficient with Bison, you can use it to develop a wide range of language parsers, from those used in simple desk calculators to complex programming languages.

Bison is upward compatible with Yacc: all properly-written Yacc grammars ought to work with Bison with no change. Anyone familiar with Yacc should be able to use Bison with little trouble. You need to be fluent in C or C++ programming in order to use Bison. Java is also supported as an experimental feature.

(use-package bison-mode
  :ensure t
  :mode ("\\.y\\'" "\\.l\\'" "\\.jison\\'"))

GN

GN is a meta-build system that generates NinjaBuild files. This system is widely used on Google’s chromium related framework.

Take a look on GN reference or GN Quick Start Guide for more info.

(use-package gn-mode
  :ensure t
  :mode ("BUILD.gn" "\\.gni?\\'"))

Markdown

Markdown is a lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor.

Here I use markdown-mode to help me integrate markdown with emacs, the README.md is bind to gfm-mode for github’s markdown flavor.

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown")
  :config
  <<markdown-config>>)

Syntax highlighting on code blocks

Syntax highlighting for code blocks in Emacs Markdown Mode give us some hint to use mmm-mode syntax highlight code block on markdown-mode.

;; http://jblevins.org/log/mmm
(use-package mmm-mode
  :ensure t
  :config
  (setq mmm-global-mode 'maybe)
  (setq mmm-parse-when-idle 't)
  (defun my/mmm-markdown-auto-class (lang &optional submode)
    "Define a mmm-mode class for LANG in `markdown-mode' using SUBMODE.
If SUBMODE is not provided, use `LANG-mode' by default."
    (let ((class (intern (concat "markdown-" lang)))
          (submode (or submode (intern (concat lang "-mode"))))
          (front (concat "^```" lang "[\n\r]+"))
          (back "^```"))
      (mmm-add-classes (list (list class :submode submode :front front :back back)))
      (mmm-add-mode-ext-class 'markdown-mode nil class)))

  ;; Mode names that derive directly from the language name
  (mapc 'my/mmm-markdown-auto-class
        '("awk" "bibtex" "c" "cpp" "css" "html" "latex" "lisp" "makefile"
          "markdown" "python" "r" "ruby" "sql" "stata" "xml" "js")))

Setup Markdown keybinding

(bind-keys :map markdown-mode-map
           ("C-c i" . markdown-insert-link))

(bind-keys :map gfm-mode-map
           ("C-c i" . markdown-insert-link))

NASM

(use-package nasm-mode :ensure t)

TOML

TOML aims to be a minimal configuration file format that’s easy to read due to obvious semantics. TOML is designed to map unambiguously to a hash table. TOML should be easy to parse into data structures in a wide variety of languages.

(use-package toml-mode
  :ensure t
  :mode "\\.toml$")

YAML

YAML is a human-readable data serialization language. It is commonly used for configuration files, but could be used in many applications where data is being stored (e.g. debugging output) or transmitted (e.g. document headers). YAML targets many of the same communications applications as XML, but has taken a more minimal approach which intentionally breaks compatibility with SGML.

(use-package yaml-mode
  :ensure t
  :mode "\\.yml$")

R

(use-package ess
  :ensure t
  :mode ("\\.[rR]\\'" . R-mode)
  :config
  <<R-config>>)

QML

(use-package qml-mode
  :ensure t
  :mode "\\.qml$"
  :config
  <<qml-config>>)

indent-guide

(use-package indent-guide
  :ensure t
  :config
  (add-hook 'qml-mode-hook #'indent-guide-mode))

Vala

Vala is a new programming language that allows modern programming techniques to be used to write applications that run on the GNOME runtime libraries, particularly GLib and GObject.

For more info about vala, please see: Vala Tutorial

(use-package vala-mode
  :ensure t
  :mode ("\\.vala\\'" "\\.vapi\\'")
  :config
  <<vala-config>>)

Verilog

(use-package verilog-mode
  :mode ("\\.v\\'")
  :config
  <<verilog-config>>)
(setq verilog-linter "verilator --lint-only")
;; https://github.com/flycheck/flycheck/issues/1250
(setq flycheck-verilog-verilator-executable "/usr/bin/verilator_bin")

Groovy

(use-package groovy-mode
  :mode (("\\.groovy" . groovy-mode)
         ("/Jenkinsfile" . groovy-mode))
  :ensure t)

Svelete

(use-package svelte-mode
  :ensure t
  :mode ("\\.svelte\\'" . svelte-mode))

Configration Files

Since emacs not built-in all configration files syntax highlight support, we need to install manually.

Docker

(use-package dockerfile-mode
  :ensure t
  :mode "Dockerfile\\'")

SSH

(use-package ssh-config-mode
  :ensure t
  :mode ((".ssh/config\\'"       . ssh-config-mode)
         ("sshd?_config\\'"      . ssh-config-mode)
         ("known_hosts\\'"       . ssh-known-hosts-mode)
         ("authorized_keys\\'"   . ssh-authorized-keys-mode)))

Systemd

(use-package systemd
  :ensure t)

CMake

(use-package cmake-mode
  :ensure t
  :mode (("CMakeLists\\.txt\\'" . cmake-mode) ("\\.cmake\\'" . cmake-mode)))
;; cmake-font-lock: emacs font lock rules for CMake
;; https://github.com/Lindydancer/cmake-font-lock
(use-package cmake-font-lock
  :ensure t
  :config
  (autoload 'cmake-font-lock-activate "cmake-font-lock" nil t)
(add-hook 'cmake-mode-hook 'cmake-font-lock-activate))

Meson

(use-package meson-mode
  :ensure t
  :mode (("meson\\.build\\'" . meson-mode)))

C/C++ Development

(use-package cc-mode
  :mode
  (("\\.h\\'" . c++-mode)
   ("\\.c\\'" . c-mode)
   ("\\.hpp\\'" . c++-mode)
   ("\\.cpp\\'" . c++-mode)
   ("\\.cc\\'" . c++-mode))
  :config
  <<cc-mode-config>>)

ElDoc supports

(use-package c-eldoc
  :ensure t
  :config
  (add-hook 'c-mode-common-hook
            #'(lambda ()
               (setq c-eldoc-includes "`pkg-config --cflags --libs` -I./ -I../")
               (c-turn-on-eldoc-mode))))

Highlight a few dangerous types in C/C++

cwarn-mode is a minor mode that ca highlight a few dangerous types in C/C++.

By default it highlights:

  • Semicolons right after conditions and loops (e.g. if (x == y);)
  • Assignments in tests (e.g. if (x = y) {)
  • Functions with reference parameters (e.g. void funct(string &p) {)
(use-package cwarn
  :config
  (add-hook 'c-mode-common-hook #'(lambda () (cwarn-mode 1))))

Comment #if 0 #endif region

(defun my/cc-mode/highlight-if-0 ()
  "highlight c/c++ #if 0 #endif macros"
  (setq cpp-known-face 'default)
  (setq cpp-unknown-face 'default)
  (setq cpp-known-writable 't)
  (setq cpp-unknown-writable 't)
  (setq cpp-edit-list '(("0" '(foreground-color . "gray")  default both)
                        ("1" default font-lock-comment-face both)))
  (cpp-highlight-buffer t))

;; Add to c/c++ mode
(defun my/cc-mode/highlight-if-0-hook ()
  (when (or (eq major-mode 'c++-mode) (eq major-mode 'c-mode)
            (eq major-mode 'c++-ts-mode) (eq major-mode 'c-ts-mode))
    (my/cc-mode/highlight-if-0)))
(add-hook 'after-save-hook #'my/cc-mode/highlight-if-0-hook)

Treat helloWorld as two words

In most C/C++ project, we naming variable in camelCase, subword-mode can help us treat helloWorld as two words.

;; subword-mode, e.g., someThing is treated as two words
(add-hook 'c-mode-common-hook #'(lambda () (subword-mode 1)))

Auto pair parentheses with electric-pair

(add-hook 'c-mode-common-hook 'electric-pair-mode)

Add refactor function support

Semantic Refactor is a C/C++ refactoring tool based on Semantic parser framework.

GitHub: https://github.com/tuhdo/semantic-refactor

(use-package srefactor
  :ensure t
  :defer t
  :after (cc-mode))

Quick switch between header and source file in C/C++

This extension allows to quickly switch between header and a source file with the same name located in the directory tree or repository. It is an alternatife to ff-find-other-file.

GitHub: https://github.com/fourier/cff

(use-package cff
  :ensure t
  :after (cc-mode))

Extra Syntax Highlight

Some syntax highlight may not support in default emacs, set it here.

Highlights stdint types

Extra hightlight for stdint.h

(dolist (m '(c-mode c++-mode))
  (font-lock-add-keywords
   m
   '(("\\<\\(int8_t\\|int16_t\\|int32_t\\|int64_t\\|uint8_t\\|uint16_t\\|uint32_t\\|uint64_t\\)\\>" . font-lock-keyword-face))))

Modern C++ ~ C++17 font-lock highlighting

;; adds font-lock highlighting for modern C++ upto C++17
;; https://github.com/ludwigpacifici/modern-cpp-font-lock
(use-package modern-cpp-font-lock
  :ensure t
  :hook (c++-mode . modern-c++-font-lock-mode)
  :after (cc-mode))

Coding Styles

Since C/C++ has too many coding styles, I need to set it up.

C

General C Coding Style

I always use linux coding style for c language by default.

(add-hook 'c-mode-hook
          #'(lambda ()
             (c-set-style "linux")
             (setq c-basic-offset 8)
             ;; Make TAB equivilent to 8 spaces
             (setq tab-width 8)))

Linux Kernel C Coding Style

As part of Linux Kernel developer, I add linux-kernel coding style rule, which use tabs as indent and follow linux kernel development rules. Use following code to make emacs switch to linux-kernel style automatically when enter linux kernel directories.

This coding style is document in https://www.kernel.org/doc/Documentation/CodingStyle.

(defun c-lineup-arglist-tabs-only (ignored)
  "Line up argument lists by tabs, not spaces"
  (let* ((anchor (c-langelem-pos c-syntactic-element))
         (column (c-langelem-2nd-pos c-syntactic-element))
         (offset (- (1+ column) anchor))
         (steps (floor offset c-basic-offset)))
    (* (max steps 1)
       c-basic-offset)))

;; Add Linux kernel style
(add-hook 'c-mode-common-hook
          (lambda ()
            (c-add-style "linux-kernel"
                         '("linux" (c-offsets-alist
                                    (arglist-cont-nonempty
                                     c-lineup-gcc-asm-reg
                                     c-lineup-arglist-tabs-only))))))

(defun linux-kernel-development-setup ()
  (let ((filename (buffer-file-name)))
    ;; Enable kernel mode for the appropriate files
    (when (and filename
               (or (locate-dominating-file filename "Kbuild")
                   (locate-dominating-file filename "Kconfig")
                   (save-excursion (goto-char 0)
                                   (search-forward-regexp "^#include <linux/\\(module\\|kernel\\)\\.h>$" nil t))))
      (setq indent-tabs-mode t)
      (setq tab-width 8)
      (setq c-basic-offset 8)
      (c-set-style "linux-kernel")
      (message "Setting up indentation for the linux kernel"))))

(add-hook 'c-mode-hook 'linux-kernel-development-setup)

C++

Use my C++ coding style.

(add-hook 'c++-mode-hook
          '(lambda ()

             ;; Use stroustrup style
             (c-set-style "stroustrup")

             ;; Setting indentation lvel
             (setq c-basic-offset 4)

             ;; Make TAB equivilent to 4 spaces
             (setq tab-width 4)

             ;; Use spaces to indent instead of tabs.
             (setq indent-tabs-mode nil)

             ;; Indent the continuation by 2
             (setq c-continued-statement-offset 2)

             ;; Brackets should be at same indentation level as the statements they open
             ;; for example:
             ;;                 if (0)        becomes        if (0)
             ;;                     {                        {
             ;;                        ;                         ;
             ;;                     }                        }
             (c-set-offset 'substatement-open 0)

             ;; make open-braces after a case
             (c-set-offset 'case-label '+)

             ;; Not indent code inside a namespace
             ;; for example:
             ;;                namespace A {
             ;;
             ;;                int namespace_global_variable;
             ;;
             ;;                class Class {
             ;;
             ;;                Class();
             ;;                //...
             ;;                };
             ;;
             ;;                }
             (c-set-offset 'innamespace 0)
             ))

Extra commands for c/c++ development

kill the function in one command

(defun my/c-kill-defun ()
  "Move backward to the beging of top level declaration and save
this declaration to the kill-ring."
  (interactive)
  (save-excursion
    (kill-region
     (progn (c-beginning-of-defun) (point))
     (progn (c-end-of-defun)       (point)))))

Use clang-format to format buffer

;; clang-format: format C/C++ buffers using clang-format
;; https://github.com/emacsorphanage/clang-format
(use-package clang-format
  :ensure t)

Setup C/C++ Keybinding

(bind-keys :map c-mode-base-map
           ;;("C-c '" . my/narrow-or-widen-dwim)
           ("C-c C-c" . compile)
           ("C-c C-g" . gdb)
           ("C-c C-o" . cff-find-other-file))

;; Some keys may override global map add here
(bind-keys :map c-mode-base-map
           ("M-." . helm-gtags-dwim)
           ("M-," . helm-gtags-pop-stack))

Lisp Development

Though LISP has many dialet, it still is the best programming language I ever met.

Emacs Lisp

(use-package elisp-mode
  :ensure nil            ; built-in
  :config
  <<emacs-lisp-mode-config>>)

Interactive macro-expander for Emacs

macrostep is an Emacs minor mode for interactively stepping through the expansion of macros in Emacs Lisp source code. It lets you see exactly what happens at each step of the expansion process by pretty-printing the expanded forms inline in the source buffer, which is temporarily read-only while macro expansions are visible. You can expand and collapse macro forms one step at a time, and evaluate or instrument the expansions for debugging with Edebug as normal (but see “Bugs and known limitations”, below). Single-stepping through the expansion is particularly useful for debugging macros that expand into another macro form. These can be difficult to debug with Emacs’ built-in macroexpand, which continues expansion until the top-level form is no longer a macro call.

GitHub: https://github.com/joddie/macrostep

(use-package macrostep
  :ensure t)

Additional flavour to emacs-lisp programming

el-spice is a minor mode that provides additional configuration to make programming in Emacs Lisp more enjoyable.

GitHub: https://github.com/vedang/el-spice

(use-package el-spice
  :ensure t)

Add eldoc support

(use-package eldoc
  :ensure t
  :config
  (add-hook 'emacs-lisp-mode-hook
            '(lambda ()
               ;; enable eldoc
               (turn-on-eldoc-mode)
               ;; fix for paredit if exist
               (eval-after-load 'paredit
                 '(progn
                    (eldoc-add-command 'paredit-backward-delete
                                       'paredit-close-round))))))

Check paren on save

(defun my/elisp/check-parens-on-save ()
  "Run `check-parens' when the current buffer is saved."
  (add-hook 'after-save-hook #'check-parens nil 'make-it-local))

(add-hook 'emacs-lis-mode
          (lambda () (my/emacs-lisp/enable-check-parens-on-save)))

On-the-fly evaluation/substitution of emacs lisp code

litable keeps a list of pure functions as a safeguard for unwanted evaluations. A function must first be accepted into this list (using M-x litable-accept-as-pure) before it can be evaluated on-the-fly.

You should take care of what function you accept as pure to avoid any unfortunate accidents. Also, note that the pure functions list persists across sessions.

GitHub: https://github.com/Fuco1/litable

(use-package litable
  :ensure t
  :config
  ;; Save cache file to `user-cache-direcotry'
  (setq litable-list-file (concat user-cache-directory ".litable-lists.el"))
  ;; Enable litable-mode globally
  (litable-mode))

Display ^L glyphs as horizontal lines

This Emacs library provides a global mode which displays ugly form feed characters as tidy horizontal rules.

GitHub: https://github.com/purcell/page-break-lines

(use-package page-break-lines
  :ensure t
  :config
  ;; enable globally
  (global-page-break-lines-mode 1))

Remove *.elc when save

(defun my/remove-elc-on-save ()
  "If you're saving an elisp file, likely the .elc is no longer valid."
  (make-local-variable 'after-save-hook)
  (add-hook 'after-save-hook
            (lambda ()
              (if (file-exists-p (concat buffer-file-name "c"))
                  (delete-file (concat buffer-file-name "c"))))))

(add-hook 'emacs-lisp-mode-hook 'my/remove-elc-on-save)

Setup Emacs-Lisp Keybindings

    (bind-keys :map emacs-lisp-mode-map
;;               ("C-c '" . my/narrow-or-widen-dwim)
)

Blogging

I now use Hexo with my fork of hexo-renderer-org for blogging, hexo.el is a nice plugin to control hexo blog post in Emacs.

However, I use my fork of hexo.el since threre’s some feature I want to but not exist in upstream.

;; Use my fork of hexo.el, save to `modules/hexo.el'
(add-to-list 'load-path
             (directory-file-name (concat user-modules-directory "hexo.el")))
(use-package hexo
  :ensure t
  :config

  (defun hexo-my-blog ()
    "My blog with hexo."
    (interactive)
    (hexo "~/Workspace/blog/"))

  (defun hexo-my-draft ()
    "My blog draft with hexo."
    (interactive)
    (hexo "~/Workspace/draft/"))

  (defun hexo-my-draft ()
    "My blog with hexo."
    (interactive)
    (hexo "~/Workspace/draft/"))
  )

Version Control

Disable all vc-mode feature

Emacs’s builtin vc-mode is not really useful, and will make emacs slow when edit file over sshfs.

I disable all setting handled by vc-mode here since I only use git and don’t want vc-mode bother with other version control system.

(setq vc-handled-backends nil)

Git

Magit is an interface to the version control system Git, implemented as an Emacs package. Magit aspires to be a complete Git porcelain. While we cannot (yet) claim that Magit wraps and improves upon each and every Git command, it is complete enough to allow even experienced Git users to perform almost all of their daily version control tasks directly from within Emacs. While many fine Git clients exist, only Magit and Git itself deserve to be called porcelains.

GitHub: https://github.com/magit/magit

(use-package magit
  :ensure t
  :config
  <<magit-config>>)

Setup default commit arguments

I always add sign-off to the commit and make magit verbose.

(setq magit-commit-arguments '("--verbose" "--signoff"))

Enable diff-highlight

If you use git in command line, you can turn on diff-highlight by following command

git config --global core.pager "diff-highlight | less --tabs=4 -RFX"

With magit, we can use M-x magit-toggle-diff-refine-hunk or make it enable by default:

(setq magit-diff-refine-hunk 'all)

Add support for git configuration files

git-modes contains gitignore, gitconfig, gitattributes related config files support, we don’t need to load it separately.

GitHub: https://github.com/magit/git-modes

(use-package git-modes :ensure t)

Add GitGutter on fringe

GitHub: https://github.com/syohex/emacs-git-gutter-fringe

(use-package git-gutter-fringe
  :ensure t
  :if window-system                     ; git-gutter-fringe only work on GUI
  :config
  ;; enable globally
  (git-gutter-mode))

IDE Features

emacs has some IDE feature, add here.

flycheck

(use-package flycheck
  :ensure t
  :config
  ;; enable globally
  (global-flycheck-mode))

treesit

https://archive.casouri.cc/note/2023/tree-sitter-starter-guide/index.html

(use-package treesit
  :ensure nil 				; build-in since emacs-29.1
  :when (and (fboundp 'treesit-available-p)
             (treesit-available-p))
  :config (setq treesit-font-lock-level 4)
  :init
  (setq treesit-language-source-alist
        '((bash       . ("https://github.com/tree-sitter/tree-sitter-bash"))
          (c          . ("https://github.com/tree-sitter/tree-sitter-c"))
          (cpp        . ("https://github.com/tree-sitter/tree-sitter-cpp"))
          (css        . ("https://github.com/tree-sitter/tree-sitter-css"))
          (cmake      . ("https://github.com/uyha/tree-sitter-cmake"))
          (csharp     . ("https://github.com/tree-sitter/tree-sitter-c-sharp.git"))
          (dockerfile . ("https://github.com/camdencheek/tree-sitter-dockerfile"))
          (elisp      . ("https://github.com/Wilfred/tree-sitter-elisp"))
          (go         . ("https://github.com/tree-sitter/tree-sitter-go"))
          (gomod      . ("https://github.com/camdencheek/tree-sitter-go-mod.git"))
          (html       . ("https://github.com/tree-sitter/tree-sitter-html"))
          (java       . ("https://github.com/tree-sitter/tree-sitter-java.git"))
          (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
          (json       . ("https://github.com/tree-sitter/tree-sitter-json"))
          (lua        . ("https://github.com/Azganoth/tree-sitter-lua"))
          (make       . ("https://github.com/alemuller/tree-sitter-make"))
          (markdown   . ("https://github.com/MDeiml/tree-sitter-markdown" nil "tree-sitter-markdown/src"))
          (ocaml      . ("https://github.com/tree-sitter/tree-sitter-ocaml" nil "ocaml/src"))
          (org        . ("https://github.com/milisims/tree-sitter-org"))
          (python     . ("https://github.com/tree-sitter/tree-sitter-python"))
          (php        . ("https://github.com/tree-sitter/tree-sitter-php"))
          (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "typescript/src"))
          (tsx        . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "tsx/src"))
          (ruby       . ("https://github.com/tree-sitter/tree-sitter-ruby"))
          (rust       . ("https://github.com/tree-sitter/tree-sitter-rust"))
          (sql        . ("https://github.com/m-novikov/tree-sitter-sql"))
          (vue        . ("https://github.com/merico-dev/tree-sitter-vue"))
          (yaml       . ("https://github.com/ikatyang/tree-sitter-yaml"))
          (toml       . ("https://github.com/tree-sitter/tree-sitter-toml"))
          (zig        . ("https://github.com/GrayJack/tree-sitter-zig"))))
  (add-to-list 'major-mode-remap-alist '(sh-mode         . bash-ts-mode))
  (add-to-list 'major-mode-remap-alist '(c-mode          . c-ts-mode))
  (add-to-list 'major-mode-remap-alist '(c++-mode        . c++-ts-mode))
  (add-to-list 'major-mode-remap-alist '(c-or-c++-mode   . c-or-c++-ts-mode))
  (add-to-list 'major-mode-remap-alist '(css-mode        . css-ts-mode))
  (add-to-list 'major-mode-remap-alist '(js-mode         . js-ts-mode))
  (add-to-list 'major-mode-remap-alist '(java-mode       . java-ts-mode))
  (add-to-list 'major-mode-remap-alist '(js-json-mode    . json-ts-mode))
  (add-to-list 'major-mode-remap-alist '(makefile-mode   . cmake-ts-mode))
  (add-to-list 'major-mode-remap-alist '(python-mode     . python-ts-mode))
  (add-to-list 'major-mode-remap-alist '(ruby-mode       . ruby-ts-mode))
  (add-to-list 'major-mode-remap-alist '(conf-toml-mode  . toml-ts-mode))
  (add-to-list 'auto-mode-alist '("\\(?:Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . dockerfile-ts-mode))
  (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
  (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode))
  (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))
  (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))
  (add-to-list 'auto-mode-alist '("\\.y[a]?ml\\'" . yaml-ts-mode)))

Snippets

(use-package yasnippet
  :ensure t
  :mode ("emacs.+/snippets/" . snippet-mode)
  :config
  ;; enable yasnippet globally
  (yas-global-mode 1)
  ;; extra yasnipet configs
  <<yasnippet-config>>)

Setup yasnippet prompt method

(setq yas-prompt-functions '(yas-dropdown-prompt
                             yas-completing-prompt
                             yas-ido-prompt))

Setup yasnippet snippet dirs

I put my snippets in ~/.emacs.d/snippets, add it to yas/snippet-dirs to make yasnippet can find the snippets.

You can use yas-new-snippet to create your new snippet file.

 (let ((my-snippet-dir (concat user-emacs-directory "snippets")))
   (if (and (file-exists-p my-snippet-dir)
	     (not (member my-snippet-dir yas/snippet-dirs)))
	(add-to-list 'yas-snippet-dirs my-snippet-dir)))

Prevent yasnippet conflict with term-mode

(add-hook 'term-mode-hook (lambda() (yas-minor-mode -1)))

Implement org-mode’s easy-template like function

I really like org-mode’s easy-template function, so I implement one called major-mode-expand which will let you use easy-template like function in any major-mode.

(defadvice yas-expand (around major-mode-expand activate)
  "Try to complete a structure template before point like org-mode does.
This looks for strings like \"<e\" on an otherwise empty line and
expands them.
Before use this function, you must setup `major-mode-name'-expand-alist variable.

Take emacs-lisp-mode as example, if you wand to use <r to expand your snippet `require'
in yasnippet, you muse setup the emacs-lisp-mode-expand-alist variable.

 (setq emacs-lisp-expand-alist '((\"r\" . \"require\")))"
  (let* ((l (buffer-substring (pos-bol) (point)))
         (expand-symbol (intern (concat (symbol-name major-mode) "-expand-alist")))
         (expand-alist (if (boundp expand-symbol) (symbol-value expand-symbol) nil))
         a)
    (when (and (looking-at "[ \t]*$")
               (string-match "^[ \t]*<\\([a-zA-Z]+\\)$" l)
               (setq a (assoc (match-string 1 l) expand-alist)))
      (delete-char (1+ (length (car-safe a))))
      (if (symbolp (cdr-safe a))
          (funcall (cdr-safe a))
        (insert (cdr-safe a)))
      t)
    ad-do-it))

Take emacs-lisp-mode as example, if I want to use <r and press TAB then yasnippet will expand the command, just add following code:

(setq emacs-lisp-mode-expand-alist '(("r" . "require")))

For c-mode, just do the same but change the relative major-mode-expand-alist like following

(setq c-mode-expand-alist '(("i" . "include")))

Programming Languages

bash

(use-package flymake-shell
  :ensure t
  :config (add-hook 'sh-set-shell-hook 'flymake-shell-load))

bitbake

(use-package bitbake
  :ensure t
  :mode ("\\.bb\\'" "\\.bbappend\\'"))

dts

(use-package dts-mode :ensure t
  :mode ("\\.dts\\'" "\\.dtsi\\'"))

dart

(use-package dart-mode
  :ensure t
  :mode ("\\.dart\\'")
  :config
  ;; enable analyzer support
  (setq dart-enable-analysis-server t)
  ;; add flycheck support
  (add-hook 'dart-mode-hook 'flycheck-mode))

gnuplot

A major mode for Emacs for interacting with Gnuplot

GitHub: https://github.com/bruceravel/gnuplot-mode

(use-package gnuplot :ensure t
  :commands gnuplot-mode
  :mode "\\.gp$")

graphviz dot

(use-package graphviz-dot-mode :ensure t
  :mode "\\.dot\\'"
  :config
  ;; alias `dot-mode' to graphviz-dot-mode
  (defalias 'dot-mode 'graphviz-dot-mode))

glsl

(use-package glsl-mode :ensure t
  :mode (("\\.vs\\'" . glsl-mode)
         ("\\.fs\\'" . glsl-mode)
         ("\\.gs\\'" . glsl-mode))
  :config
  (setq glsl-other-file-alist '(("\\.fs$" (".vs")) ("\\.vs$" (".fs")))))

javascript

(use-package js2-mode
  :ensure t
  :mode "\\.js\\'")
(use-package js2-refactor
  :ensure t)

Manage Node versions within Emacs

(use-package nvm :ensure t)

Packages we need to install in system

To make emacs work with javascript more nicer, we need to install some packages in system.

tern

tern for auto-completion and documentation features:

npm install -g tern

eslint

We also install eslint with babel and react support, to use eslint you need to add .eslintrc to your project, here’s a good example: .eslintrc .

npm install -g eslint babel-eslint eslint-plugin-react

jsxhint

npm install -g jsxhint

ref: http://umi-uyura.hatenablog.com/entry/2015/05/13/214629

js-beautify

Finally, install js-beautify to do automatic code formatting:

npm install -g js-beautify

Be sure to have the e4x option set to true on your .jsbeautifyrc here it is my configuration as an example:

{
  "indent_size": 2,
  "indent_char": " ",
  "eol": "\n",
  "indent_level": 0,
  "indent_with_tabs": false,
  "preserve_newlines": true,
  "max_preserve_newlines": 2,
  "jslint_happy": false,
  "space_after_anon_function": false,
  "brace_style": "collapse",
  "keep_array_indentation": false,
  "keep_function_indentation": false,
  "space_before_conditional": true,
  "break_chained_methods": true,
  "eval_code": false,
  "unescape_strings": false,
  "wrap_line_length": 80,
  "wrap_attributes": "auto",
  "wrap_attributes_indent_size": 2,
  "e4x": true,
  "end_with_newline": true
}

Simplify importing JS modules

import-js is a tool to automatically import dependencies in your JavaScript project. Use it in Vim or Emacs by placing your cursor on a variable and hit <leader>j (Vim), or (M-x) import-js-import (Emacs).

GitHub: https://github.com/trotzig/import-js

(use-package import-js :ensure t)

json

(use-package json-mode :ensure t
  :mode "\\.json\\'")
(use-package json-reformat :ensure t :commands json-reformat-region)
(use-package flymake-json :ensure t
  :config
  (add-hook 'json-mode-hook (lambda () (flymake-json-load))))

po

(use-package po-mode :ensure t
  :mode "\\.po\\'\\|\\.po\\."
  :config

  ;; To use the right coding system automatically under Emacs 20 or newer,
  ;; also add:
  (when (require 'po nil 'noerror)
    (modify-coding-system-alist 'file "\\.po\\'\\|\\.po\\."
                                'po-find-file-coding-system))
  )

python

(use-package python
  :mode (("SCons\\(truct\\|cript\\)\\'" . python-mode)
         ("DEPS" . python-mode)))

ruby

(use-package ruby-mode
  :ensure nil				; built-in
  :mode (("Gemfile\\'"  . ruby-mode)
         ("Kirkfile\\'" . ruby-mode)
         ("Rakefile\\'" . ruby-mode)
         ("Vagrantfile\\'" . ruby-mode)
         ("\\.builder\\'"  . ruby-mode)
         ("\\.gemspec\\'"  . ruby-mode)
         ("\\.irbrc\\'" . ruby-mode)
         ("\\.pryrc\\'" . ruby-mode)
         ("\\.rake\\'"  . ruby-mode)
         ("\\.rjs\\'"   . ruby-mode)
         ("\\.ru\\'"    . ruby-mode)
         ("\\.rxml\\'"  . ruby-mode))
  :config
  ;; We never want to edit Rubinius bytecode
  (add-to-list 'completion-ignored-extensions ".rbc"))

go

(use-package go-mode
  :ensure t
  :config
  <<go-mode-config>>)

Auto completion support

(use-package company-go
  :ensure t
  :config
  (defun my/setup-go-mode-company-go ()
    "Hook for running on company-go"
    ;; we only want to use company-go - it's so accurate we won't need
    ;; any other completion engines
    (set (make-local-variable 'company-backends) '(company-go)))
  (add-hook 'go-mode-hook 'my/setup-go-mode-company-go))

Use gofmt fotmat codes before save

(defun my/setup-go-mode-gofmt-hook ()
  ;; Use goimports instead of go-fmt
  (setq gofmt-command "goimports")
  ;; Call Gofmt before saving
  (add-hook 'before-save-hook 'gofmt-before-save))
(add-hook 'go-mode-hook 'my/setup-go-mode-gofmt-hook)

Setup Go Keybinding

(bind-keys :map go-mode-map
           ("M-." . godef-jump))

nxml

(use-package nxml-mode
  :ensure nil                  ; emacs built-in
  :mode (("\\.plist\\'" . nxml-mode)
         ("\\.rss\\'"   . nxml-mode)
         ("\\.svg\\'"   . nxml-mode)
         ("\\.xml\\'"   . nxml-mode)
         ("\\.xsd\\'"   . nxml-mode)
         ("\\.xslt\\'"  . nxml-mode)
         ("\\.pom$"     . nxml-mode))
  :config
  ;; Any file start with xml will be treat as nxml-mode
  (add-to-list 'magic-mode-alist '("<\\?xml" . nxml-mode))
  ;; Use nxml-mode instead of sgml, xml or html mode.
  (mapc
   (lambda (pair)
     (if (or (eq (cdr pair) 'xml-mode)
             (eq (cdr pair) 'sgml-mode))
         (setcdr pair 'nxml-mode)))
   auto-mode-alist))

LISP Development

Though LISP has many dialet, it still is the best programming language I ever met.

Plugins

Common setup for LISP development.

lispy

This package reimagines Paredit - a popular method to navigate and edit LISP code in Emacs.

GitHub: https://github.com/abo-abo/lispy

For more info to control lispy, please refer: http://oremacs.com/lispy/

(use-package lispy
  :ensure t
  :config

  (defun my/up-list (&optional arg)
    "My special lisp moving stragedy."
    (interactive)
    (or arg (setq arg -1))
    (condition-case ex
        (up-list arg)
      ('error (progn
                (lispy-backward arg)
                (beginning-of-line)
                (up-list arg)))))

  (defun my/down-list (&optional arg)
    "My special lisp moving stragedy."
    (interactive)
    (or arg (setq arg 1))
    (condition-case ex
        (down-list arg)
      ('error (progn
                (lispy-forward arg)
                (end-of-line)
                (down-list arg)))))

  ;; My special hack for lispy-mode
  (defun my/lispy-mode ()
    (lispy-mode 1)
    ;; `M-m' is preserved for mode setting
    (define-key lispy-mode-map (kbd "M-m") nil)
    ;; `M-s' is for my search command, rebind to `C-c s'
    (define-key lispy-mode-map (kbd "M-s") nil)
    (define-key lispy-mode-map (kbd "C-c s") 'lispy-splice)
    ;; `[' and `]' just insert them
    (define-key lispy-mode-map (kbd "[") 'lispy-open-square)
    (define-key lispy-mode-map (kbd "]") 'lispy-close-square)
    ;; My special lisp moving cmd
    (define-key lispy-mode-map (kbd "C-M-f") 'my/down-list)
    (define-key lispy-mode-map (kbd "C-M-b") 'my/up-list))

  (add-hook 'emacs-lisp-mode-hook #'my/lispy-mode)
  (add-hook 'lisp-interaction-mode-hook #'my/lispy-mode)
  (add-hook 'clojure-mode-hook #'my/lispy-mode)
  (add-hook 'scheme-mode-hook #'my/lispy-mode)
  (add-hook 'lisp-mode-hook #'my/lispy-mode))

indent-guide

(use-package indent-guide
  :ensure t
  :config
  (add-hook 'emacs-lisp-mode-hook #'indent-guide-mode)
  (add-hook 'lisp-interaction-mode-hook #'indent-guide-mode)
  (add-hook 'clojure-mode-hook #'indent-guide-mode)
  (add-hook 'scheme-mode-hook #'indent-guide-mode)
  (add-hook 'lisp-mode-hook #'indent-guide-mode))

Prettify symbols

Show lambda as λ.

(global-prettify-symbols-mode 1)

Web Development

(use-package web-mode
  :ensure t
  :mode (("\\.html?\\'" . web-mode)
         ("\\.ejs?\\'" . web-mode)))

CSS

(use-package css-mode
  :ensure nil				; built-in
  :mode "\\.css\\'")

Add support for eldoc

(use-package css-eldoc
  :ensure t
  :config
  (add-hook 'css-mode-hook 'turn-on-css-eldoc)
  (add-hook 'scss-mode-hook 'turn-on-css-eldoc)
  (add-hook 'less-css-mode-hook 'turn-on-css-eldoc))

Less

(use-package less-css-mode
  :ensure t
  :mode ("\\.less$" . less-css-mode))

SCSS

(use-package scss-mode
  :ensure t
  :mode "\\.scss\\'"
  :config
  ;; dont' build scss to css after save file
  (setq scss-compile-at-save nil))

mustache

Sometimes we will use mustache as template system, mustache-mode is a nice helper.

GitHub: https://github.com/mustache/emacs

(use-package mustache-mode :mode "\\.mustache$" :ensure t)

Use emmet-mode to add Zen Coding support

emmet-mode is a fork of zencoding-mode which add minor mode providing support for Zen Coding by producing HTML from CSS-like selectors.

GitHub: https://github.com/smihica/emmet-mode

(use-package emmet-mode :ensure t
  :config
  (progn
    ;; Following mode support emmet-mode
    (add-hook 'html-mode-hook 'emmet-mode)
    (add-hook 'sgml-mode-hook 'emmet-mode)
    (add-hook 'nxml-mode-hook 'emmet-mode)
    (add-hook 'css-mode-hook  'emmet-mode)

    ;; Move cursor between quotes after expand
    (add-hook 'emmt-mode-hook
              '(lambda()
                 (setq emmet-move-cursor-between-quotes t)))

    ;; Make tab can also expand emmt instead of use yasnippet directly
    (define-key emmt-mode-keymap (kbd "TAB") 'emmt-expand-yas)
    (define-key emmt-mode-keymap (kbd "<tab>") 'emmt-expand-yas)))

Terminal Emulator

ansi-term

Close ansi-term buffer after exit

After the ansi-term process ends it should leaves a buffer.

(defadvice term-handle-exit (after kill-buffer-after-exit activate)
  "Kill the term buffer if the process finished."
  (kill-buffer (current-buffer)))

Eshell

eshell is not really a system shell, it’s written in pure lisp. What I like is it fully integrated with emacs.

(use-package eshell
  :config
  ;; extra eshell configs
  <<eshell-config>>)

Use bash like prompt with color

;; Make eshell prompt look likes default bash prompt
(setq eshell-prompt-function
      '(lambda ()
         (concat
          user-login-name "@" system-name " "
          (if (search (directory-file-name (expand-file-name (getenv "HOME"))) (eshell/pwd))
              (replace-regexp-in-string (expand-file-name (getenv "HOME")) "~" (eshell/pwd))
            (eshell/pwd))
          (if (= (user-uid) 0) " # " " $ "))))
;; Add color for eshell prompt like Gentoo does
(defun colorfy-eshell-prompt ()
  (let* ((mpoint)
         (user-string-regexp (concat "^" user-login-name "@" (system-name))))
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward (concat user-string-regexp ".*[$#]") (point-max) t)
        (setq mpoint (point))
        (overlay-put (make-overlay (pos-bol) mpoint) 'face '(:foreground "dodger blue")))
      (goto-char (point-min))
      (while (re-search-forward user-string-regexp (point-max) t)
        (setq mpoint (point))
        (overlay-put (make-overlay (pos-bol) mpoint) 'face '(:foreground "green3"))))))
;; Make eshell prompt more colorful
(add-hook 'eshell-output-filter-functions 'colorfy-eshell-prompt)

Use ansi-term to render visual commands

(setq eshell-visual-commands
      '("less" "tmux" "htop" "top" "bash" "zsh" "fish" "ssh" "tail"
        "vi" "vim" "screen" "less" "more" "lynx" "ncftp" "pine" "tin"
        "nmtui" "alsamixer"))

(setq eshell-visual-subcommands
      '(("git" "log" "diff" "show")))

Add autojump command

Eshell Autojump is an autojump like command written in pure elisp, which add a j command to let you jump to folder you has been access.

GitHub: https://github.com/coldnew/eshell-autojump

(use-package eshell-autojump :ensure t)

Fish-like history autosuggestions in eshell

(use-package esh-autosuggest
  :hook (eshell-mode . esh-autosuggest-mode)
  :ensure t)

Eshell commands setup

..

(defun eshell/.. (&optional level)
  "Go up LEVEL directories"
  (interactive)
  (let ((level (or level 1)))
    (eshell/cd (make-string (1+ level) ?.))
    (eshell/ls)))

clear

(defun eshell/clear ()
  "Clears the shell buffer ala Unix's clear or DOS' cls"
  ;; the shell prompts are read-only, so clear that for the duration
  (let ((inhibit-read-only t))
    ;; simply delete the region
    (delete-region (point-min) (point-max))))

emacs

(defun eshell/emacs (&rest args)
  "Open a file in emacs. Some habits die hard."
  (if (null args)
      ;; If I just ran "emacs", I probably expect to be launching
      ;; Emacs, which is rather silly since I'm already in Emacs.
      ;; So just pretend to do what I ask.
      (bury-buffer)
    ;; We have to expand the file names or else naming a directory in an
    ;; argument causes later arguments to be looked for in that directory,
    ;; not the starting directory
    (mapc #'find-file (mapcar #'expand-file-name (flatten-tree (reverse args))))))

(defalias 'eshell/e 'eshell/emacs)

unpack

(defun eshell/unpack (file)
  (let ((command (cl-some (lambda (x)
                         (if (string-match-p (car x) file)
                             (cadr x)))
                       '((".*\.tar.bz2" "tar xjf")
                         (".*\.tar.gz" "tar xzf")
                         (".*\.bz2" "bunzip2")
                         (".*\.rar" "unrar x")
                         (".*\.gz" "gunzip")
                         (".*\.tar" "tar xf")
                         (".*\.tbz2" "tar xjf")
                         (".*\.tgz" "tar xzf")
                         (".*\.zip" "unzip")
                         (".*\.Z" "uncompress")
                         (".*" "echo 'Could not unpack the file:'")))))
    (eshell-command-result (concat command " " file))))

Setup Keybindings

;; FIXME: why this will still global-map ?
;; (bind-keys :map eshell-mode-map
;;            ("C-u" . eshell-kill-input))

(add-hook 'eshell-mode-hook
          (lambda ()
            (define-key eshell-mode-map (kbd "C-u") 'eshell-kill-input)))

Window Management

winner-mode

(use-package winner                     ; builtin
  :commands (winner-undo winner-redo)
  :config
  ;; I use my own keymap for winner-mode
  (setq winner-dont-bind-my-keys t)
  ;; Start winner-mode globally
  (winner-mode t))

eyebrowse

eyebrowse is a global minor mode for Emacs that allows you to manage your window configurations in a simple manner, just like tiling window managers like i3wm with their workspaces do. It displays their current state in the modeline by default. The behaviour is modeled after ranger, a file manager written in Python.

GitHub: https://github.com/wasamasa/eyebrowse

(use-package eyebrowse
  :ensure t
  :config
  ;; enable eyebrowse globally
  (eyebrowse-mode t))

window-numbering

Numbered window shortcuts for Emacs.

Enable window-numbering-mode and use M-1 through M-0 to navigate.

If you want to affect the numbers, use window-numbering-before-hook or window-numbering-assign-func. For instance, to always assign the calculator window the number 9, add the following to your .emacs:

(setq window-numbering-assign-func
      (lambda () (when (equal (buffer-name) "*Calculator*") 9)))

GitHub: https://github.com/nschum/window-numbering.el

(use-package window-numbering
  :ensure t)

Keybindings

Global Keybinding

(bind-keys :map global-map
           ("C-x C-s" . my/save-buffer-always))

Normal State

(evil-define-key 'normal my-editor-map
    (kbd "C-x C-f") 'helm-find-files
    (kbd "C-x C-q") 'read-only-mode
    (kbd "C-x M-1") 'deft-or-close
    (kbd "C-x M-2") 'eshell
    (kbd "C-x M-3") 'mu4e
    (kbd "C-x M-4") 'erc-start-or-switch
    (kbd "C-x vl") 'magit-log
    (kbd "C-x vp") 'magit-push
    (kbd "C-x vs") 'magit-status
    (kbd "C-x b") 'helm-buffers-list
    (kbd "M-[") 'winner-undo
    (kbd "M-]") 'winner-redo
    (kbd "M-x") 'helm-M-x
    (kbd "M-s") 'helm-occur
    (kbd "C-x C-o") 'other-frame
    (kbd "M-o") 'other-window)

Insert State

(evil-define-key 'insert my-editor-map
  (kbd "<delete>") 'hungry-delete-backward
  (kbd "C-;") 'iedit-mode
  (kbd "C-d") 'hungry-delete-forward
  (kbd "C-l") 'hungry-delete-backward
  (kbd "C-n") 'evil-next-line
  (kbd "C-o") 'evil-execute-in-normal-state
  (kbd "C-p") 'evil-previous-line
  (kbd "C-v") 'set-mark-mode/rectangle-mark-mode
  (kbd "C-w") 'backward-kill-word
  (kbd "C-x C-f") 'helm-find-files
  (kbd "C-x C-n") 'company-complete
  (kbd "C-x C-o") 'other-frame
  (kbd "C-x C-q") 'read-only-mode
  (kbd "C-x M-1") 'deft-or-close
  (kbd "C-x M-2") 'eshell
  (kbd "C-x M-3") 'mu4e
  (kbd "C-x M-4") 'erc-start-or-switch
  (kbd "C-x T") 'sane-term
  (kbd "C-x b") 'helm-buffers-list
  (kbd "C-x t") 'sane-term
  (kbd "C-x vl") 'magit-log
  (kbd "C-x vp") 'magit-push
  (kbd "C-x vs") 'magit-status
  (kbd "M-<SPC>") 'insert-U200B-char
  (kbd "M-[") 'winner-undo
  (kbd "M-]") 'winner-redo
  (kbd "M-s") 'helm-occur
  (kbd "M-v") 'er/expand-region
  (kbd "M-x") 'helm-M-x
  (kbd "M-y") 'helm-show-kill-ring
  (kbd "M-y") 'helm-show-kill-ring
  (kbd "M-z")   'zzz-to-char
  (kbd "s-<RET>") 'insert-empty-line
  (kbd "s-<SPC>") 'insert-U200B-char
  (kbd "C-x C-d") 'dired
  ;; (kbd "M-o") 'other-window
  ;; (kbd "TAB") 'yas/expand
  )

Space State

For space state, I use evil-leader to achive this goal.

(evil-leader/set-leader "<SPC>")
(evil-leader/set-key
  "1" 'select-window-1
  "2" 'select-window-2
  "3" 'select-window-3
  "4" 'select-window-4
  "5" 'select-window-5
  "6" 'select-window-6
  "7" 'select-window-7
  "8" 'select-window-8
  "9" 'select-window-9
  "0" 'select-window-0)

Ex Command

(evil-ex-define-cmd "ag" 'helm-ag)
(evil-ex-define-cmd "agi[nteractive]" 'helm-do-ag)
(evil-ex-define-cmd "google" 'helm-google)
(evil-ex-define-cmd "google-suggest" 'helm-google-suggest)
(evil-ex-define-cmd "gtag" 'ggtags-create-tags)
(evil-ex-define-cmd "howdoi" 'howdoi-query)

End of configuration

Oh YA!! We finish loading emacs configuration :)

In the end of configuration, I’ll load my private config from ~/.personal.el, which contains something like password or account settings.

(let ((secret "~/.personal.el"))
  (when (file-exists-p secret) (load-file secret)))