kimim / kimim-emacs

my emacs init configuration files
http://kimi.im/kimim-emacs
GNU General Public License v2.0
43 stars 6 forks source link

+TITLE: README

+LATEX_CLASS: article

+OPTIONS: toc:nil

+STARTUP: show2levels

[[https://travis-ci.org/kimim/kimim-emacs][https://travis-ci.org/kimim/kimim-emacs.svg]]

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.

** Windows/MSYS2

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:

+begin_src shell

pacman -S git mingw-w64-x86_64-emacs

+end_src

** Windows/Cygwin

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.

+begin_src bat

C:\cygwin64\bin\run.exe emacs-w32

+end_src

You can also add =emacs --daemon= in your scheduled task, and change the target field as:

+begin_src bat

C:\cygwin64\bin\run.exe emacsclient -c

+end_src

This will attach the client to the server daemon. You can get a running =emacs= very quick.

** Windows/WSL2

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.

** Linux

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:

+begin_src shell

sudo snap install emacs --classic

+end_src

It is also very quick to add emacs in ArchLinux/Manjaro:

+begin_src shell

pacman -S emacs

+end_src

** macOS

For Apple macOS, most UNIX tools are installed already. You can use homebrew [fn:7] to install additional application if it is missing.

+begin_src shell

/bin/bash -c \ "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" brew tap railwaycat/emacsmacport brew install emacs-mac

+end_src

You can follow below shell commands to prepare the =emacs= configuration files and folders:

+begin_src shell

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 ~

+end_src

This configuration uses several path to keep different information, you need to define them in =~/.emacs=:

Let's tell =emacs=, if the path is not set, report error, if the path is set, but not exists, create it.

+begin_src emacs-lisp

(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))

+end_src

Then you can execute =emacs= to bootstrap itself.

** chemacs2

Another way to try this configure out is to use [[https://github.com/plexus/chemacs2][chemacs2]].

Clone to ~~/kimim-emacs~

+begin_src shell

git clone https://github.com/kimim/kimim-emacs ~/kimim-emacs

+end_src

Add to chemacs2 profile file ~~/.emacs.profile.el~

+begin_src emacs-lisp :tangle no

(("default" . ((user-emacs-directory . "~/.emacs.default"))) ("kimim" . ((user-emacs-directory . "~/kimim-emacs"))))

+end_src

Start emacs with this config:

+begin_src shell

emacs --with-profile kimim

+end_src

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~.

+begin_src emacs-lisp :tangle no

(defvar kimim/path-kimim-emacs "~/.emacs.kimim/")

+end_src

** PATH and exec-path

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.

+begin_src emacs-lisp

(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"))

+end_src

Then append exec-path to PATH:

+begin_src emacs-lisp

(setenv "PATH" (concat (mapconcat #'identity exec-path path-separator) (getenv "PATH")))

+end_src

For Windows/MSYS64, we need to modify =executable-find= to locate shell scripts:

+begin_src emacs-lisp

(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 inexec-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))))

+end_src

** Language

I prefer to use English/UTF-8 as default language environment.

+begin_src emacs-lisp

(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)))

+end_src

** global key map

Define new command prefix for keys such as "C-x m f", "C-x m v".

+begin_src emacs-lisp

(define-prefix-command 'ctl-x-m-map) (global-set-key "\C-xm" 'ctl-x-m-map)

+end_src

=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.

+begin_src emacs-lisp

;; 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)

+end_src

Don't display menu-bar, tool-bar, tooltip and scroll-bar. Because sometimes, they may catch your attention.

+begin_src emacs-lisp

(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 (eq window-system 'w32) (menu-bar-mode 0))) ((eq window-system nil) (menu-bar-mode 0))))

+end_src

** Font and Frame Size

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=.

+begin_src emacs-lisp

(use-package cnfonts :bind (("C-+" . cnfonts-increase-fontsize) ("C--" . cnfonts-decrease-fontsize) ("C-=" . cnfonts-increase-fontsize) ("C-0" . cnfonts-reset-fontsize)))

+end_src

+begin_src emacs-lisp

(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)

+end_src

** Frames

Customize the frame title to display buffer file name.

+begin_src emacs-lisp

(setq frame-title-format '((:eval (buffer-name)))) ;; don't expand minibuffer in other frame (setq minibuffer-follows-selected-frame nil)

+end_src

** Mode Line

Display date and time, but do not display system load.

+begin_src emacs-lisp

(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))

+end_src

Show (line, column) numbers in mode line:

+begin_src emacs-lisp

(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))

+end_src

** Celestial modeline

+begin_quote

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

+end_quote

+begin_src emacs-lisp

(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 "")))

