This Emacs configuration file is written in literate programming
method [fn:1]. The emacs-lisp
code is extracted from this orgmode
[fn:2]
file, and then compiled to binary elc
file for fast loading in the
future.
First of all, you need to get emacs
and install it in your machine.
On Windows, we have three options: 1) MSYS2, 2) Cygwin, 3) WSL2.
If your msys2 [fn:3] is installed in C:\msys64
, you can open
C:\msys64\mingw64.exe
, and run following command to install Emacs:
pacman -S git mingw-w64-x86_64-emacs
Since 2020, I use MSYS2 MingW64 Emacs for most of the time, Cygwin Emacs is not actively tested now, but Emacs should work in Cygwin environment smoothly.
Cygwin project[fn:4] is a large collection of GNU and Open Source tools provide functionality similar to a Linux distribution on Windows.
You can download and install Cygwin setup [fn:5] in your machine, for
example, at C:\cygwin64
. Then you can use mintty.exe
to work with
bash
.
Then start emacs
from cygwin terminal, and right click the emacs
icon
to pin it to taskbar, then click the property of this icon to add
bellow to the target field. This way will hide the black terminal.
C:\cygwin64\bin\run.exe emacs-w32
You can also add emacs --daemon
in your scheduled task, and change the
target field as:
C:\cygwin64\bin\run.exe emacsclient -c
This will attach the client to the server daemon. You can get a
running emacs
very quick.
Nowadays, Windows is embracing Linux tightly. You can try emacs
on
Windows subsystem for Linux version 2. You can get Ubuntu Linux from
Windows App Store or Manjaro Linux for WSL2 [fn:6].
Commands to install Emacs is same as on Linux in next section.
Most of the external tools I used in this Emacs configuration should be easily installed or already available in main Linux distributions.
For example, in Ubuntu, you can install this way:
sudo snap install emacs --classic
It is also very quick to add emacs in ArchLinux/Manjaro:
pacman -S emacs
For Apple macOS, most UNIX tools are installed already. You can use homebrew [fn:7] to install additional application if it is missing.
/bin/bash -c \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew tap railwaycat/emacsmacport
brew install emacs-mac
You can follow below shell commands to prepare the emacs
configuration
files and folders:
# backup existing emacs config
cd ~ && mv .emacs .emacs-backup && mv .emacs.d .emacs.d-backup
# clone this config
git clone --recurse-submodules https://github.com/kimim/kimim-emacs
# copy default .emacs to ~
cp kimim-emacs/.emacs ~
This configuration uses several path to keep different information,
you need to define them in ~/.emacs
:
kimim/path-sync
is the root of sync folderkimim/path-emacs
to sync emacs settingskimim/path-org
to sync todo list and journalskimim/path-notes
to sync noteskimim/path-docs
to keep reference documentskimim/path-kimim-emacs
where kimim-emacs you clone to
Let’s tell emacs
, if the path is not set, report error, if the path is
set, but not exists, create it.
(mapc (lambda (path)
(if (not (boundp path))
(error (concat "please set " (symbol-name path) " in ~/.emacs"))
(if (not (file-exists-p (symbol-value path)))
(make-directory (symbol-value path)))))
'(kimim/path-sync
kimim/path-emacs
kimim/path-org
kimim/path-notes
kimim/path-docs
kimim/path-kimim-emacs))
Then you can execute emacs
to bootstrap itself.
Another way to try this configure out is to use chemacs2.
Clone to ~~/kimim-emacs~
git clone https://github.com/kimim/kimim-emacs ~/kimim-emacs
Add to chemacs2 profile file ~~/.emacs.profile.el~
(("default" . ((user-emacs-directory . "~/.emacs.default")))
("kimim" . ((user-emacs-directory . "~/kimim-emacs"))))
Start emacs with this config:
emacs --with-profile kimim
If you want use ~~/.emacs.kimim~ as the user-emacs-directory
, you need
to modify the value of variable kimim/path-kimim-emacs
in
early-init.el
.
(defvar kimim/path-kimim-emacs "~/.emacs.kimim/")
Environment variable PATH
is the the searching path of executables by
the shell running in Emacs while exec-path
is the search path of Emacs
itself. So we should set both of them to almost the same paths.
As I have a Windows box in the office, and an Apple macOS at home, so I need to specify these variables in different way.
(cond
((eq system-type 'cygwin)
(setq kimim/path-root "/"))
((eq system-type 'darwin)
(setq kimim/path-root "/")
(add-to-list 'exec-path "/Library/TeX/texbin"))
((eq system-type 'gnu/linux)
(setq kimim/path-root "/")
(add-to-list 'exec-path "/usr/local/texlive/2020/bin/x86_64-linux/")))
(add-to-list 'exec-path (concat kimim/path-root "bin"))
(add-to-list 'exec-path (concat kimim/path-root "usr/bin"))
(add-to-list 'exec-path (concat kimim/path-root "usr/local/bin"))
Then append exec-path to PATH:
(setenv "PATH"
(concat
(mapconcat #'identity exec-path path-separator)
(getenv "PATH")))
For Windows/MSYS64, we need to modify executable-find
to locate shell
scripts:
(defun executable-find (command &optional remote)
"Search for COMMAND in `exec-path' and return the absolute file name.
Return nil if COMMAND is not found anywhere in `exec-path'. If
REMOTE is non-nil, search on the remote host indicated by
`default-directory' instead."
(if (and remote (file-remote-p default-directory))
(let ((res (locate-file
command
(mapcar
(lambda (x) (concat (file-remote-p default-directory) x))
(exec-path))
exec-suffixes 'file-executable-p)))
(when (stringp res) (file-local-name res)))
;; Use 1 rather than file-executable-p to better match the
;; behavior of call-process.
(let ((default-directory (file-name-quote default-directory 'top)))
(locate-file command exec-path exec-suffixes))))
I prefer to use English/UTF-8 as default language environment.
(setenv "LANG" "en_US.UTF-8")
(setenv "LC_ALL" "en_US.UTF-8")
;; remove svn log LC_TYPE not defined warning.
(setenv "LC_CTYPE" "en_US.UTF-8")
(setenv "LC_TIME" "en_US.UTF-8")
(set-locale-environment "en_US.UTF-8")
(set-language-environment 'English)
(prefer-coding-system 'utf-8)
(set-buffer-file-coding-system 'utf-8-unix)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(set-file-name-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-clipboard-coding-system 'utf-8)
(cond
((member system-type '(windows-nt cygwin))
(set-clipboard-coding-system 'utf-16le)))
Define new command prefix for keys such as “C-x m f”, “C-x m v”.
(define-prefix-command 'ctl-x-m-map)
(global-set-key "\C-xm" 'ctl-x-m-map)
package
[fn:8] is the modern elisp
package management system, which
let you easily download and install packages that implement additional
features. Each package is a separate Emacs Lisp program, sometimes
including other components such as an Info manual.
All the extensions used in this file are installed and managed by
package
.
Here I use use-package
to defer the package loading and even
installation, When you use the :commands
keyword, it creates autoloads
for those commands and defers loading of the module until they are
used.
When I want to upgrade elpa packages, I just call list-packages
, and
let emacs refresh the package lists, then I just need to press U
to
upgrade all new packages.
If some weird things happen, for example compat-current-version
error,
a byte-force-recompile
in ~/.emacs.d
could work.
;; temporary disable signature check
(setq package-check-signature nil)
(setq package-user-dir "~/.emacs.d/elpa")
(setq package-archives
'(;;("elpa" . "https://elpa.gnu.org/packages/")
;;("melpa" . "https://melpa.org/packages/")
("gnu" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/")
("melpa" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/")))
(mapc
(lambda (package)
(unless (package-installed-p package)
(progn (message "installing %s" package)
(package-refresh-contents)
(package-install package))))
'(use-package diminish bind-key))
(require 'use-package)
(require 'diminish)
(require 'bind-key)
;; install package if missing
(setq use-package-always-ensure t)
(setq use-package-always-defer t)
(setq use-package-verbose t)
Don’t display menu-bar, tool-bar, tooltip and scroll-bar. Because sometimes, they may catch your attention.
(defun kimim/menu-and-bar ()
(cond
(window-system
;; Enable copy and paste in Win32
(setq select-enable-clipboard t)
(tool-bar-mode -1)
(tooltip-mode -1)
(scroll-bar-mode -1)
(when (or (eq window-system 'w32)
(eq window-system 'x))
(menu-bar-mode 0)))
((eq window-system nil)
(menu-bar-mode 0))))
Set default font and frame size for both window system. You should
set-default-font
first, otherwise, the frame height and width will be
calculated with original default font height and width:
frame-char-height
and frame-char-width
.
(use-package cnfonts
:bind (("C-+" . cnfonts-increase-fontsize)
("C--" . cnfonts-decrease-fontsize)
("C-=" . cnfonts-increase-fontsize)
("C-0" . cnfonts-reset-fontsize)))
(defun kimim/set-frame-alist (top left height width)
(let ((frame (selected-frame)))
(set-frame-position frame left top)
(set-frame-height frame height)
(set-frame-width frame width))
(setq default-frame-alist
`((top . ,top)
(left . ,left)
(height . ,height)
(width . ,width)
(vertical-scroll-bars)
(tool-bar-lines))))
(defun kimim/screen-width ()
(/ (display-pixel-width)
(frame-char-width)))
(defun kimim/screen-height ()
(/ (display-pixel-height)
(frame-char-height)))
(defun kimim/frame-and-font ()
(interactive)
(when window-system
(require 'cnfonts)
(cnfonts-enable)
(cnfonts-set-font)
;; top, left ... must be integer
(let* ((width (ceiling (* 0.8 (kimim/screen-width))))
(height (ceiling (* 0.8 (kimim/screen-height))))
(top (/ (display-pixel-height) 10))
(left (/ (display-pixel-width) 10)))
(kimim/menu-and-bar)
(kimim/set-frame-alist
top left height width))))
(defun kimim/frame-and-font-phone ()
(interactive)
(when window-system
(require 'cnfonts)
(cnfonts-enable)
(cnfonts-set-font)
;; top, left ... must be integer
(let* ((width (ceiling (* 0.36 (kimim/screen-width))))
(height (ceiling (* 0.9 (kimim/screen-height))))
(top (/ (display-pixel-height) 20))
(left (/ (* 34 (display-pixel-width)) 100)))
(kimim/menu-and-bar)
(kimim/set-frame-alist
top left height width))))
(defun kimim/frame-and-font-cast ()
(interactive)
(when window-system
(require 'cnfonts)
(cnfonts-enable)
(cnfonts-set-font)
;; top, left ... must be integer
(let* ((width (ceiling (* 0.36 (kimim/screen-width))))
(height (ceiling (* 0.34 (kimim/screen-height))))
(top (/ (display-pixel-height) 10))
(left (/ (* 33 (display-pixel-width)) 100)))
(kimim/set-frame-alist
top left height width))))
(defun kimim/frame-and-font-mini ()
(interactive)
;; top, left ... must be integer
(let* ((width (ceiling (* 0.8 (kimim/screen-width))))
(height (ceiling (* 0.4 (kimim/screen-height))))
(top (/ (display-pixel-height) 2))
(left (/ (display-pixel-width) 10))
(frame (selected-frame)))
(kimim/menu-and-bar)
(kimim/set-frame-alist
top left height width)))
(defun kimim/new-mini-frame-bottom ()
"Create a small frame at bottom for write note."
(interactive)
;; after-make-frame-hook make this no work, maybe change that hook
(let ((frm (make-frame
`((height . ,(ceiling (* 0.4 (kimim/screen-height))))
(width . ,(ceiling (* 0.8 (kimim/screen-width))))))))
(set-frame-position
frm
(/ (display-pixel-width) 10)
(/ (display-pixel-height) 2))))
(kimim/frame-and-font)
Customize the frame title to display buffer file name.
(setq frame-title-format
'((:eval (buffer-name))))
;; don't expand minibuffer in other frame
(setq minibuffer-follows-selected-frame nil)
Display date and time, but do not display system load.
(use-package time
:ensure nil
:defer 0
:custom
(display-time-24hr-format t)
(display-time-day-and-date t)
(display-time-interval 10)
(display-time-default-load-average nil)
:config
(display-time-mode t))
Show (line, column) numbers in mode line:
(use-package simple
:ensure nil
:defer 3
:bind
;; cycling from one space, zero space and original space
("M-SPC" . cycle-spacing)
:custom
;; put pastebin content to kill ring before kill others
(save-interprogram-paste-before-kill t)
:config
(line-number-mode 1)
(column-number-mode 1)
(toggle-word-wrap -1))
Zwei Dinge erfüllen das Gemüt mit immer neuer und zunehmender Bewunderung und Ehrfurcht, je öfter und anhaltender sich das Nachdenken damit beschäftigt: Der bestirnte Himmel über mir, und das moralische Gesetz in mir.
– Immanuel Kant
(use-package celestial-mode-line
:defer 1
:custom (celestial-mode-line-update-interval 300)
:config
;; use smaller symbols, the original one expand the modeline
(setq celestial-mode-line-sunrise-sunset-alist
'((sunrise . "☼ꜛ") (sunset . "☼ꜜ")))
(setq celestial-mode-line-phase-representation-alist
'((0 . "●") (1 . ">") (2 . "○") (3 . "<")))
(setq global-mode-string '("" celestial-mode-line-string
" " display-time-string "")))
Add sunrise/sunset info to modeline text description.
(use-package celestial-mode-line
:config
(defun kimim/celestial-mode-line--sunrise-sunset-string (time)
(cl-destructuring-bind (sun-time location)
time
(let* ((h (truncate sun-time))
(m (truncate (* 60 (- sun-time h)))))
(concat (assoc-default
(if (< h 12)
'sunrise
'sunset)
celestial-mode-line-sunrise-sunset-alist
'equal celestial-mode-line-polar-representation)
(format "%d:%02d" h m)))))
(defun kimim/celestial-mode-line--sun-description (orig-fun &rest args)
(concat
(kimim/celestial-mode-line--sunrise-sunset-string
(car (solar-sunrise-sunset (car args))))
" "
(kimim/celestial-mode-line--sunrise-sunset-string
(cadr (solar-sunrise-sunset (car args))))
", "
(apply orig-fun args)))
;; add sun rise/set info to modeline text description
(advice-add 'celestial-mode-line--text-description
:around
#'kimim/celestial-mode-line--sun-description)
(celestial-mode-line-start-timer))
awesome-tray
adds an overlay to minibuffer and hides mode line to save
window space.
(use-package awesome-tray
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/awesome-tray"))
:commands (awesome-tray-mode)
:custom
(awesome-tray-date-format "%-m-%-e %H:%M")
(awesome-tray-active-modules '("changed" "org-pomodoro"
"location-or-page" "belong"
"celestial" "date" "input-method"))
(awesome-tray-mode-line-default-height 0.85)
(awesome-tray-location-info-top "↑")
(awesome-tray-location-info-bottom "↓")
(awesome-tray-input-method-local-style "Ѱ")
:custom-face
(awesome-tray-grey-face
((((background light)) :foreground "dim grey" :bold nil)
(t :foreground "dark grey" :bold nil)))
(awesome-tray-module-buffer-name-face
((t :inherit awesome-tray-grey-face)))
(awesome-tray-module-location-or-page-face
((t :inherit awesome-tray-grey-face)))
(awesome-tray-module-git-face
((t :inherit awesome-tray-grey-face)))
(awesome-tray-module-pdf-view-page-face
((t :inherit awesome-tray-grey-face)))
(awesome-tray-module-input-method-face
((t :inherit awesome-tray-grey-face)))
(org-mode-line-clock
((t :inherit unspecified)))
(org-mode-line-clock-overrun
((t :inherit unspecified)))
:config
(advice-add
'awesome-tray-enable
:before
(lambda ()
(setq awesome-tray-mode-line-colors nil)
(setq awesome-tray-mode-line-active-color
(face-attribute 'mode-line :background))
(setq awesome-tray-mode-line-inactive-color
(face-attribute 'default :background))))
(advice-add
'awesome-tray-enable
:after
(lambda ()
(set-face-attribute
'mode-line-inactive nil
:underline (face-attribute 'mode-line :foreground))))
(advice-add
'awesome-tray-disable
:after
(lambda ()
(set-face-attribute 'mode-line-inactive nil
:underline 'unspecified)))
(defun kimim/awesome-tray-module-buffer-changed-info ()
(if (and (buffer-modified-p)
(not (eq buffer-file-name nil)))
"✶"))
(add-to-list
'awesome-tray-module-alist
'("changed" . (kimim/awesome-tray-module-buffer-changed-info
awesome-tray-grey-face)))
(defvar kimim/awesome-tray-original-modules nil)
(defun kimim/awesome-tray-minimalistic ()
(interactive)
(if kimim/awesome-tray-original-modules
(progn
(message "restore awesome tray modules")
(setq awesome-tray-active-modules
kimim/awesome-tray-original-modules)
(setq kimim/awesome-tray-original-modules nil))
(progn
(message "awesome tray minimalistic")
(setq kimim/awesome-tray-original-modules
awesome-tray-active-modules)
(setq awesome-tray-active-modules
'("buffer-name" "changed" "location-or-page"
"date" "input-method"))))))
When minimalistic emacs is on with awesome tray, we sometimes need to lookup buffer file name with header line.
(use-package path-headerline-mode
:bind ("C-x /" . kimim/toggle-path-header)
:config
(defun kimim/toggle-path-header ()
"Toggle display path header"
(interactive)
(if header-line-format
(path-header-line-off)
(path-header-line-on))))
Use rainbow-mode
to edit colorful color string and symbol. In the
beginning, I add :hook prog-mode
, that means to enable rainbow-mode
for all programming mode, but later, I find that #def
part of #define
in C is changed to gray color. Then I remove the this hook. So I will
turn on rainbow-mode
manually, if I want to see the color.
(use-package rainbow-mode
:diminish rainbow-mode)
Rainbow-delimiters is a “rainbow parentheses”-like mode which highlights parentheses, brackets, and braces according to their depth.
(use-package rainbow-delimiters
:diminish rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
Toggle Font-Lock mode in all buffers.
(use-package font-lock
:ensure nil
:custom ((font-lock-maximum-decoration t)
(font-lock-global-modes '(not shell-mode text-mode))
(font-lock-verbose t))
:config
(global-font-lock-mode 1))
Use kimim-light as default theme.
(use-package hippo-themes
:ensure nil
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/emacs-hippo-theme"))
:custom (;; do not warning when load new theme
(custom-safe-themes t)))
Always set white background color for transparent images to display clearly in dark color theme.
(use-package image
:ensure nil
:config
(setq auto-mode-alist
(append '(("\\.svg\\'" . image-mode))
auto-mode-alist))
(defun create-image-advice (im)
(when im
;; only add property when im is non-nil
(nconc im (list :background "#f8f8f8"))))
(advice-add 'create-image :filter-return #'create-image-advice))
(use-package posframe)
(setq inhibit-startup-message t)
(setq initial-scratch-message nil)
(setq visible-bell t)
(setq ring-bell-function #'ignore)
(fset 'yes-or-no-p 'y-or-n-p)
(show-paren-mode 1)
(setq blink-cursor-blinks 3)
(blink-cursor-mode 1)
(tooltip-mode -1)
;; mark highlight in other windows also
(setq highlight-nonselected-windows nil)
(use-package hi-lock
:custom (hi-lock-auto-select-face t))
In Windows environment, kimim/xterm
and kimim/dc
will look up the program
from system PATH, so you should set these to system PATH.
(use-package kimim
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp"))
:ensure nil
:commands (kimim/mail-setup
kimim/open-external
kimim/open-external-pdf)
:defines (mac-option-modifier
mac-command-modifier)
:bind
(("<f9>" . kimim/xterm)
("S-<f9>" . kimim/cmd)
;; when deploy clojars, need msys2 term to use pinetry
("C-<f9>" . kimim/msys2-term)
("C-c r" . kimim/rename-file-and-buffer)
("C-x m o" . kimim/open-external)
("C-x m O" . kimim/open-external-pdf)
("C-x m d" . kimim/dc)
("C-c d" . kimim/lookinsight)
("<f1>" . delete-other-windows)
("C-<f1>" . nuke-other-buffers)
("M-<f1>" . kimim/switch-to-scratch-and-nuke-others)
("<f2>" . other-window)
("<f5>" . kimim/switch-to-scratch-buffer)
("C-<f5>" . kimim/switch-to-scratch-other-window)
("<f7>" . bury-buffer)
("<f8>" . unbury-buffer)
("C-h" . delete-backward-char)
("M-h" . backward-kill-word)
("M-?" . mark-paragraph)
("C-x k" . kill-current-buffer)
("C-x C-v" . view-file-other-window)
("C-c C-o" . occur)
("C-z" . set-mark-command)
("C-x C-z" . pop-to-mark-command)
("C-c C-/" . comment-or-uncomment-region)
("RET" . newline-and-indent)
("C-x m h" . help)
("C-x ," . bury-buffer)
("C-x ." . unbury-buffer)
("C-x ," . bury-buffer)
("C-x 。" . unbury-buffer)
("C-x ‘" . hippie-expand)
("C-x ’" . hippie-expand)
("C-x M-s" . kimim/save-buffer-no-tws)
("C-x m 0" . kimim/frame-and-font)
("C-x m -" . kimim/frame-and-font-mini)
("C-x m SPC" . kimim/shrink-down)
("C-x m ]" . kimim/shrink-right)
("C-x m [" . kimim/shrink-left)
("C-x m ^" . kimim/shrink-up)
("C-x m '" . kimim/top-right-mouse)
("C-x m r" . kimim/rename-file-and-buffer)
("C-<pause>" . iconify-frame)
("C-x S" . eshell)
("C-x M-q" . kimim/unfill-paragraph-or-region)
("C-x 7 `" . (lambda() (interactive) (insert "à")))
("C-x 7 a" . (lambda() (interactive) (insert "ā")))
("C-x 7 A" . (lambda() (interactive) (insert "Ā")))
("C-x 7 e" . (lambda() (interactive) (insert "ē")))
("C-x 7 E" . (lambda() (interactive) (insert "Ē")))
("C-x 7 i" . (lambda() (interactive) (insert "ī")))
("C-x 7 I" . (lambda() (interactive) (insert "Ī")))
("C-x 7 o" . (lambda() (interactive) (insert "ō")))
("C-x 7 O" . (lambda() (interactive) (insert "Ō")))
("C-x 7 u" . (lambda() (interactive) (insert "ū")))
("C-x 7 U" . (lambda() (interactive) (insert "Ū"))))
:config
(unbind-key "C-x C-z")
(when (eq system-type 'darwin) ;; mac specific settings
(setq mac-option-modifier 'meta)
(setq mac-command-modifier 'super)
;; sets fn-delete to be right-delete
(global-set-key [kp-delete] 'delete-char)
;; get back hide other key
(bind-key "M-s-h" (lambda () (interactive)
(mac-send-action 'hideOtherApplications))))
(defun kimim/save-buffer-no-tws (&optional arg)
(interactive "p")
(delete-trailing-whitespace)
(save-buffer arg))
(defun kimim/switch-to-scratch-and-nuke-others ()
(interactive)
(switch-to-buffer "*scratch*") (nuke-other-buffers))
(defun kimim/switch-to-scratch-buffer ()
(interactive)
(switch-to-buffer "*scratch*")
(delete-other-windows))
(defun kimim/switch-to-scratch-other-window ()
(interactive)
(switch-to-buffer-other-window "*scratch*"))
(defun kimim/restore-text-scale ()
(interactive)
(text-scale-increase 0)))
(use-package info
:commands (info)
:hook (Info-mode . (lambda ()
(setq line-spacing 0.5)))
:config
(add-to-list 'Info-additional-directory-list
(concat kimim/path-root "usr/share/info"))
(add-to-list 'Info-additional-directory-list
(concat kimim/path-root "usr/local/share/info"))
(add-to-list 'Info-additional-directory-list
(concat kimim/path-root "ucrt64/share/info"))
;; additional info, collected from internet
(add-to-list 'Info-additional-directory-list
"~/info"))
Search dictionary with Ctrl+F3 by youdao dictionary.
(use-package youdao-dictionary
:bind (
("C-x m 1" . youdao-dictionary-search-at-point-posframe)
("C-x m 2" . youdao-dictionary-search)
:map youdao-dictionary-mode-map
("C-y" . youdao-dictionary-search-yanked)
("<mouse-3>" . youdao-dictionary-def-copied)
("f" . youdao-dictionary-search-from-input))
:config
;; redefine youdao-format-result
(defun youdao-dictionary--format-result (json)
"Format result in JSON."
(let* ((query (assoc-default 'query json)) ; string
(translation (assoc-default 'translation json)) ; array
(_errorCode (assoc-default 'errorCode json)) ; number
(web (assoc-default 'web json)) ; array
(basic (assoc-default 'basic json)) ; alist
;; construct data for display
(phonetic (assoc-default 'phonetic basic))
(translation-str (mapconcat
(lambda (trans) (concat "- " trans))
translation "\n"))
(basic-explains-str (mapconcat
(lambda (explain) (concat "- " explain))
(assoc-default 'explains basic) "\n"))
(web-str (mapconcat
(lambda (k-v)
(format "- %s :: %s"
(assoc-default 'key k-v)
(mapconcat 'identity (assoc-default 'value k-v) "; ")))
web "\n")))
(let ((result-str
(if basic
(format "%s [%s]\n\nBasic Explains\n%s\n\nWeb References\n%s\n"
query phonetic basic-explains-str web-str)
(format "%s\n\nTranslation\n%s\n"
query translation-str))))
(kill-new
(mapconcat
(lambda (x)
(concat x "\n"))
(cdr (split-string result-str "\n"))))
(kill-new (format "%s [%s]" query phonetic))
result-str)))
(defun youdao-dictionary-def-copied ()
(interactive)
(youdao-dictionary-search (gui-get-selection)))
(defun youdao-dictionary-search-yanked ()
(interactive)
(youdao-dictionary-search
(if (gui-get-selection)
(gui-get-selection)
(car kill-ring)))))
A local dictionary tool is useful when network connection has problems. You can get dictionary files from lazycat-emacs, or huzheng.org.
(use-package sdcv
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/sdcv"))
:bind
("C-M-z" . sdcv-search-input)
("C-x m ," . sdcv-search-pointer)
("C-`" . sdcv-search-pointer+)
:config
(require 'sdcv)
(setq sdcv-program "sdcv")
(setq sdcv-say-word-p t)
;; set your local dictionaries path
(setq sdcv-dictionary-data-dir (file-truename
(concat kimim/path-kimim-emacs
"site-lisp/sdcv-dict")))
(setq sdcv-dictionary-simple-list
'("lazyworm-zh-en"
"lazyworm-en-zh"))
(setq sdcv-dictionary-complete-list
'("oxford"
"lazyworm-en-zh"
"lazyworm-zh-en"
"xdict-en-zh"
"xdict-zh-en"
"stardict-en-zh"
"WordNet"
"Jargon"
"langdao-zh-en"
"cdict-en-zh"
"georges-de-lat"
"georges-lat-de"
"etymonline"))
(defun kimim/sdcv-call-process-advice (orig-fun &rest args)
(let ((default-process-coding-system '(utf-8 . utf-8)))
(apply orig-fun args)))
(advice-add 'sdcv-call-process
:around
#'kimim/sdcv-call-process-advice))
Need to install mpg123
or mpv
to play the pronunciations.
pacman -S mingw-w64-ucrt-x86_64-mpg123
A translator extension written by Youmu.
I added kimim/fanyi-dwim-add-sdcv
as :after
advice for fanyi-dwim
to
look up local dictionary with help from sdcv
.
(use-package fanyi
:ensure t
:bind
(("C-x m ." . kimim/fanyi-dwim)
("C-x m y" . kimim/fanyi-current-kill))
:custom-face
(fanyi-word-face
((t :height 1.1 :weight bold :foreground "dark cyan")))
:custom
(fanyi-providers '(;; 海词
fanyi-haici-provider
;; 有道同义词词典
fanyi-youdao-thesaurus-provider
;; Etymonline
fanyi-etymon-provider
;; Longman
fanyi-longman-provider))
:bind (:map fanyi-mode-map
("n" . outline-next-visible-heading)
("p" . outline-previous-visible-heading)
("o" . other-window)
("<mouse-3>" . quit-window))
:config
(require 'sdcv)
(defun kimim/fanyi-dwim ()
(interactive)
(if-let ((word (thing-at-point 'word)))
(progn
(fanyi-dwim word)
(cl-pushnew word fanyi-history))
(call-interactively #'fanyi-dwim)))
(defun kimim/fanyi-current-kill ()
(interactive)
(fanyi-dwim (current-kill 0)))
(defun kimim/sdcv-translate-result-advice (word dictionary-list)
(let* ((arguments
(cons word
(mapcan
(lambda (d) (list "-u" d)) dictionary-list)))
(result (mapconcat
(lambda (result)
(let-alist result
(format
"## %s\n%s\n\n" .dict .definition)))
(apply #'sdcv-call-process arguments)
"")))
(if (string-empty-p result)
sdcv-fail-notify-string
result)))
(advice-add 'sdcv-translate-result
:override
#'kimim/sdcv-translate-result-advice)
(defun kimim/fanyi-dwim-add-sdcv (word)
(let ((buf (get-buffer fanyi-buffer-name)))
(with-current-buffer buf
(let ((inhibit-read-only t)
(inhibit-point-motion-hooks t))
;; Clear the previous search result.
(point-max)
(insert "# SDCV\n\n")
(insert
(sdcv-search-with-dictionary
word sdcv-dictionary-complete-list))
(insert "\n\n")
(beginning-of-buffer)
;;(window-resize nil (- 35 (window-size nil t)) t)
))))
(advice-add 'fanyi-dwim :after
#'kimim/fanyi-dwim-add-sdcv))
(use-package wordreference
:commands (wordreference-search
kimim/wr-fren
kimim/wr-enfr
kimim/wr-deen
kimim/wr-ende
kimim/wr-sven
kimim/wr-ensv)
:config
(defun kimim/wr-ende ()
(interactive)
(let ((wordreference-source-lang "en")
(wordreference-target-lang "de"))
(wordreference-search)))
(defun kimim/wr-ensv ()
(interactive)
(let ((wordreference-source-lang "en")
(wordreference-target-lang "sv"))
(wordreference-search)))
(defun kimim/wr-enfr ()
(interactive)
(let ((wordreference-source-lang "en")
(wordreference-target-lang "fr"))
(wordreference-search)))
(defun kimim/wr-deen ()
(interactive)
(let ((wordreference-source-lang "de")
(wordreference-target-lang "en"))
(wordreference-search)))
(defun kimim/wr-sven ()
(interactive)
(let ((wordreference-source-lang "sv")
(wordreference-target-lang "en"))
(wordreference-search)))
(defun kimim/wr-fren ()
(interactive)
(let ((wordreference-source-lang "fr")
(wordreference-target-lang "en"))
(wordreference-search))))
Translate word between English and German.
(use-package leo
:bind
(:map leo-mode-map
("s" . leo-translate-word))
:custom-face
(leo-auxiliary-face
((t :inherit default)))
(leo-match-face
((t :inherit font-lock-keyword-face)))
(leo-link-face
((t :inherit font-lock-keyword-face
:foreground "tomato"))))
Translate between almost any languages.
(use-package reverso
:commands (reverso
reverso-translate))
Show eldoc in a childframe box.
(use-package eldoc-box
:diminish eldoc-box-hover-mode
;;:hook (eglot-managed-mode . eldoc-box-hover-mode)
:custom ((eldoc-box-offset '(10 40 10))
(eldoc-box-max-pixel-width 1200)
(eldoc-box-max-pixel-height 800)))
Increase right margin from default 16 to 40 to remove some overlap.
(use-package rime
:bind
(:map
rime-mode-map
("C-|" . rime-force-enable)
:map
rime-active-mode-map
("C-h" . rime--backspace)
("M-h" . rime--backspace))
:custom
(default-input-method "rime")
(rime-title "Ѱ")
(rime-disable-predicates
'(kimim/rime-predicate-p))
(rime-show-candidate 'posframe)
(rime-posframe-properties
(list :internal-border-width 14))
:config
(when (eq (window-system) 'mac)
(setq rime-librime-root "~/.emacs.d/librime/dist"))
(defun kimim/rime-predicate-space-after-ascii-p ()
"If cursor is after only ONE whitespace which follow a ascii character."
(and (> (point) (save-excursion (back-to-indentation) (point)))
(let ((string (buffer-substring
(point)
(max (line-beginning-position) (- (point) 80)))))
(and (string-match-p " $" string)
;; -+* bullet items
(not (string-match-p "[\x2a\x2b\x2d] $" string))
(not (string-match-p "\\cc +$" string))
(not (string-match-p " $" string))))))
(defun kimim/rime-predicate-current-input-punctuation-p ()
"If the current charactor entered is a punctuation."
(and rime--current-input-key
(or (and (<= #x21 rime--current-input-key)
(<= rime--current-input-key #x2c))
(= rime--current-input-key #x2e)
(and (<= #x3a rime--current-input-key)
(<= rime--current-input-key #x40))
(and (<= #x5b rime--current-input-key)
(<= rime--current-input-key #x5e))
(and (<= #x7b rime--current-input-key)
(<= rime--current-input-key #x7f)))))
(defun kimim/rime-predicate-p ()
"Always use rime punctuation."
(if (kimim/rime-predicate-current-input-punctuation-p)
nil
(or (rime-predicate-current-uppercase-letter-p)
(rime-predicate-space-after-cc-p)
(rime-predicate-after-ascii-char-p)
(kimim/rime-predicate-space-after-ascii-p)))))
Emacs smart input source (sis) can automatically switch input method for buffers and contexts.
When sis is enabled, C-x C-c
a emacs client frame will crash, so, add
an advice to disable sis when save-buffers-kill-terminal
.
By the way, you need to use C-<space>
to toggle between IME and
NonIME, otherwise, sis
will bring IME back after some keycords.
(use-package sis
:functions (kimim/sis-enable
kimim/sis-disable)
:hook (server-after-make-frame . kimim/sis-enable)
:config
(set-face-attribute
'sis-inline-face nil
:foreground (face-attribute 'font-lock-constant-face :foreground)
:underline t
:inverse-video 'unspecified)
(setq sis-other-cursor-color "tomato")
(cond ((eq system-type 'windows-nt)
(sis-ism-lazyman-config nil t 'w32))
((eq system-type 'darwin)
(sis-ism-lazyman-config
"com.apple.keylayout.ABC"
"im.rime.inputmethod.Squirrel.Hans")))
(defun kimim/sis-enable ()
;; enable the /cursor color/ mode
(sis-global-cursor-color-mode t)
;; enable the /respect/ mode
(sis-global-respect-mode t)
;; enable the /context/ mode for all buffers
(sis-global-context-mode t)
;; enable the /inline english/ mode for all buffers
(sis-global-inline-mode t))
(defun kimim/sis-disable ()
(sis-global-cursor-color-mode -1)
(sis-global-respect-mode -1)
(sis-global-context-mode -1)
(sis-global-inline-mode -1))
(advice-add 'save-buffers-kill-terminal
:before
(lambda (&optional arg)
(if (eq 1 (length server-clients))
(kimim/sis-disable))))
(add-hook 'server-after-make-frame-hook 'kimim/sis-enable))
(use-package autorevert
:ensure nil
:diminish auto-revert-mode)
(setq inhibit-eol-conversion nil)
(setq-default fill-column 70)
;; see discussion https://emacs-china.org/t/topic/2616/30
(setq word-wrap-by-category t)
(setq require-final-newline t)
(use-package drag-stuff
:diminish drag-stuff-mode
:config
(drag-stuff-global-mode 1))
(delete-selection-mode 1)
(setq kill-ring-max 200)
(setq kill-whole-line t)
(setq-default tab-width 4)
(setq tab-stop-list
(number-sequence 4 120 4))
;; stretch to tab width when on tab
(setq x-stretch-cursor t)
(setq track-eol t)
(setq backup-directory-alist '(("." . "~/temp")))
(setq version-control t)
(setq kept-old-versions 10)
(setq kept-new-versions 20)
(setq delete-old-versions t)
(setq backup-by-copying t)
(setq auto-save-interval 50)
(setq auto-save-timeout 60)
(setq auto-save-default nil)
(setq auto-save-list-file-prefix "~/temp/auto-saves-")
(setq auto-save-file-name-transforms `((".*" , "~/temp/")))
(setq create-lockfiles nil)
(use-package time-stamp
:config
(setq time-stamp-active t)
(setq time-stamp-warn-inactive t)
(setq time-stamp-format "%:y-%02m-%02d %3a %02H:%02M:%02S Kimi MA")
(add-hook 'write-file-functions 'time-stamp))
(defun kimim/save-buffer-advice (orig-fun &rest arg)
(when (not (memq major-mode '(org-mode markdown-mode text-mode)))
(delete-trailing-whitespace))
(apply orig-fun arg))
(advice-add 'save-buffer :around #'kimim/save-buffer-advice)
(setq save-silently t)
(diminish 'visual-line-mode)
(add-hook 'text-mode-hook
(lambda ()
(when (derived-mode-p 'org-mode 'markdown-mode
'text-mode 'info-mode)
(visual-line-mode)
(setq line-spacing 0.4))))
(setq-default indent-tabs-mode nil)
(setq uniquify-buffer-name-style 'forward)
(setq suggest-key-bindings 5)
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-mode))
(add-to-list 'auto-mode-alist '("\\.S\\'" . asm-mode))
(add-to-list 'auto-mode-alist '("\\.pas\\'" . delphi-mode))
(require 'saveplace)
(setq-default save-place t)
(setq save-place-file (expand-file-name "saveplace" "~"))
You can mark a region, and C-S-c C-S-c
to start edit every line in this
region. That’s amazing.
(use-package multiple-cursors
:bind
("C-S-c C-S-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C-<" . mc/mark-all-like-this)
("C-c C->" . mc/mark-all-dwim))
Highlight current line in window systems, but disable this in terminal. Because the line highlight will cause the terminal blinking.
(use-package hl-line
:if window-system
:config
(global-hl-line-mode -1))
Frequently, you may lose your point in multiple windows. pulsar
can
show a transient highlight line for you to find your point after
specified functions.
(use-package pulsar
:defer 5
:custom
(pulsar-pulse t)
(pulsar-face 'pulsar-green)
(pulsar-highlight-face 'pulsar-yellow)
:config
(setq pulsar-pulse-functions
'(recenter-top-bottom
move-to-window-line-top-bottom
reposition-window
bookmark-jump
consult-bookmark
other-window
delete-window
delete-other-windows
forward-page
backward-page
scroll-up-command
scroll-down-command
windmove-right
windmove-left
windmove-up
windmove-down
windmove-swap-states-right
windmove-swap-states-left
windmove-swap-states-up
windmove-swap-states-down
org-next-visible-heading
org-previous-visible-heading
org-forward-heading-same-level
org-backward-heading-same-level
org-agenda-goto
markdown-next-visible-heading
markdown-previous-visible-heading
markdown-forward-heading-same-level
markdown-backward-heading-same-level
markdown-outline-next-same-level
markdown-outline-previous-same-level
outline-backward-same-level
outline-forward-same-level
outline-next-visible-heading
outline-previous-visible-heading
outline-up-heading
ace-window
xref-find-definitions
xref-go-back))
(advice-add 'switch-to-buffer :after
(lambda (_ &optional _ _)
(pulsar-pulse-line)))
;; turn on visual line, pulsar will high light only visual line, but
;; this mode will mess up some buffer, such as org agenda.
;; (global-visual-line-mode)
(pulsar-global-mode 1))
Emacs 26 supports Enchant, which is a meta-spell-checker that uses providers such as Hunspell to do the actual checking. With it, users can use spell-checkers not directly supported by Emacs, such as Voikko, Hspell and AppleSpell, more easily share personal word-lists with other programs, and configure different spelling-checkers for different languages.
The benefit is that you can use the same personal word-lists for different spell checkers in Windows, Linux or macOS.
I get this idea from Eason0210 at Emacs China on [2023-07-02 Sun].
Install enchant
.
Windows/msys64:
pacman -S mingw64/mingw-w64-x86_64-enchant
macOS
sudo port install enchant2
# or
brew install enchant
Add ENCHANT_CONFIG_DIR
to your .bashrc
, if you want to keep enchant
settings in your home folder. Otherwise, it keeps personal dictionary
at Local Settings
. see enchant manual.
export ENCHANT_CONFIG_DIR=~/.config/enchant/
(use-package ispell
:custom
;; enchant-2 becomes very slow in Windows 11
(ispell-program-name "hunspell")
(ispell-silently-savep t)
:config
(cond ((eq system-type 'windows-nt)
(setq ispell-alternate-dictionary
"~/.emacs.d/dict/words.txt")))
;; remove minibuffer message "Starting look.exe process ...",
;; which is annoying
(advice-add
'ispell-lookup-words
:around
(lambda (orig-fun &rest args)
(advice-add
'message :override (lambda (format-string &rest args) ""))
(let ((result (apply orig-fun args)))
(advice-remove 'message (lambda (format-string &rest args) ""))
result))))
Check spell on the fly.
(use-package flyspell
:diminish flyspell-mode
:hook (;;(prog-mode . flyspell-prog-mode)
(org-mode . flyspell-mode)))
(use-package olivetti
:diminish olivetti-mode
:custom
(olivetti-body-width (+ fill-column 10))
:hook ((elfeed-show-mode
eww-mode
Info-mode
woman-mode
fanyi-mode)
. olivetti-mode)
:config
(add-hook 'cnfonts-set-font-finish-hook
(lambda (args)
(let ((frame-width (* (+ fill-column 7)
(frame-char-width))))
(setq org-image-actual-width `(,frame-width))
(setq markdown-max-image-size
`(,frame-width . ,(* 2 frame-width)))
(when (bound-and-true-p org-inline-image-overlays)
(org-redisplay-inline-images))
(when (bound-and-true-p markdown-inline-image-overlays)
(markdown-remove-inline-images)
(markdown-display-inline-images))))))
(use-package cns
:after text-mode
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/cns"))
:hook (text-mode . cns-mode)
:bind ((:map cns-mode-map)
("M-h" . cns-backward-kill-word))
:config
(setq cns-process-buffer "*cns*")
(setq cns-prog
(cond ((eq system-type 'windows-nt)
(concat kimim/path-kimim-emacs
"site-lisp/cns/cnws.exe"))
((eq system-type 'darwin)
(concat kimim/path-kimim-emacs
"site-lisp/cns/cnws"))
((eq system-type 'gnu/linux)
(concat kimim/path-kimim-emacs
"site-lisp/cns/cnws"))))
(setq cns-dict-directory
(concat kimim/path-kimim-emacs
"site-lisp/cns/cppjieba/dict"))
(setq cns-process-shell-command
(format "%s %s" cns-prog (cns-set-prog-args cns-dict-directory)))
(setq cns-recent-segmentation-limit 20) ; default is 10
(setq cns-debug nil))
Automatically add space between Chinese and English, and more.
(use-package wraplish
:commands wraplish-mode
:after text-mode
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/wraplish")))
Before enable wraplish, you need to install python, pip and epc.
pacman -S mingw-w64-x86_64-python mingw-w64-x86_64-python3-pip
pip install epc
Use tempel
to expand template.
;; Configure Tempel
(use-package tempel
:bind (("M-+" . tempel-complete)
("M-=" . tempel-complete)
("M-*" . tempel-insert)
:map tempel-map
("M-RET" . tempel-done)
("S-<tab>" . tempel-previous)
("<tab>" . tempel-next)
("M-<up>" . tempel-previous)
("M-<down>" . tempel-next)))
(use-package tempel-collection)
By enabling winner-mode
, you can restore to previous window
configuration by typing C-c M-<left>
.
(use-package winner
;; restore windows configuration, built-in package
:commands winner-mode
:bind
("C-x M-<left>" . winner-undo)
("C-x M-<right>" . winner-redo)
:config
(winner-mode t))
When type C-x m w
it will create a new frame with the default frame
configuration.
(use-package frame
:ensure nil
:defer 1
:bind ("C-x m w" . make-frame))
preserve the point in screen during scrolling looks nice (see scrolling). scroll slowly with touchpad, thus we adjust the scroll amount.
(setq scroll-preserve-screen-position t)
(setq mouse-wheel-scroll-amount '(0.01))
Move frame to one of the nine grids on the screen with C-x y <N>
keys.
(use-package nine-grid
:diminish nine-grid-minor-mode
:commands (nine-grid)
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/nine-grid"))
:bind (("C-c 5 1" . (lambda () (interactive) (nine-grid 1)))
("C-c 5 2" . (lambda () (interactive) (nine-grid 2)))
("C-c 5 3" . (lambda () (interactive) (nine-grid 3)))
("C-c 5 4" . (lambda () (interactive) (nine-grid 4)))
("C-c 5 5" . (lambda () (interactive) (nine-grid 5)))
("C-c 5 6" . (lambda () (interactive) (nine-grid 6)))
("C-c 5 7" . (lambda () (interactive) (nine-grid 7)))
("C-c 5 8" . (lambda () (interactive) (nine-grid 8)))
("C-c 5 9" . (lambda () (interactive) (nine-grid 9))))
:config
(require 'nine-grid)
(nine-grid-mode))
(use-package windmove
:bind
("C-x <left>" . windmove-swap-states-left)
("C-x <right>" . windmove-swap-states-right)
("C-x <up>" . windmove-swap-states-up)
("C-x <down>" . windmove-swap-states-down))
Display key candidates when you typed part key prefix with which-key-mode
.
;; https://github.com/justbur/emacs-which-key
(use-package which-key
:defer 3
:diminish which-key-mode
:custom (which-key-popup-type 'side-window)
:config
(which-key-mode 1))
List recent used commands with smex
:
;; smex will list the recent function on top of the cmd list
(use-package smex
:commands (smex)
:config
(smex-initialize))
We will use keyfreq
to record the frequency of the key typing, and get a
frequency report by M-x keyfreq-show
.
(use-package keyfreq
:custom (keyfreq-file "~/.emacs.d/emacs.keyfreq")
:config
(keyfreq-mode +1)
(keyfreq-autosave-mode +1))
(use-package eshell)
(use-package bookmark
:custom
(bookmark-default-file "~/.emacs.d/emacs.bmk")
(bookmark-save-flag 1)
(bookmark-fontify nil)
:config
(add-hook 'bookmark-after-jump-hook
(lambda ()
(recenter 'top))))
bm
is used to temporally toggle buffer local bookmarks with C-x m t
,
then you can view all the local temporally bookmarks with C-x m s
.
(use-package bm
:bind (("C-x m t" . bm-toggle)
("C-x m s" . bm-show-all)
("C-x m <left>" . bm-previous)
("C-x m <right>" . bm-next)))
You can jump to any character by triggering ace-jump-mode
(C-x m c
),
and jump to any window by triggering ace-window
(C-x m w
).
(use-package ace-window
:bind
(("C-x o" . other-window)
("M-o" . ace-window)
("C-x m w" . ace-swap-window)
("C-x m x" . ace-delete-window))
:custom
(aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))
(use-package vundo
:bind ("C-x u" . vundo))
Emacs default isearch
does not allow key binding in minibuffer,
isearch-mb
enhances at this aspect.
(use-package isearch-mb
:defer 1
:bind
(:map
isearch-mb-minibuffer-map
("C-l" . recenter-other-window)
("C-n" . isearch-repeat-forward)
("C-p" . isearch-repeat-backward)
("C-." . (lambda () (interactive)
(isearch-exit)
(sdcv-search-pointer+))))
:custom
(isearch-repeat-on-direction-change t)
;; Show match count next to the minibuffer prompt
(isearch-lazy-count t)
;; Don't be stingy with history; default is to keep just 16 entries
(search-ring-max 200)
(search-invisible nil)
(regexp-search-ring-max 200)
:config
(add-hook 'isearch-mode-hook #'display-line-numbers-mode)
(add-hook 'isearch-mode-end-hook
(lambda () (display-line-numbers-mode -1)))
(defun isearch-mb--update-prompt (&rest _)
"Update the minibuffer prompt according to search status."
(when isearch-mb--prompt-overlay
(let ((count (isearch-lazy-count-format))
(len (or (overlay-get isearch-mb--prompt-overlay
'isearch-mb--len)
0)))
(overlay-put isearch-mb--prompt-overlay
'isearch-mb--len (max len (length count)))
(overlay-put isearch-mb--prompt-overlay
'before-string
(concat count ;; Count is padded so that it only grows.
(make-string (max 0 (- len (length count))) ?\ )
(capitalize
(or
(isearch--describe-regexp-mode
isearch-regexp-function)
"")))))))
(isearch-mb-mode))
imenu
is used to navigate the function definitions in current buffer.
(use-package imenu
:functions kimim/imenu-default-goto-function-advice
:bind ("C-x i" . imenu)
:config
(advice-add 'imenu-default-goto-function
:around
#'kimim/imenu-default-goto-function-advice))
(use-package imenu-anywhere
:bind ("C-x m i" . imenu-anywhere))
(use-package ripgrep
:bind ("C-x g" . ripgrep-regexp))
(use-package eww
:custom
(eww-search-prefix "https://cn.bing.com/search?q="))
(use-package recentf
:config
(recentf-mode))
(use-package avy
:bind ("C-x m g" . avy-goto-word-or-subword-1))
To avoid accidentally delete files, let emacs move the deleted file to trash.
(setq delete-by-moving-to-trash t)
(use-package dired
:ensure nil
:defines (dired-omit-localp
dired-omit-files)
:functions (dired-omit-mode
dired-dwim-target-directory
kimim/drawio-to)
:custom
(dired-listing-switches "-AGhlgov")
(dired-recursive-copies t)
(dired-recursive-deletes t)
(ls-lisp-dirs-first t)
(dired-create-destination-dirs 'ask)
(dired-dwim-target t)
:bind
(("C-x C-j" . dired-jump)
:map dired-mode-map
("C-c l" . kimim/dired-get-org-link)
("<left>" . dired-up-directory)
("<right>" . dired-find-file)
("b" . dired-up-directory)
("e" . dired-efap)
("o" . kimim/open-external)
("M-n" . dired-narrow)
("M-c" . compose-attach-marked-files)
("C-q" . kill-dired-buffers)
("<tab>" . kimim/dired-other-window))
:config
(require 'dired-filter)
(require 'dired-recent)
(require 'dired-x)
(require 'dired-efap)
(require 'kimim) ;; for kimim/open-external
(add-hook 'dired-mode-hook
(lambda ()
(turn-on-gnus-dired-mode)
;; Set dired-x buffer-local variables here. For example:
;;(dired-omit-mode 1)
(dired-filter-mode 1)
(hl-line-mode 1)
(setq dired-omit-localp t)
(setq dired-omit-files
(concat "_minted[.]*\\|desktop.ini"
"\\|NTUSER\\|ntuser"
"\\|Cookies\\|AppData"
"\\|Contacts\\|Links"
"\\|Intel\\|NetHood"
"\\|PrintHood\\|Recent"
"\\|Start\\|SendTo"
"\\|^\\.DS_Store"
"\\|qms-bmh"))))
(if (eq system-type 'darwin)
(setq dired-listing-switches "-Avhlgo"))
(defun compose-attach-marked-files ()
"Compose mail and attach all the marked files from a dired buffer."
(interactive)
(let ((files (dired-get-marked-files))
(file-names (dired-copy-filename-as-kill)))
(compose-mail nil (concat "Attachments: " file-names) nil t)
(dolist (file files)
(if (file-regular-p file)
(mml-attach-file file
(mm-default-file-type file)
nil "attachment")
(message "skipping non-regular file %s" file)))))
(defadvice dired-next-line (after dired-next-line-advice (arg) activate)
"Move down lines then position at filename, advice"
(interactive "p")
(if (eobp)
(progn
(goto-char (point-min))
(forward-line 1)
(dired-move-to-filename))))
(defadvice dired-previous-line (before dired-previous-line-advice (arg) activate)
"Move up lines then position at filename, advice"
(interactive "p")
(if (= 2 (line-number-at-pos))
(goto-char (point-max))))
(defun kimim/dired-other-window ()
(interactive)
(let ((other-dired-buffer (dired-dwim-target-directory)))
(if other-dired-buffer
(dired-other-window other-dired-buffer)
(dired-jump-other-window))))
(defun kimim/dired-get-org-link ()
"get a link from dired for org"
(interactive)
(let ((filename (dired-get-filename)))
(kill-new (concat
"[["
(concat "~/" (file-relative-name filename "~"))
"]["
(file-name-nondirectory filename)
"]]"))))
(defun kimim/open-with-inkscape ()
(interactive)
(let* ((filename (dired-get-filename)))
(cond
((string-equal system-type "windows-nt")
(w32-shell-execute "open" "inkscape" filename))
((string-equal system-type "darwin")
(start-process "" nil "open" "-a" "inkscape" filename))
((string-equal system-type "gnu/linux")
(start-process "" nil "xdg-open" "inkscape" filename))
((string-equal system-type "cygwin")
(start-process "" nil "xdg-open" "inkscape" filename)))))
(defun kimim/pdf2svg ()
(interactive)
(let* ((filename (dired-get-filename))
(exportname (replace-regexp-in-string ".pdf$" ".svg" filename))
(counter 10))
(w32-shell-execute
"open" "inkscape"
(format "--export-filename=\"%s\" \"%s\""
exportname filename))
(while (and (not (file-exists-p exportname))
(> counter 0)) ; true-or-false-test
(sleep-for 1)
(setq counter (1- counter)))
(dired-revert)))
(defun kimim/drawio-to (ext)
(let* ((filename (dired-get-filename))
(exportname (replace-regexp-in-string "drawio$" ext filename))
(counter 10))
(w32-shell-execute
"open" (concat (getenv "MSYS64_PATH")
"\\kimikit\\draw.io\\draw.io.exe")
(format "--crop -b 5 -x \"%s\" -o \"%s\""
filename exportname))
(while (and (not (file-exists-p exportname))
(> counter 0)) ; true-or-false-test
(sleep-for 1)
(setq counter (1- counter)))
(dired-revert)))
(defun kimim/drawio2png ()
(interactive)
(kimim/drawio-to "png"))
(defun kimim/drawio2svg ()
(interactive)
(kimim/drawio-to "svg"))
(defun kimim/drawio2pdf ()
(interactive)
(kimim/drawio-to "pdf")))
Keep a list of recently visited directories. Then we can quickly revisit them.
(use-package dired-recent
:config
(dired-recent-mode 1))
dired-efap, Edit file at point, can be used to rename file name at the point:
(use-package dired-efap
:commands dired-efap)
M-n
will prompt for strings to narrow the files in current dired buffer.
(use-package dired-narrow
:commands dired-narrow)
(use-package dired-filter
:diminish dired-filter-mode)
M-o
is bound to ibuffer-visit-buffer-1-window
to visit the buffer on
this line, and delete other windows. This conflicts with global key
binding to ace-window
. Unbind M-o
from ibuffer-mode-map
.
(use-package ibuffer
:bind (("C-x C-b" . ibuffer-other-window)
:map ibuffer-mode-map
("<right>" . ibuffer-visit-buffer))
:custom
(ibuffer-formats
'((mark modified read-only " "
(name 32 32 :left :elide)
" "
(size-h 9 -1 :right)
" "
(mode 14 14 :left :elide)
" "
filename-and-process)))
:config
(unbind-key "M-o" 'ibuffer-mode-map)
;; Use human readable Size column instead of original one
(define-ibuffer-column size-h
(:name "Size" :inline t)
(cond
((> (buffer-size) 1000000)
(format "%7.1fM" (/ (buffer-size) 1000000.0)))
((> (buffer-size) 100000)
(format "%7.0fk" (/ (buffer-size) 1000.0)))
((> (buffer-size) 1000)
(format "%7.1fk" (/ (buffer-size) 1000.0)))
(t (format "%8d" (buffer-size))))))
Rich annotations in the minibuffer.
(use-package marginalia
:init
(marginalia-mode))
(use-package consult
:defines (xref-show-xrefs-function
xref-show-definitions-function)
:bind (;; C-c bindings (mode-specific-map)
("C-c h" . consult-history)
("C-c m" . consult-mode-command)
("C-c k" . consult-kmacro)
;; C-x bindings (ctl-x-map)
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
("C-x M-b" . consult-buffer-other-window)
("C-x m j" . consult-bookmark) ;; orig. bookmark-jump
("C-x m v" . find-variable)
("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
;; Custom M-# bindings for fast register access
("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
("C-M-#" . consult-register)
;; Other custom bindings
("M-y" . consult-yank-pop) ;; orig. yank-pop
("<help> a" . consult-apropos) ;; orig. apropos-command
;; M-g bindings (goto-map)
("M-g e" . consult-compile-error)
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
("M-g g" . consult-goto-line) ;; orig. goto-line
("M-g M-g" . consult-goto-line) ;; orig. goto-line
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
("M-g m" . consult-mark)
("M-g k" . consult-global-mark)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
;; M-s bindings (search-map)
("M-s d" . consult-find)
("M-s D" . consult-locate)
("M-s g" . consult-grep)
("M-s G" . consult-git-grep)
("M-s r" . consult-ripgrep)
("M-s p" . kimim/consult-ripgrep-current)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)
("M-s m" . consult-multi-occur)
("M-s k" . consult-keep-lines)
("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
("M-s L" . consult-line-multi)
:map minibuffer-local-map
("M-s" . consult-history) ;; orig. next-matching-history-element
("M-r" . consult-history)) ;; needed by consult-line to detect isearch
;; Enable automatic preview at point in the *Completions* buffer. This is
;; relevant when you use the default completion UI.
;;:hook (completion-list-mode . consult-preview-at-point-mode)
;; The :init configuration is always executed (Not lazy)
:init
(global-set-key [remap repeat-complex-command] #'consult-complex-command)
(use-package recentf)
;; Optionally configure the register formatting. This improves the register
;; preview for `consult-register', `consult-register-load',
;; `consult-register-store' and the Emacs built-ins.
(setq register-preview-delay 0.5
register-preview-function #'consult-register-format)
;; Optionally tweak the register preview window.
;; This adds thin lines, sorting and hides the mode line of the window.
(advice-add #'register-preview :override #'consult-register-window)
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; Configure other variables and modes in the :config section,
;; after lazily loading the package.
:config
(defun kimim/consult-ripgrep-current ()
(interactive)
(consult-ripgrep "."))
;; Optionally configure preview. The default value
;; is 'any, such that any key triggers the preview.
;; (setq consult-preview-key '(:debounce 1.5 any))
;; (setq consult-preview-key 'any)
;; (setq consult-preview-key "M-.")
;; (setq consult-preview-key (list "<S-down>") (kbd "<S-up>"))
;; For some commands and buffer sources it is useful to configure the
;; :preview-key on a per-command basis using the `consult-customize' macro.
(consult-customize
consult-xref
:preview-key '(:debounce 0 any)
consult-theme consult--source-buffer
:preview-key '(:debounce 5 any)
consult-ripgrep consult-git-grep consult-grep
consult-bookmark consult-recent-file
consult--source-bookmark consult--source-recent-file
consult--source-project-recent-file
:preview-key "C-.")
;; Optionally configure the narrowing key.
;; Both < and C-+ work reasonably well.
(setq consult-narrow-key "<")
)
;; Enable vertico
(use-package vertico
:init
(vertico-mode)
;; Different scroll margin
;; (setq vertico-scroll-margin 0)
;; Show more candidates
;; (setq vertico-count 20)
;; Grow and shrink the Vertico minibuffer
;; (setq vertico-resize t)
;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
;; (setq vertico-cycle t)
)
(use-package orderless
:init
(setq completion-styles '(orderless)
completion-category-defaults nil
completion-category-overrides '((file (styles partial-completion)))))
;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
:init
(savehist-mode))
;; A few more useful configurations...
(use-package emacs
:init
;; Add prompt indicator to `completing-read-multiple'.
;; Alternatively try `consult-completing-read-multiple'.
(defun crm-indicator (args)
(cons (concat "[CRM] " (car args)) (cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
;; Do not allow the cursor in the minibuffer prompt
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
;; Emacs 28: Hide commands in M-x which do not work in the current mode.
;; Vertico commands are hidden in normal buffers.
;; (setq read-extended-command-predicate
;; #'command-completion-default-include-p)
;; Enable recursive minibuffers
(setq enable-recursive-minibuffers t))
(diminish 'abbrev-mode)
(use-package emacs
:init
;; TAB cycle if there are only few candidates
(setq completion-cycle-threshold 2)
;; Enable indentation+completion using the TAB key.
;; `completion-at-point' is often bound to M-TAB.
(setq tab-always-indent t))
(use-package corfu
:defer 1
:bind
(:map
corfu-map
("<remap> <move-beginning-of-line>" . move-beginning-of-line)
("<remap> <move-end-of-line" . move-end-of-line)
("M-h" . backward-kill-word)
("<escape>" . corfu-quit))
:config
(require 'cape)
(setq corfu-auto t
corfu-fancy t
corfu-count 5
corfu-min-width 30
corfu-max-width 60
corfu-auto-delay 0.01
corfu-preview-current t
corfu-auto-prefix 2
corfu-quit-no-match 'separator
corfu-popupinfo-delay '(1.0 . 0.5))
(global-corfu-mode)
(corfu-popupinfo-mode)
(corfu-prescient-mode)
(corfu-history-mode 1)
(savehist-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history))
(use-package corfu-prescient
:commands (corfu-prescient-mode)
:config
(setq corfu-prescient-override-sorting t))
(use-package cape
:init
;; Add `completion-at-point-functions', used by `completion-at-point'.
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
(add-to-list 'completion-at-point-functions #'cape-elisp-symbol)
:custom
(cape-dabbrev-check-other-buffers nil)
(cape-dict-grep nil)
(cape-dict-file
(concat kimim/path-kimim-emacs
"site-lisp/english-words/words_alpha.txt"))
:hook
(emacs-lisp-mode
.
(lambda ()
(setq-local
completion-at-point-functions
(list
(cape-capf-super
#'tempel-expand
#'cape-elisp-symbol
;;#'cape-yasnippet
#'cape-dabbrev)))))
((text-mode org-mode markdown-mode)
.
(lambda ()
(setq-local
completion-at-point-functions
(list
(cape-capf-super
#'tempel-expand
#'cape-dabbrev
#'cape-dict
;;#'cape-yasnippet
)))))
(plantuml-mode
.
(lambda ()
(setq-local
completion-at-point-functions
(list
(cape-capf-super
;;#'cape-yasnippet
#'cape-dabbrev)))))
(eshell-mode
.
(lambda ()
(setq-local corfu-auto nil)
(corfu-mode)))
:config
(defvar cape--dict-all-words nil)
(defvar cape-dict-limit 100)
(defun kimim/cape--dict-list (input)
"Return all words from `cape-dict-file' matching INPUT without grep."
(unless (equal input "")
(let* ((inhibit-message t)
(message-log-max nil)
(files (ensure-list
(if (functionp cape-dict-file)
(funcall cape-dict-file)
cape-dict-file)))
(_ (unless cape--dict-all-words
(setq cape--dict-all-words
(split-string (with-temp-buffer
(mapc #'insert-file-contents
files)
(buffer-string))
"\n" 'omit-nulls))))
(words (let ((completion-ignore-case t)
(completion-regexp-list
(list (regexp-quote input))))
(all-completions "" cape--dict-all-words))))
(cons
(apply-partially
(if (and cape-dict-limit (length= words cape-dict-limit))
#'equal #'string-search)
input)
(cape--case-replace-list cape-dict-case-replace input words)))))
(advice-add 'cape--dict-list :override #'kimim/cape--dict-list))
project-find-file
(C-x p f
) can find files of current project, indicated by git
or other version control information.
(use-package project
:bind (("C-x p r" . project-find-ripgrep-regexp)
("C-x p s" . kimim/magit-stage-file))
:functions (magit-stage-1
magit-with-toplevel
magit-untracked-files
magit-unstaged-files
magit-file-relative-name
project--read-regexp)
:config
(require 'magit)
(defun project-find-ripgrep-regexp (regexp)
"Find all matches for REGEXP in the current project's roots."
(interactive (list (project--read-regexp)))
(require 'ripgrep)
(let* ((caller-dir default-directory)
(pr (project-current t))
(default-directory (project-root pr))
(dir
(if (not current-prefix-arg)
default-directory
(read-directory-name "Base directory: "
caller-dir nil t))))
(ripgrep-regexp regexp dir)))
(defun kimim/magit-stage-file ()
(interactive)
(let* ((current (magit-file-relative-name))
(choices (nconc (magit-unstaged-files)
(magit-untracked-files)))
(default (car (member current choices))))
(if default
(magit-with-toplevel
(magit-stage-1 nil (list default)))
(message "Already staged")))))
(setq next-error-recenter 20)
(setq compilation-scroll-output t)
(bind-key "C-<f11>" 'compile)
Bind magit
to C-x p m
with the same prefix of project
, as they have
strong relationship.
(use-package magit
:bind (("C-x p m" . magit-status-here)
(:map magit-revision-mode-map)
("C-<return>" . magit-diff-visit-file-other-window)
("<SPC>" . magit-diff-visit-worktree-file-other-window)
(:map magit-diff-mode-map)
("C-<return>" . magit-diff-visit-file-other-window)
("<SPC>" . magit-diff-visit-worktree-file-other-window))
:custom (magit-log-show-refname-after-summary t))
Following error will reported when using magit to commit changes:
server-ensure-safe-dir: The directory ‘~/.emacs.d/server’ is unsafe
The solution is to change the owner of ~/.emacs.d/server
[fn:9]
Click R-mouse on ~/.emacs.d/server and select “Properties” (last item in menu). From Properties select the Tab “Security” and then select the button “Advanced”. Then select the Tab “Owner” and change the owner from
“Administrators (\Administrators)”
into“ (\”
. Now the server code will accept this directory as secure because you are the owner.
(use-package lsp-snippet-tempel
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/lsp-snippet"))
:config
(when (featurep 'lsp-mode)
;; Initialize lsp-snippet -> tempel in lsp-mode
(lsp-snippet-tempel-lsp-mode-init))
(when (featurep 'eglot)
;; Initialize lsp-snippet -> tempel in eglot
(lsp-snippet-tempel-eglot-init)))
eglot
is simple and built-in LSP support in emacs.
(use-package eglot
:bind
(:map
eglot-mode-map
("C-x l a" . eglot-code-actions)
("C-x l t" . eglot-find-typeDefinition)
("C-x l d" . eglot-find-declaration)
("C-x l i" . eglot-find-implementation)
("C-x l b" . eldoc)
("C-x l l" . eldoc-box-help-at-point))
:hook
((c-mode c++-mode rust-mode python-mode r-mode) . eglot-ensure)
(eglot-managed-mode
. kimim/eglot-completion)
:config
(require 'cape)
(require 'lsp-snippet-tempel)
(defun kimim/eglot-completion ()
(setf (alist-get
'styles
(alist-get 'eglot completion-category-defaults))
'(orderless basic flex substring partial-completion))
(setq-local completion-at-point-functions
(list
(cape-capf-super
#'eglot-completion-at-point
#'cape-dabbrev)))))
~smartparens-mode~[fn:10] is a general purpose mode for dealing with parenthesis. We define some keys for it:
(use-package smartparens
:bind (:map
smartparens-mode-map
("C-<right>" . sp-forward-slurp-sexp)
("C-<left>" . sp-forward-barf-sexp)
("M-<right>" . sp-backward-barf-sexp)
("M-<left>" . sp-backward-slurp-sexp)
("M-<up>" . sp-splice-sexp-killing-backward)
("M-<down>" . sp-splice-sexp-killing-forward)
("C-k" . sp-kill-hybrid-sexp)
("M-k" . sp-kill-sexp)
("<backspace>" . sp-backward-delete-char)
("C-d" . sp-delete-char)
("C-M-<backspace>" . sp-backward-copy-sexp)
("C-M-w" . sp-copy-sexp))
:functions (sp-local-pair)
:hook (prog-mode . smartparens-mode)
:diminish smartparens-mode
:config
(defalias 'sp--syntax-class-to-char 'syntax-class-to-char)
(sp-with-modes '(c-mode c++-mode)
(sp-local-pair "<" ">" :actions '(wrap autoskip navigate)))
(sp-with-modes sp-lisp-modes
;; disable ', it's the quote character!
(sp-local-pair "'" nil :actions nil)
;; disable ', it's the backquote character!
(sp-local-pair "`" nil :actions nil)
;; also only use the pseudo-quote inside strings where it
;; serves as hyperlink.
(sp-local-pair "`" "'" :when '(sp-in-string-p sp-in-comment-p))))
(use-package tree-sitter-langs)
(use-package tree-sitter
:ensure nil
:hook (((c-mode c++-mode clojure-mode rust-mode) . tree-sitter-mode)
((c-mode c++-mode clojure-mode rust-mode) . tree-sitter-hl-mode))
:config
;; assoc bb-mode to clojure
(setq tree-sitter-major-mode-language-alist
(put-alist 'bb-mode 'clojure
tree-sitter-major-mode-language-alist)))
(use-package ts-fold
:init
(use-package fringe-helper)
(require 'ts-fold)
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/ts-fold"))
:bind ("C-<return>" . ts-fold-toggle))
(use-package flycheck
:commands (global-flycheck-mode)
:custom
(flycheck-global-modes '(not org-mode)))
(use-package eldoc
:hook (prog-mode . eldoc-mode)
:diminish eldoc-mode
:custom
(eldoc-echo-area-use-multiline-p nil))
(use-package cmake-mode
:mode ("CMakeLists\\.txt\\'" . cmake-mode))
(use-package xref
:ensure nil
:custom
(xref-after-return-hook '(xref-pulse-momentarily recenter))
:bind
(:map
prog-mode-map
("C-." . xref-find-references)))
(use-package clang-format)
(use-package ggtags
:bind
(:map
ggtags-navigation-mode-map
("M-o" . other-window)
("M-<" . beginning-of-buffer)
("M->" . end-of-buffer))
:hook ((c-mode c++-mode) . ggtags-mode)
:config
;; eglot use M-. for code navigation
(unbind-key "M-." ggtags-mode-map)
(setq ggtags-mode-line-project-name nil)
(setq ggtags-global-ignore-case t)
(setq ggtags-sort-by-nearness t))
(use-package cc-mode
:ensure nil
:custom (c-default-style
'((java-mode . "java")
(awk-mode . "awk")
(c-mode . "cc-mode")
(c++-mode . "stroustrup++")
(other . "k&r")))
:defines c++-mode-map
:bind (:map
c++-mode-map
("C-<tab>" . clang-format))
:config
(require 'clang-format)
(add-to-list 'auto-mode-alist '("\\.c\\'" . c-mode))
(add-to-list 'auto-mode-alist '("\\.h\\'" . c++-mode))
(add-hook 'c-mode-common-hook
(lambda ()
;;(c-set-style "gnu")
;;(c-toggle-auto-newline 0)
;;(c-toggle-auto-hungry-state 0)
;;(c-toggle-syntactic-indentation 1)
;;(highlight-indentation-mode 1)
(local-set-key "\C-co" 'ff-find-other-file)))
;; + `c-basic-offset' times 1
;; - `c-basic-offset' times -1
;; ++ `c-basic-offset' times 2
;; -- `c-basic-offset' times -2
;; * `c-basic-offset' times 0.5
;; / `c-basic-offset' times -0.5
(c-add-style "stroustrup++"
'("stroustrup"
(c-basic-offset . 4)
(c-offsets-alist
(topmost-intro . 0)
(inclass . +)
(innamespace . -)
(access-label . /)))))
(use-package ob-C
:ensure nil
:config
(add-to-list 'org-src-lang-modes '("C" . c))
(add-to-list 'org-babel-load-languages '(C . t)))
(use-package hideif
:hook ((c-mode c++-mode) . hide-ifdef-mode)
:config
(when (eq system-type 'gnu/linux)
(add-to-list 'hide-ifdef-env '(__linux__ . 1))
(add-to-list 'hide-ifdef-env '(__GNUC__ . 11)))
(when (eq system-type 'darwin)
(add-to-list 'hide-ifdef-env '(__APPLE__ . 1))
(add-to-list 'hide-ifdef-env '(__clang__ . 1))
(add-to-list 'hide-ifdef-env '(__llvm__ . 1)))
(when (eq system-type 'windows-nt)
(add-to-list 'hide-ifdef-env '(__MINGW32__ . 1))
(add-to-list 'hide-ifdef-env '(_WIN32 . 1))
(add-to-list 'hide-ifdef-env '(__GNUC__ . 1)))
:custom
(hide-ifdef-initially nil)
(hide-ifdef-shadow t))
(use-package csharp-mode
:ensure nil
:hook ((csharp-mode . tree-sitter-mode)
(csharp-mode . tree-sitter-hl-mode))
:mode ("\\.cs\\'" . csharp-mode))
Clojure[fn:11] is a lisp over JVM. Emm, I like it.
(use-package clojure-mode
:mode (("\\.cljs\\'" . clojurescript-mode)
("\\.\\(clj\\|dtm\\|edn\\|bb\\)\\'" . clojure-mode)
("\\.cljc\\'" . clojurec-mode)
("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode))
:config
(require 'cider)
(require 'flycheck)
(require 'flycheck-clj-kondo)
(require 'clojure-mode-extra-font-locking))
(use-package clojure-mode-extra-font-locking)
Install with npm:
npm install -g clj-kondo
(use-package flycheck-clj-kondo)
Cider[fn:12] extends Emacs with support for interactive programming in Clojure.
(use-package cider
:functions tramp-dissect-file-name
:custom ((cider-clojure-cli-command "clojure")
(nrepl-use-ssh-fallback-for-remote-hosts t)
(nrepl-sync-request-timeout 100))
:config
;;(setq cider-interactive-eval-output-destination 'output-buffer)
(defun nrepl--ssh-tunnel-command (ssh dir port)
"Command string to open SSH tunnel to the host associated with DIR's PORT."
(with-parsed-tramp-file-name dir v
;; this abuses the -v option for ssh to get output when the port
;; forwarding is set up, which is used to synchronise on, so that
;; the port forwarding is up when we try to connect.
(format-spec
"%s -v -N -L %p:localhost:%p %u'%h' %x"
`((?s . ,ssh)
(?p . ,port)
(?h . ,v-host)
(?u . ,(if v-user (format "-l '%s' " v-user) ""))
(?x . "-o \"ProxyCommand=nc -X connect -x 127.0.0.1:1080 %h %p\""))))))
(use-package ob-clojure
:ensure org
:custom (org-babel-clojure-backend 'cider)
:config
(add-to-list 'org-src-lang-modes '("clojure" . clojure)))
Download bb
from https://github.com/babashka/babashka/releases and put
bb
to execute PATH.
(use-package ob-bb
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/ob-bb")))
Python development configuration is quite easy. elpy
[fn:13] is used here:
;; (use-package elpy
;; :config
;; (elpy-enable))
(use-package python
:ensure nil
;; :defines elpy-rpc-backend
:mode ("\\.py\\'" . python-mode)
:interpreter ("python" . python-mode)
:config
;; (add-hook 'python-mode-hook
;; (lambda ()
;; (setq yas-indent-line nil)))
(add-to-list
'python-shell-completion-native-disabled-interpreters "python"))
;; (use-package company-jedi
;; :config
;; (setq elpy-rpc-backend "jedi"))
Following python
package is required according to elpy
mannual:
pip install rope
pip install jedi
# flake8 for code checks
pip install flake8
# importmagic for automatic imports
pip install importmagic
# and autopep8 for automatic PEP8 formatting
pip install autopep8
# and yapf for code formatting
pip install yapf
# install virtualenv for jedi
pip install virtualenv
The easiest way to install rust is to run following script:
curl https://sh.rustup.rs -sSf | sh
(use-package rustic
:hook
(rustic-mode . (lambda ()
(set (make-local-variable 'compile-command)
"cargo run")))
:custom
(rustic-lsp-client 'eglot))
(use-package ess
:custom
(inferior-ess-r-program "r"))
(use-package swift-mode
:mode ("\\.swift\\'" . swift-mode))
(use-package typescript-mode
:hook ((typescript-mode . tree-sitter-mode)
(typescript-mode . tree-sitter-hl-mode)
(typescript-mode . eglot-ensure)))
(use-package json-mode
:mode ("\\.json\\'" . json-mode)
:hook ((json-mode . tree-sitter-mode)
(json-mode . tree-sitter-hl-mode)))
Open .go
file with go-mode.
(use-package go-mode
:mode ("\\.go\\'" . go-mode))
Some dockerfile is not end with .dockerfile
, so lets guess:
(use-package dockerfile-mode
:mode ("\\dockerfile\\'" . dockerfile-mode))
(use-package elisp-mode
:ensure nil
:mode ("\\.el\\'" . emacs-lisp-mode)
:config
(define-derived-mode lisp-interaction-mode emacs-lisp-mode "ᴧ"))
ahk-mode
developed by Rich Alesi[fn:14]
(use-package ahk-mode
:mode ("\\.ahk\\'" . ahk-mode))
(use-package yaml-mode
:mode ("\\.yml\\'" . yaml-mode)
:bind (:map
yaml-mode-map
("\C-m" . newline-and-indent)))
(use-package shell
:mode ("\\.sh\\'" . shell-script-mode))
(use-package ob-shell
:ensure nil
:config
(require 'shell)
(add-to-list 'org-src-lang-modes '("shell" . shell))
(add-to-list 'org-babel-load-languages '(shell . t)))
(use-package powershell
:mode ("\\.ps1\\'" . powershell-mode))
Major mode to edit ethereum solidity smart contract code.
(use-package solidity-mode
:mode ("\\.sol\\'" . solidity-mode))
(use-package lua-mode)
(use-package fennel-mode)
That’s fun to draw UML with ob-plantuml
inside orgmode
:
For Windows Cygwin, install graphviz
in cygwin
setup tool
For macOS, install graphviz
with homebrew:
brew install graphviz
Download plantuml.jar
from https://plantuml.com/download, and put it to some
place and assign plantuml-jar-path
to there.
(use-package plantuml-mode
:mode ("\\.puml\\'" . plantuml-mode)
:custom
(plantuml-output-type "svg")
(plantuml-default-exec-mode 'jar)
(plantuml-jar-path (expand-file-name
(concat kimim/path-kimikit "plantuml/plantuml.jar")))
(plantuml-executable-args '("-charset" "utf-8")))
(use-package ob-plantuml
:ensure nil
:config
(require 'plantuml-mode)
;; WARNING: if variables are from other package, setq them at :config
(setq org-plantuml-jar-path plantuml-jar-path)
(setq org-plantuml-args plantuml-executable-args)
(add-to-list 'org-src-lang-modes '("plantuml" . plantuml)))
I want to preview plantuml result in a full window, so I set
image-auto-resize
to fit-window
. If the image is still to small, when
it is a long sequence diagram, I can use key s i
to invoke
image-transform-fit-to-width
to maximize the width of the image.
(use-package image-mode
:ensure nil
:custom
(image-auto-resize 'fit-window)
:config
(add-to-list 'auto-mode-alist '("\\.svg\\'" . image-mode)))
dot
is used to draw simple diagram in code. You need to install graphviz
:
pacman -S mingw-w64-ucrt-x86_64-graphviz
(use-package graphviz-dot-mode
:ensure t
:custom
(graphviz-dot-preview-extension "svg"))
(use-package ob-ditaa
:ensure nil
:custom
(org-ditaa-jar-path
(expand-file-name
(concat kimim/path-kimikit "ditaa/ditaa.jar")))
:config
(add-to-list 'org-src-lang-modes '("ditaa" . artist)))
Mermaid [fn:15] is another js based diagramming and charting tool. To use it inside orgmode, mermaid-cli should be installed:
yarn add @mermaid-js/mermaid-cli
mmdc
will be installed at ~~/node_modules/.bin/mmdc~. Then we just setup
ob-mermaid
and mermaid-mode
for babel evaluation and editing.
(use-package ob-mermaid
:custom
(ob-mermaid-cli-path "~/node_modules/.bin/mmdc.cmd")
:config
(add-to-list 'org-babel-load-languages '(mermaid . t)))
(use-package mermaid-mode
:mode ("\\.mermaid\\'" . mermaid-mode))
scroll images line by line.
(use-package iscroll
:diminish iscroll-mode
:hook ((org-mode markdown-mode) . iscroll-mode))
(use-package chatu
:load-path (lambda ()
(concat
kimim/path-kimim-emacs "site-lisp/chatu"))
:commands (chatu-add
chatu-open)
:hook ((org-mode markdown-mode) . chatu-mode))
(eval-and-compile
(defun lilypond-load-path ()
(cond ((eq system-type 'darwin)
"/opt/homebrew/share/emacs/site-lisp/lilypond")
((eq system-type 'windows-nt)
(concat kimim/path-kimikit
"lilypond-2.24.3/share/emacs/site-lisp"))
((eq system-type 'gnu/linux)
"/usr/local/share/emacs/site-lisp/lilypond"))))
(use-package lilypond-mode
:load-path (lambda () (list (lilypond-load-path)))
:mode ("\\.ly\\'" . LilyPond-mode))
(use-package ob-lilypond
:ensure nil
:config
(require 'lilypond-mode)
(cond ((eq system-type 'darwin)
(setq org-babel-lilypond-ly-command
"/opt/homebrew/bin/lilypond")))
(defun org-babel-lilypond-process-basic (body params)
"Execute a lilypond block in basic mode."
(let* ((out-file (cdr (assq :file params)))
(cmdline (or (cdr (assq :cmdline params))
""))
(in-file (org-babel-temp-file "lilypond-")))
(with-temp-file in-file
(insert (org-babel-expand-body:generic body params)))
(org-babel-eval
(concat
org-babel-lilypond-ly-command
" -dcrop --loglevel=ERROR "
(or (cdr (assoc (file-name-extension out-file)
'(("pdf" . "--pdf ")
("ps" . "--ps ")
("svg" . "--svg ")
("png" . "--png "))))
"--png ")
"--output="
(file-name-sans-extension out-file)
" "
cmdline
in-file
;; copy .preview file to sans .preview file
" && mv -f "
(file-name-sans-extension out-file)
".cropped."
(file-name-extension out-file)
" "
out-file) "")) nil))
Hook pdf-view-themed-minor-mode
to pdf-view-mode
and add
pdf-view-refresh-themed-buffer
to enable-theme-functions
to follow
emacs theme color.
(use-package pdf-tools
:diminish pdf-view-themed-minor-mode
:functions (pdf-view-refresh-themed-buffer)
:defines (pdf-view-themed-minor-mode)
:mode ("\\.pdf\\'" . pdf-view-mode)
:hook ((pdf-view-mode . pdf-view-themed-minor-mode)
(pdf-view-mode . pdf-isearch-minor-mode))
:custom (pdf-view-display-size 3.85)
:bind
(:map
pdf-view-mode-map
("C-s" . isearch-forward)
("C-r" . isearch-backward)
("C-o" . pdf-occur)
("C-l" . pdf-view-center-in-window)
("j" . (lambda ()
"Next line within page"
(interactive)
(image-next-line 10)))
("k" . (lambda ()
"Previous line within page"
(interactive)
(image-next-line -10)))
("n" . kimim/pdf-view-next-page)
("p" . kimim/pdf-view-previous-page)
("o" . other-window)
("<home>". (lambda ()
(interactive)
(image-scroll-up -999)))
("<end>". (lambda ()
(interactive)
(image-scroll-up 999)))
("C-c C-z" . pdf-view-open-bibtex-notes)
("/" . (lambda ()
(interactive)
(let ((filename (file-name-nondirectory
(buffer-file-name))))
(kill-new
filename)
(message "Copied %s" filename))))
("t" . kimim/pdf-view-pagetext)
("`" . kimim/fanyi-in-pdf)
("<mouse-3>" . kimim/fanyi-in-pdf))
:config
(require 'pdf-tools)
(require 'fanyi)
(defun kimim/fanyi-in-pdf ()
"Invoke fanyi in pdf-view."
(interactive)
(pdf-view-assert-active-region)
(if-let ((word (car (pdf-view-active-region-text))))
(progn
(fanyi-dwim word)
(cl-pushnew word fanyi-history))
(call-interactively #'fanyi-dwim)))
(advice-add 'enable-theme :after
(lambda (&rest _)
(when pdf-view-themed-minor-mode
(pdf-view-refresh-themed-buffer t))))
(require 'bibtex-completion)
(defun kimim/pdf-view-next-page (&optional n)
(interactive "p")
(pdf-view-next-page n)
(image-bob))
(defun kimim/pdf-view-previous-page (&optional n)
(interactive "p")
(pdf-view-previous-page n)
(image-bob))
(defun pdf-view-open-bibtex-notes ()
"From PDF file, open the notes if they exist."
(interactive)
(bibtex-completion-edit-notes (list (pdf-view-get-bibtex-key))))
(defun pdf-view-get-bibtex-key ()
"Get bibtex key from PDF buffer name in pdf-view mode"
(car (string-split (buffer-name) "[-\\(_\\.]+")))
(defun kimim/pdf-view-pagetext ()
"Show pdf text in a buffer."
(interactive)
(pdf-view-mark-whole-page)
(pdf-view-kill-ring-save)
(switch-to-buffer "*pdf-view-pagetext*")
(yank)))
To remember visited pages of PDF files.
(use-package pdf-view-restore
:after pdf-tools
:custom (pdf-view-restore-filename "~/.emacs.d/.pdf-view-restore")
:hook (pdf-view-mode . pdf-view-restore-mode))
Add indicator of remaining text when scrolling PDF pages.
(use-package pdf-view-pagemark
:after pdf-tools
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/pdf-view-pagemark"))
:hook (pdf-view-mode . pdf-view-pagemark-mode))
(use-package bibtex-completion
:defines bibtex-completion-bibliography
:custom
(bibtex-completion-display-formats
'((t . "${=key=:30} ${author:36} ${title:*} ${year:4} ${=has-pdf=:1}${=has-note=:1} ${=type=:7}")))
(bibtex-completion-bibliography (concat kimim/path-docs "references.bib"))
(bibtex-completion-library-path kimim/path-docs)
(bibtex-completion-notes-path (concat kimim/path-notes "org-ref-notes.txt"))
:bind ("C-x m b" .
(lambda ()
(interactive)
(find-file
(concat kimim/path-docs "references.bib")))))
(use-package org-ref
:functions (-flatten
f-join
org-ref-get-bibtex-key-and-file
bibtex-completion-key-at-point
bibtex-completion-candidates
bibtex-completion-init
bibtex-completion-edit-notes
org-ref-cite-hydra/body
org-ref-find-bibliography
kimim/org-ref-open-pdf
kimim/org-ref-open-pdf-in-dired
kimim/org-ref-open-notes
kimim/org-ref-open-notes-action
kimim/org-ref-get-pdf-filename
kimim/org-ref-open-pdf-action
kimim/org-ref-open-pdf-in-dired-action)
:bind (("C-x m p" . kimim/org-ref-open-pdf-at-point)
("C-x m P" . kimim/org-ref-open-pdf-in-dired-at-point)
("C-x m n" . kimim/org-ref-open-notes-at-point)
:map org-mode-map
("C-c ]" . org-ref-insert-cite-link)
("C-c o" . kimim/org-roam-ref-open-pdf))
:config
(require 'bibtex-completion)
(require 'org-roam-bibtex)
;; use MS word to open office file
(add-to-list 'org-file-apps
'("\\.docx?\\'" . default))
(add-to-list 'org-file-apps
'("\\.pptx?\\'" . default))
(add-to-list 'org-file-apps
'("\\.xlsx?\\'" . default))
;; dont put [ inside file name
(defun kimim/org-ref-get-pdf-filename (key)
(if bibtex-completion-library-path
(let* ((pdf-dirs (if (listp bibtex-completion-library-path)
bibtex-completion-library-path
(list bibtex-completion-library-path)))
(pdfs
(-flatten
(--map (file-expand-wildcards
(f-join it (format "%s*" key)))
(-flatten
(append pdf-dirs
(--map (directory-files-recursively it "" t)
pdf-dirs)))))))
(cond
((= 0 (length pdfs))
(expand-file-name (format "%s.pdf" key) bibtex-completion-library-path))
((= 1 (length pdfs))
(car pdfs))
((> (length pdfs) 1)
(completing-read "Choose: " pdfs))))
;; No bibtex-completion-library-path defined so return just a file name.
(format "%s.pdf" key)))
(defun kimim/org-ref-open-pdf-action (key)
"Open the pdf for bibtex key under point if it exists."
(let* ((pdf-file (kimim/org-ref-get-pdf-filename key)))
(if (file-exists-p pdf-file)
(find-file pdf-file)
(message "no pdf found for %s" key))))
(defun kimim/org-ref-open-bibtex-pdf ()
(interactive)
(kimim/org-ref-open-pdf-action (bibtex-completion-get-key-bibtex)))
(defun kimim/org-ref-open-pdf (&optional arg)
(interactive)
(kimim/org-ref-open-pdf-action (org-ref-read-key)))
(defun kimim/org-ref-open-pdf-at-point ()
"Open the pdf for bibtex key under point if it exists."
(interactive)
(let* ((results (condition-case nil
(if (eq 'org-mode
(buffer-local-value
'major-mode (current-buffer)))
(org-ref-get-bibtex-key-and-file))
(error nil))))
(if (or (null results)
(string= "" (car results))
(null (car results)))
(kimim/org-ref-open-pdf)
(let ((pdf-file (kimim/org-ref-get-pdf-filename (car results))))
(if (file-exists-p pdf-file)
(find-file pdf-file)
(kimim/org-ref-open-pdf))))))
(defun kimim/org-ref-open-pdf-in-dired-action (key)
"Open the pdf dired for bibtex key under point if it exists."
(let* ((pdf-file (kimim/org-ref-get-pdf-filename key)))
(if (file-exists-p pdf-file)
(dired-jump nil pdf-file)
(message "no pdf found for %s" key))))
(defun kimim/org-ref-open-pdf-in-dired (&optional arg)
(interactive)
(kimim/org-ref-open-pdf-in-dired-action (org-ref-read-key)))
(defun kimim/org-ref-open-pdf-in-dired-at-point ()
"Open the pdf dired for bibtex key under point if it exists."
(interactive)
(let* ((results (condition-case nil
(org-ref-get-bibtex-key-and-file)
(error nil)))
(key (car results)))
(if (or (string= "" key) (null key))
(kimim/org-ref-open-pdf-in-dired)
(let ((pdf-file (kimim/org-ref-get-pdf-filename key)))
(if (file-exists-p pdf-file)
(dired-jump nil pdf-file)
(message "no pdf found for %s" key))))))
(defun kimim/org-ref-open-notes-action (key)
"Open the notes for bibtex key under point if it exists."
(bibtex-completion-edit-notes (list key)))
(defun kimim/org-ref-open-notes (&optional arg)
(interactive)
(kimim/org-ref-open-notes-action (org-ref-read-key)))
(defun kimim/org-ref-open-notes-at-point ()
"Open the notes of a reference if they exist."
(interactive)
(let* ((results (condition-case nil
(org-ref-get-bibtex-key-and-file)
(error nil)))
(key (car results)))
(if (or (string= "" key) (null key))
(kimim/org-ref-open-notes)
(kimim/org-ref-open-notes-action key))))
(defun kimim/org-roam-ref-open-pdf ()
"Open pdf from ROAM_REFS of a note."
(interactive)
(kimim/org-ref-open-pdf-action
(cadr
(string-split
(car (org-property-values "ROAM_REFS"))
":"))))
(defun kimim/bibtex-completion-get-title ()
"Copy the title by KEY."
(interactive)
(kill-new
(s-format
"${title}"
'bibtex-completion-apa-get-value
(bibtex-completion-get-entry
(org-ref-get-bibtex-key-under-cursor)))))
(defhydra+ org-ref-citation-hydra ()
"Add copy action to `org-ref-citation-hydra'."
("y" kimim/bibtex-completion-get-title
"Copy title" :column "Copy")))
There is a built-in bibtex-mode
to manage references. We can extend it to
support more functions from org-ref
:
(use-package bibtex
:ensure nil
:bind (:map bibtex-mode-map
("C-x m p" . kimim/org-ref-open-bibtex-pdf)
("C-x m n" . org-ref-open-bibtex-notes)
("C-x m d" . kimim/org-ref-open-bibtex-in-dired)
("C-c C-z" . org-ref-open-bibtex-notes))
:config
(require 'org-ref)
(require 'org-roam-bibtex))
(use-package nov
:mode ("\\.epub\\'" . nov-mode)
:hook ((nov-mode . olivetti-mode)
(nov-mode . (lambda ()
(visual-line-mode)
(setq line-spacing 0.5))))
:custom
(nov-header-line-format nil)
:bind
(:map
nov-mode-map
("<home>" . beginning-of-line)
("<end>" . end-of-line)
("<mouse-3>" . kimim/fanyi-in-epub))
:config
(require 'olivetti)
(require 'fanyi)
(setq nov-text-width (- olivetti-body-width 2))
(defun kimim/fanyi-in-epub ()
"Invoke fanyi in epub view."
(interactive)
;;(mouse-set-point last-input-event)
(mark-thing-at-mouse last-input-event 'word)
(if-let ((word (thing-at-point 'word)))
(progn
(fanyi-dwim word)
(cl-pushnew word fanyi-history))
(call-interactively #'fanyi-dwim))))
(use-package org
:mode (("\\.txt\\'" . org-mode)
("\\.org\\'" . org-mode))
:hook (org-mode . olivetti-mode)
:bind
(:map org-mode-map
("C-c b" . org-iswitchb)
("C-c l" . org-store-link)
("C-c C-x l" . org-toggle-link-display)
("C-c !" . org-time-stamp-inactive)
("C-c 。" . org-time-stamp)
("M-." . org-open-at-point)
("M-*" . org-mark-ring-last-goto)
("M-h" . nil)
("C-'" . org-emphasize)
("C-c p" . kimim/preview-babel-image)
("C-c C-w" . org-refile-reverse)
("C-c w" . org-refile)
("M-," . org-mark-ring-goto))
:custom
(org-modules '(org-habit
ol-w3m ol-bbdb ol-bibtex
ol-docview ol-gnus ol-info
ol-irc ol-mhe ol-rmail ol-eww))
(org-export-with-sub-superscripts '{})
(org-startup-folded 'showall)
(org-startup-with-inline-images t)
(org-export-with-tags nil)
(org-tags-column (- fill-column))
;; image width in preview
(org-image-actual-width `(,(* (+ fill-column 10)
(frame-char-width))))
:config
;; no use for me, I always press this key accidentally
(unbind-key "C-'" 'org-mode-map)
(setq org-hide-emphasis-markers t)
(setq org-support-shift-select t)
;; no empty line after collapsed
(setq org-cycle-separator-lines 0)
(if window-system
(setq org-startup-indented t)
(setq org-startup-indented nil)))
(use-package org-appear
:commands (org-appear-mode)
:ensure t
:custom (org-appear-autolinks nil)
:hook (org-mode . org-appear-mode))
Add advice to org-indent--compute-prefixes
to remove needless indent
spaces before plain text line.
(use-package org-indent
:ensure nil
:custom (org-indent-mode-turns-on-hiding-stars nil)
:hook (org-mode . org-indent-mode)
:diminish org-indent-mode
:config
(defun kimim/org-indent-adjust-indent ()
(dotimes (n org-indent--deepest-level)
(let ((indentation (* (1- org-indent-indentation-per-level)
n)))
;; Text line prefixes.
(aset org-indent--text-line-prefixes
n
(org-add-props
(concat (make-string indentation ?\s)
(and (> n 0)
(char-to-string
org-indent-boundary-char)))
nil 'face 'org-indent)))))
(advice-add 'org-indent--compute-prefixes :after
#'kimim/org-indent-adjust-indent))
(use-package org-modern
:custom
;; https://github.com/minad/org-modern/issues/134
(org-modern-star '("○" "●" "▼"))
(org-modern-list
'((?- . "▬")
(?+ . "♦")
(?* . "●")))
(org-modern-checkbox
'((?X . "☑")
(?- . #("☐–" 0 2 (composition ((2)))))
(?\s . "☐")))
(org-modern-timestamp nil)
(org-modern-tag nil)
(org-modern-todo nil)
(org-modern-block-name nil)
(org-modern-block-fringe nil)
(org-modern-keyword nil)
(org-modern-priority nil)
:hook
(org-mode . org-modern-mode)
:config
(advice-add
'org-modern--make-font-lock-keywords
:filter-return
(lambda (result)
(append
result
(when-let ((bullet (alist-get ?- org-modern-list)))
`(("^\\(-\\)[ \t]" 1 '(face nil display ,bullet))))
(when-let ((bullet (alist-get ?+ org-modern-list)))
`(("^[ ]\\{2\\}\\(-\\)[ \t]" 1 '(face nil display ,bullet))))
(when-let ((bullet (alist-get ?* org-modern-list)))
`(("^[ ]\\{4\\}\\(-\\)[ \t]" 1 '(face nil display ,bullet))))))))
(use-package org
:ensure nil
:hook
(org-mode . prettify-symbols-in-org-mode)
:config
(defun prettify-symbols-in-org-mode ()
"Beautify Org Symbols"
(push '(":category:" . "▲") prettify-symbols-alist)
(push '(":PROPERTIES:" . "🗅") prettify-symbols-alist)
(push '(":END:" . "∎") prettify-symbols-alist)
(push '("#+TITLE:" . "🗎") prettify-symbols-alist)
(push '("#+SUBTITLE:" . "⮱") prettify-symbols-alist)
(push '(":SETTINGS:" . "⌘") prettify-symbols-alist)
(push '("#+begin_src" . "«" ) prettify-symbols-alist)
(push '("#+end_src" . "»" ) prettify-symbols-alist)
(push '("#+begin_comment" . "♯" ) prettify-symbols-alist)
(push '("#+end_comment" . "▪" ) prettify-symbols-alist)
(prettify-symbols-mode)))
Sometimes, SVG images are not properly converted in LaTeX PDF
file. Including a PDF image can solve this problem. Then we need a
method to toggle PDF image display. org-inline-pdf
solves this issue
by using pdf2svg
to convert PDF to SVG on-the-fly.
(use-package org-inline-pdf
:hook (org-mode . org-inline-pdf-mode))
(use-package orgalist
:commands (orgalist-mode))
(use-package org-download
:commands (org-download-enable)
:custom
(org-download-heading nil)
:functions kimim/org-download-annotate
:config
(setq org-download-timestamp "")
(setq-default org-download-image-dir "./images")
(setq org-download-method 'directory)
(defun kimim/org-download-annotate (link)
"Annotate LINK with the time of download."
(format "#+NAME: fig:%s\n#+CAPTION: %s\n"
(file-name-base link) (file-name-base link)))
(setq org-download-annotate-function #'kimim/org-download-annotate)
(setq org-download-display-inline-images nil)
(setq image-file-name-extensions
(quote
("png" "jpeg" "jpg" "gif" "tiff" "tif" "xbm"
"xpm" "pbm" "pgm" "ppm" "pnm" "svg" "pdf" "bmp")))
(defun org-download--dir-2 () "."))
(use-package org
:custom (org-num-skip-footnotes t)
:config
(require 'org-download)
(setq org-hide-leading-stars t)
(setq org-footnote-auto-adjust t)
(setq org-footnote-define-inline nil))
(use-package org
:config
(setq org-src-window-setup 'current-window)
(setq org-src-fontify-natively t)
(setq org-src-preserve-indentation t)
(setq org-edit-src-content-indentation 0)
(setq org-confirm-babel-evaluate nil)
(add-hook 'org-babel-after-execute-hook 'org-display-inline-images)
;; get the idea from John Kitchen(author of org-ref)
(defadvice org-babel-execute-src-block (around load-language nil activate)
"Load language if needed"
(let* ((language (org-element-property :language (org-element-at-point)))
(language-to-load (if (string= language "C++")
"C"
language)))
(unless (cdr (assoc (intern language-to-load) org-babel-load-languages))
(require (intern (concat "ob-" language-to-load)))
(add-to-list 'org-babel-load-languages (cons (intern language-to-load) t))
(org-babel-do-load-languages 'org-babel-load-languages
org-babel-load-languages))
ad-do-it)))
When exporting, do not export with author and date.
(use-package org
:bind ("C-c C-'" . org-insert-structure-template)
:functions (org-export--collect-headline-numbering
org-export--get-min-level)
:custom
(org-export-allow-BIND t)
(org-export-html-validation-link nil)
;;(org-export-with-sub-superscripts '{})
(org-export-with-author nil)
(org-export-with-date t)
(org-structure-template-alist '(("a" . "export ascii")
("c" . "center")
("C" . "comment")
("d" . "definition")
("e" . "example")
("E" . "export")
("h" . "export html")
("l" . "export latex")
("L" . "lemma")
("p" . "proof")
("o" . "corollary")
("?" . "question")
("q" . "quote")
("Q" . "quotation")
("r" . "result")
("s" . "src")
("t" . "theorem")
("v" . "verse")))
(org-html-head "<style>
pre.src-bad { background: wheat; }
pre { margin: 0.0em;
padding: 4pt;
}
</style>")
:config
(require 'ox-latex)
(defun kimim/org-html-src-block-advice (oldfun src-block contents info)
(let* ((class-tag (org-export-read-attribute
:attr_html src-block :class))
(html-block (funcall oldfun src-block contents info)))
(if (string-empty-p class-tag)
html-block
(string-replace
;; add src-bad class
"class=\"src" (concat "class=\"src src-" class-tag) html-block))))
(advice-add 'org-html-src-block :around
#'kimim/org-html-src-block-advice)
(defmacro by-backend (&rest body)
`(pcase
(if (boundp 'backend)
(org-export-backend-name backend) nil)
,@body))
(defun org-export--collect-tree-properties (data info)
"Extract tree properties from parse tree.
DATA is the parse tree from which information is retrieved. INFO
is a list holding export options.
Following tree properties are set or updated:
`:headline-offset' Offset between true level of headlines and
local level. An offset of -1 means a headline
of level 2 should be considered as a level
1 headline in the context.
`:headline-numbering' Alist of all headlines as key and the
associated numbering as value.
`:id-alist' Alist of all ID references as key and associated file
as value.
Return updated plist."
;; Install the parse tree in the communication channel.
(setq info (plist-put info :parse-tree data))
;; Compute `:headline-offset' in order to be able to use
;; `org-export-get-relative-level'.
(setq info
(plist-put info
:headline-offset
(- 1 (org-export--get-min-level data info))))
;; From now on, properties order doesn't matter: get the rest of the
;; tree properties.
(org-combine-plists
info
(list :headline-numbering (org-export--collect-headline-numbering data info)
:id-alist
(org-element-map data 'link
(lambda (l)
(and (string= (org-element-property :type l) "id")
(let* ((id (org-element-property :path l))
(file (car (org-id-find id))))
;; replace txt extension with exported PDF file
(and file
(let ((pdf-file (concat (file-name-sans-extension file) ".pdf")))
(if (file-exists-p pdf-file)
(cons id (file-relative-name pdf-file))
(cons id (file-relative-name file)))))))))))))
LaTeX is required to convert org-mode
to PDF.
For MacOS:
brew cask install mactex-no-gui
For Windows, there are three options:
- download and install CTEX from http://www.ctex.org
- install texlive-collection in cygwin
apt-cyg install texlive-collection-xetex \ texlive-collection-latex \ texlive-collection-fontsrecommended
- download and install texlive from tug.org
For Linux, download texlive install pacakge from ctan.org
tar zxvf install-tl-unx.tar.gz
cd install-tl-20200908/
sudo ./install-tl
Then for all the OS platforms, use tlmgr
to install user level tex
packages (notes that, in windows, you may need to run tlmgr.bat
):
tlmgr init-usertree
tlmgr --usermode install ctex titlesec enumitem ms fontspec abstract \
zhnumber fandol lastpage pdftexcmds infwarerr \
minted fvextra etoolbox fancyvrb upquote \
lineno catchfile xstring framed float \
grffile wrapfig ulem lettrine minifp \
capt-of xcolor svg koma-script trimspaces \
titling layaureo parskip extsizes pgf \
moderncv microtype
fmtutil-sys --all
Recently, I adopted to mainly use texlive on Windows. It works fine and provide
a GUI tool to maintain packages: tlshell.exe
. You can use it to install and
update latex packages.
To export org-mode
to PDF, with code style highlight, you need to install
python
and pygments
. Because pygmentize
from pygments
is used to
generate latex
markups for font highlighting.
For MacOS, the OS shipped python2.7
does not accompanied with pip
package
management script. So you need to install pip
, and then add pygments
,
acc. https://pip.pypa.io/en/stable/installing/ , pygmentize
will be installed
under $HOME/Library/Python/2.7/bin
, which is added to exec-path
and PATH.
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
Get pygments
with pip
:
pip install pygments
For Ubuntu Linux:
sudo apt install python3-pygments
(use-package latex-classes
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp"))
:config
(require 'bibtex-completion))
(use-package ox-latex
:ensure nil
:defines (kimim/latex-classes
org-beamer-frame-level
org-latex-minted-langs)
:functions (org-html--make-attribute-string)
:commands org-latex-publish-to-pdf
:diminish org-beamer-mode
:custom
(org-latex-listings 'minted)
(org-latex-minted-options
'(("frame" "lines") ("fontsize" "\\scriptsize")))
(org-latex-pdf-process
'("latexmk -xelatex -shell-escape -output-directory=%o %F"))
;; avoid warning when preview tikz in org babel
(org-babel-latex-preamble
(lambda (_)
"\\documentclass[preview]{standalone}"))
;; emacs cannot handle relative path correctly in OneDrive folder
;; use full-name instead. %f -> %F
(org-babel-latex-pdf-svg-process
"inkscape \
--pdf-poppler \
--export-area-drawing \
--export-text-to-path \
--export-plain-svg \
--export-filename=%O \
%F")
:config
(require 'ox)
(require 'ox-beamer)
(require 'latex-classes)
(setq org-startup-with-beamer-mode t)
(setq org-beamer-frame-level 2)
;; export quotes match
(setq org-export-with-smart-quotes t)
(add-to-list 'org-latex-minted-langs
'(plantuml "text"))
(add-to-list 'org-latex-minted-langs
'(ditaa "text"))
(add-to-list 'org-latex-minted-langs
'(bb "clojure"))
(add-to-list 'org-latex-minted-langs
'(conf "text"))
(add-to-list 'org-latex-minted-langs
'(mermaid "text"))
(add-to-list 'org-latex-minted-langs
'(lilypond "text"))
(defun ref-headline-removal (backend)
"Remove reference headline with tag: ref"
(org-map-entries
(lambda ()
(when (member "ref" org-scanner-tags)
(delete-region (point) (line-beginning-position 2))))))
(add-hook 'org-export-before-parsing-functions 'ref-headline-removal)
;;;;; Nicolas Goaziou, http://article.gmane.org/gmane.emacs.orgmode/67692
;; (defun org-latex-ignore-heading-filter-headline (headline backend info)
;; "Strip headline from HEADLINE. Ignore BACKEND and INFO."
;; (when (and (org-export-derived-backend-p backend 'latex)
;; (string-match "\\`.*ignoreheading.*\n" headline))
;; (replace-match "" nil nil headline)))
;; (add-to-list 'org-export-filter-headline-functions
;; 'org-latex-ignore-heading-filter-headline)
;; most of the time, I do not need table of contents
(setq org-latex-toc-command nil)
;; https://www.tuicool.com/articles/ZnAnym
;; remove error: ! LaTeX Error: Command \nobreakspace unavailable in encoding T1.
;; add: \DeclareRobustCommand\nobreakspace{\leavevmode\nobreak\ }
;; put long latex classes in a separate file
(require 'latex-classes)
(setq org-latex-classes kimim/latex-classes)
(setq org-latex-default-class "article")
;; removed, it will make small image too large!
;;(setq org-latex-image-default-option
;; "height=0.8\\textheight,width=\\textwidth,keepaspectratio")
(setq org-latex-image-default-width "")
;; remove fontenc, and AUTO in front of inputenc,
;; then francais can be processed
(setq org-latex-default-packages-alist
(quote
(("" "inputenc" t ("pdflatex"))
("" "minted" t nil)
("" "amsfonts" t nil)
("" "graphicx" t nil)
("inkscapeopt = -C --export-ignore-filters, inkscapelatex=false" "svg" t nil)
("" "grffile" t nil)
("" "longtable" nil nil)
("" "wrapfig" nil nil)
("" "rotating" nil nil)
("normalem" "ulem" t nil)
("" "amsmath" t nil)
("" "textcomp" t nil)
("" "lettrine" t nil)
("" "capt-of" nil nil))))
;; latex preview report exception with bibref
;; (setq org-latex-packages-alist
;; `(,(concat "\\addbibresource{"
;; (expand-file-name bibtex-completion-bibliography)
;; "}")))
;; (mapconcat
;; (lambda (it)
;; (concat "\addbibresource{" (expand-file-name it) "}\n"))
;; bibtex-completion-bibliography)
;; increase latex preview size.
;;(setq org-format-latex-options
;; (plist-put org-format-latex-options :scale 2.3))
;; scale latex equation preview according to frame char height
(cond
((eq system-type 'darwin)
(add-hook 'cnfonts-set-font-finish-hook
(lambda (args)
(setq org-format-latex-options
(plist-put org-format-latex-options :scale
(/ (frame-char-height) 9.0))))))
((eq system-type 'windows-nt)
(add-hook 'cnfonts-set-font-finish-hook
(lambda (args)
(setq org-format-latex-options
(plist-put org-format-latex-options :scale
(/ (frame-char-height) 30.0)))))))
;; preview latex equation with SVG image
;; [2024-04-25 Thu] kimim: use %F for input file,
;; because the relative path is not correct when in a symbol link folder.
(add-to-list
'org-preview-latex-process-alist
'(xdvsvgm
:progams
("xelatex" "dvisvgm")
:discription "xdv > svg"
:message
"you need install the programs: xelatex and dvisvgm."
:image-input-type "xdv"
:image-output-type "svg"
:image-size-adjust (1.3 . 1.3)
:latex-compiler
("xelatex -no-pdf -shell-escape -output-directory=%o %F")
:image-converter ("dvisvgm %F -n -b min -c %S -o %O")))
(setq org-preview-latex-default-process 'xdvsvgm))
(use-package ox-html
:ensure org
:functions (org-html-encode-plain-text
org-html-close-tag
f-read)
:commands (org-html-publish-to-html)
:config
(setq org-html-validation-link nil)
(defadvice org-html-paragraph (before fsh-org-html-paragraph-advice
(paragraph contents info) activate)
"Join consecutive Chinese lines into a single long line without
unwanted space when exporting org-mode to html."
(let ((fixed-contents)
(orig-contents (ad-get-arg 1))
(reg-han "[[:multibyte:]]"))
(setq fixed-contents (replace-regexp-in-string
(concat "\\(" reg-han "\\) *\n *\\(" reg-han "\\)")
"\\1\\2" orig-contents))
(ad-set-arg 1 fixed-contents)))
;; embed svg image to html file
(defun org-html--format-image (source attributes info)
"Return \"img\" tag with given SOURCE and ATTRIBUTES.
SOURCE is a string specifying the location of the image.
ATTRIBUTES is a plist, as returned by
`org-export-read-attribute'. INFO is a plist used as
a communication channel."
(if (string= "svg" (file-name-extension source))
;;(plist-get attributes :embed-svg)
;; remove width and height information, to fix image to the parent div
(let ((svg-txt (replace-regexp-in-string
"<svg.*\\(width=\"\\w+\"\s+height=\"\\w+\"\\).*>"
""
(f-read source)
nil nil 1)))
(format
"<div align=\"center\">%s</div>" svg-txt))
(org-html-close-tag
"img"
(org-html--make-attribute-string
(org-combine-plists
(list :src source
:alt (if (string-match-p
(concat "^" org-preview-latex-image-directory) source)
(org-html-encode-plain-text
(org-find-text-property-in-string 'org-latex-src source))
(file-name-nondirectory source)))
(if (string= "svg" (file-name-extension source))
(org-combine-plists '(:class "org-svg") attributes '(:fallback nil))
attributes)))
info))))
(use-package org-re-reveal
:bind ("C-x r v" . org-re-reveal-export-to-html-and-browse)
:config
(use-package htmlize :ensure t)
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/[email protected]/")
(setq org-re-reveal-theme "none")
(setq org-re-reveal-width 1000)
(setq org-re-reveal-height 750)
(setq org-re-reveal-transition "none")
(setq org-re-reveal-hlevel 2)
(setq org-re-reveal-extra-css "./kimim.css"))
(use-package org
:functions org-supdf-open
:config
(org-link-set-parameters "supdf"
:follow #'org-supdf-open)
(defun org-supdf-open (path)
"Visit the pdf page"
(let ((file-page (split-string path "::")))
(cond
((eq system-type 'windows-nt)
(progn
(w32-shell-execute
"open" (concat kimim/path-kimikit "sumatrapdf/sumatrapdf.exe")
(concat "\"" (expand-file-name (car file-page))
"\" -page " (cadr file-page)))))))))
New link to use Office Onenote.
(use-package org
:functions org-onenote-open
:config
(org-link-set-parameters "onenote"
:follow #'org-onenote-open)
(defun org-onenote-open (path)
"Visit the onenote link"
(cond
((eq system-type 'windows-nt)
(progn
(w32-shell-execute
"open" (concat "onenote:" path))))
((eq window-system 'ns)
(shell-command
(replace-regexp-in-string
"&" "\\\\&" (format "open onenote:%s" path)))))))
(use-package o2jk
:load-path (lambda ()
(concat kimim/path-kimim-emacs
"site-lisp/o2jk"))
:functions (o2jk-input-directory
org-publish-cache-get-file-property
org-ref-export-to-file-nomarks-noopen
org-export-output-file-name
org-ref-process-buffer
org-export-expand-include-keyword)
:commands (o2jk-publish
o2jk-list-drafts
o2jk-create-draft)
:bind (("C-x m k" . o2jk-create-draft)
("C-x m l" . o2jk-list-source))
:custom ((o2jk-blog-author "kimim")
(o2jk-source-directory "~/notes/kimi.im/_notes/_posts")
(o2jk-jekyll-directory "~/notes/kimi.im/_posts")
(o2jk-jekyll-drafts-dir "~/notes/_draft")
(o2jk-jekyll-posts-dir ""))
:config
(require 'org-ref)
(setq org-publish-project-alist
`(("post" ;; dynamic pages like blog articles
:base-directory ,(o2jk-input-directory)
:base-extension "org\\|txt"
:publishing-directory ,(o2jk-output-directory)
:publishing-function org-ref-html-publish-to-html
:headline-levels 4
:html-preamble t
:recursive t
:make-index t
:html-extension "html"
:body-only t)))
;; Very simplified version of org-ref-export-to from org-ref-export.el
;; that export to filename
(defun org-ref-export-to-file-nomarks-noopen
(backend filename &optional async subtreep visible-only body-only info)
(org-export-with-buffer-copy
(org-export-expand-include-keyword)
(org-ref-process-buffer backend subtreep)
(org-export-to-file backend filename
async subtreep visible-only
body-only info)))
;; org-html-publish-to-html from ox-html.el adapted to org-ref
;; Instead of org-export-to-file calls org-ref-export-to-file-nomarks-noopen
(defun org-ref-html-publish-to-html (plist filename pub-dir)
(unless (or (not pub-dir)
(file-exists-p pub-dir))
(make-directory pub-dir t))
;; Check if a buffer visiting FILENAME is already open.
(let* ((org-inhibit-startup t)
(visiting (find-buffer-visiting filename))
(work-buffer (or visiting (find-file-noselect filename))))
(unwind-protect
(with-current-buffer work-buffer
(let ((output (org-export-output-file-name ".html" nil pub-dir)))
(org-ref-export-to-file-nomarks-noopen
'html output
nil nil nil (plist-get plist :body-only)
(org-combine-plists
plist
`(:crossrefs
,(org-publish-cache-get-file-property
;; Normalize file names in cache.
(file-truename filename) :crossrefs nil t)
:filter-final-output
(org-publish--store-crossrefs
org-publish-collect-index
,@(plist-get plist :filter-final-output)))))))))))
Org-roam implements zettelkasten
method [fn:16] used by famous German socialogist
Niklas Luhmann[fn:17].
First you should install sqlite3
, which is used to index the links.
Windows/MSYS2:
pacman -S mingw-w64-x86_64-sqlite3
Windows/Cygwin:
apt-cyg install sqlite3
sqlite3 is shipped in macOS by default.
(use-package org-roam
:commands (kimim/non-cite-filter)
:ensure t
:custom
(org-roam-directory kimim/path-notes)
(org-roam-db-location (file-truename
(concat user-emacs-directory
"org-roam.db")))
(org-roam-link-auto-replace nil)
(org-roam-file-extensions '("txt" "org"))
(org-roam-node-display-template "${title:70}${tags:30}${refs}")
(org-roam-capture-templates
'(("d" "default" plain "%?"
:if-new
(file+head
"%(concat (kimim/genfile-timestamp) \"${slug}.txt\")"
"#+TITLE: ${title}\n")
:unnarrowed t)))
(org-roam-dailies-capture-templates
'(("d" "default" plain "- /%<%H:%M>/ %?" :target
(file+datetree "%<%Y>.org" :day))))
(orb-preformat-keywords '("citekey"))
:bind
(("C-c n f" . (lambda ()
(interactive)
(org-roam-node-find
nil nil 'kimim/non-cite-filter)))
("C-c n F" . (lambda ()
(interactive)
(org-roam-node-find
nil nil 'kimim/cite-filter)))
("C-c n o" . kimim/ebdb-link-open-note)
("C-c n c" . org-roam-capture)
("C-c n j" . org-roam-dailies-capture-today)
("C-c n ." . org-roam-dailies-goto-today)
("C-c n r" . org-roam-ref-add)
("C-c n x" . org-roam-node-random)
:map org-roam-mode-map
(("C-c n l" . org-roam)
("C-c n g" . org-roam-graph))
:map org-mode-map
(("C-c n i" . (lambda ()
(interactive)
(org-roam-node-insert 'kimim/non-cite-filter)))
("C-c n I" . (lambda ()
(interactive)
(org-roam-node-insert 'kimim/cite-filter)))
("C-c n t" . org-roam-tag-add)
("C-c n a" . org-roam-alias-add)
("C-c n g" . org-id-get-create)
("C-c n b" . org-roam-buffer-toggle)))
:config
(add-to-list 'load-path
(concat kimim/path-kimim-emacs
"site-lisp/"))
(require 'org-roam-dailies)
(require 'org-roam-bibtex)
(require 'emacsql)
(require 'kimim)
;; open org link in current window
(add-to-list
'org-link-frame-setup
'(file . find-file))
;;(setq org-roam-v2-ack t)
(setq emacsql-global-timeout 60) ;; default 30 seconds will timeout
;;(org-roam-db-autosync-enable)
(defvar orb-templates
'(("r" "reference" plain
"#+ROAM_KEY: %^{citekey}\n\n%?"
:target
(file+head
"references/%(concat (kimim/genfile-timestamp) \"${citekey}.txt\")"
"#+title: ${title}\n")
:unnarrowed t)))
(defun kimim/cite-filter (node)
(orb-get-node-citekey node))
(defun kimim/non-cite-filter (node)
(or
(not (orb-get-node-citekey node))
(-contains? (org-roam-node-tags node) "standard")
(-contains? (org-roam-node-tags node) "terminology")))
(advice-add 'org-roam-node-visit
:after (lambda (&rest r) (reposition-window)))
(advice-add
#'orb--new-note :around
(lambda (origin-fun &rest args)
(let ((org-roam-capture-templates orb-templates))
(apply origin-fun args))))
(defun kimim/ebdb-link-open-note ()
(interactive)
(let* ((context (org-element-context))
(name (buffer-substring
(org-element-property :contents-begin context)
(org-element-property :contents-end context)))
(path (org-element-property :path context))
;; TODO: extend to citeref in the future
(node (kimim/ebdb-note-exists-p path)))
(if node
(org-roam-node-open node)
(kimim/ebdb-note-new uuid name)))))
It is useful to create reference notes with org-roam-bibtex
. C-c C-z
used in org-ref
is calling orb-org-ref-edit-note
to edit org-roam
note.
(use-package org-roam-bibtex
:functions (orb-get-node-citekey
orb--new-note)
:hook (bibtex-mode . org-roam-bibtex-mode)
:diminish org-roam-bibtex-mode
;; use the original title captalization
:custom (orb-bibtex-entry-get-value-function
#'bibtex-completion-get-value))
Add a virtual piece of other org file to current one.
(use-package org-transclusion
:after org
:custom (org-transclusion-include-first-section nil)
:hook (org-mode . org-transclusion-add-all)
:bind ("<f12>" . org-transclusion-add-all))
Markdown is widely used as plain text file format. Pandoc [fn:18] can
be used to convert markdown file to html and other formats. We can
download the latest version and put the binary file to system path,
such as /usr/local/bin
, and then set markdown-command
to pandoc
.
(use-package markdown-mode
:mode ("\\.\\(?:md\\|markdown\\)\\'" . markdown-mode)
:functions (s-starts-with?
markdown--get-remote-image)
:custom ((markdown-hide-urls t)
(markdown-command "pandoc")
(markdown-max-image-size
`(,(* (+ fill-column 10)
(frame-char-width))
. ,(* 2 (+ fill-column 10)
(frame-char-width)))))
:hook ((markdown-mode . markdown-toggle-inline-images)
(markdown-mode . olivetti-mode))
:bind(:map
markdown-mode-map
("C-<tab>" . outline-hide-entry)
("M-<up>" . markdown-move-up)
("M-<down>" . markdown-move-down)
("C-c C-x C-v" . markdown-toggle-inline-images))
:config
(setq markdown-fontify-code-blocks-natively t)
(setq markdown-list-item-bullets
'("▬" "►" "•"))
(setq markdown-list-indent-width 2)
(advice-add
'markdown-fontify-list-items :override
(lambda (last)
(when (markdown-match-list-items last)
(when (not (markdown-code-block-at-point-p (match-beginning 2)))
(let* ((indent (length (match-string-no-properties 1)))
(level (/ indent markdown-list-indent-width))
;; level = 0, 1, 2, ...
(bullet (nth (mod level (length markdown-list-item-bullets))
markdown-list-item-bullets)))
(add-text-properties
(match-beginning 2) (match-end 2) '(face markdown-list-face))
(cond
;; Unordered lists
((string-match-p "[\\*\\+-]" (match-string 2))
(add-text-properties
(match-beginning 2) (match-end 2) `(display ,bullet)))
;; Definition lists
((string-equal ":" (match-string 2))
(let ((display-string
(char-to-string (markdown--first-displayable
markdown-definition-display-char))))
(add-text-properties (match-beginning 2) (match-end 2)
`(display ,display-string)))))))
t)))
;; to support azure markdown format with =WxH after file link
(setq markdown-regex-link-inline
"\\(?1:!\\)?\\(?2:\\[\\)\\(?3:\\^?\\(?:\\\\\\]\\|\
[^]]\\)*\\|\\)\\(?4:\\]\\)\\(?5:(\\)\\s-*\\(?6:[^)]*?\\)\\\
(?:\\s-+\\(?7:\"[^\"]*\"\\|=.*\\)\\)?\\s-*\\(?8:)\\)")
(defun markdown--browse-url (url)
(let* ((struct (url-generic-parse-url url))
(full (url-fullness struct))
(file url))
(message file)
;; Parse URL, determine fullness, strip query string
(setq file (car (url-path-and-query struct)))
(let ((file (if (and (s-starts-with? "/" file)
(not (file-exists-p file)))
(concat (project-root (project-current t))
file)
file)))
;; Open full URLs in browser, files in Emacs
(if full
(browse-url url)
(when (and file (> (length file) 0))
(let ((link-file
(funcall markdown-translate-filename-function file)))
(if (and markdown-open-image-command
(string-match-p
(image-file-name-regexp) link-file))
(if (functionp markdown-open-image-command)
(funcall markdown-open-image-command link-file)
(process-file
markdown-open-image-command nil nil nil link-file))
(find-file-other-window link-file))))))))
(defun markdown-display-inline-images ()
"Add inline image overlays to image links in the buffer.
This can be toggled with `markdown-toggle-inline-images'
or \\[markdown-toggle-inline-images]."
(interactive)
(unless (display-images-p)
(error "Cannot show images"))
(save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(while (re-search-forward markdown-regex-link-inline nil t)
(let* ((start (match-beginning 0))
(imagep (match-beginning 1))
(end (match-end 0))
(file (match-string-no-properties 6))
;; make file path if starts with /
(file (if (s-starts-with? "/" file)
(concat (project-root (project-current t))
file)
file)))
(when (and imagep
(not (zerop (length file))))
(unless (file-exists-p file)
(let* ((download-file
(funcall
markdown-translate-filename-function file))
(valid-url
(ignore-errors
(member
(downcase
(url-type
(url-generic-parse-url download-file)))
markdown-remote-image-protocols))))
(if (and markdown-display-remote-images valid-url)
(setq file (markdown--get-remote-image download-file))
(when (not valid-url)
;; strip query parameter
(setq file (replace-regexp-in-string "?.+\\'" "" file))
(unless (file-exists-p file)
(setq file (url-unhex-string file)))))))
(when (file-exists-p file)
(let* ((abspath (if (file-name-absolute-p file)
file
(concat default-directory file)))
(image
(cond ((and markdown-max-image-size
(image-type-available-p 'imagemagick))
(create-image
abspath 'imagemagick nil
:max-width (car markdown-max-image-size)
:max-height (cdr markdown-max-image-size)))
(markdown-max-image-size
(create-image
abspath nil nil
:max-width (car markdown-max-image-size)
:max-height (cdr markdown-max-image-size)))
(t (create-image abspath)))))
(when image
(let ((ov (make-overlay start end)))
(overlay-put ov 'display image)
(overlay-put ov 'face 'default)
(push ov markdown-inline-image-overlays))))))))))))
(when (not (boundp 'kimim/file-diary))
(defvar kimim/file-diary (concat kimim/path-org "diary"))
(if (not (file-exists-p kimim/file-diary))
(write-region "" nil kimim/file-diary)))
(use-package calendar
:defines (calendar-chinese-celestial-stem
calendar-chinese-terrestrial-branch)
:custom
(diary-file kimim/file-diary)
(calendar-latitude +30.16)
(calendar-longitude +120.12)
(calendar-location-name "Hangzhou")
(calendar-remove-frame-by-deleting t)
(calendar-week-start-day 1)
(calendar-mark-holidays-flag t)
(holiday-christian-holidays nil)
(holiday-hebrew-holidays nil)
(holiday-islamic-holidays nil)
(holiday-solar-holidays nil)
(holiday-bahai-holidays nil)
(holiday-general-holidays
'((holiday-fixed 1 1 "元旦")
(holiday-float 5 0 2 "父親節")
(holiday-float 6 0 3 "母親節")))
(calendar-mark-diary-entries-flag t)
(calendar-view-holidays-initially-flag nil)
(calendar-chinese-celestial-stem
["甲" "乙" "丙" "丁" "戊" "己" "庚" "辛" "壬" "癸"])
(calendar-chinese-terrestrial-branch
["子" "丑" "寅" "卯" "辰" "巳" "午" "未" "申" "酉" "戌" "亥"])
:config
(require 'ebdb)
;; redefine Chinese sexagesimal format
(defun calendar-chinese-sexagesimal-name (n)
"The N-th name of the Chinese sexagesimal cycle.
N congruent to 1 gives the first name, N congruent to 2 gives the
second name, ..., N congruent to 60 gives the sixtieth name."
(format
"%s%s 年"
(aref calendar-chinese-celestial-stem (% (1- n) 10))
(aref calendar-chinese-terrestrial-branch (% (1- n) 12))))
;; https://www.emacswiki.org/emacs/CalendarWeekNumbers
(defface calendar-iso-week-face
'((t :inherit 'calendar-weekend-header
:height 0.7 :bold t))
"Week number.")
(setq calendar-week-start-day 1
calendar-intermonth-header
'(propertize
" "
'font-lock-face 'calendar-iso-week-face)
calendar-intermonth-text
'(propertize
(format "%2d"
(car
(calendar-iso-from-absolute
(calendar-absolute-from-gregorian
(list month day year)))))
'font-lock-face 'calendar-iso-week-face)))
(use-package org-capture
:ensure nil
:bind (:map
org-capture-mode-map
("C-c C-w" . kimim/org-capture-refile-reverse)
("C-c w" . org-capture-refile))
:custom
(org-capture-templates
'(("c" "Capture" entry (file+headline "capture.org" "Inbox")
"* %?\n:PROPERTIES:\n:CAPTURED: %U\n:END:\n")
("t" "TODO Task" entry (file+headline "capture.org" "Inbox")
"* TODO %?\n:PROPERTIES:\n:CAPTURED: %U\n:END:\n")
("s" "SCHED Task" entry (file+headline "capture.org" "Inbox")
"* SCHED %?\nSCHEDULED: %t\n:PROPERTIES:\n:CAPTURED: %U\n:END:\n")
("o" "OPEN Issue" entry (file+headline "capture.org" "Inbox")
"* OPEN %?\n:PROPERTIES:\n:CAPTURED: %U\n:END:\n")
("w" "WAIT Task" entry (file+headline "capture.org" "Inbox")
"* WAIT %?\nSCHEDULED: %t\n:PROPERTIES:\n:CAPTURED: %U\n:END:\n")
("h" "Habit" entry (file+headline "global.org" "Habit")
"* %? :habit:\n:PROPERTIES:\n:CAPTURED: %U\n:END:\n")))
:config
(defun kimim/org-capture-refile-reverse ()
(interactive)
(let ((org-reverse-note-order t))
(org-capture-refile))))
(use-package org
:functions (org-agenda-kill-all-agenda-buffers
org-agenda-todo
org-agenda-error
org-agenda-check-no-diary)
; :hook (org-agenda-mode . hl-line-mode)
:defines org-agenda-mode-map
:commands (org-toggle-office org-toggle-home org-toggle-home-or-office)
:bind (("C-c a" . org-agenda)
("C-c c" . org-capture)
:map org-agenda-mode-map
("o" . other-window)
("C-c M-s" . kimim/org-agenda-unschedule)
("C-c M-d" . kimim/org-agenda-undeadline)
("C-c C-x C-p" . org-previous-link)
("C-c C-x C-n" . org-next-link)
("C-c C-k" . org-agenda-kill-files)
("<C-left>" . org-agenda-do-date-earlier)
("<C-right>" . org-agenda-do-date-later)
("<S-left>" . (lambda ()
(interactive)
(org-agenda-todo 'left)))
("<S-right>" . (lambda ()
(interactive)
(org-agenda-todo 'right)))
("C-c C-w" . org-agenda-refile-reverse)
("C-c w" . org-agenda-refile))
:custom
(org-directory kimim/path-org)
(org-agenda-files
(file-expand-wildcards (concat kimim/path-org "*.org*")))
(org-tags-exclude-from-inheritance '("project" "category" "info"))
(org-log-done t)
(org-fontify-done-headline nil)
(org-todo-repeat-to-state "REPEAT")
(org-deadline-warning-days 2)
(org-todo-keywords
'(
;; for tasks
(sequence "TODO(t!)" "SCHED(s)" "|" "DONE(d@/!)")
;; for risks, actions, problems
(sequence "OPEN(o!)" "WAIT(w@/!)" "|" "CLOSE(c@/!)")
(sequence "|" "SOMEDAY(m)")
(sequence "|" "ABORT(a@/!)")
(sequence "REPEAT(r)" "|")))
(org-tag-alist
'(("@office" . ?o) ("@home" . ?h)
("team" . ?t) ("leader" . ?l) ("risk" . ?k)
("reading" . ?r) ("writing" . ?w)
("project" . ?p) ("category" . ?c) ("info" . ?i)))
(org-stuck-projects
'("+LEVEL>=2-category-habit-info"
("TODO" "SCHED" "DONE"
"OPEN" "WAIT" "CLOSE"
"ABORT" "SOMEDAY" "REPEAT")
nil nil))
(org-agenda-include-diary t)
(org-agenda-span 2)
(org-agenda-skip-scheduled-if-done t)
(org-agenda-skip-deadline-if-done t)
(org-agenda-custom-commands
'(("j" "agenda"
((agenda "" ((org-agenda-span 2)
(org-agenda-show-log t)))
(todo "TODO|OPEN"
((org-agenda-sorting-strategy '(priority-down))))
(tags "+LEVEL>=2-category-habit-info
/-TODO-SCHED-DONE-OPEN-WAIT-CLOSE-ABORT-SOMEDAY-REPEAT"
((org-agenda-sorting-strategy '(priority-down))))))
("t" todo "TODO|OPEN"
((org-agenda-sorting-strategy '(priority-down))))
("w" todo "SCHED|WAIT"
((org-agenda-sorting-strategy '(priority-down))))
("d" todo "TODO|SCHED|OPEN|WAIT"
((org-agenda-sorting-strategy '(priority-down))))
("f" todo "SOMEDAY"
((org-agenda-sorting-strategy '(priority-down))))
("h" tags "habit/-ABORT-CLOSE"
((org-agenda-sorting-strategy '(todo-state-down))))
("c" tags "clock"
((org-agenda-sorting-strategy '(priority-down))))))
(org-refile-targets
'(;; refile to maxlevel 1 of current file
(nil . (:maxlevel . 1))
;; refile to maxlevel 1 of org-agenda-files
(org-agenda-files . (:maxlevel . 1))
;; refile to item with `project' or `category'
;; tag in org-agenda-files
(org-agenda-files . (:tag . "project"))
(org-agenda-files . (:tag . "category"))))
(org-habit-show-all-today t)
:config
(require 'org-capture)
(require 'org-agenda)
(add-hook 'kill-emacs-hook
(lambda ()
(org-clock-out nil t nil)
(org-save-all-org-buffers)))
;; kill diary when exit agenda
(advice-add 'org-agenda-exit
:after (lambda () (kill-buffer "diary")))
(diminish 'auto-fill-function)
(defadvice org-schedule (after add-todo activate)
(if (or (string= "OPEN" (org-get-todo-state))
(string= "WAIT" (org-get-todo-state))
(string= "CLOSE" (org-get-todo-state)))
(org-todo "WAIT")
(org-todo "SCHED")))
(defadvice org-deadline (after add-todo activate)
(if (or (string= "OPEN" (org-get-todo-state))
(string= "WAIT" (org-get-todo-state))
(string= "CLOSE" (org-get-todo-state)))
(org-todo "WAIT")
(org-todo "SCHED")))
(defun kimim/org-agenda-unschedule ()
(interactive)
(org-agenda-schedule `(4))
(org-agenda-todo 'left))
(defun kimim/org-agenda-undeadline ()
(interactive)
(org-agenda-deadline `(4))
(org-agenda-todo 'left))
(defun org-agenda-kill-files ()
(interactive)
(org-agenda-kill-all-agenda-buffers)
(mapcar (lambda (file)
(if-let (buf (get-file-buffer file))
(kill-buffer buf)))
org-agenda-files))
(defun org-agenda-refile-reverse (&optional goto rfloc no-update)
"Refile the item at point, reversely."
(interactive "P")
(cond
((member goto '(0 (64)))
(org-refile-cache-clear))
((equal goto '(16))
(org-refile-goto-last-stored))
(t
(let* ((buffer-orig (buffer-name))
(marker (or (org-get-at-bol 'org-hd-marker)
(org-agenda-error)))
(buffer (marker-buffer marker))
;; (pos (marker-position marker))
(rfloc (or rfloc
(org-refile-get-location
(if goto "Goto" "Refile to") buffer
org-refile-allow-creating-parent-nodes))))
(with-current-buffer buffer
(org-with-wide-buffer
(goto-char marker)
(let ((org-agenda-buffer-name buffer-orig))
(org-remove-subtree-entries-from-agenda))
(org-refile-reverse goto buffer rfloc))))
(unless no-update (org-agenda-redo))))))
Type C-x m m
on agenda task to invoke org-pomodoro
. When an pomodoro
is completed, a posframe showing break countdown in large font.
(use-package org-pomodoro
:after org
:custom ((org-pomodoro-audio-player "mpg123")
(org-pomodoro-format "(*) %s"))
:bind ("C-x m m" . org-pomodoro)
:config
(require 'posframe)
(defun kimim/org-pomodoro-tick-hook ()
(if (or (eq org-pomodoro-state :short-break)
(eq org-pomodoro-state :long-break))
(posframe-show
"*pomodoro*"
:string (propertize
(cadr org-pomodoro-mode-line)
'face
'(:height 400 :inherit org-pomodoro-mode-line-break))
:poshandler 'posframe-poshandler-frame-center
:timeout 10)
(posframe-delete "*pomodoro*")))
(add-hook 'org-pomodoro-tick-hook
#'kimim/org-pomodoro-tick-hook)
(defun kimim/org-pomodoro-finished-action ()
(raise-frame))
(add-hook 'org-pomodoro-finished-hook
#'kimim/org-pomodoro-finished-action))
(use-package ebdb
:functions ebdb-gethash
:commands ebdb ebdb-mail-aliases
:custom
(ebdb-mua-pop-up nil)
:bind (:map
ebdb-mode-map
("C-c C-z" . kimim/ebdb-note-find)
("w i" . kimim/ebdb-citekey))
:config
(setq ebdb-sources (concat kimim/path-org "ebdb.gpg"))
(setq ebdb-i18n-countries-pref-scripts
'(("中国" . chn)))
(require 'ebdb-gnus)
(require 'ebdb-message)
(require 'ebdb-org)
(add-hook 'message-setup-hook 'ebdb-mail-aliases)
(setq org-link-make-description-function
(lambda (link desc)
(let* ((link-content (split-string link ":"))
(key (car link-content))
(link-str (cadr link-content))
(link-uuid (cadr (split-string link-str "/"))))
(if desc
desc
(if (string= "ebdb" key)
(ebdb-record-name-string
(ebdb-gethash link-uuid 'uuid)))))))
(defvar kimim/ebdb-notes-cache nil
"Cache of EBDB notes.")
(defun kimim/ebdb-get-db-cite-refs ()
"Get a list of `cite` refs from Org Roam database."
(let* ((types "ebdb")
(refs (org-roam-db-query
[:select [ref nodes:file id pos title type]
:from refs
:left-join nodes
:on (= refs:node-id nodes:id)
:where (= type $s1)]
types))
result)
(dolist (ref refs result)
(push (-interleave '(:ref :file :id :pos :title :type) ref) result))))
(defun kimim/ebdb-make-notes-cache ()
"Update ORB notes hash table `kimim/ebdb-notes-cache'."
(let* ((db-entries (kimim/ebdb-get-db-cite-refs))
(size (round (/ (length db-entries) 0.8125))) ;; ht oversize
(ht (make-hash-table :test #'equal :size size)))
(dolist (entry db-entries)
(puthash (plist-get entry :ref)
(org-roam-node-create
:id (plist-get entry :id)
:file (plist-get entry :file)
:title (plist-get entry :title)
:point (plist-get entry :pos))
ht))
(setq kimim/ebdb-notes-cache ht)))
(defun kimim/ebdb-note-exists-p (uuid)
"Check if a note exists whose :ROAM_REFS is uuid.
Return Org Roam node or nil."
(gethash uuid (kimim/ebdb-make-notes-cache)))
(defun kimim/ebdb-citekey ()
"Get people citekey"
(interactive)
(if-let* ((record (ebdb-current-record))
(name (ebdb-record-name-string record))
(uuid (ebdb-record-uuid record))
(citekey-ref (format "[[ebdb:uuid/%s][%s]]" uuid name)))
(kill-new citekey-ref)))
(defun kimim/ebdb-note-new (uuid name)
"Create people note."
(if-let* ((citekey-ref (format "[[ebdb:uuid/%s][%s]]" uuid name))
(title name)
(node (org-roam-node-create
:title title
:file (concat kimim/path-notes
"people/"
(kimim/genfile-timestamp)
(s-downcase
(s-join
"_"
(s-split-words title)))
".txt"))))
(org-roam-capture-
:node node
:info (list :ref citekey-ref))
(user-error "Abort")))
(defun kimim/ebdb-note-find ()
"Find or create people note."
(interactive)
(let* ((record (ebdb-current-record))
(name (ebdb-record-name-string record))
(uuid (ebdb-record-uuid record))
(ebdb-ref (concat "uuid/" uuid))
(node (kimim/ebdb-note-exists-p ebdb-ref)))
(if node
(org-roam-node-open node)
(kimim/ebdb-note-new uuid name)))))
;; erc settings
(use-package erc
:functions erc-autojoin-enable
:commands erc
:custom
(erc-autojoin-channels-alist
'(("irc.freenode.net" "#emacs")))
(erc-hide-list '("JOIN" "PART" "QUIT"))
:config
(require 'erc-join)
(erc-autojoin-enable)
(setq erc-default-server "irc.freenode.net"))
(use-package gnus-dired
:ensure nil
:commands (turn-on-gnus-dired-mode)
:config
;; make the `gnus-dired-mail-buffers' function also work on
;; message-mode derived modes, such as mu4e-compose-mode
(defun gnus-dired-mail-buffers ()
"Return a list of active message buffers."
(let (buffers)
(save-current-buffer
(dolist (buffer (buffer-list t))
(set-buffer buffer)
(when (and (derived-mode-p 'message-mode)
(null message-sent-message-via))
(push (buffer-name buffer) buffers))))
(nreverse buffers)))
(setq gnus-dired-mail-mode 'mu4e-user-agent))
(use-package sendmail
:ensure nil
:custom
(mail-user-agent 'sendmail-user-agent)
(mail-signature nil)
(mail-self-blind t)
(mail-signature-file (concat kimim/path-emacs "signature.txt")))
(use-package mu-cite
:commands (mu-cite-original)
:config
(setq mu-cite-top-format '("On " date ", " from " wrote:\n\n"))
(setq mu-cite-prefix-format '(" > ")))
(eval-and-compile
(defun mu4e-load-path ()
(cond ((eq system-type 'darwin)
"/usr/local/Cellar/mu/1.0_1/share/emacs/site-lisp/mu/mu4e")
((eq system-type 'windows-nt)
"/usr/local/share/emacs/site-lisp/mu4e")
((eq system-type 'gnu/linux)
"/usr/local/share/emacs/site-lisp/mu4e/"))))
(use-package mu4e
:ensure nil
:functions (mu4e-compose-reply
mu4e~view-quit-buffer)
:defines (mu4e-html2text-command
mu4e-mu-binary
mu4e-get-mail-command
mu4e-update-interval
mu4e-hide-index-messages
mu4e-use-fancy-chars
mu4e-view-show-images
mu4e-view-fields
mu4e-headers-fields
mu4e-compose-cite-function
mu4e-compose-reply-recipients
mu4e-headers-mode-map
mu4e-compose-mode-map
mu4e-view-mode-map
shr-color-visible-luminance-min
shr-color-visible-distance-min)
:custom
(mu4e-compose-reply-recipients 'sender)
(mu4e-compose-signature-auto-include nil)
:commands (mu4e mu4e-compose-new)
:bind (
:map mu4e-headers-mode-map
("r" . kimim/mu4e-compose-reply-sender)
("R" . kimim/mu4e-compose-reply-all)
("f" . kimim/mu4e~view-quit-buffer)
:map mu4e-compose-mode-map
("<C-tab>" . message-tab)
:map mu4e-view-mode-map
("<home>" . move-beginning-of-line)
("<end>" . move-end-of-line)
("r" . kimim/mu4e-compose-reply-sender)
("R" . kimim/mu4e-compose-reply-all))
:load-path (lambda () (list (mu4e-load-path)))
:config
(require 'sendmail)
;; turn html email to lighter color in dark theme
(require 'mu4e-contrib)
(setq mu4e-html2text-command 'mu4e-shr2text)
(setq shr-color-visible-luminance-min 60)
(setq shr-color-visible-distance-min 5)
(setq shr-use-colors nil)
(advice-add #'shr-colorize-region :around (defun shr-no-colourise-region (&rest ignore)))
(require 'org-mu4e) ;; capture link
(add-to-list 'Info-additional-directory-list "/usr/local/share/info")
(setq mu4e-mu-binary "/usr/local/bin/mu")
;; (cond ((eq system-type 'gnu/linux)
;; (setq mu4e-mu-binary "/snap/bin/mu")))
(setq mail-user-agent 'mu4e-user-agent)
;; Fetch mail by offlineimap
(setq mu4e-get-mail-command "offlineimap -c ~/.offlineimaprc -u quiet")
;; Fetch mail in 60 sec interval
(setq mu4e-update-interval 300)
;; hide indexing messages from minibuffer
(setq mu4e-hide-index-messages t)
(setq mu4e-use-fancy-chars nil)
(setq mu4e-view-show-images t)
(setq mu4e-view-fields
'(:subject :from :to :cc :date :mailing-list
:attachments :signature :decryption))
(setq mu4e-headers-fields
'( (:human-date . 12)
(:flags . 6)
(:from . 22)
(:subject . nil)))
(setq mu4e-compose-cite-function 'mu-cite-original)
(add-hook 'mu4e-view-mode-hook 'visual-line-mode)
(add-hook 'mu4e-compose-mode-hook 'kimim/mail-setup)
(add-hook 'mu4e-compose-mode-hook 'orgalist-mode)
(add-hook 'mu4e-compose-mode-hook (lambda ()
(auto-fill-mode -1)))
(defun kimim/mu4e~view-quit-buffer ()
(interactive)
(when (get-buffer "*mu4e-view*")
(switch-to-buffer "*mu4e-view*")
(mu4e~view-quit-buffer)))
(defun kimim/mu4e-compose-reply-sender ()
(interactive)
(set (make-local-variable 'mu4e-compose-reply-recipients) 'sender)
(mu4e-compose-reply))
(defun kimim/mu4e-compose-reply-all ()
(interactive)
(set (make-local-variable 'mu4e-compose-reply-recipients) 'all)
(mu4e-compose-reply)))
(use-package restclient
:mode ("\\.http\\'" . restclient-mode)
:bind (:map restclient-mode-map
("C-c C-c" . restclient-http-send-current-stay-in-window)
("C-c C-v" . restclient-http-send-current)))
Sometimes, you need to encrypt some secret files, setting epa-pinentry-mode
to
loopback
will prompt password inside minibuffer, while not show a dialog for it.
And we also cache the symmetric key in the same
(use-package epa
:ensure nil
:custom
(epa-pinentry-mode 'loopback)
(epa-file-cache-passphrase-for-symmetric-encryption t))
doc-view-mode can view many kind of documents, such as PDF, PS and images. You should install postscript in cygwin.
(use-package doc-view
:custom
(doc-view-continuous t)
(doc-view-image-width 500)
(doc-view-resolution 300))
[fn:1] http://www.literateprogramming.com/
[fn:2] https://orgmode.org/
[fn:3] https://www.msys2.org/
[fn:4] http://cygwin.com/
[fn:5] https://cygwin.com/setup-x86_64.exe
[fn:6] http://kimi.im/2021-01-28-emacs-inside-manjaro-wsl2-windows
[fn:7] http://brew.sh/
[fn:8] https://www.gnu.org/software/emacs/manual/html_node/emacs/Packages.html
[fn:9] syl20bnr/spacemacs#381
[fn:10] https://github.com/Fuco1/smartparens
[fn:11] https://clojure.org/
[fn:12] https://cider.mx/
[fn:13] https://github.com/jorgenschaefer/elpy
[fn:14] https://github.com/ralesi/ahk-mode
[fn:15] https://mermaid-js.github.io/
[fn:16] https://zettelkasten.de/
[fn:17] https://en.wikipedia.org/wiki/Niklas_Luhmann
[fn:18] https://pandoc.org/