Skip to content

Latest commit

 

History

History
executable file
·
4107 lines (3425 loc) · 127 KB

configuration.org

File metadata and controls

executable file
·
4107 lines (3425 loc) · 127 KB

Emacs Configuration

Configure use-package

(require 'use-package-ensure)
(setq use-package-verbose t)
(setq use-package-always-ensure t)

use auto-package-update to keep your packages updated automatically

(use-package auto-package-update
  :config
  (setq auto-package-update-delete-old-versions t)
  (setq auto-package-update-hide-results t)
  (auto-package-update-maybe))

Always compile packages, and use the newest version available.

(use-package auto-compile
  :config (auto-compile-on-load-mode)
  (setq load-prefer-newer t))

use-package supports git repository

(use-package quelpa-use-package
  :disabled t
  :config
  (setq use-package-ensure-function 'quelpa))
(setq comp-deferred-compilation t)

Use sensible-defaults.el

Use sensible-defaults.el for some basic settings.

(load-file "~/.emacs.d/sensible-defaults.el")
(sensible-defaults/use-all-settings)
(sensible-defaults/use-all-keybindings)
(sensible-defaults/backup-to-temp-directory)

Add resources to load-path

(add-to-list 'load-path "~/.emacs.d/resources/")

Utility functions

Define a big ol’ bunch of handy utility functions.

(defun my/view-buffer-name ()
  "Display the filename of the current buffer."
  (interactive)
  (message (buffer-file-name)))

(defun my/rename-file (new-name)
  (interactive "FNew name: ")
  (let ((filename (buffer-file-name)))
    (if filename
        (progn
          (when (buffer-modified-p)
             (save-buffer))
          (rename-file filename new-name t)
          (kill-buffer (current-buffer))
          (find-file new-name)
          (message "Renamed '%s' -> '%s'" filename new-name))
      (message "Buffer '%s' isn't backed by a file!" (buffer-name)))))

(defun my/generate-scratch-buffer ()
  "Create and switch to a temporary scratch buffer with a random
     name."
  (interactive)
  (switch-to-buffer (make-temp-name "scratch-")))

(defun my/unfill-paragraph ()
  "Takes a multi-line paragraph and makes it into a single line of text."
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))

(defun my/visit-last-dired-file ()
  "Open the last file in an open dired buffer."
  (end-of-buffer)
  (previous-line)
  (dired-find-file))

(defun my/add-auto-mode (mode &rest patterns)
  "Add entries to `auto-mode-alist' to use `MODE' for all given file `PATTERNS'."
  (dolist (pattern patterns)
    (add-to-list 'auto-mode-alist (cons pattern mode))))

(defun my/find-file-as-sudo ()
  (interactive)
  (let ((file-name (buffer-file-name)))
    (when file-name
      (find-alternate-file (concat "/sudo::" file-name)))))

(defun my/region-or-word ()
  (if mark-active
      (buffer-substring-no-properties (region-beginning)
                                      (region-end))
    (thing-at-point 'word)))

(defun my/insert-random-string (len)
  "Insert a random alphanumeric string of length len."
  (interactive)
  (let ((mycharset "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstyvwxyz"))
    (dotimes (i len)
      (insert (elt mycharset (random (length mycharset)))))))

(defun my/generate-password ()
  "Insert a good alphanumeric password of length 30."
  (interactive)
  (my/insert-random-string 30))

(defun my/append-to-path (path)
  "Add a path both to the $PATH variable and to Emacs' exec-path."
  (setenv "PATH" (concat (getenv "PATH") ":" path))
  (add-to-list 'exec-path path))

proxy settings

;; https://github.com/happyo/emacs.d/blob/main/lisp/init-proxy.el
(setq my/socks-proxy "127.0.0.1:7891")    ; SOCKS proxy
(setq my/remote-host-name "mac")
(setq my/socks5 nil)

(defun my/new-socks5-proxy ()
  (apply #'start-process "socks5-proxy"
         (format "*socks5-proxy-%s*" my/remote-host-name)
         "ssh" (list "-ND" my/socks-proxy my/remote-host-name)))

(defun proxy-socks-show ()
  "Show SOCKS proxy."
  (interactive)
  (if (process-live-p my/socks5)
      (message "Current SOCKS%d proxy is %s:%s"
               (cadddr socks-server) (cadr socks-server) (caddr socks-server))
    (message "No SOCKS proxy")))

(defun proxy-socks-enable ()
  "Enable SOCKS proxy."
  (interactive)
  (unless (process-live-p my/socks5)
    (setq my/socks5 (my/new-socks5-proxy)))
  (require 'socks)
  (setq url-gateway-method 'socks
        socks-noproxy '("localhost"))
  (let* ((proxy (split-string my/socks-proxy ":"))
         (host (car proxy))
         (port (string-to-number (cadr proxy))))
    (setq socks-server `("Default server" ,host ,port 5)))
  ;; (setenv "all_proxy" (concat "socks5://" my/socks-proxy))
  (proxy-socks-show))

(defun proxy-socks-disable ()
  "Disable SOCKS proxy."
  (interactive)
  (when my/socks5
    (kill-process my/socks5)
    (setq my/socks5 nil))
  (setq url-gateway-method 'native
        socks-noproxy nil
        socks-server nil)
  ;; (setenv "all_proxy" "")
  (proxy-socks-show))

(defun proxy-socks-toggle ()
  "Toggle SOCKS proxy."
  (interactive)
  (if (process-live-p my/socks5)
      (proxy-socks-disable)
    (proxy-socks-enable)))

UI preferences

Tweak window chrome

I don’t usually use the menu or scroll bar, and they take up useful space.

(tool-bar-mode -1)
(if (display-graphic-p)
    (progn (scroll-bar-mode -1))
  (menu-bar-mode -1))

Use fancy lambdas

Why not?

(global-prettify-symbols-mode t)

Load up a theme

personal preferences

better vertical border and region color

;; Set symbol for the border │ or ┃
(set-display-table-slot standard-display-table
                        'vertical-border
                        (make-glyph-code ?┃))
(defun transparency (value)
  "Sets the transparency of the frame window. 0=transparent/100=opaque."
  (interactive "Transparency Value 0 - 100 opaque:")
  (set-frame-parameter (selected-frame) 'alpha value))

theme manager

Those themes are what I like most.

(use-package modus-themes
  :no-require t
  :custom
  (modus-themes-italic-constructs t)
  (modus-themes-bold-constructs nil))
(use-package ef-themes
  :no-require t)

Theme loading symbols.

(defvar theme-pool
  '(modus-vivendi
    ;; modus-operandi
    ;; modus-operandi-tinted
    ;; modus-vivendi-tinted
    ;; modus-operandi-deuteranopia
    ;; modus-vivendi-deuteranopia
    ;; modus-operandi-tritanopia
    ;; modus-vivendi-tritanopia
    ef-melissa-light
    ;; ef-dark
    ;; ef-day
    ;; ef-night
    ))

(defvar my/choosing-theme 'ef-melissa-light)
(defun my/ef-themes-custom-faces ()
  (ef-themes-with-colors
    (if (equal "ef-melissa-light" (symbol-name (car custom-enabled-themes)))
        (custom-set-faces
         `(font-lock-comment-face ((,c :inherit italic :foreground "grey"))))
      (custom-set-faces
       `(font-lock-comment-face ((,c :inherit italic :foreground ,fg-dim)))))
    ))

;; Using the hook lets our changes persist when we use the commands
;; `ef-themes-toggle', `ef-themes-select', and `ef-themes-load-random'.
(add-hook 'ef-themes-post-load-hook #'my/ef-themes-custom-faces)
(defun my/load-theme (theme)
  (face-remap-add-relative 'vertical-border '(:inherit default))
  (load-theme theme t)
  (my/ef-themes-custom-faces))
(defun my/themes-toggle ()
  "Toggle between themes defined in theme-pool."
  (interactive)
  (let* ((current-theme (car custom-enabled-themes))
         (index (cl-position current-theme theme-pool)))
    (if index
        (setq my/choosing-theme
              (nth (% (+ 1 index) (length theme-pool)) theme-pool))
      (setq my/choosing-theme (car theme-pool)))
    (disable-theme current-theme)
    (my/load-theme my/choosing-theme)
    (message "change theme to `%s'" my/choosing-theme)))

Using light theme on MacOS.

(defun my/apply-light-theme ()
  (transparency 98)
  (load-theme 'ef-day t))
(if (display-graphic-p)
    (my/apply-light-theme)
  (my/load-theme my/choosing-theme))
(if (daemonp)
    (add-hook 'after-make-frame-functions
              (lambda (frame)
                (with-selected-frame frame
                  (my/load-theme my/choosing-theme)))))

Custom face when running in 256-color terminal

(defun my/load-custom-face-el ()
  (if (and (eq my/choosing-theme 'nord)
           (eq 256 (display-color-cells)))
      (load-file "~/.emacs.d/custom-face.el")))

(if (daemonp)
    (add-hook 'after-make-frame-functions
              (lambda (frame)
                (with-selected-frame
                    frame (my/load-custom-face-el))))
  (my/load-custom-face-el))

Mode-Line

(use-package minions
  :custom
  (minions-mode-line-delimiters (cons "" ""))
  (minions-prominent-modes '(flymake-mode))

  :config
  (defun +set-minions-mode-line-lighter ()
    (setq minions-mode-line-lighter
          (if (display-graphic-p) "" "#")))

  (add-hook 'server-after-make-frame-hook #'+set-minions-mode-line-lighter)
  (minions-mode 1))
(use-package time
  :config
  (display-time)
  :custom
  (display-time-default-load-average nil))
(size-indication-mode)
(column-number-mode)
(setq mode-line-frame-identification " ")
(setq mode-line-end-spaces "")

Set default font and configure font resizing

I’m partial to Inconsolata.

The standard text-scale- functions just resize the text in the current buffer; I’d generally like to resize the text in every buffer, and I usually want to change the size of the modeline, too (this is especially helpful when presenting). These functions and bindings let me resize everything all together!

Note that this overrides the default font-related keybindings from sensible-defaults.

(if (eq system-type 'gnu/linux)
    (setq my/default-font "Ubuntu Mono")
  (setq my/default-font "Iosevka SS09"))

(setq my/default-font-size 15)
(setq my/current-font-size my/default-font-size)

(setq my/font-change-increment 1.1)

(defun my/font-code ()
  "Return a string representing the current font (like \"Inconsolata-14\")."
  (concat my/default-font "-" (number-to-string my/current-font-size)))

(defun my/set-font-size ()
  "Set the font to `my/default-font' at `my/current-font-size'.
      Set that for the current frame, and also make it the default for
      other, future frames."
  (let ((font-code (my/font-code)))
    (add-to-list 'default-frame-alist (cons 'font font-code))
    (set-frame-font font-code)))

(defun my/reset-font-size ()
  "Change font size back to `my/default-font-size'."
  (interactive)
  (setq my/current-font-size my/default-font-size)
  (my/set-font-size))

(defun my/increase-font-size ()
  "Increase current font size by a factor of `my/font-change-increment'."
  (interactive)
  (setq my/current-font-size
        (ceiling (* my/current-font-size my/font-change-increment)))
  (my/set-font-size))

(defun my/decrease-font-size ()
  "Decrease current font size by a factor of `my/font-change-increment', down to a minimum size of 1."
  (interactive)
  (setq my/current-font-size
        (max 1
             (floor (/ my/current-font-size my/font-change-increment))))
  (my/set-font-size))

(when (member my/default-font
              (font-family-list))
  (my/reset-font-size))

Highlight the current line

global-hl-line-mode softly highlights the background color of the line containing point. It makes it a bit easier to find point, and it’s useful when pairing or presenting code.

(global-hl-line-mode)

Highlight uncommitted changes

Use the git-gutter package to highlight changed-and-uncommitted lines when programming.

(use-package git-gutter
  :custom
  ;; │┃▕▐
  (git-gutter:window-width 1)
  (git-gutter:update-interval 2)
  (git-gutter:always-show-separator t)
  (git-gutter:added-sign "")
  (git-gutter:deleted-sign "")
  (git-gutter:modified-sign "")
  :custom-face
  (git-gutter:added ((t (:foreground "#438340" :background nil))))
  (git-gutter:deleted ((t (:foreground "#815EDD" :background nil))))
  (git-gutter:modified ((t (:foreground "#3471E3" :background nil))))
  ;; :hook
  ;; (git-gutter-mode
  ;; . (lambda ()
  ;;     (git-gutter:set-window-margin (git-gutter:window-margin))))
  :init
  (global-git-gutter-mode t)
  :bind
  ("C-x C-g" . git-gutter))

Display line number

(setq-default display-line-numbers-width 4)
(setq display-line-numbers-grow-only t)
(add-hook 'text-mode-hook (lambda () (display-line-numbers-mode t)))
(add-hook 'prog-mode-hook (lambda () (display-line-numbers-mode t)))
(add-hook 'conf-mode-hook (lambda () (display-line-numbers-mode t)))

display system Information

(defun symon--display-update-modify (orig-fun &rest args)
  "symon step aside other message"
  (if (current-message)
      (when (string-match-p "\\(MEM:+.*CPU:+.*RX:+.*TX:+.*\\|Quit\\|Reverting\\ buffer\\|Mark\\ set\\)"
                            (current-message))
        (apply orig-fun args))
    (apply orig-fun args)))
(use-package symon
  :load-path "~/.emacs.d/third_party/symon/"
  :config
  (symon-mode)
  (advice-add 'symon--display-update
              :around 'symon--display-update-modify))

Hydra

(use-package hydra)

Project management

I use a few packages in virtually every programming or writing environment to manage the project, handle auto-completion, search for terms, and deal with version control. That’s all in here.

exec-path-from-shell

A GNU Emacs library to ensure environment variables inside Emacs look the same as in the user’s shell.

(use-package exec-path-from-shell
  :if (eq system-type 'darwin)
  :init
  (exec-path-from-shell-initialize))

project-cmake

The package project-cmake incorporates the required logic to understand that a project is to be configured, built and tested using CMake and CTest.

(use-package project-cmake
  :load-path "~/.emacs.d/third_party/project-cmake/"
  :custom
  (project-vc-merge-submodules nil)
  (project-vc-extra-root-markers '(".dir-locals.el"))
  (project-cmake-build-directory-name "build")
  :config
  ;; Rebind C-x C-p to `project-prefix` to prevent mistyping and improve project navigation
  (define-key global-map (kbd "C-x C-p") project-prefix-map)
  (put 'compilation-environment 'safe-local-variable #'listp)
  (put 'project-cmake-configuration-arguments 'safe-local-variable #'listp)
  (put 'project-cmake-build-type 'safe-local-variable #'stringp)
  (put 'project-cmake-kit 'safe-local-variable #'symbolp)
  (put 'project-cmake-generator 'safe-local-variable #'stringp)
  (put 'project-cmake-source-directory-name 'safe-local-variable #'stringp)
  (project-cmake-scan-kits))

ripgrep

Install and configure deadgrep as an interface to ripgrep.

(use-package deadgrep
  :bind
  ("C-c s" . deadgrep)
  ("C-x s" . deadgrep))

(use-package wgrep-deadgrep)

find-file-in-project

Find file/directory and review Diff/Patch/Commit quickly everywhere.

(use-package find-file-in-project
  :custom
  (ffip-use-rust-fd t)
  :bind
  ("C-x f" . find-file-in-project)
  :config
  (push "*/.cache" ffip-prune-patterns))

dumb-jump

The dumb-jump package works well enough in a ton of environments, and it doesn’t require any additional setup. bounding as official recommendation.

(use-package dumb-jump
  :commands (dumb-jump-xref-activate)
  :custom
  (dumb-jump-quiet t)
  (dumb-jump-selector 'completing-read)
  (dumb-jump-force-searcher 'rg)
  :init
  (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))

flymake

(use-package flymake
  :pin gnu
  :init
  (package-install 'flymake t) ;; install the latest gnu-flymake
  :custom
  (flymake-mode-line-lighter "Φ")
  (flymake-margin-indicator-position 'right-margin)
  (flymake-autoresize-margins t))

magit

I use magit to handle version control. It’s lovely, but I tweak a few things:

  • I bring up the status menu with C-x g.
  • The default behavior of magit is to ask before pushing. I haven’t had any problems with accidentally pushing, so I’d rather not confirm that every time.
  • Per tpope’s suggestions, highlight commit text in the summary line that goes beyond 50 characters.
  • I’d like to start in the insert state when writing a commit message.
(use-package magit
  :bind
  ("C-x g" . magit-status)
  :config
  (use-package with-editor)
  (setq magit-bind-magit-project-status nil)
  :hook
  (git-commit-mode . (lambda ()
                       "Set up the Git commit message buffer."
                       (setq fill-column 72)))
  :custom
  (magit-push-always-verify nil)
  (git-commit-summary-max-length 50)
  (magit-blame-goto-chunk-hook '(magit-blame-maybe-show-message)))

I’m also partial to git-timemachine, which lets you quickly page through the history of a file.

(use-package git-timemachine)

Some project is managed by git-lfs, we need to extent magit

(use-package magit-lfs
  :after (magit))

undo-tree

I like tree-based undo management. I only rarely need it, but when I do, oh boy.

(use-package undo-tree
  :custom
  (global-undo-tree-mode t)
  (undo-tree-auto-save-history nil)
  (undo-tree-visualizer-relative-timestamps t)
  (undo-tree-visualizer-timestamps t))

Shell config

Indent with 2 spaces.

(add-hook 'sh-mode-hook
          (lambda ()
            (setq sh-basic-offset 2
                  sh-indentation 2)))

Force open shell in the current buffer

(push (cons "\\*shell\\*" display-buffer--same-window-action) display-buffer-alist)

Workgroups another way to organize workspace

Winner-mode

(when (fboundp 'winner-mode)
  (winner-mode 1))

Bazel

Bazel is a build system created by Google:

(use-package bazel
  :defer t)

imenu-list

show function and variable tree in side buffer

(use-package imenu-list
  :init
  (use-package nav-flash)
  :bind ("C-c i" . imenu-list-smart-toggle)
  :config
  (setq imenu-list-focus-after-activation t)
  (setq imenu-list-auto-update nil)
  :hook
  (imenu-after-jump . nav-flash-show))

ELF

read elf symbols

(use-package elf-mode
  :demand
  :config
  (elf-setup-default))
(use-package demangle-mode
  :config
  (advice-add 'elf-mode :after 'demangle-mode)
  (add-to-list 'demangle-languages
               '("LLVM-IR"
                 "\\(?:_Z\\|\\(?:_GLOBAL_[._$][DI]_\\)\\)[_$[:alnum:]]+"
                 ("llvm-cxxfilt" "--no-strip-underscore")))
  :hook
  (llvm-mode . demangle-mode))

UML

(use-package plantuml-mode
  :mode "\\.plantuml$"
  :config
  ;; not promote to `utxt`.
  (defun plantuml-jar-output-type-opt (output-type)
    "Create the flag to pass to PlantUML according to OUTPUT-TYPE."
    (concat "-t" output-type))
  (setq plantuml-jar-path "~/.emacs.d/plantuml.jar")
  (when (file-exists-p plantuml-jar-path)
    (setq plantuml-default-exec-mode 'jar))
  (add-to-list
   'org-src-lang-modes '("plantuml" . plantuml)))

Tramp

(let ((my/shell (if (eq system-type 'darwin)
                    "/bin/zsh"
                  "/bin/bash")))
  (setq tramp-remote-shell my/shell)
  (setq explicit-shell-file-name my/shell)  ;; using bash by default.
  (setq shell-file-name my/shell))  ;; using bash by default.

Gerrit

(use-package gerrit
  :custom
  (gerrit-use-ssl nil)
  (gerrit-host "gerrit.houmo.ai") ;; is needed for REST API calls
  :config
  (put 'gerrit-project-to-local-workspace-alist 'safe-local-variable #'listp)
  (setq gerrit-dashboard-query-alist
        '(("Has draft comments" . "has:draft")
          ("Work in progress" . "is:open owner:self is:wip")
          ("Outgoing reviews" . "is:open owner:self -is:wip -is:ignored")
          ("Incoming reviews" . "is:open -owner:self -is:wip -is:ignored (reviewer:self OR assignee:self)")
          ("CCed on" . "is:open -is:ignored cc:self")
          ("toolchain/hmcc" . "project:toolchain/hmcc is:open limit:15")
          ("Recently closed" . "is:closed -is:ignored (-is:wip OR owner:self) (owner:self OR reviewer:self OR assignee:self OR cc:self) limit:15")))

  (progn
    (global-set-key (kbd "C-x i") 'gerrit-upload-transient)
    (global-set-key (kbd "C-x o") 'gerrit-download)))

Programming customization

Look for executables in /usr/local/bin.

Add system path to emacs.

(if (eq system-type 'darwin)
    (mapcar #'my/append-to-path
            '("/usr/local/bin"
              "/Library/TeX/texbin"
              "/usr/local/opt/llvm/bin/")))

common settings

editing

I like shallow indentation, but tabs are displayed as 8 characters by default. This reduces that.

(setq-default tab-width 4)

Treating terms in CamelCase symbols as separate words makes editing a little easier for me, so I like to use subword-mode everywhere.

(use-package subword
  :config (global-subword-mode 1))

Compilation output goes to the *compilation* buffer. I rarely have that window selected, so the compilation output disappears past the bottom of the window. This automatically scrolls the compilation window so I can always see the output.

(setq compilation-scroll-output t)

Use smartparens

(use-package smartparens
  :config
  (smartparens-global-mode 1))

eglot LSP and more

Emacs Polyglot: an Emacs LSP client that stays out of your way.

(defun my/test-then-add-eglot (mode-list program &optional command)
  (when (executable-find program)
    (if command
        (add-to-list 'eglot-server-programs
                     (list mode-list program command))
      (add-to-list 'eglot-server-programs
                   (list mode-list program)))
    (mapcar
     #'(lambda (sym)
         (eval `(add-hook
                 (quote ,(intern (concat (symbol-name sym) "-hook")))
                 'eglot-ensure)))
     mode-list)))
(use-package eglot
  :pin gnu
  :init
  (package-install 'eglot t) ;; install the latest gnu-eglot
  (use-package consult-eglot)
  :commands
  (eglot eglot-ensure)
  ;; :hook
  ;; (eglot-managed-mode . (lambda () (eglot-inlay-hints-mode -1)))
  :bind (:map eglot-mode-map
              ("C-M-\\" . eglot-format)
              ("C-c e r" . eglot-rename)
              ("C-c e a" . eglot-code-actions)
              ("C-c e c" . eglot-reconnect)
              ("C-c e k" . eglot-shutdown)
              ("C-c e s" . consult-eglot-symbols)
              ("C-c e i" . eglot-inlay-hints-mode))
  :custom
  ;; (eglot-autoshutdown t)
  (eglot-events-buffer-size 0)
  (eglot-extend-to-xref t)
  (eglot-sync-connect nil)
  :config
  (add-to-list 'eglot-stay-out-of 'eldoc)

  (my/test-then-add-eglot '(c-or-c++-mode c-mode c++-mode c++-ts-mode c-ts-mode) "clangd")
  (my/test-then-add-eglot '(python-mode python-ts-mode) "pyright-langserver" "--stdio")
  (my/test-then-add-eglot '(LaTeX-mode tex-mode context-mode texinfo-mode bibtex-mode) "texlab")
  (my/test-then-add-eglot '(javascript-mode js-ts-mode) "typescript-language-server" "--stdio")
  (my/test-then-add-eglot '(sh-mode bash-ts-mode) "bash-language-server" "start")
  (my/test-then-add-eglot '(rust-ts-mode) "rust-analyzer")
  (my/test-then-add-eglot '(yaml-ts-mode) "yaml-language-server" "--stdio")
  (my/test-then-add-eglot '(dockerfile-ts-mode) "docker-langserver" "--stdio")
  ;; (my/test-then-add-eglot '(json-ts-mode) "vscode-json-language-server" "--stdio")
  (my/test-then-add-eglot '(mlir-mode) "mlir-lsp-server")

  (defun eglot-tblgen-command-args (interactive-p)
    (let* ((build-directory (project-cmake-build-directory))
           (database (expand-file-name "tablegen_compile_commands.yml"
                                       build-directory)))
      (list "tblgen-lsp-server"
            (format "--tablegen-compilation-database=%s" database))))

  (when (executable-find "tblgen-lsp-server")
    (add-to-list 'eglot-server-programs
                 '((tablegen-mode) . eglot-tblgen-command-args))
    (add-hook 'tablegen-mode-hook 'eglot-ensure))
  (setq completion-category-defaults nil))

DAP mode

Dape is a debug adapter client for Emacs.

(use-package dape ;; should use gnu-dev:version
  :custom
  (dape-buffer-window-arrangement 'right) ;; Info buffers to the right
  (dape-breakpoint-margin-string "") ;; ◯
  :config
  ;; (setq dape-debug t)
  (put 'dape-command 'safe-local-variable #'listp)
  (put 'dape-adapter-dir 'safe-local-variable #'stringp)
  (put 'dape--overlay-arrow-position
       'overlay-arrow-string   ;; ▶ →
       (propertize "" 'face 'dape-stack-trace-face)))

Xref jump

Use minibuffer as the interface to select from xref candidates.

(use-package xref)

Terminal

Emacs-libvterm (vterm) is fully-fledged terminal emulator inside GNU Emacs based on libvterm, a C library. As a result of using compiled code (instead of elisp), emacs-libvterm is fully capable, fast, and it can seamlessly handle large outputs.

(defun my/vterm-env ()
  (interactive)
  (setq vterm-environment (cdr (assq 'vterm-env dir-local-variables-alist))) ;; FixMe
  (vterm))

(define-derived-mode my/vterm-mode fundamental-mode "my/VTerm"
  "Major mode for vterm buffer."
  (hack-dir-local-variables)
  (let ((vterm-env (cdr (assq 'vterm-environment dir-local-variables-alist))))
    (when vterm-env
      (make-local-variable 'vterm-environment)
      (setq vterm-environment vterm-env)))
  )
(use-package vterm
  :init
  (put 'vterm-environment 'safe-local-variable #'listp)
  :bind ("C-c v" . vterm) ;; terminal
  :custom
  (vterm-max-scrollback 10000)
  (vterm-kill-buffer-on-exit nil)
  (vterm-always-compile-module t)
  (term-copy-exclude-prompt t)
  (vterm-buffer-name-string "*vterm %s*"))

Eat: Emulate A Terminal

(use-package eat
  :bind (("C-c t" . eat-new-term)
         (:map eat-line-mode-map ("C-x C-f" . find-file-at-point)))
  :config
  (define-key eat-line-mode-map [xterm-paste] #'xterm-paste)

  (defun eat-new-term ()
    "Create a new Eat terminal buffer."
    (interactive)
    (eat-other-window nil '(4)))

  (defun my/eat-add-compilation-environment ()
    (hack-dir-local-variables-non-file-buffer)
    (setq-local font-lock-defaults '(nil t))
    (setq-local process-environment
                (append compilation-environment process-environment)))
  :hook
  ;; https://codeberg.org/akib/emacs-eat/issues/129
  (eat-exec . (lambda (&rest _) (eat-line-mode)))
  (eat-mode . my/eat-add-compilation-environment)

  :custom
  (xterm-store-paste-on-kill-ring nil)
  (eat-enable-auto-line-mode t)
  (eat-shell-prompt-annotation-failure-margin-indicator "")
  (eat-shell-prompt-annotation-running-margin-indicator "")
  (eat-shell-prompt-annotation-success-margin-indicator ""))

configure comint-mode

(setq comint-buffer-maximum-size 8192)
(setq comint-prompt-read-only nil) ;; If non-nil, the comint prompt is read only.
(setq read-process-output-max (* 1024 1024))
(add-hook 'comint-output-filter-functions #'comint-osc-process-output)
(setq process-adaptive-read-buffering nil)
(use-package term
  :config
  (defun my/term ()
    (interactive)
    (let* ((title (generate-new-buffer-name "*terminal*"))
           (buffer (get-buffer-create title)))
      (cond ((not (term-check-proc buffer))
             (with-current-buffer buffer
               (term-mode)) ; Install local vars, mode, keymap, ...
             (term-exec buffer title explicit-shell-file-name nil nil)))
      (set-buffer buffer)
      (term-line-mode)
      (pop-to-buffer-same-window buffer)))

  (defun my/add-compilation-environment ()
    (hack-dir-local-variables-non-file-buffer)
    (hl-line-mode -1)
    (setq-local global-hl-line-mode nil)
    (setq-local process-environment
                (append compilation-environment process-environment)))

  :bind (;; ("C-c t" . my/term)
         (:map term-mode-map
               ("C-x C-f" . find-file-at-point)
               ("TAB" . completion-at-point)
               ("M-s". nil)))
  :hook
  (term-mode . my/add-compilation-environment))

Fold and unfold code blocks

Hydra short-keys

(defhydra hydra-hs (:idle 1.0)
  "
   Hide^^            ^Show^            ^Toggle^    ^Navigation^
   ----------------------------------------------------------------
   _h_ hide all      _s_ show all      _t_oggle    _n_ext line
   _d_ hide block    _a_ show block              _p_revious line
   _l_ hide level

   _SPC_ cancel _q_ cancel
   "
  ("s" hs-show-all)
  ("h" hs-hide-all)
  ("a" hs-show-block)
  ("d" hs-hide-block)
  ("t" hs-toggle-hiding)
  ("l" hs-hide-level)
  ("n" forward-line)
  ("p" (forward-line -1))
  ("SPC" nil)
  ("q" nil))

Emacs has a minor mode called hs-minor-mode that allows users to fold and hide blocks of text

(defun my/display-code-line-counts (ov)
  (when (eq 'code (overlay-get ov 'hs))
    (overlay-put ov 'display
                 (propertize
                  (format " … <%d>"
                          (count-lines (overlay-start ov)
                                       (overlay-end ov)))
                  'face '(:background "#ff0066" :foreground "#000000")))))

(use-package hideshow
  :hook (prog-mode . hs-minor-mode)
  :bind (:map hs-minor-mode-map
              ("C-c @" . hydra-hs/body))
  :config
  (setq hs-set-up-overlay 'my/display-code-line-counts))

Highlight variables

(use-package symbol-overlay
  :bind (("M-i" . symbol-overlay-put)
         ("M-n" . symbol-overlay-jump-next)
         ("M-p" . symbol-overlay-jump-prev)
         ("M-N" . symbol-overlay-switch-forward)
         ("M-P" . symbol-overlay-switch-backward)
         ("M-C" . symbol-overlay-remove-all))
  :custom
  (symbol-overlay-priority 100))

Tree-sitter

using tree-sitter to manage Parser

(use-package treesit
  :ensure nil
  :if (and (fboundp 'treesit-available-p) (treesit-available-p))
  :custom
  ;; (treesit-extra-load-path `(,treesit-langs-folder))
  (treesit-max-buffer-size (* 100 1024 1024))
  ;; disable checking the ‘-*-’ line, this will disable loading .dir-locals.el
  ;; (enable-local-variables nil)
  :config
  (setq major-mode-remap-alist
        (append major-mode-remap-alist
                '((sh-mode . bash-ts-mode)
                  (javascript-mode . js-ts-mode)
                  (js-json-mode . json-ts-mode)
                  (python-mode . python-ts-mode)
                  (c-or-c++-mode . c++-ts-mode)
                  (c-mode . c-ts-mode)
                  (c++-mode . c++-ts-mode))))
  (mapcar #'require '(cmake-ts-mode
                      dockerfile-ts-mode
                      rust-ts-mode
                      yaml-ts-mode))
  :mode
  ("\\.h\\.inc\\'" . c++-ts-mode)
  ("\\.cpp\\.inc\\'" . c++-ts-mode)
  ("\\.hu$" . c++-ts-mode))

Indent Highlight

(use-package indent-bars
  :config
  (require 'indent-bars-ts)
  :custom
  (indent-bars-treesit-support t)
  (indent-bars-no-descend-string t)
  (indent-bars-treesit-scope '((python function_definition class_definition for_statement
                                       if_statement with_statement while_statement)))
  (indent-bars-treesit-ignore-blank-lines-types '("module"))
  :config
  (defun my/set-indent-bars-bg-fg ()
    (setq
     indent-bars-unspecified-fg-color (face-attribute 'default :foreground)
     indent-bars-unspecified-bg-color (face-attribute 'default :background)
     indent-bars-color '(highlight :face-bg t :blend 0.15)
     indent-bars-highlight-current-depth '(:blend 0.5) ;; pump up the BG blend on current
     ;; blend=1: blend with BG only
     indent-bars-color-by-depth '(:regexp "outline-\\([0-9]+\\)" :blend 1))
    (indent-bars-reset))

  (my/set-indent-bars-bg-fg)

  :hook ((python-base-mode c-ts-base-mode) . indent-bars-mode))

Visual-regexp

(use-package visual-regexp
  :bind
  ("C-c r" . vr/replace)
  ("C-c q" . vr/query-replace)
  ;; if you use multiple-cursors, this is for you:
  ("C-c m" . vr/mc-mark))

Python

Pip can install binary file.

(defun my/get-first-valid-path (path-list)
  ;; get build path
  ;; this dirctiry should contain the excutable server and config file.
  ;; check path validation and return list.
  (let ((path (cl-remove-if-not
               (lambda (x)
                 (file-directory-p (eval x)))
               path-list)))
    (if path
        (eval (car path))
      nil)))
(use-package virtualenvwrapper
  :config
  (venv-initialize-interactive-shells) ;; if you want interactive shell support
  (venv-initialize-eshell) ;; if you want eshell support
  (setq venv-location '("~/py3/"
                        "~/py3.10/"))
  (let ((venv-py (my/get-first-valid-path venv-location)))
    (when venv-py
      (venv-workon (car (last (split-string venv-py "\/") 2)))))
  (my/test-then-add-eglot '(cmake-ts-mode) "cmake-language-server")
  ;; (my/test-then-add-eglot '(python-mode python-ts-mode) "pylsp")
  )

Set ipython as interpreter

(use-package python
  :config
  (put 'python-shell-process-environment 'safe-local-variable #'listp)
  :custom
  (python-shell-completion-native-enable nil)
  (python-shell-interpreter "ipython")
  (python-shell-interpreter-args "--simple-prompt -i")
  (python-indent-offset 4)
  :hook
  (python-mode . (lambda ()
                   (setq indent-tabs-mode nil)
                   (setq python-indent 4)
                   (setq tab-width 4))))
(use-package cython-mode)

code format

(defun my/python-format ()
  (interactive)
  (if (region-active-p)
      (python-black-partial-dwim)
    (python-black-buffer)))

(defun my/python-format-key ()
  (define-key (current-local-map) [remap eglot-format] 'my/python-format)
  (local-set-key (kbd "C-M-\\") 'my/python-format))

(use-package python-black
  :if (executable-find "black")
  :init
  (put 'python-black-extra-args 'safe-local-variable #'listp)
  :after python
  :hook
  (python-mode . my/python-format-key)
  (python-ts-mode . my/python-format-key))

C++

LLVM-format-style

(defun llvm-lineup-statement (langelem)
  (let ((in-assign (c-lineup-assignments langelem)))
    (if (not in-assign)
        '++
      (aset in-assign 0
            (+ (aref in-assign 0)
               (* 2 c-basic-offset)))
      in-assign)))

;; Add a cc-mode style for editing LLVM C and C++ code
(c-add-style "llvm.org"
             '("gnu"
               (fill-column . 80)
               (c++-indent-level . 2)
               (c-basic-offset . 2)
               (indent-tabs-mode . nil)
               (c-offsets-alist . ((arglist-intro . ++)
                                   (innamespace . 0)
                                   (member-init-intro . ++)
                                   (statement-cont . llvm-lineup-statement)))))

clang-format OVERVIEW: A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf/C# code.

(defun my/clang-format (&optional beg end)
  (interactive
   (and (region-active-p) (list (region-beginning) (region-end))))
  (if (and beg end)
      (clang-format beg end)
    (clang-format-buffer)))

(defun my/clang-format-key ()
  (when (current-local-map)
    (define-key (current-local-map) [remap eglot-format] 'my/clang-format))
  (local-set-key (kbd "C-M-\\") 'my/clang-format))

(use-package clang-format
  :if (executable-find "clang-format")
  :custom
  (clang-format-fallback-style "llvm")
  :hook ((c-mode c++-mode java-mode js-mode tablegen-mode c++-ts-mode c-ts-mode) .
         my/clang-format-key))

Google Test For running Google Tests from a given buffer

(use-package gtest-mode
  :load-path "~/.emacs.d/third_party/danielmartin-gtest"
  :after cc-mode)

Rust

(use-package rust-playground
  :bind
  ("C-x r p" . rust-playground)
  :config
  (add-to-list 'project-vc-extra-root-markers "Cargo.toml")
  (defun rust-playground ()
    "Run playground for Rust language in a new buffer."
    (interactive)
    ;; get the dir name
    (let* ((snippet-dir (rust-playground-dir-name))
           (snippet-file-name (rust-playground-snippet-main-file-name snippet-dir))
           (snippet-cargo-toml (rust-playground-toml-file-name snippet-dir)))
      ;; create a buffer for Cargo.toml and switch to it
      (make-directory snippet-dir t)
      (set-buffer (create-file-buffer snippet-cargo-toml))
      (set-visited-file-name snippet-cargo-toml t)
      (rust-playground-mode)
      (rust-playground-insert-template-head "snippet of code" snippet-dir)
      (insert rust-playground-cargo-toml-template)
      (save-buffer)
      ;;now do src/main.rs
      (make-directory (concat snippet-dir "src"))
      (let ((new-buffer (create-file-buffer snippet-file-name)))
        (set-buffer new-buffer)
        (set-visited-file-name snippet-file-name t)
        (rust-playground-insert-template-head "snippet of code" snippet-dir)
        (insert rust-playground-main-rs-template)
        (save-buffer)
        (switch-to-buffer new-buffer)
        ;; back up to a good place to edit from
        (backward-char 27)
        (insert-tab))
      (rust-playground-mode))))

JavaScript and CoffeeScript

Indent everything by 2 spaces.

(setq js-indent-level 2)

(add-hook 'coffee-mode-hook
          (lambda ()
            (yas-minor-mode 1)
            (setq coffee-tab-width 2)))

Lisps

rainbow-delimiters is convenient for coloring matching parentheses.

(use-package rainbow-delimiters
  :hook ((emacs-lisp-mode lisp-mode racket-mode) . rainbow-delimiters-mode))

If I’m writing in Emacs lisp I’d like to use eldoc-mode to display documentation.

(use-package eldoc
  :config
  (add-hook 'emacs-lisp-mode-hook 'eldoc-mode))

scheme

(use-package geiser
  :config
  (setq geiser-active-implementations '(mit chicken guile racket chez)))

web-mode

If I’m in web-mode, I’d like to:

  • Color color-related words with rainbow-mode.
  • Still be able to run RSpec tests from web-mode buffers.
  • Indent everything with 2 spaces.
(use-package web-mode
  :init
  (use-package rainbow-mode)
  (use-package rspec-mode)
  :config
  (add-hook 'web-mode-hook 'rainbow-mode)
  (add-hook 'web-mode-hook 'rspec-mode)
  (setq web-mode-markup-indent-offset 2)
  :mode "\\.erb$"
         "\\.html$"
         "\\.php$"
         "\\.rhtml$")

Use web-mode with embedded Ruby files, regular HTML, and PHP.

FlatBuffer

(use-package flatbuffers-mode)

ProtoBuffer

add Google protocol buffer support

(defun my/prototxt-mode-hook ()
  (when (and (stringp buffer-file-name)
             (string-match "\\.prototxt\\'" buffer-file-name))
    (setq-local comment-start "# ")
    (setq-local comment-start-skip "#+\\s-*")
    (font-lock-add-keywords nil
                            '(("#.+" . font-lock-comment-face)))))
(use-package protobuf-mode
  :mode "\\.prototxt$"
  :hook
  (protobuf-mode . my/prototxt-mode-hook))

ReStructuredText

(use-package rst)

LLVM mode

These are syntax highlighting files for the Emacs and XEmacs editors.

(use-package llvm-mode
  :mode "\\.ll$"
  :load-path "~/.emacs.d/third_party/llvm-mode+")
(use-package tablegen-mode
  :mode "\\.td$"
  :hook
  (tablegen-mode . turn-on-font-lock)
  (tablegen-mode . git-gutter--turn-on)
  (tablegen-mode . display-line-numbers--turn-on)
  :load-path "~/.emacs.d/third_party/llvm-mode+")
(use-package llvm-mir-mode
  :mode "\\.mir$"
  :load-path "~/.emacs.d/third_party/llvm-mode+")
(use-package mlir-mode
  :mode "\\.pdll$" "\\.mlir$"
  :load-path "~/.emacs.d/third_party/llvm-mode+")

Groovy mode

(use-package groovy-mode
  :config
  (let ((groovy-lsp "/usr/local/lib/groovy-language-server-all.jar"))
    (when (file-exists-p groovy-lsp)
      (add-to-list 'eglot-server-programs
                   `(groovy-mode . ("java" "-jar" ,groovy-lsp)))
      (add-hook 'groovy-mode-hook 'eglot-ensure))))

Lua mode

(use-package lua-mode)

JQ mode

jq is a lightweight and flexible command-line JSON processor akin to sed,awk,grep, and friends for JSON data.

(use-package jq-mode
  :bind ("C-c j" . jq-interactively)
  :mode
  ("\\.jq$" . jq-mode))

EPUB mode

(use-package nov
  :mode
  ("\\.epub\\'" . nov-mode))

ChatGPT

(defun my/internet-up-p (&optional host)
  (= 0 (call-process "ping" nil nil nil "-c" "1" "-W" "1"
                     (if host host "www.wikipedia.org"))))
(use-package gptel
  :defer nil
  :bind (("C-c RET" . gptel-send)
         ("C-c g" . (lambda () (interactive) (gptel "*GPT*" nil nil t)))
         ("C-c <return>" . gptel-send)
         ("C-c C-<return>" . gptel-menu)
         :map gptel-mode-map
         ("C-c C-x t" . gptel-set-topic))
  :config
  ;; (setq gptel--debug t)
  (setq gptel-default-mode 'org-mode)
  (unless (my/internet-up-p)
    (setq-default gptel-proxy
                  (format "socks5h://%s" my/socks-proxy)))

  (setq gptel--openai
        (gptel-make-openai "ChatGPT"
          :key 'gptel-api-key
          :stream t
          :models '("o1-mini" "chatgpt-4o-latest" "gpt-4o")))

  (defvar gptel--gemini
    (gptel-make-gemini "Gemini"
      :key 'gptel-api-key
      :stream t
      :models '("gemini-exp-1206" "gemini-1.5-pro-latest")))

  (defvar gptel--anthropic
    (gptel-make-anthropic "Claude"
      :key 'gptel-api-key
      :stream t
      :models '("claude-3-5-sonnet-latest")))

  (defvar gptel--deepseek
    (gptel-make-openai "DeepSeek"
      :stream t
      :key 'gptel-api-key
      :host "api.deepseek.com"
      :endpoint "/chat/completions"
      :models '("deepseek-coder" "deepseek-chat")))

  (setq-default gptel-backend gptel--gemini)

  (defun my/ediff-cleanup ()
    (ediff-kill-buffer-carefully "*Ediff Control Panel*")
    (ediff-kill-buffer-carefully "*ediff-errors*")
    (ediff-kill-buffer-carefully "*ediff-diff*")
    (ediff-kill-buffer-carefully "*Ediff Registry*")
    (ediff-kill-buffer-carefully "*ediff-fine-diff*"))

  (add-hook 'ediff-quit-hook #'my/ediff-cleanup)

  :custom
  (gptel-curl-file-size-threshold 2000000)
  (gptel-model 'gpt-4o-mini)
  (gptel-use-curl t)
  (gptel-playback t))

This defines a code refactor function for Embark. The default gptel Ediff is incompatible with Eglot due to bugs in Eglot’s track-changes feature. We need to use the original buffer as the source of the diff, not the cloned one.

(defvar my/gptel--ediff-restore nil
  "Function to restore window configuration after ediff.")

(defvar my/gptel--ediff-restore-cwc nil
  "Variable to record window configuration before ediff.")

(defun my/get-full-line-region (buffer)
  ;; enlarge the region to hold full lines
  (with-current-buffer buffer
    (save-excursion
      (let* ((reg-beg  (region-beginning))
             (reg-end  (region-end))
             (full-beg (progn (goto-char reg-beg)
                              (line-beginning-position)))
             (full-end (if (eq (char-before reg-end) ?\n)
                           reg-end
                         (goto-char reg-end)
                         (line-beginning-position 2)))) ; include the newline char
        (cons full-beg full-end)))))
(defun my/gpt-refacotr (gpt-backend rewrite-message prompt)
  (let ((gptel-backend gpt-backend)
        (gptel-model (car (gptel-backend-models gpt-backend))))

    (setq my/gptel--ediff-restore-cwc (current-window-configuration))
    (setq my/gptel--ediff-restore
          (lambda ()
            (when (window-configuration-p my/gptel--ediff-restore-cwc)
              (set-window-configuration my/gptel--ediff-restore-cwc))
            (ediff-kill-buffer-carefully "*gptel-rewrite-Region.B-*")
            (remove-hook 'ediff-quit-hook my/gptel--ediff-restore)))
    (message "Waiting for %s response..." gptel-model)
    (gptel-request
        ;; o1 model does not support system message, but we can embeded it into the prompt.
        (format "%s\n%s" rewrite-message prompt)
        :system rewrite-message
        :context (my/get-full-line-region (current-buffer))
        :callback
        (lambda (response info)
          (if (not response)
              (message "ChatGPT response error: %s" (plist-get info :status))
            (let* ((gptel-buffer (plist-get info :buffer))
                   (gptel-bounds (plist-get info :context))
                   (buffer-mode
                    (buffer-local-value 'major-mode gptel-buffer)))
              (pcase-let ((`(,new-buf ,new-beg ,new-end)
                           (with-current-buffer (get-buffer-create "*gptel-rewrite-Region.B-*")
                             (let ((inhibit-read-only t))
                               (erase-buffer)
                               (funcall buffer-mode)
                               (insert response)
                               (goto-char (point-min))
                               (list (current-buffer) (point-min) (point-max))))))
                (require 'ediff)
                (add-hook 'ediff-quit-hook my/gptel--ediff-restore)
                (apply
                 #'ediff-regions-internal
                 gptel-buffer
                 (car gptel-bounds) (cdr gptel-bounds)
                 new-buf new-beg new-end
                 nil
                 ;; (list 'ediff-regions-wordwise 'word-wise nil)
                 (list 'ediff-regions-wordwise nil nil)
                 ))))))))

BuildBot

(use-package buildbot)

Graphviz-dot

(use-package graphviz-dot-mode
  :config
  (setq graphviz-dot-indent-width 4))

Compiler Explorer

There are numerous plugins available that function similarly to Godbolt, each with its own unique features and differences. You can explore Godbolt itself at the following link: [https://godbolt.org](https://godbolt.org).

(use-package compiler-explorer)

A supercharged implementation of the godbolt compiler-explorer for Emacs.

(use-package rmsbolt)

Beardbolt shows assembly output for given source code file, making it easy to see what the compiler is doing.

(use-package beardbolt
  :if (eq system-type 'gnu/linux)
  :load-path "~/.emacs.d/third_party/beardbolt/"
  :config
  (add-to-list 'beardbolt-languages '(c++-ts-mode beardbolt--c/c++-setup :base-cmd "g++" :language "c++")))

Org

Including org-tempo restores the <s-style easy-templates that were deprecated in Org 9.2.

(use-package org
  :ensure org-contrib
  :config
  (put 'narrow-to-region 'disabled nil)
  (setq org-modules (cl-remove-duplicates
                     (append org-modules
                             '(org-tempo
                               ox-md
                               ox-beamer
                               org-capture
                               ox-latex
                               ox-odt
                               org-gnus))))
  (define-key org-mode-map (kbd "C-j") nil)
  (define-key org-mode-map (kbd "C-c C-j") nil)
  (bind-keys*
   ("C-c l" . org-store-link)
   ("C-c C-l" .  org-insert-link)))

I’d like the initial scratch buffer to be in Org:

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

Display preferences

I like to see an outline of pretty bullets instead of a list of asterisks.

(defun my/change-cdr-value (in-list key value)
  (when (consp in-list)
    (if (eq (car in-list) key)
        (setcdr in-list value)
      (progn
        (my/change-cdr-value (car in-list) key value)
        (my/change-cdr-value (cdr in-list) key value))
      )))
(use-package org-bullets
  :config
  (add-hook 'org-mode-hook 'org-bullets-mode))

I like seeing a little downward-pointing arrow instead of the usual ellipsis (...) that org displays when there’s stuff under a header.

(setq org-ellipsis "")

Use syntax highlighting in source blocks while editing.

(setq org-src-fontify-natively t)

Make TAB act as if it were issued in a buffer of the language’s major mode.

(setq org-src-tab-acts-natively t)

When editing a code snippet, use the current window rather than popping open a new one (which shows the same information).

(setq org-src-window-setup 'current-window)

Using build-in hide leading starts

(setq org-hide-leading-stars t)
(setq org-pretty-entities t)
(setq org-allow-promoting-top-level-subtree t)
(setq org-email-link-description-format "%c: %.50s")

Wrap long text lines.

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

Key-bindings

Bind a few handy keys.

(define-key global-map "\C-ca" 'org-agenda)
(define-key global-map "\C-cc" 'org-capture)
(define-key global-map "\C-cL" 'org-occur-link-in-agenda-files)
(define-key global-map "\C-c+" 'org-increase-number-at-point)
(define-key global-map "\C-c-" 'org-decrease-number-at-point)

Hit C-c g to quickly open up my todo list.

(defun open-gtd-file ()
  "Open the master org TODO list."
  (interactive)
  ;; (my/copy-tasks-from-inbox)
  (find-file org-gtd-file)
  (end-of-buffer))

;; (global-set-key (kbd "C-c g") 'open-gtd-file)

Hit M-n to quickly open up a capture template for a new todo.

(defun org-capture-todo ()
  (interactive)
  (org-capture :keys "t"))
(setq org-special-ctrl-a/e 'reversed)
(setq org-special-ctrl-k t)
(setq org-support-shift-select t)

GTD

Store my org files in ~/org, maintain an inbox in Dropbox, define the location of an index file (my main todo list), and archive finished tasks in ~/org/archive.org.

(use-package org-pomodoro)

Keywords

(setq org-directory "~/.Org")

(defun org-file-path (filename)
  "Return the absolute address of an org file, given its relative name."
  (let ((fun (lambda (x)
               (concat (file-name-as-directory org-directory) x))))
    (if (listp filename)
        (mapcar fun filename)
      (eval (list fun filename)))))

(setq org-gtd-file (org-file-path "gtd.org"))
(setq org-default-notes-file (org-file-path "note.org"))
(setq org-scheduled-past-days 100)
(setq org-stuck-projects '("+LEVEL=1" ("NEXT" "TODO" "DONE")))
(setq org-tag-persistent-alist '(("Write" . ?w) ("Read" . ?r)))
(setq org-tag-alist
      '((:startgroup)
        ("Handson" . ?o)
        (:grouptags)
        ("Write" . ?w) ("Code" . ?c) ("Tel" . ?t)
        (:endgroup)
        (:startgroup)
        ("Handsoff" . ?f)
        (:grouptags)
        ("Read" . ?r) ("View" . ?v) ("Listen" . ?l)
        (:endgroup)
        ("Mail" . ?@) ("Search" . ?s) ("Buy" . ?b)))
(setq org-tags-column -74)
(setq org-todo-keywords '((type "TODO" "STRT" "NEXT" "WAIT" "|" "DONE" "DELEGATED" "CANCELED")))
(setq org-todo-repeat-to-state t)
(setq org-use-property-inheritance t)
(setq org-use-sub-superscripts nil)
(setq org-todo-keyword-faces
      '(("STRT" . (:foreground "white" :inverse-video t))
        ("NEXT" . (:foreground "brightcyan" :weight bold))
        ("WAIT" . (:foreground "#889699" :inverse-video t))
        ("CANCELED" . (:foreground "#889699"))))
(setq org-enforce-todo-dependencies t)
(setq org-enforce-todo-checkbox-dependencies t)
(setq org-deadline-warning-days 7)

Capturing

Define a few common tasks as capture templates. Specifically

(setq org-capture-templates
      '(("C" "Misc [inbox]" entry (file "~/.Org/inbox.org")
         "* TODO %a\n  :PROPERTIES:\n  :CAPTURED: %U\n  :END:\n"
         :prepend t :immediate-finish t)

        ("c" "Misc [inbox] (edit)" entry (file "~/.Org/inbox.org")
         "* TODO %?\n  :PROPERTIES:\n  :CAPTURED: %U\n  :END:\n\n- %a" :prepend t)

        ("r" "RDV Perso" entry (file+headline "~/.Org/rdv.org" "RDV Perso")
         "* RDV avec %:fromname %?\n  :PROPERTIES:\n  :CAPTURED: %U\n  :END:\n\n- %a" :prepend t)

        ("R" "RDV Etalab" entry (file+headline "~/.Org/rdv-etalab.org" "RDV Etalab")
         "* RDV avec %:fromname %?\n  :PROPERTIES:\n  :CAPTURED: %U\n  :END:\n\n- %a" :prepend t)

        ("t" "Tickler" entry (file+headline "~/.Org/tickler.org" "Tickler")
         "* %i%? \n  :PROPERTIES:\n  :CAPTURED: %U\n  :END:\n\n- %a\n\n%i" :prepend t)))

(setq org-capture-templates-contexts
      '(("r" ((in-mode . "gnus-summary-mode")
              (in-mode . "gnus-article-mode")
              (in-mode . "message-mode")))
        ("R" ((in-mode . "gnus-summary-mode")
              (in-mode . "gnus-article-mode")
              (in-mode . "message-mode")))))

Refine & Archive

set org-refile level deep to max 3

(setq org-refile-targets '((("~/.Org/gtd.org") . (:maxlevel . 3))
                           (("~/.Org/someday.org") . (:maxlevel . 1))
                           (("~/.Org/tickler.org") . (:maxlevel . 2))))

(setq org-refile-use-outline-path 'file)
(setq org-refile-allow-creating-parent-nodes 'confirm)
(setq org-refile-use-cache nil)
(setq org-reverse-note-order t)
(setq org-outline-path-complete-in-steps nil)
;; (setq org-archive-default-command 'org-archive-to-archive-sibling)

Hitting C-c C-x C-s will mark a todo as done and move it to an appropriate place in the archive.

(setq org-archive-location
      (concat (org-file-path "archive.org") "::datetree/"))

(defun my/mark-done-and-archive ()
  "Mark the state of an org-mode item as DONE and archive it."
  (interactive)
  (let ((ts (org-get-todo-state)))
    (when (not (or (equal ts "DONE")
                   (equal ts "DELEGATED")
                   (equal ts "CANCELLED")))
      (org-todo 'done)))
  (org-archive-subtree))

(define-key org-mode-map (kbd "C-c C-x C-s") 'my/mark-done-and-archive)

Record the time that a todo was archived.

(setq org-log-done 'time)

auto save org file

(advice-add 'org-archive-subtree :after 'org-save-all-org-buffers)
(advice-add 'org-agenda-quit :before 'org-save-all-org-buffers)

Agenda

(use-package org-super-agenda)
;; Set headlines to STRT when clocking in
(add-hook 'org-clock-in-hook (lambda () (org-todo "STRT")))
;; (setq org-agenda-diary-file "/home/guerry/org/rdv.org")
(setq org-agenda-dim-blocked-tasks nil)
(setq org-log-into-drawer "LOGBOOK")
(setq org-agenda-entry-text-maxlines 10)
(setq org-timer-default-timer 25)
(setq org-agenda-diary-file (org-file-path '("rdv.org" "gtd.org" "inbox.org")))
(setq org-agenda-files (org-file-path '("inbox.org" "gtd.org" "tickler.org" "someday.org")))
(setq org-agenda-prefix-format
      '((agenda . " %i %-12:c%?-14t%s")
        (timeline . "  % s")
        (todo . " %i %-14:c")
        (tags . " %i %-14:c")
        (search . " %i %-14:c")))
(setq org-agenda-restore-windows-after-quit t)
(setq org-agenda-show-inherited-tags nil)
(setq org-agenda-skip-deadline-if-done t)
(setq org-agenda-skip-deadline-prewarning-if-scheduled t)
(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-skip-timestamp-if-done t)
(setq org-agenda-sorting-strategy
      '((agenda time-up) (todo time-up) (tags time-up) (search time-up)))
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-use-tag-inheritance nil)
(setq org-agenda-window-frame-fractions '(0.0 . 0.5))
(setq org-agenda-deadline-faces
      '((1.0001 . org-warning)              ; due yesterday or before
        (0.0    . org-upcoming-deadline)))  ; due today or later

Review

list stuck projects

(setq org-stuck-projects
      '("TODO={.+}/-DONE" nil nil "SCHEDULED:\\|DEADLINE:"))

using priority to organize my life

(setq org-agenda-custom-commands
      `(
        ;; Week agenda for rendez-vous and tasks
        ("%" "Rendez-vous" agenda* "Week planning"
         ((org-agenda-span 'week)
          (org-agenda-files (org-file-path '("rdv.org")))
          ;; (org-deadline-warning-days 3)
          (org-agenda-sorting-strategy
           '(todo-state-up time-up priority-down))))

        ("!" tags-todo "+DEADLINE<=\"<+7d>\"")
        ("=" tags-todo "+SCHEDULED<=\"<now>\"")
        ("?" "WAIT (gtd)" tags-todo "TODO={WAIT}"
         ((org-agenda-files (org-file-path '("gtd.org")))
          (org-agenda-sorting-strategy
           '(todo-state-up priority-down time-up))))
        ("@" tags-todo "+Mail+TODO={NEXT\\|STRT\\|WAIT}")

        ("w" "Report DONE/CANCELED/DELEGATED"
         agenda ""
         ((org-agenda-span 'week)
          (org-agenda-start-on-weekday 0)
          (org-agenda-start-with-log-mode '(closed state clock))
          (org-agenda-files (org-file-path '("gtd.org" "archive.org")))
          (org-agenda-skip-function
           '(org-agenda-skip-entry-if 'nottodo 'done))
          (org-agenda-sorting-strategy '(timestamp-up))))

        ("
" . "Task and rendez-vous for today")
        ("

" "Travail (tout)" agenda "Tasks and rdv for today"
         ((org-agenda-span 1)
          (org-agenda-files (org-file-path '("gtd.org" "my.org")))
          (org-deadline-warning-days 3)
          (org-agenda-sorting-strategy
           '(todo-state-up time-up priority-down))))
        ("
 " "Libre (tout)" agenda "Tasks and rdv for today"
         ((org-agenda-span 1)
          (org-agenda-files (org-file-path '("libre.org")))
          (org-deadline-warning-days 3)
          (org-agenda-sorting-strategy
           '(todo-state-up priority-down time-up))))
        ("e" "Etalab TODO" tags-todo "TODO={STRT\\|NEXT\\|TODO}"
         ((org-agenda-files (org-file-path '("libre.org")))
          (org-agenda-category-filter-preset '("+ETL"))
          (org-agenda-sorting-strategy
           '(todo-state-up time-up priority-down))))

        ("n" "NEXT action" tags-todo "TODO={NEXT\\|STRT}"
         ((org-agenda-files (org-file-path '("gtd.org")))
          (org-agenda-sorting-strategy
           '(todo-state-down time-up priority-down))))

        ("x" . "Scheduled for today")
        ("xx" "Agenda work" agenda "Work scheduled for today"
         ((org-agenda-span 1)
          (org-deadline-warning-days 3)
          (org-agenda-entry-types '(:timestamp :scheduled))
          (org-agenda-sorting-strategy
           '(todo-state-up priority-down time-up))))
        ("xX" "Agenda libre" agenda "Libre scheduled for today"
         ((org-agenda-span 1)
          (org-deadline-warning-days 3)
          (org-agenda-files (org-file-path '("libre.org")))
          (org-agenda-entry-types '(:timestamp :scheduled))
          (org-agenda-sorting-strategy
           '(todo-state-up priority-down time-up))))

        ("z" . "Deadlines for today")
        ("zz" "Work deadlines" agenda "Past/upcoming work deadlines"
         ((org-agenda-span 1)
          (org-deadline-warning-days 15)
          (org-agenda-entry-types '(:deadline))
          (org-agenda-sorting-strategy
           '(todo-state-up priority-down time-up))))
        ("zZ" "Libre deadlines" agenda "Past/upcoming leisure deadlines"
         ((org-agenda-span 1)
          (org-deadline-warning-days 15)
          (org-agenda-files (org-file-path '("libre.org")))
          (org-agenda-entry-types '(:deadline))
          (org-agenda-sorting-strategy
           '(todo-state-up priority-down time-up))))

        ("r" . "Read")
        ("rr" tags-todo "+Read+TODO={NEXT\\|STRT}")
        ("rR" tags-todo "+Read+TODO={NEXT\\|STRT}"
         ((org-agenda-files '("~/org/libre.org"))))
        ("v" . "View")
        ("vv" tags-todo "+View+TODO={NEXT\\|STRT}")
        ("vV" tags-todo "+View+TODO={NEXT\\|STRT}"
         ((org-agenda-files (org-file-path '("libre.org")))))
        ("l" . "Listen")
        ("ll" tags-todo "+Listen+TODO={NEXT\\|STRT}")
        ("lL" tags-todo "+Listen+TODO={NEXT\\|STRT}"
         ((org-agenda-files (org-file-path '("libre.org")))))
        ("w" . "Write")
        ("ww" tags-todo "+Write+TODO={NEXT\\|STRT}")
        ("wW" tags-todo "+Write+TODO={NEXT\\|STRT}"
         ((org-agenda-files (org-file-path '("libre.org")))))
        ("c" . "Code")
        ("cc" tags-todo "+Code+TODO={NEXT\\|STRT}")
        ("cC" tags-todo "+Code+TODO={NEXT\\|STRT}"
         ((org-agenda-files (org-file-path '("libre.org")))))
        ))

Edit

ob-async enables asynchronous execution of org-babel src blocks, using :async

(use-package ob-async
  :config
  (require 'org))

Programming languages support

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (shell . t)
   (org . t)
   (scheme . t)
   (python . t)
   (dot . t)
   (gnuplot . t)
   (C . t)))
(setq org-babel-default-header-args
      '((:session . "none")
        (:results . "replace")
        (:exports . "code")
        (:cache . "no")
        (:noweb . "yes")
        (:hlines . "no")
        (:tangle . "no")
        (:padnewline . "yes")))

Don’t ask before evaluating code blocks.

(setq org-confirm-babel-evaluate nil)

Associate the “dot” language with the graphviz-dot major mode.

(add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))

Quickly insert a block of elisp:

(add-to-list 'org-structure-template-alist
             '("el" . "src emacs-lisp"))

Hook to update all blocks before saving

(add-hook 'org-mode-hook
          (lambda() (add-hook 'before-save-hook
                              'org-update-all-dblocks t t)))
(setq org-insert-heading-respect-content t)
(setq org-id-method 'uuidgen)
(setq org-id-uuid-program "uuidgen")
(setq org-use-speed-commands
      (lambda nil
        (and (looking-at org-outline-regexp-bol)
             (not (org-in-src-block-p t)))))
(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)
(setq org-link-display-descriptive nil)
(setq org-loop-over-headlines-in-active-region t)
;; (setq org-create-formula-image-program 'dvipng) ;; imagemagick
(setq org-blank-before-new-entry '((heading . t) (plain-list-item . auto)))
(setq org-fontify-whole-heading-line t)
(setq org-global-properties '(("Effort_ALL" . "0:10 0:30 1:00 2:00 3:30 7:00")))
(setq org-confirm-elisp-link-function nil)
(setq org-confirm-shell-link-function nil)

LaTex

Automatically parse the file after loading it.

(setq TeX-parse-self t)

Always use pdflatex when compiling LaTeX documents. I don’t really have any use for DVIs.

(setq TeX-PDF-mode t)

Open compiled PDFs in evince instead of in the editor.

(add-hook 'org-mode-hook
          '(lambda ()
             (delete '("\\.pdf\\'" . default) org-file-apps)
             (add-to-list 'org-file-apps '("\\.pdf\\'" . "evince %s"))))

Enable a minor mode for dealing with math (it adds a few useful keybindings), and always treat the current file as the “main” file. That’s intentional, since I’m usually actually in an org document.

(add-hook 'LaTeX-mode-hook
          (lambda ()
            (LaTeX-math-mode)
            (setq TeX-master t)))

Exporting

Translate regular ol’ straight quotes to typographically-correct curly quotes when exporting.

(setq org-export-with-smart-quotes t)
(setq org-export-default-language "en")
(setq org-export-backends '(latex odt icalendar html ascii))
(setq org-export-with-archived-trees nil)
(setq org-export-with-drawers '("HIDE"))
(setq org-export-with-sub-superscripts nil)
(setq org-export-with-tags 'not-in-toc)
(setq org-export-with-timestamps t)
(setq org-export-with-toc nil)
(setq org-export-with-priority t)
(setq org-export-dispatch-use-expert-ui t)
(setq org-export-babel-evaluate t)
(setq org-export-allow-bind-keywords t)
(setq org-publish-list-skipped-files nil)
(setq org-fast-tag-selection-single-key 'expert)
(setq org-fontify-done-headline t)
(setq org-footnote-auto-label 'confirm)
(setq org-footnote-auto-adjust t)
(setq org-hide-emphasis-markers t)
(setq org-hide-macro-markers t)
(setq org-icalendar-include-todo 'all)
(setq org-link-frame-setup '((gnus . gnus) (file . find-file-other-window)))
(setq org-log-note-headings
      '((done . "CLOSING NOTE %t") (state . "State %-12s %t") (clock-out . "")))
(setq org-footnote-section "Notes")
(setq org-attach-directory "~/.Org/data/")
(setq org-link-display-descriptive nil)
(setq org-export-filter-planning-functions
      '(my/org-html-export-planning))
(setq org-export-with-broken-links t)

Exporting to HTML

Don’t include a footer with my contact and publishing information at the bottom of every exported HTML document.

(setq org-html-head "")
(setq org-html-head-include-default-style nil)
(setq org-html-postamble nil)
(setq org-html-table-row-tags
      (cons '(cond (top-row-p "<tr class=\"tr-top\">")
                   (bottom-row-p "<tr class=\"tr-bottom\">")
                   (t (if (= (mod row-number 2) 1)
                          "<tr class=\"tr-odd\">"
                        "<tr class=\"tr-even\">")))
            "</tr>"))
(setq org-gnus-prefer-web-links nil)
(setq org-html-head-include-default-style nil)
(setq org-html-head-include-scripts nil)
(defun my/org-html-export-planning (planning-string backend info)
  (when (string-match "<p>.+><\\([0-9]+-[0-9]+-[0-9]+\\)[^>]+><.+</p>" planning-string)
    (concat "<span class=\"planning\">" (match-string 1 planning-string) "</span>")))

Exporting to HTML and opening the results triggers /usr/bin/sensible-browser, which checks the $BROWSER environment variable to choose the right browser. I’d like to always use Firefox, so:

(setenv "BROWSER" "safari")

Exporting to PDF

I want to produce PDFs with syntax highlighting in the code. The best way to do that seems to be with the minted package, but that package shells out to pygments to do the actual work. pdflatex usually disallows shell commands; this enables that.

(setq org-latex-listings t)
(add-to-list 'org-latex-classes
	       '("my-letter"
		 "\\documentclass\{scrlttr2\}
	      \\usepackage[english,frenchb]{babel}
	      \[NO-DEFAULT-PACKAGES]
	      \[NO-PACKAGES]
	      \[EXTRA]"))
(setq org-latex-pdf-process
      '("xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Include the minted package in all of my LaTeX exports.

(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)

Writing thesis

Write raw LaTex document using auctex

(use-package tex
  :ensure auctex
  :custom
  (TeX-engine 'xetex)
  :config
  (setq TeX-auto-save t)
  (setq TeX-parse-self t)
  (setq-default TeX-master nil)
  (add-hook 'LaTeX-mode-hook 'visual-line-mode)
  (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  (setq reftex-plug-into-AUCTeX t))

Set some usefull commands of latex

Enable forward and inverse search

(setq TeX-source-correlate-method (quote synctex))
(setq TeX-source-correlate-mode t)
(setq TeX-source-correlate-start-server t)

Set pdf viewer

(setq TeX-view-program-selection  '((output-pdf "PDF Viewer")))
(setq TeX-view-program-list
      '(("PDF Viewer" "/Applications/Skim.app/Contents/SharedSupport/displayline -b -g %n %o %b")))

Set reftex References, labels, citations

;;   (use-package org-ref)
(add-hook 'LaTeX-mode-hook 'turn-on-reftex) ; with Auctex Latex mode
(add-hook 'latex-mode-hook 'turn-on-reftex) ; with Emacs latex mode
(setq reftex-plug-into-AUCTeX t)

Writing prose

I write prose in several modes: I might be editing an Org document, or a commit message, or an email. These are the main ones, with sub-items being derived from their parents:

  • git-commit-mode
  • text-mode
    • markdown-mode
      • gfm-mode
    • message-mode
      • mu4e-compose-mode
    • org-mode

Recall that derived modes “inherit” their parent’s hooks, so a hook added onto e.g. text-mode will also be executed by mu4e-compose-mode.

There are some exceptions, but I can usually associate a hook with every prose-related mode, so I store those in a list:

(defvar prose-modes
  '(gfm-mode
    git-commit-mode
    org-mode
    markdown-mode
    message-mode
    text-mode))

(defvar prose-mode-hooks
  (mapcar (lambda (mode) (intern (format "%s-hook" mode)))
          prose-modes))

Enable spell-checking in the usual places

I want to make sure that I’ve enabled spell-checking if I’m editing text, composing an email, or authoring a Git commit.

flyspell

https://github.com/accelbread/dotfiles/blob/a6061976a51c3335543c02d0aef3e222509e2a59/emacs/init.el#L295

(use-package flyspell
  :if (eq system-type 'darwin)
  :config
  ;; (use-package flyspell-lazy)
  ;; (flyspell-lazy-mode 1)
  :config
  (setq ispell-dictionary "en_US"
        ispell-program-name "aspell"
        ispell-extra-args '("--camel-case" "--sug-mode=ultra")
        flyspell-issue-message-flag nil
        flyspell-mode-line-string nil
        flyspell-duplicate-distance 0)

  (add-to-list 'flyspell-delayed-commands 'scroll-down-command)
  (add-to-list 'flyspell-delayed-commands 'scroll-up-command)
  (add-to-list 'flyspell-delayed-commands 'previous-line)
  (add-to-list 'flyspell-delayed-commands 'next-line)
  (add-to-list 'flyspell-delayed-commands 'line-move)
  (add-to-list 'flyspell-delayed-commands 'compilation-read-command)
  (add-to-list 'flyspell-delayed-commands 'completion-at-point-functions)

  :hook
  (prog-mode . (lambda ()
                 (unless (derived-mode-p 'json-ts-mode)
                   (flyspell-prog-mode))))
  (text-mode . flyspell-mode))

jinx

Jinx is a fast just-in-time spell-checker for Emacs. Jinx highlights misspelled words in the text of the visible portion of the buffer.

(use-package jinx
  :if (eq system-type 'gnu/linux)
  :hook (emacs-startup . global-jinx-mode)
  :config
  ;; (length (jinx--get-overlays (point-min) (point-max) nil))
  ;; (length (jinx--get-overlays (point-min) (point-max) t))
  ;; (length (jinx--force-overlays (point-min) (point-max) :check t))
  (setq jinx-camel-modes
        (append jinx-camel-modes
                '(cc-ts-mode c-mode c-or-c++-mode c++-mode c++-ts-mode c-ts-mode)))

  (defun jinx--mode-line-format ()
    "updata the spelling check error informatin."
    (format "J{%d}" (length (jinx--get-overlays (window-start) (window-end) nil))))

  (add-to-list 'mode-line-misc-info
               '(jinx-mode
                 (:eval (jinx--mode-line-format))) t)

  :bind (:map jinx-mode-map
              ("M-$" . jinx-correct)
              ("M-p" . nil)
              ("M-n" . nil)
              ("C-M-c" . jinx-correct)
              ("C-M-p" . jinx-previous)
              ("C-M-n" . jinx-next)))

define-word

A minimalists’ Emacs extension to search http://www.bing.com/dict. Support English to Chinese and Chinese to English.

(use-package bing-dict
  :config
  ;; from fanyi.el
  (defun bing-dict-dwim ()
    "A more dwim version of `bing-dict'.
No prompt if the region is active or `thing-at-point' returns
non-nil."
    (interactive)
    (if-let* ((word (if (use-region-p)
                        (buffer-substring-no-properties (region-beginning) (region-end))
                      (thing-at-point 'word t))))
        (progn
          (bing-dict-brief word))
      (call-interactively #'bing-dict-brief)))
  :bind ("C-c d" . 'bing-dict-dwim))

fanyi.el is a simple yet powerful multi-dictionaries interface for Emacs, currently it includes: 海词 有道同义词, Unofficial API etymonline Longman.

(use-package fanyi
  :config
  :bind ("C-c D" . 'fanyi-dwim2))

Wrap paragraphs automatically

AutoFillMode automatically wraps paragraphs, kinda like hitting M-q. I wrap a lot of paragraphs, so this automatically wraps ‘em when I’m writing text, Markdown, or Org.

(dolist (hook prose-mode-hooks)
  (add-hook hook 'turn-on-auto-fill))

Use Org-style lists and tables everywhere

Enable Org-style tables.

(add-hook 'git-commit-mode-hook 'orgtbl-mode)
(add-hook 'markdown-mode-hook 'orgtbl-mode)
(add-hook 'message-mode-hook 'orgtbl-mode)

Use the =orgalist= package for more convenient list manipulation.

(use-package orgalist
  :config
  (add-hook 'git-commit-mode-hook 'orgalist-mode)
  (add-hook 'markdown-mode-hook 'orgalist-mode)
  (add-hook 'message-mode-hook 'orgalist-mode))

Editing with Markdown

Because I can’t always use org.

  • Associate .md files with GitHub-flavored Markdown.
  • Use pandoc to render the results.
  • Leave the code block font unchanged.
(use-package markdown-mode
  :commands gfm-mode
  :mode (("\\.md$" . gfm-mode))
  :config
  (setq markdown-command "pandoc --standalone --mathjax --from=markdown"
        markdown-fontify-code-blocks-natively t))

Cycle between spacing alternatives

Successive calls to cycle-spacing rotate between changing the whitespace around point to:

  • A single space,
  • No spaces, or
  • The original spacing.

Binding this to M-SPC is strictly better than the original binding of just-one-space.

(global-set-key (kbd "M-SPC") 'cycle-spacing)

Enable region case modification

(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)

Quickly explore my “notes” directory with deft

(use-package deft
  :bind ("C-c n" . deft)
  :commands (deft)
  :config

  (setq deft-directory "~/.notes"
        deft-recursive t
        deft-use-filename-as-title t))

Editing settings

Scroll Hydra

Scroll preserve screen position or not.

(setq scroll-preserve-screen-position t)
(defhydra hydra-scroll ()
  "move"
  ;; scroll preserve t
  ("n" scroll-up-line)
  ("p" scroll-down-line)
  ;; scroll preserve always
  ("N" (progn
         (next-line)
         (scroll-up-line)))
  ("P" (progn
         (previous-line)
         (scroll-down-line)))
  ("v" scroll-up-command)
  ("V" scroll-down-command)
  ("q" nil "quit"))
(global-set-key (kbd "C-c b") #'hydra-scroll/body)

Quickly visit Emacs

configuration

I futz around with my dotfiles a lot. This binds C-c f to quickly open my Emacs configuration file.

(defun my/visit-emacs-config ()
  (interactive)
  (find-file "~/.emacs.d/configuration.org"))

(global-set-key (kbd "C-c f") 'my/visit-emacs-config)

Always kill current buffer

Assume that I always want to kill the current buffer when hitting C-x k.

(defun my/kill-current-buffer ()
  "Kill the current buffer without prompting."
  (interactive)
  (kill-buffer (current-buffer)))
(global-set-key (kbd "C-x k") 'my/kill-current-buffer)

Set up helpful

The helpful package provides, among other things, more context in Help buffers.

(use-package helpful
  :bind
  (("C-h f" . helpful-callable)
   ("C-h v" . helpful-variable)
   ("C-h k" . helpful-key)))

Always indent with spaces

Never use tabs. Tabs are the devil’s whitespace.

(setq-default indent-tabs-mode nil)

Install and configure which-key

which-key displays the possible completions for a long keybinding. That’s really helpful for some modes (like project, for example).

(use-package which-key
  :config (which-key-mode))

marginalia + consult + embark

;; A few more useful configurations...
(use-package emacs
  :init
  (fido-vertical-mode t)
  ;; set completion case insensitive
  (setq completion-auto-help nil)
  (setq completion-ignore-case t)
  (setq read-file-name-completion-ignore-case t)
  (setq read-buffer-completion-ignore-case t)
  ;; 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.
  (setq read-extended-command-predicate
        #'command-completion-default-include-p))
(use-package icomplete
  :config
  (defun fussy-fido-setup ()
    "Use `fussy' with `fido-mode'."
    (setq-local completion-styles '(fussy)))
  (advice-add 'icomplete--fido-mode-setup :after 'fussy-fido-setup)
  (add-to-list 'completion-ignored-extensions "../")
  ;; :bind
  ;; (:map icomplete-minibuffer-map
  ;; ("TAB" . icomplete-force-complete)
  ;; ([tab] . icomplete-force-complete))
  :custom
  (icomplete-tidy-shadowed-file-names t)
  (icomplete-show-matches-on-no-input t)
  (icomplete-compute-delay 0)
  (icomplete-delay-completions-threshold 50))
(defun my/quit-all ()
  (interactive)
  (if (eq last-command 'my/quit-all)
      (cond ((region-active-p)
             (deactivate-mark))
            ((> (minibuffer-depth) 0)
             (abort-recursive-edit))
            (current-prefix-arg
             nil)
            ((> (recursion-depth) 0)
             (exit-recursive-edit))
            (buffer-quit-function
             (funcall buffer-quit-function)))
    (keyboard-quit)))

(setq enable-recursive-minibuffers t)
(global-set-key (kbd "C-g") 'my/quit-all)

fussy

(if (eq system-type 'darwin)
    (setq fuz-core-bin "/usr/local/lib/libfuz_core.dylib")
  (setq fuz-core-bin "/usr/local/lib/libfuz_core.so"))
(use-package fussy
  :config
  (when (file-exists-p fuz-core-bin)
    (use-package fuz
      :config
      (load fuz-core-bin nil t))
    (setq fussy-score-fn 'fussy-fuz-score))

  (add-hook 'minibuffer-setup-hook
            (lambda ()
              (setq-local fussy-use-cache nil)))

  :custom
  (fussy-use-cache t)
  (fussy-compare-same-score-fn 'fussy-histlen->strlen<)
  (fussy-filter-fn 'fussy-filter-default)
  (fussy-default-regex-fn 'fussy-pattern-flex-1)
  (completion-styles '(fussy))
  (completion-category-defaults nil)
  (completion-category-overrides nil))
;; For cache functionality.
(advice-add 'corfu--capf-wrapper :before 'fussy-wipe-cache)

(add-hook 'corfu-mode-hook
          (lambda ()
            (setq-local fussy-max-candidate-limit 5000
                        fussy-default-regex-fn 'fussy-pattern-first-letter
                        fussy-prefer-prefix nil)))

marginalia

Marginalia are marks or annotations placed at the margin of the page of a book or in this case helpful colorful annotations placed at the margin of the minibuffer for your completion candidates.

(use-package marginalia
  :custom
  (marginalia-field-width 120)
  :init
  ;; Must be in the :init section of use-package such that the mode gets
  ;; enabled right away. Note that this forces loading the package.
  (marginalia-mode))

consult

This package provides various commands based on the Emacs completion function completing-read, in particular a more advanced buffer switching command consult-buffer and a Swiper-like search command consult-line.

(use-package consult
  ;; Replace bindings. Lazily loaded due by `use-package'.
  :bind (;; C-c bindings (mode-specific-map)
         ("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 r b" . consult-bookmark)            ;; orig. bookmark-jump
         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)
         ;; Other custom bindings
         ("M-y" . consult-yank-pop)                ;; orig. yank-pop
         ("<help> a" . consult-apropos)            ;; orig. apropos-command
         ;; M-g bindings (goto-map)
         ("M-g e" . consult-compile-error)
         ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
         ("M-g g" . consult-goto-line)             ;; orig. goto-line
         ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
         ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)
         ;; M-s bindings (search-map)
         ("M-s d" . consult-find)
         ("M-s D" . consult-locate)
         ("M-s G" . consult-git-grep)
         ("M-s r" . consult-ripgrep)
         ("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))           ;; 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. You may want to also
  ;; enable `consult-preview-at-point-mode` in Embark Collect buffers.
  :hook (completion-list-mode . consult-preview-at-point-mode)

  ;; The :init configuration is always executed (Not lazy)
  :init

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

  ;; Optionally replace `completing-read-multiple' with an enhanced version.
  (advice-add #'completing-read-multiple :override #'consult-completing-read-multiple)

  ;; Use Consult to select xref locations with preview
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)
  :config
  (consult-customize
   consult-theme
   :preview-key '(:debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-bookmark consult--source-recent-file
   consult--source-project-recent-file
   :preview-key '(:debounce 0.4 any))

  (setq consult-narrow-key "<") ;; (kbd "C-+")
  )

embark

(defun my/scp-file (arg input1)
  ;; Copy files to My local device.
  (interactive "P\nsInput 1:")
  (let* ((device (completing-read "Select: " '("Mac" "EVB")))
         (cmd (format "rsync -rlt %s %s:~/man.lu.huawei/" input1 device))
         (cmd-out (shell-command-to-string cmd)))
    (if (equal cmd-out "")
        (message "Success: %s" cmd)
      (message "Failed: %s\n%s"cmd cmd-out))))

embark with ace-windows https://karthinks.com/software/fifteen-ways-to-use-embark/

(eval-when-compile
  (defmacro my/embark-ace-action (fn)
    `(defun ,(intern (concat "my/embark-ace-" (symbol-name fn))) ()
       (interactive)
       (with-demoted-errors "%s"
         (require 'ace-window)
         (let ((aw-dispatch-always t))
           (aw-switch-to-window (aw-select nil))
           (call-interactively (symbol-function ',fn)))))))
(defun embark-which-key-indicator ()
  "An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further
targets."
  (lambda (&optional keymap targets prefix)
    (if (null keymap)
        (which-key--hide-popup-ignore-command)
      (which-key--show-keymap
       (if (eq (plist-get (car targets) :type) 'embark-become)
           "Become"
         (format "Act on %s '%s'%s"
                 (plist-get (car targets) :type)
                 (embark--truncate-target (plist-get (car targets) :target))
                 (if (cdr targets) "" "")))
       (if prefix
           (pcase (lookup-key keymap prefix 'accept-default)
             ((and (pred keymapp) km) km)
             (_ (key-binding prefix 'accept-default)))
         keymap)
       nil nil t (lambda (binding)
                   (not (string-suffix-p "-argument" (cdr binding))))))))

(defun embark-hide-which-key-indicator (fn &rest args)
  "Hide the which-key indicator immediately when using the completing-read prompter."
  (which-key--hide-popup-ignore-command)
  (let ((embark-indicators
         (remq #'embark-which-key-indicator embark-indicators)))
    (apply fn args)))

Emacs Mini-Buffer Actions Rooted in Keymaps

(use-package embark
  :config
  (defmacro my/embark-gpt-refacotr (gpt-backend rewrite-message)
    `(defun ,(intern (format "%s-Refactor" (eval `(gptel-backend-name ,gpt-backend)))) (prompt)
       (my/gpt-refacotr ,gpt-backend ,rewrite-message prompt)))

  (define-key embark-file-map     (kbd "o") (my/embark-ace-action find-file))
  (define-key embark-buffer-map   (kbd "o") (my/embark-ace-action switch-to-buffer))
  (define-key embark-bookmark-map (kbd "o") (my/embark-ace-action bookmark-jump))
  (setq my/gpt-refacotr-prompt "You are Chris Lattner. Refactor the following code. Generate only code, no explanation, no code fences. Additionally, revise the comment and consider renaming variables if appropriate.")
  (keymap-set embark-region-map "g" (my/embark-gpt-refacotr gptel--gemini my/gpt-refacotr-prompt))
  (keymap-set embark-region-map "o" (my/embark-gpt-refacotr gptel--openai my/gpt-refacotr-prompt))
  (keymap-set embark-region-map "a" (my/embark-gpt-refacotr gptel--anthropic my/gpt-refacotr-prompt))
  (keymap-set embark-region-map "d" (my/embark-gpt-refacotr gptel--deepseek my/gpt-refacotr-prompt))
  (define-key embark-file-map  (kbd "s") 'my/scp-file)
  :bind
  ("C-h B" . embark-bindings) ;; alternative for `describe-bindings'
  :init
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command)
  (bind-keys* ("C-;" . embark-act))
  :config
  (setq embark-indicators
        '(embark-which-key-indicator
          embark-highlight-indicator
          embark-isearch-highlight-indicator))
  (advice-add #'embark-completing-read-prompter
              :around #'embark-hide-which-key-indicator))

;; Consult users will also want the embark-consult package.
(use-package embark-consult
  :after (embark consult)
  :demand t ; only necessary if you have the hook below
  ;; if you want to have consult previews as you move around an
  ;; auto-updating embark collect buffer
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

corfu

(use-package corfu
  :custom
  (corfu-auto-delay 0.1)
  (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
  (corfu-auto t)                 ;; Enable auto completion
  ;; (global-corfu-modes '((not shell-mode) t))
  (global-corfu-modes t)
  (corfu-preview-current nil)
  ;; https://github.com/minad/corfu/discussions/457
  (text-mode-ispell-word-completion nil) ;; disable ispell

  :config
  (when (featurep 'tty-child-frames)
    (setq corfu--frame-parameters
          '((no-accept-focus . t)
            (no-focus-on-map . t)
            (min-width . t)
            (min-height . t)
            (border-width . 0)
            (outer-border-width . 0)
            (internal-border-width . 0)
            (child-frame-border-width . 1)
            (vertical-scroll-bars . nil)
            (horizontal-scroll-bars . nil)
            (menu-bar-lines . 0)
            (tool-bar-lines . 0)
            (tab-bar-lines . 0)
            (no-other-frame . t)
            (unsplittable . t)
            (undecorated . t)
            (cursor-type . nil)
            (no-special-glyphs . t)
            (desktop-dont-save . t))))

  ;; https://github.com/minad/corfu/issues/330
  (advice-add 'python-shell-completion-at-point :around
              (lambda (fun &optional arg)
                (cape-wrap-noninterruptible (lambda () (funcall fun arg)))))

  :bind
  (:map corfu-map
        ("TAB" . corfu-next)
        ([tab] . corfu-next)
        ("S-TAB" . corfu-previous)
        ([backtab] . corfu-previous))
  :init
  (global-corfu-mode))

;; Add extensions
(use-package cape
  :init
  ;; Add `completion-at-point-functions', used by `completion-at-point'.
  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  ;; (add-to-list 'completion-at-point-functions #'cape-history)
  (add-to-list 'completion-at-point-functions #'cape-keyword)
  :config
  (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)
  :custom
  (dabbrev-check-all-buffers nil))

;; A few more useful configurations...
(use-package emacs
  :custom
  ;; TAB cycle if there are only few candidates
  (completion-cycle-threshold 3)
  (warning-minimum-level :error)
  ;; Emacs 28: Hide commands in M-x which do not apply to the current mode.
  ;; Corfu commands are hidden, since they are not supposed to be used via M-x.
  (read-extended-command-predicate #'command-completion-default-include-p)

  ;; Enable indentation+completion using the TAB key.
  ;; `completion-at-point' is often bound to M-TAB.
  (tab-always-indent 'complete))

terminal popup

(use-package corfu-terminal
  :init
  (unless (corfu--popup-support-p)
    (corfu-terminal-mode +1))
  :custom
  (corfu-terminal-position-right-margin 1))

crux

A Collection of Ridiculously Useful eXtensions for Emacs. crux bundles many useful interactive commands to enhance your overall Emacs experience.

(use-package crux
  :after xterm ;; xterm will redefine input-decode-map
  :bind
  (("C-x d" . crux-duplicate-current-line-or-region)
   ("C-x x r" . crux-rename-file-and-buffer)
   ("C-k" . crux-kill-and-join-forward)
   ("C-S-k" . crux-kill-whole-line)
   ("C-<S-k>" . crux-kill-whole-line)
   ("C-x M-c" . crux-capitalize-region))
  :init
  ;; 75 is the ASCII code for 'K', and 6 indicates Ctrl+Shift modifier.
  (define-key input-decode-map "\e[75;6~" [C-S-k]))

Switch and rebalance windows when splitting

When splitting a window, I invariably want to switch to the new window. This makes that automatic.

(defun my/split-window-below-and-switch ()
  "Split the window horizontally, then switch to the new pane."
  (interactive)
  (split-window-below)
  (balance-windows)
  (other-window 1))

(defun my/split-window-right-and-switch ()
  "Split the window vertically, then switch to the new pane."
  (interactive)
  (split-window-right)
  (balance-windows)
  ;; (let ((total-width (window-total-width)))
  ;; (split-window-right (floor (* 0.6 total-width))))
  (other-window 1))

(global-set-key (kbd "C-x 2") 'my/split-window-below-and-switch)
(global-set-key (kbd "C-x 3") 'my/split-window-right-and-switch)

Mass editing of grep results

I like the idea of mass editing grep results the same way I can edit filenames in dired. These keybindings allow me to use C-x C-q to start editing grep results and C-c C-c to stop, just like in dired.

(eval-after-load 'grep
  '(define-key grep-mode-map
               (kbd "C-x C-q") 'wgrep-change-to-wgrep-mode))

(eval-after-load 'wgrep
  '(define-key grep-mode-map
               (kbd "C-c C-c") 'wgrep-finish-edit))

(setq wgrep-auto-save-buffer t)

Configure wrap-region

warp selected region with punctuate.

(use-package wrap-region
  :config
  (wrap-region-global-mode t)
  (wrap-region-add-wrappers
   '(("$" "$")
     ("/" "/" nil ruby-mode)
     ("/* " " */" "#" (java-mode javascript-mode css-mode))
     ("`" "`" nil (markdown-mode ruby-mode)))))

Use multiple cursors

Set multiple cursors for better marker words

(use-package multiple-cursors
  :bind
  ("C->" . mc/mark-next-like-this)
  ("C-<" . mc/mark-previous-like-this)
  ("C-M-m" . mc/mark-all-like-this)
  :init
  (multiple-cursors-mode))

Use expand region

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

(use-package expand-region
  :config
  ;; Replace the original org-mode excursion function to avoid performance overhead
  (defun er/save-org-mode-excursion (action)
    (funcall action))

  :bind
  ("C-c q" . er/expand-region))
(use-package expreg
  :bind
  ("C-=" . expreg-expand)
  ("C--" . expreg-contract))

Use ace-jump

Use ace-jump-mode to quick jump to words or char

(use-package avy
  :bind
  ("C-j" . avy-goto-char-timer)
  ("C-c C-j" . avy-resume)

  :custom
  (avy-single-candidate-jump nil)
  (avy-background nil)
  (avy-timeout-seconds 0.3))

Scrolling one line

(global-set-key (kbd "C-S-n") "\C-u1\C-v")
(global-set-key (kbd "C-S-p") "\C-u1\M-v")

Use goto-last-change to jump between

(use-package goto-chg
  :config
  (bind-keys* ("C-." . goto-last-change))
  (bind-keys* ("C-," . goto-last-change-reverse)))

windows manager

hydra-frame-window is designed from ace-window (C-x f) and matches aw-dispatch-alist with a few extra

(defhydra hydra-frame-window (:color red :hint nil)
  "
^Delete^                       ^Frame resize^             ^Window^                Window Size^^^^^^   ^TEXT^   ^Text^                 (__)
_0_: delete-frame              _g_: resize-frame-right    _t_: toggle               ^ ^ _k_ ^ ^        _-_      _K_                   (oo)
_1_: delete-other-frames       _H_: resize-frame-left     _e_: ace-swap-win         _h_ ^+^ _l_        _=_      _L_             /------\\/
_2_: make-frame                _F_: fullscreen            ^ ^                       ^ ^ _j_ ^ ^        _+_      _J_            / |    ||
_d_: kill-and-delete-frame     _n_: new-frame-right       _w_: ace-delete-window    _b_alance^^^^      ^ ^      ^ ^           *  /\\---/\\  ~~  C-x w ;
"
  ("0" delete-frame :exit t)
  ("1" delete-other-frames :exit t)
  ("2" make-frame  :exit t)
  ("b" balance-windows :exit t)
  ("d" kill-and-delete-frame :exit t)
  ("e" ace-swap-window)
  ("F" toggle-frame-fullscreen)   ;; is <f11>
  ("g" resize-frame-right :exit t)
  ("H" resize-frame-left :exit t)  ;; aw-dispatch-alist uses h, I rebind here so hjkl can be used for size
  ("n" new-frame-right :exit t)
  ;; ("r" reverse-windows)
  ("t" toggle-window-spilt)
  ("w" ace-delete-window :exit t)
  ("x" delete-frame :exit t)
  ("K" text-scale-decrease)
  ("J" text-scale-increase)
  ("L" sensible-defaults/reset-text-size)
  ("+" my/increase-font-size)
  ("-" my/decrease-font-size)
  ("=" my/reset-font-size)
  ("h" shrink-window-horizontally)
  ("k" shrink-window)
  ("j" enlarge-window)
  ("l" enlarge-window-horizontally)
  ("q" nil :exit t))
(use-package ace-window
  :bind
  ("M-o" . 'ace-window)
  ("C-x w" . hydra-frame-window/body)
  :config
  (setq aw-scope 'frame)   ; only the windows of the current frame
  (setq aw-background t)
  (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))

mange ephemeral windows

Popper is a minor-mode to tame the flood of ephemeral windows Emacs produces, while still keeping them within arm’s reach.

(use-package popper
  :bind (("C-<tab>" . popper-toggle)
         ("C-M-<tab>" . popper-cycle)
         ("C-M-<return>" . popper-toggle-type)
         ("C-M-k" . popper-kill-latest-popup))
  :custom
  (popper-mode-line nil)
  :config
  (bind-keys* ("C-<tab>" . popper-toggle))
  (setq popper-group-function #'popper-group-by-project)

  (defun popper--fit-window-height (win)
    "Determine the height of popup window WIN by fitting it to the buffer's content."
    (fit-window-to-buffer
     win
     (floor (frame-height) 3)
     (floor (frame-height) 4)))

  :init
  (setq popper-reference-buffers
        '(;;("Output\\*$" . hide)
          (completion-list-mode . hide)
          occur-mode
          "^\\*socks5-proxy-.*\\*$"
          "\\*Async Shell Command\\*"
          "^\\*vterm.*\\*$"   vterm-mode   ;vterm as a popup
          "^\\*eat*.*$"  eat-mode
          "^\\*TeX Help.*\\*$"  tex-mode
          "^\\*tex-shell.*\\*$"  tex-mode
          compilation-mode))
  (popper-mode +1)
  (popper-echo-mode +1))                ; For echo area hints

Handle layer file

(setq fast-but-imprecise-scrolling t)

too-long-lines

using too-long-lines-mode to discard extremely long lines

(use-package too-long-lines-mode
  ;; using use-package PATH
  :load-path "~/.emacs.d/third_party/too-long-lines-mode"
  ;; using quelpa URL
  ;; :quelpa
  ;; ((too-long-lines-mode
  ;;   :fetcher github
  ;;   :repo "rakete/too-long-lines-mode")
  ;;  :upgrade t)
  :config
  (setq too-long-lines-threshold 5000)
  (set 'too-long-lines-special-buffer-modes
       '(shell-mode ag-mode inferior-python-mode comint-mode compilation-mode))
  (too-long-lines-mode))

jump back and forth

Hydra short-keys

(defhydra hydra-back-and-forth ()
  "back-and-forth"
  ("<right>" back-button-local-forward "")
  ("<left>" back-button-local-backward "")
  ("<up>" back-button-global-backward "")
  ("<down>" back-button-global-forward "")
  ("q" nil "exit" :exit t))

back-button offers local and global jumping

(use-package back-button
  :bind (:map back-button-mode-map
              ("C-x <C-right>" . hydra-back-and-forth/back-button-local-forward)
              ("C-x <C-left>" . hydra-back-and-forth/back-button-local-backward)
              ("C-x <up>" . hydra-back-and-forth/back-button-global-backward)
              ("C-x <down>" . hydra-back-and-forth/back-button-global-forward))
  :init
  (back-button-mode 1)
  (define-key back-button-mode-map (kbd "C-x <C-left>") nil)
  (define-key back-button-mode-map (kbd "C-x <C-right>") nil)
  (define-key back-button-mode-map (kbd "C-x <left>") nil)
  (define-key back-button-mode-map (kbd "C-x <right>") nil)
  (define-key back-button-mode-map (kbd "C-x C-SPC") nil)
  (define-key back-button-mode-map (kbd "C-x SPC") nil))

Pcache required by persistent-soft required by back-botton, but it freeze emacs when quit.

(remove-hook 'kill-emacs-hook 'pcache-kill-emacs-hook)

Ediff

I like Ediff’s control panel to show in the same frame, even on graphical environments.

(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-split-window-function 'split-window-horizontally)

Hydra ediff, more function with less key bindings

(defhydra hydra-ediff (:color blue :hint nil)
  "
^Buffers           Files           VC                     Ediff regions
----------------------------------------------------------------------
_b_uffers           _f_iles (_=_)       _r_evisions              _l_inewise
_B_uffers (3-way)   _F_iles (3-way)                          _w_ordwise
                  _c_urrent file
"
  ("b" ediff-buffers)
  ("B" ediff-buffers3)
  ("=" ediff-files)
  ("f" ediff-files)
  ("F" ediff-files3)
  ("c" ediff-current-file)
  ("r" ediff-revision)
  ("l" ediff-regions-linewise)
  ("w" ediff-regions-wordwise)
  ("q" nil :exit t))

(global-set-key (kbd "C-c w") #'hydra-ediff/body)

dired

Use c-x c-j to enter current folder.

These are the switches that get passed to ls when dired gets a list of files. We’re using:

  • l: Use the long listing format.
  • h: Use human-readable sizes.
  • v: Sort numbers naturally.
  • A: Almost all. Doesn’t include ”.” or ”..”.
(setq-default dired-listing-switches "-lhvA")

Hydra Dired till a work in progress but this has helped me navigate and use dired.

(defhydra hydra-dired (:hint nil :color pink)
  "
_+_ mkdir          _v_iew           _m_ark             _(_ details        _i_nsert-subdir    wdired
_C_opy             _O_ view other   _U_nmark all       _)_ omit-mode      _$_ hide-subdir    C-x C-q : edit
_D_elete           _o_pen other     _u_nmark           _l_ redisplay      _w_ kill-subdir    C-c C-c : commit
_R_ename           _M_ chmod        _t_oggle           _g_ revert buf     _e_ ediff          C-c ESC : abort
_Y_ rel symlink    _G_ chgrp        _E_xtension mark   _s_ort             _=_ pdiff
_S_ymlink          ^ ^              _F_ind marked      _._ toggle hydra   \\ flyspell
_r_sync            ^ ^              ^ ^                ^ ^                _?_ summary
_z_ compress-file  _A_ find regexp
_Z_ compress       _Q_ repl regexp

T - tag prefix
"
  ("\\" dired-do-ispell)
  ("(" dired-hide-details-mode)
  (")" dired-omit-mode)
  ("+" dired-create-directory)
  ("=" diredp-ediff)         ;; smart diff
  ("?" dired-summary)
  ("$" diredp-hide-subdir-nomove)
  ("A" dired-do-find-regexp)
  ("C" dired-do-copy)        ;; Copy all marked files
  ("D" dired-do-delete)
  ("E" dired-mark-extension)
  ("e" dired-ediff-files)
  ("F" dired-do-find-marked-files)
  ("G" dired-do-chgrp)
  ("g" revert-buffer)        ;; read all directories again (refresh)
  ("i" dired-maybe-insert-subdir)
  ("l" dired-do-redisplay)   ;; relist the marked or singel directory
  ("M" dired-do-chmod)
  ("m" dired-mark)
  ("O" dired-display-file)
  ("o" dired-find-file-other-window)
  ("Q" dired-do-find-regexp-and-replace)
  ("R" dired-do-rename)
  ("r" dired-do-rsynch)
  ("S" dired-do-symlink)
  ("s" dired-sort-toggle-or-edit)
  ("t" dired-toggle-marks)
  ("U" dired-unmark-all-marks)
  ("u" dired-unmark)
  ("v" dired-view-file)      ;; q to exit, s to search, = gets line #
  ("w" dired-kill-subdir)
  ("Y" dired-do-relsymlink)
  ("z" diredp-compress-this-file)
  ("Z" dired-do-compress)
  ("q" nil)
  ("C-g" nil :color blue)
  ("." nil :color blue))

Use “[” and “]” to jump out and in dired.

(eval-after-load "dired"
  '(progn
     (define-key dired-mode-map (kbd "[") 'dired-up-directory)
     (define-key dired-mode-map (kbd "]") 'dired-view-file)
     (define-key dired-mode-map (kbd "K") 'dired-kill-subdir)
     (define-key dired-mode-map "." 'hydra-dired/body)))
(eval-after-load "view"
  '(define-key view-mode-map (kbd "[") 'View-quit))

fast copy and past

(setq dired-dwim-target t)

Kill buffers of files/directories that are deleted in dired.

(setq dired-clean-up-buffers-too t)

Always copy directories recursively instead of asking every time.

(setq dired-recursive-copies 'always)

Ask before recursively deleting a directory, though.

(setq dired-recursive-deletes 'top)

Files are normally moved and copied synchronously. This is fine for small or local files, but copying a large file or moving a file across a mounted network drive blocks Emacs until the process is completed. Unacceptable!

reuse dired buffer

(put 'dired-find-alternate-file 'disabled nil)

narrow dired to match filter

(use-package dired-narrow
  :bind (:map dired-mode-map
              ("/" . dired-narrow)))

dired-rsync – asynchronous rsync from dired

(use-package dired-rsync
  :config
  (bind-key "C-c C-r" 'dired-rsync dired-mode-map))

easy-kill

Provide commands easy-kill and easy-mark to let users kill or mark things easily.

(use-package easy-kill
  :init
  (global-set-key [remap kill-ring-save] 'easy-kill)
  ;; :bind
  ;; ("C-=" . easy-mark)
  ;; (:map easy-kill-base-map
  ;;       ("=" . easy-kill-expand)
  ;;       ("C-=" . easy-kill-expand)
  ;;       ("q" . easy-kill-expand))
  )

useful settings

https://youtu.be/51eSeqcaikM

(recentf-mode 1)
(setq history-length 25)
(savehist-mode 1)
(save-place-mode 1)

I-search

(setq-default
 ;; 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 100
 regexp-search-ring-max 100)

MonkeyType

(use-package monkeytype
  :bind (:map monkeytype-mode-map
              ("C-c C-c k" .
               (lambda ()
                 (interactive)
                 (kill-matching-buffers "\\*fortune\\*" nil t)
                 (kill-matching-buffers "^\\*Monkeytype\\*\\(<[0-9]*>\\)?$" nil t))))
  :custom
  (monkeytype-directory "~/.emacs.d/.monkeytype")
  (fortune-program "/usr/games/fortune")
  (fortune-dir "/usr/share/games/fortunes")
  (fortune-file "/usr/share/games/fortunes/fortunes")
  (monkeytype-auto-fill t)
  (monkeytype-colored-mode-line nil)
  (monkeytype-downcase nil)
  (monkeytype-mode-line-interval-update 50)
  (monkeytype-most-mistyped-amount 10))

performance tuning

(setq gc-cons-threshold 100000000)
(setq read-process-output-max (* 8 1024 1024)) ;; 8mb

Persistent Prefix Keymaps

https://karthinks.com/software/persistent-prefix-keymaps-in-emacs/

(use-package repeat-help
  :hook (repeat-mode . repeat-help-mode))

TS-movement

(use-package ts-movement
  :load-path "~/.emacs.d/third_party/ts-movement"
  :ensure multiple-cursors
  :ensure hydra
  :config
  (defhydra tsm/hydra ()
    "TS Movement"
    ("d" #'tsm/delete-overlay-at-point)
    ("D" #'tsm/clear-overlays-of-type)
    ("C-b" #'tsm/backward-overlay)
    ("C-f" #'tsm/forward-overlay)
    ("b" #'tsm/node-prev)
    ("f" #'tsm/node-next)
    ("u" #'tsm/node-parent)
    ("d" #'tsm/node-child)
    ("N" #'tsm/node-children)
    ("s" #'tsm/node-children-of-type)
    ("a" #'tsm/node-start)
    ("e" #'tsm/node-end)
    ("m" #'tsm/node-mark)
    ("c" #'tsm/mc/mark-all-overlays))
  (push 'tsm/hydra/tsm/mc/mark-all-overlays mc--default-cmds-to-run-once)
  (advice-add 'symbol-overlay-remove-all :after (lambda () (tsm/clear-overlays (point))))

  (defun my/treesit-backward-up-list ()
    (interactive)
    (let* ((start-point (point))
           (node (treesit-node-on start-point start-point)))
      (while (and node
                  (= (point) start-point))
        (setq node (treesit-node-parent node))
        (when node
          (goto-char (treesit-node-start node))))
      (when (= (point) start-point)
        (message "No parent node found or already at root"))))

  (defun my/down-list ()
    (interactive)
    (let* ((start-point (point))
           (next-sibling (treesit-node-on start-point start-point))
           found-node)
      (while (and next-sibling (not found-node))
        (if (> (treesit-node-child-count next-sibling) 0)
            (setq found-node next-sibling)
          (setq next-sibling (treesit-node-next-sibling next-sibling))))
      (if found-node
          (progn
            (goto-char (treesit-node-start found-node))
            (message "Moved to next non-leaf sibling"))
        (message "No next non-leaf sibling found"))))

  (defun my/backward-node ()
    "Move backward to the previous node in the tree, following a pre-order traversal."
    (interactive)
    ;; (message "node at: %s" (treesit-node-string (treesit-node-at (point))))
    (when-let* ((node (treesit-node-at (point))))
      (let ((target-node nil))
        (while (and node (not target-node))
          (if-let* ((prev-sibling (treesit-node-prev-sibling node)))
              (progn
                (setq node prev-sibling)
                (while (treesit-node-child node -1)
                  (setq node (treesit-node-child node -1)))
                (unless (treesit-node-check node 'missing) ; Skip invalid node, continue loop
                  (setq target-node node)))
            (setq node (treesit-node-parent node))))
        (when target-node
          ;; (message "target-node: %s" (treesit-node-string target-node))
          (goto-char (treesit-node-start target-node))))))

  (defun my/forward-node ()
    (interactive)
    (let ((node (treesit-node-at (point))))
      (while node
        (if-let* ((next-sibling (treesit-node-next-sibling node)))
            (if (treesit-node-check next-sibling 'missing)
                (setq node next-sibling) ; Skip invalid node, continue loop
              (goto-char (treesit-node-start next-sibling))
              (setq node nil))  ; Exit the loop after moving
          (setq node (treesit-node-parent node))))))  ; Move to parent if no next sibling

  :bind (:map ts-movement-map
              ("C-M-u" . my/treesit-backward-up-list)
              ("C-M-d" . my/down-list)
              ("M-f" . my/forward-node)
              ("M-b" . my/backward-node)
              ("C-c ." . tsm/hydra/body))

  :hook
  ((bash-ts-mode c++-ts-mode c-ts-mode cmake-ts-mode dockerfile-ts-mode json-ts-mode
                 python-ts-mode rust-ts-mode toml-ts-mode typescript-ts-mode yaml-ts-mode)
   . ts-movement-mode))

Set custom keybindings

Just a few handy functions.

(global-set-key (kbd "M-/") 'hippie-expand)
(setq ns-right-option-modifier 'super)
(global-unset-key (kbd "C-x 5 1"))
(local-unset-key (kbd "C-x 5 1"))

get current buffer’s name

(global-set-key (kbd "C-c o")
                (lambda () (interactive)
                  (message buffer-file-name)
                  (kill-new buffer-file-name)))