+end_src

Add sunrise/sunset info to modeline text description.

+begin_src emacs-lisp

(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))

+end_src

** awesome tray

~awesome-tray~ adds an overlay to minibuffer and hides mode line to save window space.

+begin_src emacs-lisp

(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"))))))

+end_src

** path in headerline

When minimalistic emacs is on with awesome tray, we sometimes need to lookup buffer file name with header line.

+begin_src emacs-lisp

(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))))

+end_src

** Color Theme

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.

+begin_src emacs-lisp

(use-package rainbow-mode :diminish rainbow-mode)

+end_src

Rainbow-delimiters is a "rainbow parentheses"-like mode which highlights parentheses, brackets, and braces according to their depth.

+begin_src emacs-lisp

(use-package rainbow-delimiters :diminish rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode))

+end_src

Toggle Font-Lock mode in all buffers.

+begin_src emacs-lisp

(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))

+end_src

Use kimim-light as default theme.

+begin_src emacs-lisp

(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)))

+end_src

** Image

Always set white background color for transparent images to display clearly in dark color theme.

+begin_src emacs-lisp

(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))

+end_src

** posframe

+begin_src emacs-lisp

(use-package posframe)

+end_src

** Other Visual Element

+begin_src emacs-lisp

(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)

+end_src

+begin_src emacs-lisp

(use-package hi-lock :custom (hi-lock-auto-select-face t))

+end_src

In Windows environment, =kimim/xterm= and =kimim/dc= will look up the program from system PATH, so you should set these to system PATH.

+begin_src emacs-lisp

(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 (("" . kimim/xterm) ("S-" . kimim/cmd) ;; when deploy clojars, need msys2 term to use pinetry ("C-" . 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) ("" . delete-other-windows) ("C-" . nuke-other-buffers) ("M-" . kimim/switch-to-scratch-and-nuke-others) ("" . other-window) ("" . kimim/switch-to-scratch-buffer) ("C-" . kimim/switch-to-scratch-other-window) ("" . bury-buffer) ("" . 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-" . 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)))

+end_src

+begin_src emacs-lisp

(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"))

+end_src

** Youdao dictionary

Search dictionary with Ctrl+F3 by youdao dictionary.

+begin_src emacs-lisp

(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) ("" . 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)))))

+end_src

** sdcv

A local dictionary tool is useful when network connection has problems. You can get dictionary files from [[https://github.com/manateelazycat/lazycat-emacs/tree/master/site-lisp/sdcv-dict][lazycat-emacs]], or [[http://download.huzheng.org/dict.org/][huzheng.org]].

+begin_src emacs-lisp

(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))

+end_src

Need to install ~mpg123~ or ~mpv~ to play the pronunciations.

+begin_src shell

pacman -S mingw-w64-ucrt-x86_64-mpg123

+end_src

** fanyi.el

A translator extension written by [[https://condy0919.github.io][Youmu]].

I added ~kimim/fanyi-dwim-add-sdcv~ as ~:after~ advice for ~fanyi-dwim~ to look up local dictionary with help from ~sdcv~.

+begin_src emacs-lisp

(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) ("" . 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))

+end_src

** Wordreference

+begin_src emacs-lisp

(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))))

+end_src

** dict.leo.org Translate word between English and German.

+begin_src emacs-lisp

(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"))))

+end_src

** reverso Translate between almost any languages.

+begin_src emacs-lisp

(use-package reverso :commands (reverso reverso-translate))

+end_src

** eldoc-box

Show eldoc in a childframe box.

+begin_src emacs-lisp

(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)))

+end_src

Increase right margin from default 16 to 40 to remove some overlap.

+begin_src emacs-lisp

(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)))))

+end_src

