烟台炖土豆丝
- 切丝浸入冷水中
- 干辣椒蒜头切碎小火至略焦黄 (两三个蒜瓣对应四个土豆)
- 放入土豆丝八角(一到两个,不要更多)炒到变软
- 放入水,几滴醋,盖上小火焖一下,留点水
Enhance emacs-git-gutter with ivy-mode
CREATED:
UPDATED:
emacs-git-gutter shows an icon in the gutter area. The icon indicating whether a line has been inserted, modified or deleted in Emacs.
I usually use M-x git-gutter:previous-hunk
or M-x git-gutter:next-hunk
to navigate between the hunks.
But if there are too many hunks in one file, ivy-mode is more efficient:
(require 'ivy)
(require 'git-gutter)
(defun my-reshape-git-gutter (gutter)
"Re-shape gutter for `ivy-read'."
(let* ((linenum-start (aref gutter 3))
(linenum-end (aref gutter 4))
(target-line "")
(target-linenum 1)
(tmp-line "")
(max-line-length 0))
(save-excursion
(while (<= linenum-start linenum-end)
(goto-line linenum-start)
(setq tmp-line (replace-regexp-in-string "^[ \t]*" ""
(buffer-substring (line-beginning-position)
(line-end-position))))
(when (> (length tmp-line) max-line-length)
(setq target-linenum linenum-start)
(setq target-line tmp-line)
(setq max-line-length (length tmp-line)))
(setq linenum-start (1+ linenum-start))))
;; build (key . linenum-start)
(cons (format "%s %d: %s"
(if (eq 'deleted (aref gutter 1)) "-" "+")
target-linenum target-line)
target-linenum)))
(defun my-goto-git-gutter ()
(interactive)
(if git-gutter:diffinfos
(ivy-read "git-gutters:"
(mapcar 'my-reshape-git-gutter git-gutter:diffinfos)
:action (lambda (e)
;; ivy9+ keep `(car e)'
;; ivy8- strip the `(car e)'
;; we handle both data structure
(unless (numberp e) (setq e (cdr e)))
(goto-line e)))
(message "NO git-gutters!")))
Screenshot:
Turn off linum-mode when file is too big
CREATED:
UPDATED:
It's well known that linum-mode slows Emacs when the file contains thousands of lines.
Here is the fix,
(defun buffer-too-big-p ()
(or (> (buffer-size) (* 5000 80))
(> (line-number-at-pos (point-max)) 5000)))
(add-hook 'prog-mode-hook
(lambda ()
;; turn off `linum-mode' when there are more than 5000 lines
(if (buffer-too-big-p) (linum-mode -1))))
Though nlinum-mode has performance, I still stick to linum-mode because git-gutter only supports linum-mode.
You can check the interesting discussion about git-gutter/linum-mode/nlinum-mode. Syohei Yoshida made git-gutter 95% functional when linum-mode off.
How to manage Emacs packages effectively
Here are a few techniques I developed after reading Steve Purcell's setup.
The techniques are compatible with use-package because it uses Emacs API.
Mid-level Lisp knowledge is required to read this article.
Do NOT use package.el for certain packages
Create the directory ~/.emacs.d/site-lisp
. Then insert below code into ~/.emacs
,
(if (fboundp 'normal-top-level-add-to-load-path)
(let* ((my-lisp-dir "~/.emacs.d/site-lisp/")
(default-directory my-lisp-dir))
(progn
(setq load-path
(append
(loop for dir in (directory-files my-lisp-dir)
unless (string-match "^\\." dir)
collecting (expand-file-name dir))
load-path)))))
You can place a package's source code at sub-directory of ~/.emacs.d/site-lisp/
. That's all you need to do to install packages.
Create your own package repository
Step 1, Place two files "archive-contents" and "hello-1.0.0.el" in any directory. Say ~/.emacs.d/localelpa
.
Here is the content of archive-contents
:
(1
(hello . [(1 0 0) nil "Say hello" single])
)
Here is the content of hello-1.0.0.el
:
;;;###autoload
(defun hello-say ()
(interactive)
(message "Hi, hello!"))
(provide 'hello)
Step 2, insert below code into ~/.emacs
,
(add-to-list 'package-archives '("localelpa" . "~/.emacs.d/localelpa"))
Step 3, restart Emacs and run M-x list-packages
. As you can see, you can install package named "hello" now!
Here is a real world example how I apply this technique. I use rainbow-mode
from https://elpa.gnu.org/ which shuts down sometimes. So I built a local repository to host rainbow-mode
and a few other packages to remove dependency on GNU site.
I also create a elpa-mirror which creates a local repository from the installed packages. This local repository could be converted to remote repository using Dropbox and Github easily.
Orphan package issue is also resolved by elpa-mirror
. You can delete everything from ~/.emacs.d/elpa
and set the repository to the local repository created by elpa-mirror
. It only takes 30 seconds to install 300 packages.
Advice package--add-to-archive-contents
to filter packages
Insert below code into ~/.emacs,
;; List of VISIBLE packages from melpa-unstable (http://melpa.org)
;; Feel free to add more packages!
(defvar melpa-include-packages
'(bbdb
color-theme
company-c-headers)
"Don't install any mELPA packages except these packages")
(defvar package-filter-function nil
"Optional predicate function used to internally filter packages used by package.el.
The function is called with the arguments PACKAGE VERSION ARCHIVE, where
PACKAGE is a symbol, VERSION is a vector as produced by `version-to-list', and
ARCHIVE is the string name of the package archive.")
;; Don't take MELPA versions of certain packages
(setq package-filter-function
(lambda (package version archive)
(or (not (string-equal archive "melpa"))
;; install package in whitelist
(memq package melpa-include-packages)
;; use all color themes
(string-match (format "%s" package) "-theme"))))
(defadvice package--add-to-archive-contents
(around filter-packages (package archive) activate)
"Add filtering of available packages using `package-filter-function', if non-nil."
(when (or (null package-filter-function)
(funcall package-filter-function
(car package)
(funcall (if (fboundp 'package-desc-version)
'package--ac-desc-version
'package-desc-vers)
(cdr package))
archive))
ad-do-it))
The above code builds the filter defined in package-filter-function
to get the final version of packages list.
The filter accepts the package if it's NOT from melpa-unstable OR it's listed in melpa-include-packages
OR its name contains "-theme".
Surely you can build your own filter.
This solution is copied from Steve Purcell's setup with a little modification.
Summary
You can combine above techniques to solve any package issue.
For example, package A is dependent on package B. Both A and B have two versions, 1.0 and 2.0:
- A 2.0 can use B 1.0 and B 2.0, but A 1.0 can ONLY use B 1.0
- A 2.0 can ONLY use B 2.0, and A 1.0 can only use B 1.0
The solution is simple. We create a local repository to host B 1.0
and A 1.0
. As I said, package-filter-function
only returns a boolean expression. So you can design any strategy.
I know some one believs "Emacs package manager sucks" after mastering it for seven years. That's certainly not the truth as I have proved. I spent 15 minutes to reach the opposite conclusion when I was a Emacs dummy.
It's possibly I started my journey by learning from experts instead of "studiyng" by myself.
New git-timemachine UI based on ivy-mode
UPDATED:
CREATED:
When using git-timemachine, I prefer start from my selected revision instead of HEAD.
Here is my code based on ivy-mode,
(defun my-git-timemachine-show-selected-revision ()
"Show last (current) revision of file."
(interactive)
(let* ((collection (mapcar (lambda (rev)
;; re-shape list for the ivy-read
(cons (concat (substring-no-properties (nth 0 rev) 0 7) "|" (nth 5 rev) "|" (nth 6 rev)) rev))
(git-timemachine--revisions))))
(ivy-read "commits:"
collection
:action (lambda (rev)
;; compatible with ivy 9+ and ivy 8
(unless (string-match-p "^[a-z0-9]*$" (car rev))
(setq rev (cdr rev)))
(git-timemachine-show-revision rev)))))
(defun my-git-timemachine ()
"Open git snapshot with the selected version. Based on ivy-mode."
(interactive)
(unless (featurep 'git-timemachine)
(require 'git-timemachine))
(git-timemachine--start #'my-git-timemachine-show-selected-revision))
Screenshot after M-x my-git-timemachine
,
Complete line with ivy-mode
Complete current line by git grep and ivy-mode.
(defun counsel-escape (keyword)
(setq keyword (replace-regexp-in-string "\\$" "\\\\\$" keyword))
(replace-regexp-in-string "\"" "\\\\\"" keyword))
(defun counsel-replace-current-line (leading-spaces content)
(beginning-of-line)
(kill-line)
(insert (concat leading-spaces content))
(end-of-line))
(defun counsel-git-grep-complete-line ()
(interactive)
(let* (cmd
(cur-line (buffer-substring-no-properties (line-beginning-position)
(line-end-position)))
(default-directory (locate-dominating-file
default-directory ".git"))
keyword
(leading-spaces "")
collection)
(setq keyword (counsel-escape (if (region-active-p)
(buffer-substring-no-properties (region-beginning)
(region-end))
(replace-regexp-in-string "^[ \t]*" "" cur-line))))
;; grep lines without leading/trailing spaces
(setq cmd (format "git --no-pager grep -I -h --no-color -i -e \"^[ \\t]*%s\" | sed s\"\/^[ \\t]*\/\/\" | sed s\"\/[ \\t]*$\/\/\" | sort | uniq" keyword))
(when (setq collection (split-string (shell-command-to-string cmd) "\n" t))
(if (string-match "^\\([ \t]*\\)" cur-line)
(setq leading-spaces (match-string 1 cur-line)))
(cond
((= 1 (length collection))
(counsel-replace-current-line leading-spaces (car collection)))
((> (length collection) 1)
(ivy-read "lines:"
collection
:action (lambda (l)
(counsel-replace-current-line leading-spaces l))))))
))
(global-set-key (kbd "C-x C-l") 'counsel-git-grep-complete-line)
I also tried grep
which is too slow for my project.
Make Messages Buffer modifiable in Emacs 24.4
(defadvice switch-to-buffer (after switch-to-buffer-after-hack activate)
(if (string= "*Messages*" (buffer-name))
(read-only-mode -1)))
How to input Non-English character in evil-mode efficiently
You can M-x toggle-input-method
or C-\
to input Non-English characters.
Analysis about evil-mode,
- You only input Non-English characters in evil-insert-state. So you need go into evil-insert-state at first before toggle on input method
- In evil-insert-state, you can toggle off input method to input English characters while staying in evil-insert state
- When press
ESC
, you quit from evil-insert-state. But input method could be still activated. So when you re-enter evil-insert-state, you need notification of input method status
Here is the setup,
;; {{ make IME compatible with evil-mode
(defun evil-toggle-input-method ()
"when toggle on input method, goto evil-insert-state. "
(interactive)
;; load IME when needed, less memory footprint
;; (unless (featurep 'chinese-pyim)
;; (require 'chinese-pyim))
(cond
((and (boundp 'evil-mode) evil-mode)
;; evil-mode
(cond
((eq evil-state 'insert)
(toggle-input-method))
(t
(evil-insert-state)
(unless current-input-method
(toggle-input-method))
))
(if current-input-method (message "IME on!")))
(t
;; NOT evil-mode, some guy don't use evil-mode at all
(toggle-input-method))))
(defadvice evil-insert-state (around evil-insert-state-hack activate)
ad-do-it
(if current-input-method (message "IME on!")))
(global-set-key (kbd "C-\\") 'evil-toggle-input-method)
;; }}
Chinese version:
在evil-mode中切换输入法有以下要点,
- 输中文前须进入evil-insert-state
- 在evil-insert-state中可能会切换输入法
- 按ESC退出evil-insert-state时输入法可能还开着,所以再进入evil-insert-state时需提示输入法状态
代码见上.
Swiper/Ivy tip
Please install evil-escape at first.
When Swiper/Ivy candidate window pops up. You can press fd
quickly to close the window.
fd
is the default key binding from evil-escape. I changed it to kj
instead.
Use general.el to provide multiple leader key in evil-mode
General.el "provides a more convenient way to bind keys in emacs for both evil and non-evil users".
I have been using it for more than one month without any issue. I prefer general.el to evil-leader because I need both comma and space key as leader key.
Minimum setup is simple,
(require 'general)
(general-evil-setup t)
(nvmap :prefix ","
"bb" 'back-to-previous-buffer
"ww" 'save-buffer
"oo" 'compile)
;; all keywords arguments are still supported
(nvmap :prefix "SPC"
; save windows layout
"ss" 'wg-create-workgroup
;; load windows layout
"ll" 'my-wg-switch-workgroup)
My real world setup is more complicated.