** sis Emacs smart input source ([[https://github.com/laishulu/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-= to toggle between IME and NonIME, otherwise, ~sis~ will bring IME back after some keycords.

+begin_src emacs-lisp :results none

(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))

+end_src

** General

+begin_src emacs-lisp

(use-package autorevert :ensure nil :diminish auto-revert-mode)

+end_src

+begin_src emacs-lisp

(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" "~"))

+end_src

** multi cursors

You can mark a region, and ~C-S-c C-S-c~ to start edit every line in this region. That's amazing.

+begin_src emacs-lisp

(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))

+end_src

** Highlight

Highlight current line in window systems, but disable this in terminal. Because the line highlight will cause the terminal blinking.

+begin_src emacs-lisp

(use-package hl-line :if window-system :config (global-hl-line-mode -1))

+end_src

** pulsar

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.

+begin_src emacs-lisp

(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))

+end_src

** ispell

[[https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS.26#n1155][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 [[https://github.com/Eason0210][Eason0210]] at [[https://emacs-china.org/t/emacs-enchant-spell-checker/24035][Emacs China]] on [2023-07-02 Sun].

Install ~enchant~.

Windows/msys64:

+begin_src shell

pacman -S mingw64/mingw-w64-x86_64-enchant

+end_src

macOS

+begin_src shell

sudo port install enchant2

or

brew install enchant

+end_src

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 [[https://abiword.github.io/enchant/src/enchant.html][enchant manual]].

+begin_src shell

export ENCHANT_CONFIG_DIR=~/.config/enchant/

+end_src

+begin_src emacs-lisp

(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))))

+end_src

** flyspell

Check spell on the fly.

+begin_src emacs-lisp

(use-package flyspell :diminish flyspell-mode :hook (;;(prog-mode . flyspell-prog-mode) (org-mode . flyspell-mode)))

+end_src

** olivetti

+begin_src emacs-lisp

(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))))))

+end_src

** Chinese word segmentation

+begin_src emacs-lisp

(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")))) (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))

+end_src

** wraplish

Automatically add space between Chinese and English, and more.

+begin_src emacs-lisp

(use-package wraplish :commands wraplish-mode :after text-mode :load-path (lambda () (concat kimim/path-kimim-emacs "site-lisp/wraplish")))

+end_src

Before enable wraplish, you need to install python, pip and epc.

+begin_src shell

pacman -S mingw-w64-x86_64-python mingw-w64-x86_64-python3-pip pip install epc

+end_src

** tempel

Use ~tempel~ to expand template.

+begin_src emacs-lisp

;; Configure Tempel (use-package tempel :bind (("M-+" . tempel-complete) ("M-=" . tempel-complete) ("M-*" . tempel-insert) :map tempel-map ("M-RET" . tempel-done) ("S-" . tempel-previous) ("" . tempel-next) ("M-" . tempel-previous) ("M-" . tempel-next)))

(use-package tempel-collection)

+end_src

By enabling ~winner-mode~, you can restore to previous window configuration by typing ~C-c M-~.

+begin_src emacs-lisp

(use-package winner ;; restore windows configuration, built-in package :commands winner-mode :bind ("C-x M-" . winner-undo) ("C-x M-" . winner-redo) :config (winner-mode t))

+end_src

When type ~C-x m w~ it will create a new frame with the default frame configuration.

+begin_src emacs-lisp

(use-package frame :ensure nil :defer 1 :bind ("C-x m w" . make-frame))

+end_src

preserve the point in screen during scrolling looks nice (see [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Scrolling.html][scrolling]]). scroll slowly with touchpad, thus we adjust the scroll amount.

+begin_src emacs-lisp

(setq scroll-preserve-screen-position t) (setq mouse-wheel-scroll-amount '(0.01))

+end_src

** Move Frame

Move frame to one of the nine grids on the screen with ~C-x y ~ keys.

+begin_src emacs-lisp

(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))

+end_src

** Windmove

+begin_src emacs-lisp

(use-package windmove :bind ("C-x " . windmove-swap-states-left) ("C-x " . windmove-swap-states-right) ("C-x " . windmove-swap-states-up) ("C-x " . windmove-swap-states-down))

+end_src

** Command

Display key candidates when you typed part key prefix with ~which-key-mode~.

+begin_src emacs-lisp

;; 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))

+end_src

List recent used commands with ~smex~:

+begin_src emacs-lisp

;; smex will list the recent function on top of the cmd list (use-package smex :commands (smex) :config (smex-initialize))

+end_src

** Key Frequency

We will use =keyfreq= to record the frequency of the key typing, and get a frequency report by =M-x keyfreq-show=.

+begin_src emacs-lisp

(use-package keyfreq :custom (keyfreq-file "~/.emacs.d/emacs.keyfreq") :config (keyfreq-mode +1) (keyfreq-autosave-mode +1))

+end_src

** eshell

+begin_src emacs-lisp

(use-package eshell)

+end_src

** Navigation

+begin_src emacs-lisp

(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))))

+end_src

~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~.

+begin_src emacs-lisp

(use-package bm :bind (("C-x m t" . bm-toggle) ("C-x m s" . bm-show-all) ("C-x m " . bm-previous) ("C-x m " . bm-next)))

+end_src

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~).

+begin_src emacs-lisp

(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)))

+end_src

** vundo

+begin_src emacs-lisp

(use-package vundo :bind ("C-x u" . vundo))

+end_src

Emacs default ~isearch~ does not allow key binding in minibuffer, ~isearch-mb~ enhances at this aspect.

+begin_src emacs-lisp

(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))

+end_src

** imenu & imenu-anywhere

=imenu= is used to navigate the function definitions in current buffer.

+begin_src emacs-lisp

(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))

+end_src

** ripgrep: a fast command line search tool

+begin_src emacs-lisp

(use-package ripgrep :bind ("C-x g" . ripgrep-regexp))

+end_src

** search from web

+begin_src emacs-lisp

(use-package eww :custom (eww-search-prefix "https://cn.bing.com/search?q="))

+end_src

** recentf

+begin_src emacs-lisp

(use-package recentf :config (recentf-mode))

+end_src

** avy

+begin_src emacs-lisp

(use-package avy :bind ("C-x m g" . avy-goto-word-or-subword-1))

+end_src

To avoid accidentally delete files, let emacs move the deleted file to trash.

+begin_src emacs-lisp

(setq delete-by-moving-to-trash t)

+end_src

** dired

+begin_src emacs-lisp

(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) ("" . dired-up-directory) ("" . 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) ("" . 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")))

+end_src

** dired-recent

Keep a list of recently visited directories. Then we can quickly revisit them.

+begin_src emacs-lisp

(use-package dired-recent :config (dired-recent-mode 1))

+end_src

** dired-efap

dired-efap, Edit file at point, can be used to rename file name at the point:

+begin_src emacs-lisp

(use-package dired-efap :commands dired-efap)

+end_src

** dired-narrow

~M-n~ will prompt for strings to narrow the files in current dired buffer.

+begin_src emacs-lisp

(use-package dired-narrow :commands dired-narrow)

+end_src

** dired-filter

+begin_src emacs-lisp

(use-package dired-filter :diminish dired-filter-mode)

+end_src

** ibuffer

~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~.

+begin_src emacs-lisp

(use-package ibuffer :bind (("C-x C-b" . ibuffer-other-window) :map ibuffer-mode-map ("" . 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))))))

+end_src

+begin_src emacs-lisp

(use-package marginalia :init (marginalia-mode))

+end_src

** consult

+begin_src emacs-lisp

(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 (" 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 "") (kbd "")) ;; 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' andvertico-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 tryconsult-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))

+end_src

** abbrev

+begin_src emacs-lisp

(diminish 'abbrev-mode)

+end_src

** corfu

+begin_src emacs-lisp

(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))

+end_src

+begin_src emacs-lisp

(use-package corfu :defer 1 :bind (:map corfu-map (" " . move-beginning-of-line) (" <move-end-of-line" . move-end-of-line) ("M-h" . backward-kill-word) ("" . 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))

+end_src

+begin_src emacs-lisp

(use-package corfu-prescient :commands (corfu-prescient-mode) :config (setq corfu-prescient-override-sorting t))

+end_src

** cape

+begin_src emacs-lisp

(use-package cape :init ;; Add completion-at-point-functions', used bycompletion-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))

+end_src

~project-find-file~ (~C-x p f~) can find files of current project, indicated by git or other version control information.

+begin_src emacs-lisp :results none

(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")))))

+end_src

** Compiling

+begin_src emacs-lisp

(setq next-error-recenter 20) (setq compilation-scroll-output t) (bind-key "C-" 'compile)

+end_src

** Version Control

Bind ~magit~ to ~C-x p m~ with the same prefix of ~project~, as they have strong relationship.

+begin_src emacs-lisp :results none

(use-package magit :bind (("C-x p m" . magit-status-here) (:map magit-revision-mode-map) ("C-" . magit-diff-visit-file-other-window) ("" . magit-diff-visit-worktree-file-other-window) (:map magit-diff-mode-map) ("C-" . magit-diff-visit-file-other-window) ("" . magit-diff-visit-worktree-file-other-window)) :custom (magit-log-show-refname-after-summary t))

+end_src

Following error will reported when using magit to commit changes:

+BEGIN_QUOTE

server-ensure-safe-dir: The directory ‘~/.emacs.d/server’ is unsafe

+END_QUOTE

The solution is to change the owner of =~/.emacs.d/server= [fn:9]

+BEGIN_QUOTE

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.

+END_QUOTE

** Use tempel for LSP

+begin_src emacs-lisp

(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)))

+end_src

** LSP with Eglot

~eglot~ is simple and built-in LSP support in emacs.

+begin_src emacs-lisp

(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)))))

+end_src

** Parenthesis

~smartparens-mode~[fn:10] is a general purpose mode for dealing with parenthesis. We define some keys for it:

+begin_src emacs-lisp :results none

(use-package smartparens :bind (:map smartparens-mode-map ("C-" . sp-forward-slurp-sexp) ("C-" . sp-forward-barf-sexp) ("M-" . sp-backward-barf-sexp) ("M-" . sp-backward-slurp-sexp) ("M-" . sp-splice-sexp-killing-backward) ("M-" . sp-splice-sexp-killing-forward) ("C-k" . sp-kill-hybrid-sexp) ("M-k" . sp-kill-sexp) ("" . sp-backward-delete-char) ("C-d" . sp-delete-char) ("C-M-" . 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))))

+end_src

** COMMENT parinfer-rust-mode

+begin_src emacs-lisp

(use-package parinfer-rust-mode :commands parinfer-rust-mode :hook ((clojure-mode clojurec-mode clojurescript-mode) . parinfer-rust-mode))

+end_src

** tree-sitter

+begin_src emacs-lisp

(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)))

+end_src

** ts-fold

+begin_src emacs-lisp

(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-" . ts-fold-toggle))

+end_src

** COMMENT Code folding

With ~yafolding-mode~, you can:

+begin_src emacs-lisp

(use-package yafolding :hook (prog-mode . yafolding-mode))

+end_src

** static code analysis

+begin_src emacs-lisp

(use-package flycheck :commands (global-flycheck-mode) :custom (flycheck-global-modes '(not org-mode)))

+end_src

** eldoc

+begin_src emacs-lisp

(use-package eldoc :hook (prog-mode . eldoc-mode) :diminish eldoc-mode :custom (eldoc-echo-area-use-multiline-p nil))

+end_src

** cmake mode

+begin_src emacs-lisp

(use-package cmake-mode :mode ("CMakeLists\.txt\'" . cmake-mode))

+end_src

** xref

+begin_src emacs-lisp

(use-package xref :ensure nil :custom (xref-after-return-hook '(xref-pulse-momentarily recenter)) :bind (:map prog-mode-map ("C-." . xref-find-references)))

+end_src

+begin_src emacs-lisp

(use-package clang-format)

+End_src

+begin_src emacs-lisp

(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))

+end_src

+begin_src emacs-lisp

(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-" . 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 . /)))))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

+begin_src emacs-lisp

(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))

+end_src

** C#

+begin_src emacs-lisp

(use-package csharp-mode :ensure nil :hook ((csharp-mode . tree-sitter-mode) (csharp-mode . tree-sitter-hl-mode)) :mode ("\.cs\'" . csharp-mode))

+end_src

** Clojure

Clojure[fn:11] is a lisp over JVM. Emm, I like it.

+begin_src emacs-lisp

(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))

+end_src

+begin_src emacs-lisp

(use-package clojure-mode-extra-font-locking)

+end_src

*** clj-kondo

Install with npm:

+begin_src shell

npm install -g clj-kondo

+end_src

+begin_src emacs-lisp

(use-package flycheck-clj-kondo)

+end_src

*** Cider

Cider[fn:12] extends Emacs with support for interactive programming in Clojure.

+begin_src emacs-lisp

(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\""))))))

+end_src

+begin_src emacs-lisp

(use-package ob-clojure :ensure org :custom (org-babel-clojure-backend 'cider) :config (add-to-list 'org-src-lang-modes '("clojure" . clojure)))

+end_src

** Babashka Download ~bb~ from https://github.com/babashka/babashka/releases and put ~bb~ to execute PATH.

+begin_src emacs-lisp

(use-package ob-bb :load-path (lambda () (concat kimim/path-kimim-emacs "site-lisp/ob-bb")))

+end_src

** Python

Python development configuration is quite easy. =elpy= [fn:13] is used here:

+begin_src emacs-lisp

;; (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"))

+end_src

Following =python= package is required according to =elpy= mannual:

+begin_src shell

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

+end_src

** Rust

The easiest way to install rust is to run following script:

+begin_src shell

curl https://sh.rustup.rs -sSf | sh

+end_src

+begin_src emacs-lisp

(use-package rustic :hook (rustic-mode . (lambda () (set (make-local-variable 'compile-command) "cargo run"))) :custom (rustic-lsp-client 'eglot))

+end_src

** R ESS

+begin_src emacs-lisp

(use-package ess :custom (inferior-ess-r-program "r"))

+end_src

** Swift

+begin_src emacs-lisp

(use-package swift-mode :mode ("\.swift\'" . swift-mode))

+end_src

** TypeScript

+begin_src emacs-lisp

(use-package typescript-mode :hook ((typescript-mode . tree-sitter-mode) (typescript-mode . tree-sitter-hl-mode) (typescript-mode . eglot-ensure)))

+end_src

** json

+begin_src emacs-lisp :results none

(use-package json-mode :mode ("\.json\'" . json-mode) :hook ((json-mode . tree-sitter-mode) (json-mode . tree-sitter-hl-mode)))

+end_src

** Golang Open =.go= file with go-mode.

+begin_src emacs-lisp

(use-package go-mode :mode ("\.go\'" . go-mode))

+end_src

** Docker file

Some dockerfile is not end with =.dockerfile=, so lets guess:

+begin_src emacs-lisp

(use-package dockerfile-mode :mode ("\dockerfile\'" . dockerfile-mode))

+end_src

** Emacs lisp

+begin_src emacs-lisp

(use-package elisp-mode :ensure nil :mode ("\.el\'" . emacs-lisp-mode) :config (define-derived-mode lisp-interaction-mode emacs-lisp-mode "ᴧ"))

+end_src

** AutoHotKey

=ahk-mode= developed by Rich Alesi[fn:14]

+begin_src emacs-lisp

(use-package ahk-mode :mode ("\.ahk\'" . ahk-mode))

+end_src

** yaml mode

+begin_src emacs-lisp

(use-package yaml-mode :mode ("\.yml\'" . yaml-mode) :bind (:map yaml-mode-map ("\C-m" . newline-and-indent)))

+end_src

** shell

+begin_src emacs-lisp

(use-package shell :mode ("\.sh\'" . shell-script-mode))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

** powershell

+begin_src emacs-lisp

(use-package powershell :mode ("\.ps1\'" . powershell-mode))

+end_src

** solidity

Major mode to edit ethereum solidity smart contract code.

+begin_src emacs-lisp

(use-package solidity-mode :mode ("\.sol\'" . solidity-mode))

+end_src

** lua and fennel

+begin_src emacs-lisp

(use-package lua-mode) (use-package fennel-mode)

+end_src

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:

+begin_src shell

brew install graphviz

+end_src

Download =plantuml.jar= from https://plantuml.com/download, and put it to some place and assign ~plantuml-jar-path~ to there.

+begin_src emacs-lisp

(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")))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

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.

+begin_src emacs-lisp

(use-package image-mode :ensure nil :custom (image-auto-resize 'fit-window) :config (add-to-list 'auto-mode-alist '("\.svg\'" . image-mode)))

+end_src

** graphviz & dot

~dot~ is used to draw simple diagram in code. You need to install ~graphviz~:

+begin_src shell

pacman -S mingw-w64-ucrt-x86_64-graphviz

+end_src

+begin_src emacs-lisp

(use-package graphviz-dot-mode :ensure t :custom (graphviz-dot-preview-extension "svg"))

+end_src

** ditaa

+begin_src emacs-lisp

(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)))

+end_src

** Mermaid

Mermaid [fn:15] is another js based diagramming and charting tool. To use it inside orgmode, mermaid-cli should be installed:

+begin_src shell

yarn add @mermaid-js/mermaid-cli

+end_src

~mmdc~ will be installed at ~~/node_modules/.bin/mmdc~. Then we just setup ~ob-mermaid~ and ~mermaid-mode~ for babel evaluation and editing.

+begin_src emacs-lisp

(use-package ob-mermaid :custom (ob-mermaid-cli-path "~/node_modules/.bin/mmdc.cmd") :config (add-to-list 'org-babel-load-languages '(mermaid . t)))

+end_src

+begin_src emacs-lisp

(use-package mermaid-mode :mode ("\.mermaid\'" . mermaid-mode))

+end_src

** iscroll

scroll images line by line.

+begin_src emacs-lisp

(use-package iscroll :diminish iscroll-mode :hook ((org-mode markdown-mode) . iscroll-mode))

+end_src

** chatu

+begin_src emacs-lisp

(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))

+end_src

+begin_src emacs-lisp

(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))

+end_src

+begin_src emacs-lisp

(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))

+end_src

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.

+begin_src emacs-lisp :results value silent

(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) ("". (lambda () (interactive) (image-scroll-up -999))) ("". (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) ("" . 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)))

+end_src

** pdf-view-store

To remember visited pages of PDF files.

+begin_src emacs-lisp

(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))

+end_src

** pdf-view-pagemark

Add indicator of remaining text when scrolling PDF pages.

+begin_src emacs-lisp

(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))

+end_src

** org-ref

+begin_src emacs-lisp

(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")))))

+end_src

+begin_src emacs-lisp

(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")))

+end_src

There is a built-in =bibtex-mode= to manage references. We can extend it to support more functions from =org-ref=:

+begin_src emacs-lisp

(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))

+end_src

** nov: reading epub books

+begin_src emacs-lisp

(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 ("" . beginning-of-line) ("" . end-of-line) ("" . 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))))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

** org-appear

+begin_src emacs-lisp

(use-package org-appear :commands (org-appear-mode) :ensure t :custom (org-appear-autolinks nil) :hook (org-mode . org-appear-mode))

+end_src

** org-indent

Add advice to ~org-indent--compute-prefixes~ to remove needless indent spaces before plain text line.

+begin_src emacs-lisp

(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))

+end_src

** org-modern

+begin_src emacs-lisp

(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))))))))

+end_src

** pretty-symbols-in-org-mode

+begin_src emacs-lisp

(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)))

+end_src

** org-inline-pdf 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.

+begin_src emacs-lisp

(use-package org-inline-pdf :hook (org-mode . org-inline-pdf-mode))

+end_src

** orgalist

+begin_src emacs-lisp

(use-package orgalist :commands (orgalist-mode))

+end_src

** org for writing

+begin_src emacs-lisp

(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 () "."))

+end_src

+begin_src emacs-lisp

(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))

+end_src

** org with source code

+begin_src emacs-lisp

(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)))

+end_src

** org exporting

When exporting, do not export with author and date.

+begin_src emacs-lisp

(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 "") :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)))))))))))))

+end_src

*** org to pdf

LaTeX is required to convert =org-mode= to PDF.

For MacOS:

+begin_src shell

brew cask install mactex-no-gui

+end_src

For Windows, there are three options:

  1. download and install CTEX from http://www.ctex.org
  2. install texlive-collection in cygwin

    +begin_src shell

    apt-cyg install texlive-collection-xetex \ texlive-collection-latex \ texlive-collection-fontsrecommended

    +end_src

  3. download and install texlive from [[http://tug.org/texlive/acquire-netinstall.html][tug.org]]

For Linux, download texlive install pacakge from [[http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz][ctan.org]]

+begin_src shell

tar zxvf install-tl-unx.tar.gz cd install-tl-20200908/ sudo ./install-tl

+end_src

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=):

+begin_src shell

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

+end_src

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.=

+begin_src shell

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py python get-pip.py

+end_src

Get =pygments= with =pip=:

+begin_src shell

pip install pygments

+end_src

For Ubuntu Linux:

+begin_src shell

sudo apt install python3-pygments

+end_src

+begin_src emacs-lisp

(use-package latex-classes :load-path (lambda () (concat kimim/path-kimim-emacs "site-lisp")) :config (require 'bibtex-completion))

+end_src

+begin_src emacs-lisp

(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))

+end_src

*** org to html page

+begin_src emacs-lisp

(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

" 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))))

+end_src

*** org to html slides

+begin_src emacs-lisp

(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/reveal.js@3.9.2/") (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"))

+end_src

** org link: page in pdf

+begin_src emacs-lisp

(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)))))))))

+end_src

** org link: onenote

New link to use Office Onenote.

+begin_src emacs-lisp

(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)))))))

+end_src

** org publish to jekyll

+begin_src emacs-lisp

(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)))))))))))

+end_src

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:

+begin_src shell

pacman -S mingw-w64-x86_64-sqlite3

+end_src

Windows/Cygwin:

+begin_src shell

apt-cyg install sqlite3

+end_src

sqlite3 is shipped in macOS by default.

+begin_src emacs-lisp

(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)))))

+end_src

** org-roam-bibtex

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.

+begin_src emacs-lisp

(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))

+end_src

** org-transclusion

Add a virtual piece of other org file to current one.

+begin_src emacs-lisp

(use-package org-transclusion :after org :custom (org-transclusion-include-first-section nil) :hook (org-mode . org-transclusion-add-all) :bind ("" . org-transclusion-add-all))

+end_src

** markdown mode

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 [[https://github.com/jgm/pandoc/releases/latest][latest version]] and put the binary file to system path, such as ~/usr/local/bin~, and then set ~markdown-command~ to ~pandoc~.

+begin_src emacs-lisp

(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))))))))))))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

** org as GTD system

+begin_src emacs-lisp

(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))))

+end_src

+begin_src emacs-lisp

(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) ("" . org-agenda-do-date-earlier) ("" . org-agenda-do-date-later) ("" . (lambda () (interactive) (org-agenda-todo 'left))) ("" . (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' orcategory' ;; 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))))))

+end_src

** org-pomodoro

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.

+begin_src emacs-lisp

(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))

+end_src

+begin_src emacs-lisp

(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)))))

+end_src

** erc

+begin_src emacs-lisp

;; 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"))

+end_src

** GNUS dired

+begin_src emacs-lisp

(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))

+end_src

** mu4e

+begin_src emacs-lisp

(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")))

+end_src

+begin_src emacs-lisp

(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 '(" > ")))

+end_src

+begin_src emacs-lisp

(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 ("" . message-tab) :map mu4e-view-mode-map ("" . move-beginning-of-line) ("" . 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)))

+end_src

+begin_src emacs-lisp

(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)))

+end_src

** COMMENT Reading News

+begin_src emacs-lisp

(use-package elfeed :functions elfeed-show-refresh :commands (elfeed) :custom (elfeed-curl-timeout 100) (browse-url-browser-function 'browse-url-default-browser) (elfeed-feeds '(("http://kimi.im/atom.xml" blog) ("https://blog.tecosaur.com/tmio/rss.xml" Org) ("www.chinadaily.com.cn/rss/bizchina_rss.xml" CN) ;; from https://www.foxnews.com/story/foxnews-com-rss-feeds ("http://feeds.foxnews.com/foxnews/world" EN) ("http://feeds.foxnews.com/foxnews/scitech" EN Tech) ;; from https://www.lefigaro.fr/rss ("https://www.lefigaro.fr/rss/figaro_secteur_high-tech.xml" FR Hightech) ("https://www.lefigaro.fr/rss/figaro_management.xml" FR Management) ;; from https://www.zeit.de/hilfe/hilfe#rss ("http://newsfeed.zeit.de/wissen/index" DE Wissen) ("http://newsfeed.zeit.de/kultur/index" DE Kultur))) :bind (:map elfeed-search-mode-map ("" . scroll-up-command) ("" . scroll-down-command) :map elfeed-show-mode-map ("M-q" . article-fill-long-lines) ("j" . next-line) ("k" . previous-line)) :config (require 'org) ;; open feed link with eww (require 'gnus-art) (advice-add 'elfeed-show-entry :after (lambda (entry) (setq line-spacing 0.4) (elfeed-show-refresh))))

+end_src

** Encryption

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

+begin_src emacs-lisp

(use-package epa :ensure nil :custom (epa-pinentry-mode 'loopback) (epa-file-cache-passphrase-for-symmetric-encryption t))

+end_src

** Viewing Documents

doc-view-mode can view many kind of documents, such as PDF, PS and images. You should install postscript in cygwin.

+begin_src emacs-lisp

(use-package doc-view :custom (doc-view-continuous t) (doc-view-image-width 500) (doc-view-resolution 300))

+end_src

[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] https://github.com/syl20bnr/spacemacs/issues/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/