Skip to content

LemonBreezes/emacs-grandview

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Grandview

Overview

Introduction

Emacs grandview is my literate Emacs configuration which has a pretty straightforward project structure:

  • Early init: early-init.el
  • Init: init.el
  • User config (optional): user.el
  • Main config file (.org): This file (defaults to grandview.org)

User config

user.el is the file to place your:

  1. grandview specfic options which include:
    • grandview-cache-dir: Cache directory for grandview
    • grandview-org-file: Path for grandview main config .org file
    • grandview-gc-cons-threshold: The default value to use for gc-cons-threshold
  2. other arbitrary codes to load before loading the main config
  3. packages expecting further evaluation or testing

This .org file

The elisp source blocks in this file is tangled to a regular .el file which is then evaluated by Emacs. Sections named Autoload are tangled to separate files upon which corresponding autoloads are generated. Whenever the content of this file changes, this tangling process is fired up on the execution of kill-emacs command, after that the old config is replaced by the newly generated one.

A section with bold and uppercase title means it is a core config in Grandview. Unless you know exactly what you are doing, it is NOT recommended to comment out these sections. Since codes in other sections may rely on orientations in the section to work properly, so be sure to edit these sections with caution.

Screenshots

File manager

https://user-images.githubusercontent.com/16313743/169456875-ed5af1e7-57cd-4203-96e9-9038119721b9.png

IDE

https://user-images.githubusercontent.com/16313743/169660050-b66d09b7-617e-46a0-a2a9-138c570d1336.png

Org-mode

https://user-images.githubusercontent.com/16313743/169660518-bb1fd05c-089a-41db-830d-43784ae14d6d.png

Lightning fast startup

https://user-images.githubusercontent.com/16313743/169660620-c5f7bef5-499a-4ea2-8a39-3e0f2801cb24.png

Installation

Make sure to backup your own emacs config before installation.

cp -r ~/.emacs.d ~/.emacs.d.bak

Dependencies

Here are the dependencies of this project.

PackageDescriptionRequired
fdA modern find
rgA modern grep
gitVersion control
exaA modern lsoptional
noto-fonts-emojiFont for emojisoptional
wordsEnglish words completionoptional
font-victor-monoDefault fixed pitch fontoptional
ttf-sarasa-gothicDefault variable pitch fontoptional
xdotoolAutomation tool for X11optional

Tangling

Clone the repo and tangle the config.

git clone https://www.github.com/alexluigit/emacs-grandview
## move the repo to ~/.config/emacs, which conforms to the XDG spec
mv ~/emacs-grandview ~/.config/emacs/
## or use symlink
# mv ~/emacs-grandview ~/Code/emacs-grandview
# ln -sf ~/Code/emacs-grandview ~/.config/emacs
## or you can put it to ~/.emacs.d, it's an old convention
# mv ~/emacs-grandview ~/.emacs.d

## let it tangles itself
emacs --daemon
## hooray, enjoy the Grandview
emacsclient -cn
## NOTE: You should be able to start/restart Emacs by 'rem' bash
## command from now on, see the "** Restart it!"  section below.

Restart it!

Although Emacs already provided functionalities like unload-feature and remove-hook to eliminate the side effects of certain packages or user configurations, most of the time it’s still easier to reload your Emacs configurations through a completely restart.

The following section provided a CLI for restarting Grandview.

CLI source

(pcase type
  ("main" (format "%s" grandview-cache-dir))
  ("pkg-builds" (straight--build-dir))
  ("pkg-repos" (straight--repos-dir)))
_is_ime_rime () { [[ -d ~/.config/rime ]] || [[ -d ~/.local/share/fcitx5/rime ]]; }

ELCs=false REPOs=false RESET=false DEBUG=""

while getopts "pPrd" opt; do
  case $opt in
    p) ELCs=true;;
    P) REPOs=true;;
    r) RESET=true;;
    d) DEBUG=--debug-init;;
  esac
done
shift $((OPTIND -1))

emacs_cmd="emacs --daemon $DEBUG"
notify-send "Restarting emacs..." 2>/dev/null
emacs_pid=$(pidof emacs)
timeout=300
counter=0
emacsclient --eval '(kill-emacs)' 2>/dev/null &
while $(kill -0 $emacs_pid 2>/dev/null) && [[ $counter -lt $timeout ]]; do
  counter=$((counter + 1))
  sleep 0.01
done
kill -9 $emacs_pid 2>/dev/null
$RESET && rm -rf '<<grandview-cache(type="main")>>' 2>/dev/null
$ELCs && rm -rf '<<grandview-cache(type="pkg-builds")>>' 2>/dev/null
$REPOs && rm -rf '<<grandview-cache(type="pkg-repos")>>' 2>/dev/null
_is_ime_rime && eval GTK_IM_MODULE=emacs XMODIFIERS=@im=emacs $emacs_cmd || eval $emacs_cmd
command -v xdotool >/dev/null 2>&1 && xdotool set_desktop 0
emacsclient -cne '(delete-file "~/nohup.out")'

This script first dispatches a kill-emacs command to the living Emacs process, which triggers the grandview-tangle hook, the main process of Emacs only get killed after the tangle function finishes. It usually takes less than 1s to tangle this org file, so if Emacs keeps alive for over 3s after the kill-emacs command, it probably gets frozen, so we kill it with kill -9. After the old Emacs get killed, the Emacs daemon is initialized followed by a new frame.

You don’t need to install this script if you have followed Installation section. This script will be tangled to ~/.local/bin/rem, so make sure ~/.local/bin is in your PATH.

Elisp wrapper

Ideally, we can restart Emacs right inside of it via M-x restart-emacs which is a wrapper for rem bash command.

(defun restart-emacs ()
  "A elisp wrapper to `rem' command."
  (interactive)
  (let ((default-directory "~"))
    (start-process "" nil "nohup" "rem")))

CLI usage

But when Emacs hangs up, we can not expect it to evaluate any elisp code. The only thing we can do by then is spawn a terminal call the rem (alias for restart-emacs) bash command.

Here are the available flags of this command.

  • -r: delete Grandview’s cache before restarting. (re-tangle)
  • -p: delete all .elc build of packages before restarting. (rebuild)
  • -P: delete all package repos before restarting. (re-download & rebuild)
  • -d: use –debug-init flag for the daemon

TEXT EDITING

Basic editing (simple.el)

simple.el consists of a grab-bag of basic Emacs commands not specifically related to some major mode or to file-handling.

(use-package simple
  :straight (:type built-in)
  :bind
  (nil
   :map messages-buffer-mode-map
   ("SPC" . nil))
  :config
  ;; added in Emacs 28.1
  (setq next-error-message-highlight t))

Autoload

;;;###autoload
(defadvice! delete-backward-char-ad (fn &rest args)
  "Do not try to delete char when the last char is read-only."
  :around #'delete-backward-char
  (unless (get-text-property (1- (point)) 'read-only) (apply fn args)))

;;;###autoload
(defadvice! keyboard-escape-quit-ad (fn &rest args)
  "Do not close any window when calling `keyboard-escape-quit'."
  :around #'keyboard-escape-quit
  (let ((buffer-quit-function #'ignore)) (apply fn args)))

;;;###autoload
(defadvice! next-error-no-select-ad (fn &rest args)
  "Do not open new window when calling `next-error-no-select'."
  :around #'next-error-no-select
  (let ((split-width-threshold nil)) (apply fn args)))

;;;###autoload
(defadvice! previous-error-no-select-ad (fn &rest args)
  "Do not open new window when calling `previous-error-no-select'."
  :around #'previous-error-no-select
  (let ((split-width-threshold nil)) (apply fn args)))

;;;###autoload
(defadvice! yank-ad (&rest _)
  "Make `yank' behave like paste (p) command in vim."
  :before #'yank
  (when-let ((clip (condition-case nil (current-kill 0 t) (error ""))))
    (set-text-properties 0 (length clip) nil clip)
    (when (string-suffix-p "\n" clip)
      (goto-char (line-beginning-position)))))

;;;###autoload
(defun +simple-pop-local-mark-ring ()
  "Move cursor to last mark position of current buffer.
Call this repeatedly will cycle all positions in `mark-ring'."
  (interactive)
  (set-mark-command t))

;;;###autoload
(defun +simple-join-line ()
  "Join the current line with the line beneath it."
  (interactive)
  (delete-indentation 1))

;;;###autoload
(defun +simple-mark-inner-line ()
  "Mark inner line and move cursor to bol."
  (interactive)
  (save-window-excursion
    (end-of-visual-line)
    (set-mark-command nil)
    (back-to-indentation)))

;; Copied from `xah-fly-keys'
;;;###autoload
(defun +toggle-letter-case ()
  "Toggle the letter case of current word or selection.
Always cycle in this order: Init Caps, ALL CAPS, all lower.
URL `http://xahlee.info/emacs/emacs/modernization_upcase-word.html'
Version: 2020-06-26"
  (interactive)
  (let ((deactivate-mark nil) $p1 $p2)
    (if (region-active-p)
        (setq $p1 (region-beginning) $p2 (region-end))
      (save-excursion
        (skip-chars-backward "[:alpha:]")
        (setq $p1 (point))
        (skip-chars-forward "[:alpha:]")
        (setq $p2 (point))))
    (when (not (eq last-command this-command))
      (put this-command 'state 0))
    (cond
     ((equal 0 (get this-command 'state))
      (upcase-initials-region $p1 $p2)
      (put this-command 'state 1))
     ((equal 1 (get this-command 'state))
      (upcase-region $p1 $p2)
      (put this-command 'state 2))
     ((equal 2 (get this-command 'state))
      (downcase-region $p1 $p2)
      (put this-command 'state 0)))))

Modal editing (meow.el)

Unlike evil-mode, which tries to create a whole vim emulation in emacs, meow only focus on bringing the goodness of modal editing to vanilla emacs.

You may noticed that I didn’t include any keybindings of meow here, that’s because it can be very lengthy and should be configured separately, see Keybindings for details.

If you want to know more about meow or modal editing in general, check out meow.

(use-package meow
  :demand t
  :init
  (setq meow-keymap nil)
  (meow-global-mode)
  :config
  (setq meow-visit-sanitize-completion nil)
  (setq meow-use-clipboard t)
  (setq meow-esc-delay 0.001)
  (setq meow-keypad-start-keys '((?c . ?c) (?x . ?x)))
  (setq meow-keypad-describe-delay 0.5)
  (setq meow-select-on-change t)
  (setq meow-cursor-type-normal 'box)
  (setq meow-cursor-type-insert '(bar . 4))
  (setq meow-cursor-type-default 'hbar)
  (setq meow-selection-command-fallback
        '((meow-replace . meow-yank)
          (meow-reverse . back-to-indentation)
          (meow-change . meow-change-char)
          (+meow-save . +pulse-save-line)
          (meow-kill . meow-kill-whole-line)
          (meow-pop-selection . meow-pop-grab)
          (meow-beacon-change . meow-beacon-change-char)
          (meow-cancel . keyboard-quit)
          (meow-delete . meow-C-d)))
  (setq meow-char-thing-table
        '((?r . round)
          (?b . square) ;; `b' for bracket
          (?c . curly)
          (?s . string)
          (?e . symbol)
          (?w . window)
          (?B . buffer)
          (?p . paragraph)
          (?\[ . line)
          (?\] . line)
          (?d . defun)
          (?i . indent)
          (?x . extend)
          (?. . sentence)))
  (appendq! meow-mode-state-list '((helpful-mode . normal)
                                   (Man-mode . normal)
                                   (message-buffer-mode . normal))))

Autoload

(defcustom +meow-fallback-key-cmd-alist
  '(("SPC" . consult-buffer)
    ("/" . consult-line)
    ("q" . quit-window))
  "A list of (KEY . CMD) pairs.
Setup KEY's corresponding fallback CMD in `meow-motion-mode'."
  :group 'grandview :type 'alist)

;;;###autoload
(defadvice! meow-query-replace-ad (&rest _)
  "Call `meow-query-replace' and auto fill prompt with region text."
  :before #'meow-query-replace
  (unless (region-active-p) (meow-mark-symbol 1))
  (let ((text (buffer-substring-no-properties (region-beginning) (region-end))))
    (exchange-point-and-mark)
    (deactivate-mark t)
    (run-with-timer 0.05 nil 'insert text)))

;;;###autoload
(defadvice! meow-insert-exit-ad (&rest _)
  "Quit `completion-in-region-mode' after `meow-insert-exit'."
  :after #'meow-insert-exit
  (completion-in-region-mode -1))

;;;###autoload
(defadvice! meow-inhibit-highlight-num-positions-ad (&rest _)
  "Do not highlight number positions."
  :override #'meow--maybe-highlight-num-positions
  (ignore))

;;;###autoload
(defadvice! meow--save-origin-cmds-ad (&rest _)
  "Save original sub keymaps as well."
  :override #'meow--save-origin-commands
  (cl-loop
   for key-code being the key-codes of meow-motion-state-keymap do
   (ignore-errors
     (let* ((key (meow--parse-input-event key-code))
            (cmd-or-kmap (key-binding (kbd key))))
       (when (or (and (commandp cmd-or-kmap)
                      (not (equal cmd-or-kmap 'undefined)))
                 (keymapp cmd-or-kmap))
         (let ((rebind-key (concat meow-motion-remap-prefix key)))
           (local-set-key (kbd rebind-key) cmd-or-kmap)))))))

;;;###autoload
(defun +meow-save ()
  (interactive)
  (save-excursion
    (meow--with-selection-fallback
     (meow--prepare-region-for-kill)
     (call-interactively 'kill-ring-save))))

;;;###autoload
(defun +meow-escape ()
  (interactive)
  (cond
   ((minibufferp)
    (keyboard-escape-quit))
   ((region-active-p)
    (meow-cancel))
   (t (call-interactively 'execute-extended-command))))

;;;###autoload
(defun +meow-with-key-fallback ()
  "Execute fallback command if exists.
If `last-input-event' is bounded to any command in current
major-mode, call that command, otherwise call its fallback
command defined in `+meow-fallback-key-cmd-alist'."
  (interactive)
  (let* ((key (meow--parse-input-event last-input-event))
         (rebind-key (concat meow-motion-remap-prefix key)))
    (if (key-binding (kbd rebind-key))
        (meow--execute-kbd-macro rebind-key)
      (funcall (alist-get key +meow-fallback-key-cmd-alist #'ignore nil #'string=)))))

;;;###autoload
(defun +meow-insert ()
  (interactive)
  (meow--switch-state 'insert))

;;;###autoload
(defun +meow-insert-at-first-non-whitespace ()
  (interactive)
  (back-to-indentation)
  (meow-insert))

Quick goto char (avy.el)

Jump to any visible text.

(use-package avy
  :config
  (setq avy-timeout-seconds 0.3)
  (setq avy-all-windows nil)
  (setq avy-keys '(?a ?r ?s ?t ?n ?e ?i ?o)))

Symbol pairs (embrace.el)

embrace.el is a package for symbol pairs insert/change/delete which resembles to surround.vim in vim.

I’ve forked this package to extract embrace-default-pairs out, so we can use keys like ~,r~ to select an inner parenthesis block (this assumes your comma key has been bound to meow-inner-of-thing.)

(use-package embrace
  :straight
  (embrace :type git :depth full :host github
           :repo "cute-jumper/embrace.el"
           :fork (:host github :repo "alexluigit/embrace.el"))
  :after-call set-mark
  :preface
  (setq embrace-default-pairs
        '((?r . ("(" . ")"))
          (?R . ("( " . " )"))
          (?c . ("{" . "}"))
          (?C . ("{ " . " }"))
          (?\[ . ("[" . "]"))
          (?\] . ("[ " . " ]"))
          (?a . ("<" . ">"))
          (?A . ("< " . " >"))
          (?s . ("\"" . "\""))
          (?\' . ("\'" . "\'"))
          (?` . ("`" . "`")))))

Tab for Indentation (indent.el)

I believe tabs, in the sense of inserting the tab character, are best suited for indentation. While spaces are superior at precisely aligning text. However, I understand that elisp uses its own approach, which I do not want to interfere with. Also, Emacs tends to perform alignments by mixing tabs with spaces, which can actually lead to misalignments depending on certain variables such as the size of the tab. As such, I am disabling tabs by default.

If there ever is a need to use different settings in other modes, we can customise them via hooks. This is not an issue I have encountered yet and am therefore refraining from solving a problem that does not affect me.

Note that tab-always-indent will first do indentation and then try to complete whatever you have typed in.

(use-package indent
  :straight (:type built-in)
  :preface
  (setq-default tab-always-indent t)
  (setq-default tab-first-completion 'word-or-paren-or-punct) ; Emacs 27
  (setq-default indent-tabs-mode nil))

USER INTERFACE

Basics

Show current key strokes in echo area after 0.25s

Disable bidirectional text scanning for a modest performance boost. I’ve set this to nil in the past, but the bidi-display-reordering’s docs say that is an undefined state and suggest the value left-to-right to be just as good.

Do not display continuation lines Do not disable the erase-buffer command

By default, page scrolling should keep the point at the same visual position, rather than force it to the top or bottom of the viewport. This eliminates the friction of guessing where the point has warped to.

As for per-line scrolling, I dislike the default behaviour of visually re-centring the point: it is too aggressive as a standard mode of interaction. With the following setq-default, the point will stay at the top/bottom of the screen while moving in that direction (use C-l to reposition it).

(setq-default bidi-display-reordering 'left-to-right)
(setq-default bidi-paragraph-direction 'left-to-right)
(setq bidi-inhibit-bpa t)  ; Emacs 27 only
(setq-default truncate-lines t)
(setq echo-keystrokes 0.25)
(setq scroll-conservatively 101)
(setq scroll-up-aggressively 0.01)
(setq scroll-down-aggressively 0.01)
(setq auto-window-vscroll nil)
(setq scroll-step 1)
(setq scroll-margin 1)
(setq hscroll-step 1)
(setq hscroll-margin 1)
(put 'erase-buffer 'disabled nil)

Window placement (window.el)

The display-buffer-alist is intended as a rule-set for controlling the display of windows. The objective is to create a more intuitive workflow where targeted buffer groups or types are always shown in a given location, on the premise that predictability improves usability.

For each buffer action in it we can define several functions for selecting the appropriate window. These are executed in sequence, but my usage thus far suggests that a simpler method is just as effective for my case.

Disable cursor-in-non-selected-windows and highlight-nonselected-windows reduces rendering/line scan work for Emacs in non-focused windows.

(use-package window
  :straight (:type built-in)
  :config
  (setq-default cursor-in-non-selected-windows nil)
  (setq highlight-nonselected-windows nil)
  (setq display-buffer-alist
        `(("\\*\\(Flymake\\|Messages\\|Backtrace\\|Warnings\\|Compile-Log\\|Custom\\)\\*"
           (display-buffer-in-side-window)
           (window-height . 0.2)
           (side . top))
          ("^\\*\\(Help\\|helpful\\).*"
           (display-buffer-in-side-window)
           (window-width . 0.4)
           (side . right))
          ("\\*\\vc-\\(incoming\\|outgoing\\|Output\\|Register Preview\\).*"
           (display-buffer-at-bottom))))
  (setq help-window-select t)
  (setq window-combination-resize t)
  (setq even-window-sizes 'height-only)
  (setq window-sides-vertical nil)
  (setq switch-to-buffer-in-dedicated-window 'pop)
  (setq split-height-threshold nil)
  (setq split-width-threshold 120))

Autoload

;;;###autoload
(defun +show-messages (&optional erase)
  "Show *Messages* buffer in other frame.
If ERASE is non-nil, erase the buffer before switching to it."
  (interactive "P")
  (when erase
    (let ((inhibit-read-only t))
      (with-current-buffer "*Messages*" (erase-buffer))))
  (let ((win (get-buffer-window "*Messages*" t))
        (after-make-frame-functions nil))
    (if (window-live-p win)
        (delete-frame (window-frame win))
      (with-selected-frame (make-frame)
        (set-window-parameter (selected-window) 'no-other-window t)
        (switch-to-buffer "*Messages*")))))

(defvar +monocle--saved-window-configuration nil
  "Last window configuration before enabling `+monocle-mode'.")

;;;###autoload
(define-minor-mode +monocle-mode
  "Toggle between multiple windows and single window.
This is the equivalent of maximising a window.  Tiling window
managers such as DWM, BSPWM refer to this state as 'monocle'."
  :global t
  (let ((config +monocle--saved-window-configuration)
        (buf (current-buffer)))
    (if (one-window-p)
        (when config
          (set-window-configuration config))
      (setq +monocle--saved-window-configuration (current-window-configuration))
      (when (window-parameter nil 'window-side) (delete-window))
      (delete-other-windows)
      (switch-to-buffer buf))))

Frame parameters (frame.el)

Adjust frame parameters such as opacity dynamically.

(use-package emacs
  :if EMACS29+
  :hook (window-configuration-change . +frame-opacity-auto))

Autoload

(defvar +frame-cursor-saved-color
  (frame-parameter nil 'cursor-color))

(defcustom +frame-cursor-dim-color "#606060"
  "Cursor color for `+frame-cursor-dim-mode'."
  :group 'cursor :type 'string)

(defcustom +frame-opacity-alpha-background 80
  "Default frame opacity."
  :group 'grandview
  :type 'integer)

(defcustom +frame-opacity-disabled-predicates '()
  "A list of predicate functions in which the `+frame-opacity-auto-mode' will not be turned on."
  :group 'grandview
  :type 'hook)

;;;###autoload
(defun +frame-opacity-auto ()
  "Setup frame opacity according to current major-mode."
  (if (seq-find 'funcall +frame-opacity-disabled-predicates)
      (set-frame-parameter (selected-frame) 'alpha-background 100)
    (set-frame-parameter (selected-frame) 'alpha-background +frame-opacity-alpha-background)))

;;;###autoload
(defun +frame-opacity-set (&optional percent)
  (interactive "P")
  (cond ((or (and percent (not current-prefix-arg))
             (numberp percent))
         (setq +frame-opacity-alpha-background (* 10 percent))
         (set-frame-parameter (selected-frame) 'alpha-background +frame-opacity-alpha-background))
        ((equal current-prefix-arg '(4))
         (set-frame-parameter (selected-frame) 'alpha-background +frame-opacity-alpha-background))
        (t
         (let ((opa (frame-parameter nil 'alpha-background))
               (low 60) (high 100))
           (if (eq opa low)
               (set-frame-parameter (selected-frame) 'alpha-background high)
             (set-frame-parameter (selected-frame) 'alpha-background low))))))

;;;###autoload
(define-minor-mode +frame-cursor-dim-mode
  "Enable dimmed `cursor-color' for current frame."
  :global t
  :lighter nil
  :group 'cursor
  (if +frame-cursor-dim-mode
      (progn
        (setq-local cursor-type nil)
        (blink-cursor-mode -1)
        (set-cursor-color +frame-cursor-dim-color))
    (blink-cursor-mode +1)
    (set-cursor-color +frame-cursor-saved-color)))

Pulse line (pulse.el)

(if HAS-GUI
    (add-hook 'after-make-frame-functions (lambda (f) (with-selected-frame f (+pulse-line-mode))))
  (+pulse-line-mode))

Autoload

(require 'pulse)

(defcustom +pulse-command-list
  '(recenter-top-bottom
    reposition-window
    consult--jump-nomark
    ace-select-window)
  "Commands that should automatically `+pulse-pulse-line'.
You must restart function `+pulse-line-mode' for changes to
take effect."
  :group 'grandview :type '(repeat function))

(defface +pulse-line-face
  '((default :extend t)
    (((class color) (min-colors 88) (background light))
     :background "#8eecf4")
    (t :inverse-video t :background "#004065"))
  "Default face for `+pulse-pulse-line'."
  :group 'grandview)

;;;###autoload
(defun +pulse-line (&optional face kill)
  "Temporarily highlight the current line with optional FACE."
  (interactive)
  (let ((beg (if (eobp)
                 (line-beginning-position 0)
               (line-beginning-position)))
        (end (line-beginning-position 2))
        (pulse-delay .05)
        (face (or face '+pulse-line-face)))
    (pulse-momentary-highlight-region beg end face)
    (when kill (kill-ring-save beg end))))

;;;###autoload
(defun +pulse-save-line ()
  "Temporarily highlight the current line and copy it."
  (interactive)
  (+pulse-line nil t))

;;;###autoload
(define-minor-mode +pulse-line-mode
  "Set up for `+pulse-command-list'."
  :init-value nil
  :global t
  (if +pulse-line-mode
      (dolist (fn +pulse-command-list)
        (advice-add fn :after (lambda (&rest _) (interactive) (+pulse-line))))
    (dolist (fn +pulse-command-list)
      (advice-remove fn (lambda (&rest _) (interactive) (+pulse-line))))))

Icon library (all-the-icons.el)

all-the-icons is a utility for using and formatting various Icon fonts within Emacs. Icon Fonts allow you to propertize and format icons the same way you would normal text. This enables things such as better scaling of and anti aliasing of the icons.

(use-package all-the-icons
  :config
  (assoc-delete-all "bookmark" all-the-icons-regexp-icon-alist #'equal))

COMPLETION FRAMEWORK

The optimal way of using Emacs is through searching and narrowing selection candidates. Spend less time worrying about where things are on the screen and more on how fast you can bring them into focus. This is, of course, a matter of realigning priorities, as we still wish to control every aspect of the interface.

Minibuffer and completion functions (minibuffer.el)

The minibuffer is the epicentre of extended interactivity with all sorts of Emacs workflows: to select a buffer, open a file, provide an answer to some prompt, such as a number, regular expression, password, and so on.

What my minibuffer config does:

Intangible cursors

Disallow user move cursors into prompt.

Recursive minibuffers

Enable recursive minibuffers. This practically means that you can start something in the minibuffer, switch to another window, call the minibuffer again, run some commands, and then move back to what you initiated in the original minibuffer. Or simply call an M-x command while in the midst of a minibuffer session. To exit, hit C-[ (abort-recursive-edit), though the regular C-g should also do the trick.

The minibuffer-depth-indicate-mode will show a recursion indicator, represented as a number, next to the minibuffer prompt, if a recursive edit is in progress.

(use-package minibuffer
  :straight (:type built-in)
  :config
  (setq enable-recursive-minibuffers t)
  (setq minibuffer-eldef-shorten-default t)
  (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt))
  (minibuffer-depth-indicate-mode 1))

Autoload

;;;###autoload
(defun +minibuffer-append-metadata (metadata candidates)
  "Append METADATA for CANDIDATES."
  (let ((entry (if (functionp metadata)
                   `(metadata (annotation-function . ,metadata))
                 `(metadata (category . ,metadata)))))
    (lambda (string pred action)
      (if (eq action 'metadata)
          entry
        (complete-with-action action candidates string pred)))))

Minibuffer history (savehist.el)

Keeps a record of actions involving the minibuffer.

(use-package savehist
  :straight (:type built-in)
  :after-call minibuffer-setup-hook
  :config
  (setq savehist-file (locate-user-emacs-file "savehist"))
  (setq history-length 10000)
  (setq history-delete-duplicates t)
  (setq savehist-save-minibuffer-history t)
  (savehist-mode))

Vertical completion candidates (vertico.el)

Vertico provides a performant and minimalistic vertical completion UI based on the default completion system. By reusing the built-in facilities, Vertico achieves full compatibility with built-in Emacs completion commands and completion tables.

Here I just modified face for current candidate and make height of vertico window as a constant value.

(use-package vertico
  :after-call pre-command-hook
  :custom-face
  (vertico-current ((t (:inherit lazy-highlight))))
  :config
  (setq resize-mini-windows 'grow-only)
  (vertico-mode 1))

Match candidates made easy (orderless.el)

This package provides an orderless completion style that divides the pattern into components (space-separated by default), and matches candidates that match all of the components in any order.

Setup completion styles in minibuffer.

Not that we have set orderless-component-separator to the function orderless-escapable-split-on-space. This allows us to match candidates with literal spaces. Suppose you are browsing dired.el and try to locate the dired function, you can issue a consult-outline command and input “defun dired\ \(\)”, this gives you (defun dired (dirname &optional switches) as the sole match rather than all of the dired-* noise.

(use-package orderless
  :after-call minibuffer-setup-hook
  :config
  (use-package pinyinlib
    :commands pinyinlib-build-regexp-string)
  (setq completion-styles '(orderless))
  (setq orderless-component-separator #'orderless-escapable-split-on-space)
  (setq orderless-matching-styles
        '(+orderless-pinyin-only-initialism
          orderless-initialism
          orderless-prefixes
          orderless-regexp))
  (setq orderless-style-dispatchers
        '(+orderless-literal-dispatcher
          +orderless-initialism-dispatcher
          +orderless-without-literal-dispatcher
          +orderless-pinyin-dispatcher)))

Autoload

(defun +orderless-pinyin-only-initialism (pattern)
  "Leading pinyin initialism regex generator."
  (if (< (length pattern) 10)
      (pinyinlib-build-regexp-string pattern t nil t)
    pattern))

;;;###autoload
(defun +orderless-literal-dispatcher (pattern _index _total)
  "Literal style dispatcher using the equals sign as a prefix."
  (when (string-suffix-p "=" pattern)
    `(orderless-literal . ,(substring pattern 0 -1))))

;;;###autoload
(defun +orderless-initialism-dispatcher (pattern _index _total)
  "Leading initialism dispatcher using the comma sign as a prefix."
  (when (string-prefix-p "," pattern)
    `(orderless-strict-leading-initialism . ,(substring pattern 1))))

;;;###autoload
(defun +orderless-pinyin-dispatcher (pattern _index _total)
  "Pinyin initialism dispatcher using the backtick sign as a prefix."
  (when (string-prefix-p "`" pattern)
    `(+orderless-pinyin-only-initialism . ,(substring pattern 1))))

;;;###autoload
(defun +orderless-without-literal-dispatcher (pattern _index _total)
  (when (string-prefix-p "~" pattern)
    `(orderless-without-literal . ,(substring pattern 1))))

Useful commands using completion (consult.el)

Consult implements a set of consult-<thing> commands which use completing-read to select from a list of candidates. Consult provides an enhanced buffer switcher consult-buffer and search and navigation commands like consult-imenu and consult-line. Searching through multiple files is supported by the asynchronous consult-grep command. Many Consult commands allow previewing candidates - if a candidate is selected in the completion view, the buffer shows the candidate immediately.

The Consult commands are compatible with completion systems based on the Emacs completing-read API, including the default completion system, Icomplete, Selectrum, Vertico and Embark.

(use-package consult
  :after-call minibuffer-setup-hook
  :init
  (setq completion-in-region-function #'consult-completion-in-region)
  :bind
  (nil
   :map meow-normal-state-keymap
   ("/" . consult-line)
   :map grandview-mct-map
   ("/" . consult-line-multi)
   ("b" . consult-bookmark)
   ("e" . consult-compile-error)
   ("r" . consult-ripgrep)
   ("k" . consult-keep-lines)
   ("i" . consult-imenu-multi)
   ("n" . consult-focus-lines) ; narrow
   ("o" . consult-outline)
   ("R" . consult-register)
   ("y" . consult-yank-from-kill-ring)
   ("m" . consult-minor-mode-menu)
   ("c" . consult-complex-command)
   ("C" . consult-mode-command))
  :config
  (advice-add #'register-preview :override #'consult-register-window)
  (setq register-preview-delay 0.2)
  (setq register-preview-function #'consult-register-format)
  (setq xref-show-xrefs-function #'consult-xref)
  (setq xref-show-definitions-function #'consult-xref)
  (setq consult-project-root-function #'ale-consult-project-root)
  (setq consult-line-numbers-widen t)
  (setq consult-async-min-input 3)
  (setq consult-async-input-debounce 0.5)
  (setq consult-async-input-throttle 0.8)
  (setq consult-narrow-key ">"))

Candidate annotation (marginalia.el)

This is a utility jointly developed by Daniel Mendler and Omar Antolín Camarena that provides annotations to completion candidates. It is meant to be framework-agnostic, so it works with Selectrum, Icomplete, vertico, and Embark.

(use-package marginalia
  :after-call minibuffer-setup-hook
  :config
  (marginalia-mode)
  (setq marginalia-align 'left))

Completion overlay region function (corfu.el)

Corfu enhances the default completion in region function with a completion overlay. The current candidates are shown in a popup below or above the point. Corfu can be considered the minimalistic completion-in-region counterpart of Vertico.

We also enabled corfu-doc-mode to show documentation of the candidates in a pop-up window.

In addition, thanks to the snippet from @smallzhan in this post, we have all-the-icons integration for corfu.

(use-package corfu
  :straight (:files (:defaults "extensions/*.el"))
  :after-call post-self-insert-hook
  :custom
  (corfu-auto t)
  (corfu-auto-delay 0.05)
  (corfu-auto-prefix 2)
  (corfu-cycle t)
  (corfu-preselect-first nil)
  (corfu-on-exact-match nil)
  :bind
  (nil
   :map corfu-map
   ("TAB" . corfu-next)
   ([tab] . corfu-next)
   ("S-TAB" . corfu-previous)
   ([backtab] . corfu-previous)
   ("M-n" . nil)
   ("M-p" . nil))
  :config
  (add-to-list 'corfu-margin-formatters #'+corfu-icons-margin-formatter)
  (global-corfu-mode))

(use-package corfu-doc
  :hook
  (corfu-mode . corfu-doc-mode))

Autoload

(require 'all-the-icons)

(defvar +corfu-icons--cache nil
  "The cache of styled and padded label (text or icon).
An alist.")

(defvar +corfu-icons--icons
  `((unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :v-adjust -0.15))
    (text . ,(all-the-icons-faicon "text-width" :height 0.8 :v-adjust -0.02))
    (method . ,(all-the-icons-faicon "cube" :height 0.8 :v-adjust -0.02 :face 'all-the-icons-purple))
    (function . ,(all-the-icons-faicon "cube" :height 0.8 :v-adjust -0.02 :face 'all-the-icons-purple))
    (fun . ,(all-the-icons-faicon "cube" :height 0.8 :v-adjust -0.02 :face 'all-the-icons-purple))
    (constructor . ,(all-the-icons-faicon "cube" :height 0.8 :v-adjust -0.02 :face 'all-the-icons-purple))
    (ctor . ,(all-the-icons-faicon "cube" :height 0.8 :v-adjust -0.02 :face 'all-the-icons-purple))
    (field . ,(all-the-icons-octicon "tag" :height 0.85 :v-adjust 0 :face 'all-the-icons-lblue))
    (variable . ,(all-the-icons-octicon "tag" :height 0.85 :v-adjust 0 :face 'all-the-icons-lblue))
    (var . ,(all-the-icons-octicon "tag" :height 0.85 :v-adjust 0 :face 'all-the-icons-lblue))
    (class . ,(all-the-icons-material "settings_input_component" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-orange))
    (interface . ,(all-the-icons-material "share" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-lblue))
    (i/f . ,(all-the-icons-material "share" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-lblue))
    (module . ,(all-the-icons-material "view_module" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-lblue))
    (mod . ,(all-the-icons-material "view_module" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-lblue))
    (property . ,(all-the-icons-faicon "wrench" :height 0.8 :v-adjust -0.02))
    (prop . ,(all-the-icons-faicon "wrench" :height 0.8 :v-adjust -0.02))
    (unit . ,(all-the-icons-material "settings_system_daydream" :height 0.8 :v-adjust -0.15))
    (value . ,(all-the-icons-material "format_align_right" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-lblue))
    (enum . ,(all-the-icons-material "storage" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-orange))
    (keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :v-adjust -0.15))
    (k/w . ,(all-the-icons-material "filter_center_focus" :height 0.8 :v-adjust -0.15))
    (snippet . ,(all-the-icons-material "format_align_center" :height 0.8 :v-adjust -0.15))
    (sn . ,(all-the-icons-material "format_align_center" :height 0.8 :v-adjust -0.15))
    (color . ,(all-the-icons-material "palette" :height 0.8 :v-adjust -0.15))
    (file . ,(all-the-icons-faicon "file-o" :height 0.8 :v-adjust -0.02))
    (reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :v-adjust -0.15))
    (ref . ,(all-the-icons-material "collections_bookmark" :height 0.8 :v-adjust -0.15))
    (folder . ,(all-the-icons-faicon "folder-open" :height 0.8 :v-adjust -0.02))
    (dir . ,(all-the-icons-faicon "folder-open" :height 0.8 :v-adjust -0.02))
    (enum-member . ,(all-the-icons-material "format_align_right" :height 0.8 :v-adjust -0.15))
    (enummember . ,(all-the-icons-material "format_align_right" :height 0.8 :v-adjust -0.15))
    (member . ,(all-the-icons-material "format_align_right" :height 0.8 :v-adjust -0.15))
    (constant . ,(all-the-icons-faicon "square-o" :height 0.8 :v-adjust -0.1))
    (const . ,(all-the-icons-faicon "square-o" :height 0.8 :v-adjust -0.1))
    (struct . ,(all-the-icons-material "settings_input_component" :height 0.8 :v-adjust -0.15 :face 'all-the-icons-orange))
    (event . ,(all-the-icons-octicon "zap" :height 0.8 :v-adjust 0 :face 'all-the-icons-orange))
    (operator . ,(all-the-icons-material "control_point" :height 0.8 :v-adjust -0.15))
    (op . ,(all-the-icons-material "control_point" :height 0.8 :v-adjust -0.15))
    (type-parameter . ,(all-the-icons-faicon "arrows" :height 0.8 :v-adjust -0.02))
    (param . ,(all-the-icons-faicon "arrows" :height 0.8 :v-adjust -0.02))
    (template . ,(all-the-icons-material "format_align_left" :height 0.8 :v-adjust -0.15))
    (t . ,(all-the-icons-material "find_in_page" :height 0.8 :v-adjust -0.15))))

(defsubst +corfu-icons--metadata-get (metadata type-name)
  (or (plist-get completion-extra-properties (intern (format ":%s" type-name)))
      (cdr (assq (intern type-name) metadata))))

(defun +corfu-icons-formatted (kind)
  "Format icon kind with all-the-icons."
  (or (alist-get kind +corfu-icons--cache)
      (let ((map (assq kind +corfu-icons--icons)))
        (let*  ((icon (if map (cdr map) (cdr (assq t +corfu-icons--icons))))
                (half (/ (default-font-width) 2))
                (pad (propertize " " 'display `(space :width (,half))))
                (disp (concat pad icon pad)))
          (setf (alist-get kind +corfu-icons--cache) disp)
          disp))))

;;;###autoload
(defun +corfu-icons-margin-formatter (metadata)
  "Return a margin-formatter function which produces kind icons.
METADATA is the completion metadata supplied by the caller (see
info node `(elisp)Programmed Completion').  To use, add this
function to the relevant margin-formatters list."
  (when-let ((kind-func (+corfu-icons--metadata-get metadata "company-kind")))
    (lambda (cand)
	    (if-let ((kind (funcall kind-func cand)))
	        (+corfu-icons-formatted kind)
	      (+corfu-icons-formatted t)))))

Completion at point Extensions (cape.el)

Let your completions fly! This package provides additional completion backends in the form of Capfs (completion-at-point-functions).

(use-package cape
  :after-call post-self-insert-hook
  :custom
  (cape-dict-file "/usr/share/dict/words")
  :bind
  (nil
   :map grandview-prog-map
   ("c" . completion-at-point) ;; capf
   ("t" . complete-tag)        ;; etags
   ("d" . cape-dabbrev)        ;; or dabbrev-completion
   ("f" . cape-file)
   ("k" . cape-keyword)
   ("s" . cape-symbol)
   ("a" . cape-abbrev)
   ("i" . cape-ispell)
   ("l" . cape-line)
   ("w" . cape-dict)
   ("\\" . cape-tex)
   ("_" . cape-tex)
   ("^" . cape-tex)
   ("&" . cape-sgml)
   ("r" . cape-rfc1345))
  :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-keyword)
  (add-to-list 'completion-at-point-functions #'cape-ispell)
  (add-to-list 'completion-at-point-functions #'cape-dict))

Keybindings

This section contains all core keybindings of Grandview.

INSERT

(when (featurep 'meow)
  (bind-keys
   :map meow-insert-state-keymap
   ("C-;" . meow-kill-whole-line)
   ("<C-i>" . meow-right)
   ("C-o" . meow-left)))

NORMAL

(when (featurep 'meow)
  (meow-normal-define-key
   '("0" . meow-digit-argument)
   '("1" . meow-digit-argument)
   '("2" . meow-digit-argument)
   '("3" . meow-digit-argument)
   '("4" . meow-digit-argument)
   '("5" . meow-digit-argument)
   '("6" . meow-digit-argument)
   '("7" . meow-digit-argument)
   '("8" . meow-digit-argument)
   '("9" . meow-digit-argument)
   '("<escape>" . +meow-escape)
   '("<backspace>" . meow-pop-selection)
   '("," . meow-inner-of-thing)
   '("." . meow-bounds-of-thing)
   '("[" . meow-beginning-of-thing)
   '("]" . meow-end-of-thing)
   '("-" . negative-argument)
   '("=" . meow-query-replace)
   '("+" . meow-query-replace-regexp)
   '("^" . meow-last-buffer)
   '("a" . +meow-insert)
   '("A" . +meow-insert-at-first-non-whitespace)
   '("b" . meow-block)
   '("B" . meow-to-block)
   '("c" . meow-change)
   '("C" . meow-change-save)
   '("d" . meow-delete)
   '("e" . meow-line)
   '("E" . +simple-mark-inner-line)
   '("f" . meow-find)
   '("F" . forward-sexp)
   '("g" . meow-grab)
   '("G" . meow-sync-grab)
   '("h" . embrace-commander)
   '("i" . meow-right)
   '("I" . meow-right-expand)
   '("j" . +simple-join-line)
   '("J" . meow-join)
   '("k" . meow-kill)
   '("K" . meow-C-k)
   '("l" . consult-goto-line)
   '("L" . meow-kmacro-lines)
   '("m" . meow-mark-word)
   '("M" . meow-mark-symbol)
   '("n" . meow-next)
   '("N" . meow-open-below)
   '("o" . meow-left)
   '("O" . meow-left-expand)
   '("p" . meow-prev)
   '("P" . meow-open-above)
   '("q" . +meow-with-key-fallback)
   '("r" . meow-reverse)
   '("R" . repeat)
   '("s" . meow-search)
   '("S" . meow-pop-search)
   '("t" . avy-goto-char-timer)
   '("T" . avy-resume)
   '("u" . undo)
   '("U" . undo-redo)
   '("v" . consult-mark)
   '("V" . consult-global-mark)
   '("w" . meow-next-word)
   '("W" . meow-back-word)
   '("x" . +meow-save)
   '("y" . meow-replace)
   '("Y" . meow-yank-pop)
   '("z" . meow-start-kmacro-or-insert-counter)
   '("Z" . meow-end-or-call-kmacro)))

LEADER

(when (featurep 'meow)
  (meow-leader-define-key
   '("SPC" . +meow-with-key-fallback)
   '("0" . delete-window)
   '("1" . delete-other-windows)
   '("2" . split-window-below)
   '("3" . split-window-right)
   '("4" . ctl-x-4-prefix)
   '("5" . ctl-x-5-prefix)
   '("8" . insert-char)
   '("9" . grandview-tab-map)
   '("?" . describe-keymap)
   '("/" . describe-symbol)
   '(";" . comment-line)
   '("," . beginning-of-buffer)
   '("." . end-of-buffer)
   '("a" . grandview-apps-map)
   '("e" . dired-jump)
   '("E" . eval-expression)
   '("f" . grandview-files-map)
   '("i" . list-buffers)
   '("k" . kill-this-buffer)
   '("n" . ale-project-find-file)
   '("o" . grandview-org-map)
   '("p" . grandview-prog-map)
   '("P" . grandview-project-map)
   '("r" . grandview-reg-map)
   '("t" . grandview-mct-map)
   '("w" . grandview-win-map)
   '("z" . window-toggle-side-windows)))

(bind-keys
 :map grandview-files-map
 ("g" . grandview-config)
 :map grandview-win-map
 ("m" . +show-messages)
 :map grandview-apps-map
 ("d" . toggle-debug-on-error)
 ("o" . +frame-opacity-set)
 ("=" . count-words)
 ("n" . ale-tab-next)
 ("p" . ale-tab-previous))

GLOBAL

For historical reason, terminal can not tell the difference between some key storkes. For example, C-i and tab, C-m and Return, etc. By default, emacs follow this convention, but it doesn’t mean emacs are not able to tell the difference. On GUI, we can use input-decode-map to give C-i different meaning. On terminal, we rebind <f6> to C-i, so make sure you have relevant settings in your terminal emulator’s settings.

(if HAS-GUI
    (add-hook 'after-make-frame-functions
              (lambda (f) (with-selected-frame f (define-key input-decode-map [?\C-i] [C-i]))))
  (bind-keys
   ("<f6>" . +simple-pop-local-mark-ring)
   :map minibuffer-local-map
   ("<f6>" . forward-char)
   :map meow-insert-state-keymap
   ("<f6>" . meow-right)))
(bind-keys
 :map global-map
 ("M-SPC" . +monocle-mode) ; replaced `just-one-space'
 ("M-u"   . +toggle-letter-case)
 ("<C-i>" . +simple-pop-local-mark-ring)
 ("C-o" . pop-global-mark)
 ("s-n" . scroll-up-command)
 ("s-p" . scroll-down-command)
 ("M-n" . forward-paragraph)
 ("M-p" . backward-paragraph)
 :map minibuffer-local-map
 ("C-;" . meow-kill-whole-line)
 ("<C-i>" . forward-char)
 ("C-o" . backward-char))

These keybindings are available when the current major mode doesn’t define that key.

(when (featurep 'meow)
  (meow-motion-overwrite-define-key
   '("<escape>" . +meow-escape)
   '("/" . +meow-with-key-fallback)))

File management

File/Directory handling functions (files.el)

(use-package files
  :straight (:type built-in)
  :config
  (auto-save-visited-mode)
  (setq ale-files-dir-alist
        '(((title . "  Shows")        (path . "/mnt/HDD/Share"))
          ((title . "  Coding")       (path . "/mnt/HDD/Dev"))
          ((title . "  Books")        (path . "/mnt/HDD/Book"))
          ((title . "  Videos")       (path . "/mnt/HDD/Video"))
          ((title . "  Notes")        (path . "~/Documents/notes"))
          ((title . "  Photos")       (path . "~/Pictures"))
          ((title . "  Downloads")    (path . "~/Downloads"))))
  (setq confirm-kill-processes nil)
  (setq large-file-warning-threshold 50000000)
  (setq permanently-enabled-local-variables '(lexical-binding encoding))
  :bind
  (nil
   :map grandview-files-map
   ("." . ale-files-find-dotfiles)
   ("r" . ale-files-rename-file-and-buffer)
   ("u" . ale-files-find-user-files))
  :preface
  (setq auto-save-default nil)
  (setq make-backup-files nil))

Autoload

(defcustom ale-files-dotfiles-repo (getenv "DOTPATH")
  "Doc."
  :group 'ale :type 'string)

(defcustom ale-files-dir-alist
  '(((title . "  Photos")       (path . "~/Pictures/"))
    ((title . "  Videos")       (path . "~/Video/"))
    ((title . "  Downloads")    (path . "~/Downloads/")))
  "Doc."
  :group 'ale :type '(repeat list))

(defun ale-files--in-directory (dir &optional prompt)
  "Use `fd' to list files in DIR."
  (let* ((default-directory dir)
         (command "fd -H -t f -0")
         (output (shell-command-to-string command))
         (files-raw (split-string output "\0" t))
         (files (+minibuffer-append-metadata 'file files-raw))
         (file (completing-read (or prompt "Open file: ") files)))
    (find-file (concat dir "/" file))))

;;;###autoload
(defun ale-files-rename-file-and-buffer (name)
  "Apply NAME to current file and rename its buffer.
Do not try to make a new directory or anything fancy."
  (interactive
   (list (read-string "Rename current file: " (buffer-file-name))))
  (let* ((file (buffer-file-name)))
    (if (vc-registered file)
        (vc-rename-file file name)
      (rename-file file name))
    (set-visited-file-name name t t)))

;;;###autoload
(defun ale-files-find-dotfiles ()
  "Open files in dotfiles repo."
  (interactive)
  (unless ale-files-dotfiles-repo
    (user-error "`ale-files-dotfiles-repo' is undefined"))
  (ale-files--in-directory ale-files-dotfiles-repo " Dotfiles: "))

;;;###autoload
(defun ale-files-sudo-find ()
  "Reopen current file as root."
  (interactive)
  (let ((file (buffer-file-name)))
    (find-file (if (file-writable-p file)
                   file
                 (concat "/sudo::" file)))))

;;;###autoload
(defun ale-files-find-user-files ()
  "Open files in directories defined in `ale-files-dir-alist'."
  (interactive)
  (let* ((cands-raw (mapcar (lambda (i) (cdr (assq 'title i))) ale-files-dir-alist))
         (get-item (lambda (s field) (cl-dolist (i ale-files-dir-alist)
                                       (when (string= s (cdr (assq 'title i)))
                                         (cl-return (cdr (assq field i)))))))
         (annotation (lambda (s) (marginalia--documentation (funcall get-item s 'path))))
         (cands (+minibuffer-append-metadata annotation cands-raw))
         (title (completing-read "Open: " cands nil t))
         (path (funcall get-item title 'path)))
    (ale-files--in-directory path (concat title ": "))))

Find libraries (find-func.el)

This packages provides the find-library command which allows us browsing the source code of Emacs efficiently, want to have to look on dired.el? Just M-x find-library RET dired. Even better, we can introspect the C code of Emwacs itself as long as the find-function-C-source-directory is set properly.

(use-package find-func
  :config
  (setq find-function-C-source-directory
        "~/.cache/paru/clone/emacs-git/src/emacs-git/src")
  :bind
  (nil
   :map grandview-files-map
   ("l" . find-library)))

MIME media types (mailcap.el)

Provides configuration of MIME media types via the usual mailcap mechanism (RFC 1524). Deals with mime.types similarly.

(use-package mailcap
  :after-call find-file-noselect
  :config
  (mailcap-parse-mimetypes)
  (cl-dolist (mt '((".ape" . "audio/ape")
                   (".rmvb" . "video/rm")
                   (".f4v" . "video/f4v")))
    (add-to-list 'mailcap-mime-extensions mt)))

Recent files (recentf.el)

Keep a record of all recently opened files.

(use-package recentf
  :straight (:type built-in)
  :after-call find-file-hook dirvish
  :config
  (setq recentf-max-saved-items 100)
  (add-to-list 'recentf-exclude (lambda (f) (not (string= (file-truename f) f))))
  (recentf-mode 1))

Restore file place (saveplace.el)

Just remember where the point is in any given file. This can often be a subtle reminder of what you were doing the last time you visited that file, allowing you to pick up from there.

(use-package saveplace
  :straight (:type built-in)
  :after-call find-file-hook
  :config
  (setq save-place-file (locate-user-emacs-file "saveplace"))
  (setq save-place-forget-unreadable-files t)
  (save-place-mode 1))

Auto refresh file content (autorevert.el)

This mode ensures that the buffer is updated whenever the file changes. A change can happen externally or by some other tool inside of Emacs (e.g. kill a Magit diff).

(use-package autorevert
  :straight (:type built-in)
  :after-call post-self-insert-hook
  :config
  (setq auto-revert-verbose t)
  (global-auto-revert-mode))

Dired (dired.el)

Dired is a built-in tool that performs file management operations inside of an Emacs buffer. It is simply superb!

(use-package dired
  :straight (:type built-in)
  :bind
  (nil
   :map dired-mode-map
   ("/" . dired-goto-file)
   ("a" . dired-create-empty-file)
   ("i" . wdired-change-to-wdired-mode)
   ("I" . dired-insert-subdir)
   ("K" . dired-kill-subdir)
   ("O" . dired-find-file-other-window)
   ("[" . dired-prev-dirline)
   ("]" . dired-next-dirline)
   ("o" . dired-up-directory)
   ("^" . mode-line-other-buffer)
   ("x" . dired-do-delete)
   ("X" . dired-do-flagged-delete)
   ("y" . dired-do-copy)
   ("." . dired-omit-mode))
  :config
  (setq dired-mouse-drag-files t)
  (setq mouse-drag-and-drop-region-cross-program t) ;; added in Emacs 29
  (setq dired-kill-when-opening-new-dired-buffer t) ;; added in Emacs 28
  (setq dired-recursive-copies 'always)
  (setq dired-recursive-deletes 'always)
  (setq delete-by-moving-to-trash t)
  (setq dired-dwim-target t)
  (setq dired-listing-switches
        "-g --almost-all --human-readable --time-style=long-iso --group-directories-first --no-group"))

(use-package dired-x
  :straight (:type built-in)
  :after dired
  :init
  (setq! dired-bind-info nil)
  (setq! dired-bind-man nil)
  :config
  (setq dired-clean-confirm-killing-deleted-buffers nil)
  (setq dired-omit-files
        (concat dired-omit-files "\\|^\\..*$")))

(use-package dired-aux
  :straight (:type built-in)
  :after dired
  :config
  (setq dired-do-revert-buffer t))

A better dired (dirvish.el)

This package empowers dired by giving it a modern UI in a unintrusive way. Emacs users deserve a file manager better than those popular ones on terminal such as ranger, vifm, lf since Emacs is more than a terminal emulator.

(use-package dirvish
  :defer 0.5
  :hook (dirvish-setup . dirvish-emerge-mode)
  :bind
  (nil
   :map grandview-files-map
   ("f" . dirvish-fd)
   :map dired-mode-map
   ("SPC" . dirvish-history-jump)
   ("M-n" . dirvish-history-go-forward)
   ("M-p" . dirvish-history-go-backward)
   ("^"   . dirvish-history-last)
   ("TAB" . dirvish-toggle-subtree)
   ("r"   . dirvish-bookmark-jump)
   ("*"   . dirvish-mark-menu)
   ("f"   . dirvish-file-info-menu)
   ("b"   . dirvish-fd-roam)
   ("E"   . dirvish-emerge-menu)
   ("N"   . dirvish-narrow)
   ("M-f" . dirvish-toggle-fullscreen)
   ("M-s" . dirvish-setup-menu)
   ([remap dired-sort-toggle-or-edit] . dirvish-quicksort)
   ([remap dired-do-redisplay] . dirvish-ls-switches-menu)
   ([remap dired-summary] . dirvish-dispatch)
   ([remap dired-do-copy] . dirvish-yank-menu)
   :map mode-specific-map
   ("e" . dirvish-dwim)
   :map grandview-files-map
   ("e" . dirvish)
   ("n" . dirvish-side)
   ("b" . dirvish-fd-roam))
  :config
  (dirvish-override-dired-mode)
  (dirvish-peek-mode)
  (dirvish-define-preview exa (file)
    "Use `exa' to generate directory preview."
    (when (file-directory-p file)
      `(shell . ("exa" "--color=always" "-al" "--group-directories-first" ,file))))
  (add-to-list 'dirvish-preview-dispatchers 'exa)
  (setq! dirvish-mode-line-format
         '(:left (sort file-time " " file-size symlink) :right (omit yank index)))
  (setq! dirvish-header-line-format '(:left (path) :right (vc-info free-space)))
  (setq! dirvish-bookmark-entries
         '(("o" "~/"                                "Home")
           ("d" "/opt/dotfiles/"                    "Dotfiles")
           ("u" "~/.cache/emacs/"                   "Emacs cache")
           ("p" "~/Code/"                           "Code")
           ("n" "~/Downloads/"                      "Downloads")
           ("w" "~/Pictures/wallpaper/"             "Wallpaper")
           ("m" "/mnt/"                             "Drives")
           ("r" "FD####~/Documents/####\\\\.org$"   "All org files in ~/Documents")
           ("t" "~/.local/share/Trash/files/" "Trash")))
  (setq! dirvish-open-with-programs
         `((,dirvish-video-exts . ("tdrop" "-h" "25%" "-w" "25%" "-x" "1000" "-y" "1000" "mpv" "%f"))
           (,dirvish-audio-exts . ("tdrop" "-h" "25%" "-w" "25%" "-x" "1000" "-y" "1000" "mpv" "%f"))))
  (setq! dirvish-attributes '(vc-state file-size git-msg subtree-state all-the-icons collapse))
  (setq! dirvish-all-the-icons-height 0.8)
  (setq! dirvish-side-attributes dirvish-attributes)
  (setq! dirvish-side-preview-dispatchers (append '(vc-diff) dirvish-preview-dispatchers))
  (setq! dirvish-side-display-alist '((side . left) (slot . -1) (window-width . 0.2)))
  (setq! dirvish-side-header-line-format '(:left (project) :right (vc-info)))
  (setq! dirvish-side-mode-line-format '(:left (sort omit) :right (index))))

Project management (project.el)

(use-package project
  :straight (:type built-in)
  :config
  (setq project-switch-commands
        '((project-find-file "File" ?\r)
          (ale-project-find-subdir "Subdir" ?s)
          (project-find-regexp "Grep" ?g)
          (project-dired "Dired" ?d)
          (ale-project-retrieve-tag "Tag switch" ?t)
          (ale-project-magit-status "Magit" ?m)
          (ale-project-commit-log "Log VC" ?l)))
  (setq ale-project-commit-log-limit 25)
  :bind
  (nil
   :map project-prefix-map
   ("l" . ale-project-commit-log)
   ("m" . ale-project-magit-status)
   ("s" . ale-project-find-subdir)
   ("t" . ale-project-retrieve-tag)))

Autoload

(require 'cl-lib)
(require 'project)
(require 'vc)

(defcustom ale-project-commit-log-limit 25
  "Limit commit logs for project to N entries by default.
A value of 0 means 'unlimited'."
  :type 'integer
  :group 'ale)

;;;###autoload
(cl-defmethod project-root ((project (head local)))
  "Project root for PROJECT with HEAD and LOCAL."
  (if (< emacs-major-version 29)
      (cdr-safe project)
    (car (project-roots project))))

;; Copied from Manuel Uberti and tweaked accordingly:
;; <https://www.manueluberti.eu/emacs/2020/11/14/extending-project/>.
(defun ale-project--project-files-in-directory (dir)
  "Use `fd' to list files in DIR."
  (unless (executable-find "fd")
    (error "Cannot find 'fd' command is shell environment $PATH"))
  (let* ((default-directory dir)
         (localdir (file-local-name (expand-file-name dir)))
         (command (format "fd -t f -H -0 . %s" localdir)))
    (project--remote-file-names
     (split-string (shell-command-to-string command) "\0" t))))

(cl-defmethod project-files ((project (head vc)) &optional dirs)
  "Override `project-files' to use `fd' in local projects.
Project root for PROJECT with HEAD and VC, plus optional
DIRS."
  (mapcan #'ale-project--project-files-in-directory
          (or dirs (list (project-root project)))))

(defun ale-project--directory-subdirs (dir)
  "Return list of subdirectories in DIR."
  (cl-remove-if (lambda (x) (string-match-p "\\.git" x))
                (cl-remove-if-not (lambda (x) (file-directory-p x))
                                  (directory-files-recursively dir ".*" t t))))

;;;###autoload
(defun ale-project-find-subdir ()
  "Find subdirectories in the current project, using completion."
  (interactive)
  (let* ((pr (project-current t))
         (dir (project-root pr))
         (dirs-raw (ale-project--directory-subdirs dir))
         (subdirs (+minibuffer-append-metadata 'file dirs-raw))
         (directory (completing-read "Select Project subdir: " subdirs)))
    (dired directory)))

;;;###autoload
(defun ale-project-commit-log (&optional arg)
  "Print commit log for the current project.
With optional prefix ARG (\\[universal-argument]) shows expanded
commit messages and corresponding diffs.

The log is limited to the integer specified by
`ale-project-commit-log-limit'.  A value of 0 means
'unlimited'."
  (interactive "P")
  (let* ((pr (project-current t))
         (dir (cdr pr))
         (default-directory dir) ; otherwise fails at spontaneous M-x calls
         (backend (vc-responsible-backend dir))
         (num ale-project-commit-log-limit)
         (int (if (numberp num) num (error "%s is not a number" n)))
         (limit (if (= int 0) t int))
         (diffs (if arg 'with-diff nil))
         (vc-log-short-style (unless diffs '(directory))))
    (vc-print-log-internal backend (list dir) nil nil limit diffs)))

;;;###autoload
(defun ale-project-retrieve-tag ()
  "Run `vc-retrieve-tag' on project and switch to the root dir.
Basically switches to a new branch or tag."
  (interactive)
  (let* ((pr (project-current t))
         (dir (cdr pr))
         (default-directory dir) ; otherwise fails at spontaneous M-x calls
         (name
          (vc-read-revision "Tag name: "
                            (list dir)
                            (vc-responsible-backend dir))))
    (vc-retrieve-tag dir name)
    (project-dired)))

(autoload 'magit-status "magit")

;;;###autoload
(defun ale-project-magit-status ()
  "Run `magit-status' on project."
  (interactive)
  (let* ((pr (project-current t))
         (dir (project-root pr)))
    (magit-status dir)))

;;;###autoload
(defun ale-project-find-file (&optional force)
  "Same as `project-find-file' except using magit for project
choosing.
With a universal prefix to choose project anyway."
  (interactive "P")
  (if (or force (null (project-current)))
      (let ((current-prefix-arg '(4))
            (display-buffer-alist '(("magit: .*" (display-buffer-same-window)))))
        (call-interactively 'magit-status))
    (project-find-file)))

Writable dired (wdired.el)

(use-package wdired
  :config
  (setq wdired-allow-to-change-permissions t)
  (setq wdired-create-parent-directories t))

Working with remote files (tramp.el)

(use-package tramp
  :straight (:type built-in)
  :after-call (find-file-hook dired-after-readin-hook)
  :config
  (add-to-list 'tramp-connection-properties
               (list (regexp-quote "/ssh:alex:")
                     "direct-async-process" t))
  (setq tramp-verbose 0)
  (setq tramp-auto-save-directory (locate-user-emacs-file "tramp/"))
  (setq tramp-chunksize 2000)
  (setq! tramp-use-ssh-controlmaster-options nil))

Org mode

Org (org.el)

In its purest form, Org is a markup language that is similar to Markdown: symbols are used to denote the meaning of a construct in its context, such as what may represent a headline element or a phrase that calls for emphasis.

What lends Org its super powers though is everything else built around it: a rich corpus of Elisp functions that automate, link, combine, enhance, structure, or otherwise enrich the process of using this rather straightforward system of plain text notation.

Couched in those terms, Org is at once a distribution of well integrated libraries and a vibrant ecosystem that keeps producing new ideas and workflows on how to organise one’s life with plain text.

This section is all about basic configurations for how does a .org file should look like which can be described briefly as follows:

  • use bigger fonts for different levels of heading
  • show ellipsis marker when a node is folded
  • center text when make sense
  • indent text according to outline structure
  • display inline images in url automatically
(use-package org
  :straight (:type built-in)
  :hook
  (org-mode . ale-org-font-setup)
  (org-mode . org-indent-mode)
  (org-tab-first . org-end-of-line)
  :config
  (setq org-adapt-indentation nil)
  (setq org-hide-leading-stars t)
  (setq org-startup-folded t)
  (setq org-confirm-babel-evaluate nil)
  (setq org-ellipsis "")
  (setq org-agenda-start-with-log-mode t)
  (setq org-log-done 'time)
  (setq org-log-into-drawer t)
  (setq org-image-actual-width nil)
  (setq org-display-remote-inline-images 'download)
  :bind
  (nil
   :map grandview-org-map
   ("o" . consult-org-heading)
   :map org-mode-map
   ("C-c S-l" . org-toggle-link-display)
   ("C-c C-S-l" . org-insert-last-stored-link)))

Autoload

;;;###autoload
(defadvice! org-fill-paragraph-ad (&rest _)
  "Let `org-fill-paragraph' works inside of src block in Org-mode."
  :before-while #'org-fill-paragraph
  (let* ((element (save-excursion (beginning-of-line) (org-element-at-point)))
         (type (org-element-type element)))
    (if (and (eq type 'src-block)
             (> (line-beginning-position)
                (org-element-property :post-affiliated element))
             (< (line-beginning-position)
                (org-with-point-at (org-element-property :end element)
                  (skip-chars-backward " \t\n")
                  (line-beginning-position))))
        (progn (org-babel-do-in-edit-buffer (fill-paragraph)) nil)
      t)))

;;;###autoload
(defun ale-org-font-setup ()
  "Setup variable-pitch fonts for org-mode."
  (interactive)
  (variable-pitch-mode)
  (let ((variable-pitch `(:font ,ale-variable-font))
        (default `(:font ,ale-default-font)))
    (custom-theme-set-faces
     'user
     `(org-level-1 ((t (,@variable-pitch :height 1.5))))
     `(org-level-2 ((t (,@variable-pitch :height 1.4))))
     `(org-level-3 ((t (,@variable-pitch :height 1.3))))
     `(org-level-4 ((t (,@variable-pitch :height 1.2))))
     `(org-table ((t (,@default))))
     `(org-verbatim ((t (,@default))))
     `(org-formula ((t (,@default))))
     `(org-code ((t (,@default))))
     `(org-block ((t (,@default))))
     `(org-block-begin-line ((t (:foreground "#606060" :extend t))))
     '(org-tag ((t (:inherit (shadow) :weight bold :height 0.8)))))))

Identifiers for org entries (org-id.el)

(use-package org-id
  :straight (:type built-in)
  :config
  (setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
  :hook
  (org-mode . ale-org-id-update))

Autoload

(require 'org-id)

(defvar-local ale-org-id-auto nil)

(defun ale-org-id-new (&optional prefix)
  "Create a new globally unique ID.

An ID consists of two parts separated by a colon:
- a prefix
- a unique part that will be created according to `org-id-method'.

PREFIX can specify the prefix, the default is given by the
variable `org-id-prefix'.  However, if PREFIX is the symbol
`none', don't use any prefix even if `org-id-prefix' specifies
one. So a typical ID could look like \"Org-4nd91V40HI\"."
  (let* ((prefix (if (eq prefix 'none)
                     ""
                   (concat (or prefix org-id-prefix) "-")))
         unique)
    (when (equal prefix "-") (setq prefix ""))
    (cond
     ((memq org-id-method
            '(uuidgen uuid))
      (setq unique (org-trim (shell-command-to-string org-id-uuid-program)))
      (unless (org-uuidgen-p unique)
        (setq unique (org-id-uuid))))
     ((eq org-id-method 'org)
      (let* ((etime (org-reverse-string (org-id-time-to-b36)))
             (postfix (when org-id-include-domain
                        (require 'message)
                        (concat "@"
                                (message-make-fqdn)))))
        (setq unique (concat etime postfix))))
     (t (error "Invalid `org-id-method'")))
    (concat prefix (car (split-string unique "-")))))

;;;###autoload
(defun ale-org-custom-id-get (&optional pom create prefix)
  "Get the CUSTOM_ID property of the entry at point-or-marker POM.

If POM is nil, refer to the entry at point. If the entry does not
have an CUSTOM_ID, the function returns nil. However, when CREATE
is non nil, create a CUSTOM_ID if none is present already. PREFIX
will be passed through to `ale-org-id-new'. In any case, the
CUSTOM_ID of the entry is returned."
  (interactive)
  (org-with-point-at pom
    (let* ((orgpath (mapconcat #'identity (org-get-outline-path) "-"))
           (heading (replace-regexp-in-string
                     "/\\|~\\|\\[\\|\\]" ""
                     (replace-regexp-in-string
                      "[[:space:]]+" "_" (if (string= orgpath "")
                                             (org-get-heading t t t t)
                                           (concat orgpath "-" (org-get-heading t t t t))))))
           (id (org-entry-get nil "CUSTOM_ID")))
      (cond
       ((and id (stringp id) (string-match "\\S-" id))
        id)
       (create (setq id (ale-org-id-new (concat prefix heading)))
               (org-entry-put pom "CUSTOM_ID" id)
               (org-id-add-location id
                                    (buffer-file-name (buffer-base-buffer)))
               id)))))

;;;###autoload
(defun ale-org-add-ids-to-headlines-in-file (&optional force)
  "Add CUSTOM_ID properties to all headlines in the current file
which do not already have one.

Only adds ids if the `auto-id' option is set to `t' in the file
somewhere. ie, #+OPTIONS: auto-id:t"
  (interactive "P")
  (save-excursion
    (widen)
    (goto-char (point-min))
    (when ale-org-id-auto
      (when force
        (org-map-entries (lambda () (org-entry-delete nil "CUSTOM_ID"))))
      (org-map-entries (lambda () (ale-org-custom-id-get (point) 'create))))))

;;;###autoload
(defun ale-org-id-update ()
  (add-hook 'before-save-hook
            (lambda ()
              (when (and (eq major-mode 'org-mode)
                         (eq buffer-read-only nil))
                (ale-org-add-ids-to-headlines-in-file)))))

Literate programming (ob.el)

Thanks to https://blog.d46.us/advanced-emacs-startup

(use-package ob
  :straight (:type built-in)
  :after-call org-mode-hook
  :hook (org-babel-after-execute . org-redisplay-inline-images)
  :config
  (use-package org-tempo
    :straight (:type built-in)
    :after-call post-self-insert-hook
    :config
    (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")))
  (setq org-babel-default-header-args:sh    '((:results . "output replace"))
        org-babel-default-header-args:bash  '((:results . "output replace"))
        org-babel-default-header-args:shell '((:results . "output replace"))))

Source code block (org-src.el)

(use-package org-src
  :straight (:type built-in)
  :after-call org-mode
  :config
  (push '("conf-unix" . conf-unix) org-src-lang-modes)
  (setq org-edit-src-content-indentation 0)
  (setq org-src-window-setup 'split-window-right))

Reveal invisible org elements (org-appear.el)

(use-package org-appear
  :hook (org-mode . org-appear-mode)
  :config
  (setq org-appear-autolinks t)
  (setq org-hide-emphasis-markers t))

Modern org style (org-modern.el)

(use-package org-modern
  :hook
  ((org-mode . org-modern-mode)
   (org-agenda-finalize . org-modern-agenda)))

Visual alignment (valign.el)

This package provides visual alignment for Org Mode, Markdown and table.el tables on GUI Emacs. It can properly align tables containing variable-pitch font, CJK characters and images. In the meantime, the text-based alignment generated by Org mode (or Markdown mode) is left untouched.

(use-package valign
  :after-call org-mode-hook
  :hook (org-mode . valign-mode)
  :config
  (setq valign-fancy-bar t))

Habit (org-habit.el)

(use-package org-habit
  :straight (:type built-in)
  :config
  (add-to-list 'org-modules 'org-habit)
  (setq org-habit-graph-column 60))

Wiki (org-roam.el)

(use-package org-roam
  :init
  (setq org-id-link-to-org-use-id t)
  (setq org-roam-v2-ack t)
  :custom
  (org-roam-directory (file-truename "~/Documents/roam"))
  (org-roam-completion-everywhere t)
  :bind
  (nil
   :map grandview-org-map
   ("l" . org-roam-buffer-toggle)
   ("f" . org-roam-node-find)
   ("g" . org-roam-graph)
   ("i" . org-roam-node-insert)
   ("c" . org-roam-capture)
   ("j" . org-roam-dailies-capture-today))
  :config
  (org-roam-setup))

Slide (org-tree-slide.el)

org-tree-slide.el is a presentation tool using org-mode.

(use-package org-tree-slide
  :bind
  (nil
   :map org-tree-slide-mode-map
   ("<left>" . org-tree-slide-move-previous-tree)
   ("<right>" . org-tree-slide-move-next-tree)
   :map grandview-org-map
   ("S" . org-tree-slide-mode))
  :config
  (setq org-tree-slide-activate-message " ")
  (setq org-tree-slide-deactivate-message " ")
  (setq org-tree-slide-modeline-display nil)
  (setq org-tree-slide-heading-emphasis t)
  (setq org-tree-slide-breadcrumbs
        (propertize "" 'display `(height ,(face-attribute 'org-level-1 :height))))
  (add-hook 'org-tree-slide-after-narrow-hook #'org-display-inline-images)
  (add-hook 'org-tree-slide-after-narrow-hook #'+frame-cursor-dim-mode)
  (add-hook 'org-tree-slide-mode-hook #'+org-tree-slide-hide-elements-h)
  (add-hook 'org-tree-slide-play-hook #'+org-tree-slide-hide-elements-h)
  (add-hook 'org-tree-slide-mode-hook #'+org-tree-slide-prettify-slide-h)
  (advice-add 'org-tree-slide--set-slide-header :override '+org-tree-slide--simple-header))

Autoload

(defcustom +org-tree-slide-text-scale 1.5
  "Text scaling for `org-tree-slide-mode'."
  :group 'org-tree-slide
  :type 'number)

(defcustom +org-tree-hide-elements
  '("^[[:space:]]*\\(#\\+\\)\\(\\(?:BEGIN\\|END\\|ATTR\\)[^[:space:]]+\\).*" ; src block
    "^\\(\\*+\\)"                                                            ; leading stars
    "\\(^:PROPERTIES:\\(.*\n\\)+?:END:\\)")                                  ; :PROPERTIES:.*:END:
  "Regexps of org elements to hide in `org-tree-slide-mode'."
  :group 'org-tree-slide
  :type '(repeat string))

;;;###autoload
(defun +org-tree-slide--simple-header (blank-lines)
  "Set the header with overlay.

Some number of BLANK-LINES will be shown below the header."
  (org-tree-slide--hide-slide-header)
  (setq org-tree-slide--header-overlay
        (make-overlay (point-min) (+ 1 (point-min))))
  (overlay-put org-tree-slide--header-overlay
               'face
               'org-tree-slide-header-overlay-face)
  (if org-tree-slide-header
      (overlay-put org-tree-slide--header-overlay 'display
                   (concat
                    (when org-tree-slide-breadcrumbs
                      (concat "\n" (org-tree-slide--get-parents
                                    org-tree-slide-breadcrumbs)))
                    (org-tree-slide--get-blank-lines blank-lines)))
    (overlay-put org-tree-slide--header-overlay 'display
                 (org-tree-slide--get-blank-lines blank-lines))))

;;;###autoload
(defun +org-tree-slide-hide-elements-h ()
  "Hide org constructs defined in `+org-tree-hide-elements'."
  (dolist (reg +org-tree-hide-elements)
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward reg nil t)
        (org-flag-region (match-beginning 1) (match-end 0) org-tree-slide-mode t)))))

;;;###autoload
(defun +org-tree-slide-prettify-slide-h ()
  "Set up the org window for presentation."
  (cond (org-tree-slide-mode
         (when (bound-and-true-p flyspell-mode) (flyspell-mode -1))
         (text-scale-set +org-tree-slide-text-scale)
         (+monocle-mode +1)
         (when (fboundp 'writeroom-mode) (writeroom-mode +1))
         (ignore-errors (org-latex-preview '(4))))
        (t
         (text-scale-set 0)
         (when (fboundp 'writeroom-mode) (writeroom-mode -1))
         (+monocle-mode -1)
         (+frame-cursor-dim-mode -1)
         (org-clear-latex-preview)
         (org-mode))))

Text editing

Long line text (so-long.el)

Consistent performance is the reason to enable global-so-long-mode, built into Emacs versions >= 27, which allows the active major mode to gracefully adapt to buffers with very long lines. What “very long” means is, of course, configurable: M-x find-library so-long covers several customisation options, though I find that the defaults require no further intervention from my part.

(use-package so-long
  :after-call find-file-hook
  :straight (:type built-in)
  :config
  (global-so-long-mode))

Fill line (fill.el)

The fill.el library is a tiny wrapper around some Emacs settings and modes that are scrattered around several files, which control (i) how paragraphs or comments in programming modes should be wrapped to a given column count, and (ii) what constitutes a sentence. Although fill-column variable is not defined in fill.el, I believe put them all together here make things easier to track.

With regard to paragraphs, I find that a double space is the best way to delimit sentences in source form, where a monospaced typeface is customary. There is no worry that this will be shown on a website or rendered version of a document, because processors know how to handle spacing. We do this to make phrases easier to tell apart, but also to render unambiguous commands like forward-sentence.

(use-package fill
  :straight (:type built-in)
  :init
  (setq-default fill-column 80)
  :hook
  (text-mode . turn-on-auto-fill)
  :config
  (setq colon-double-space nil)
  (setq adaptive-fill-mode t))

(use-package paragraphs
  :straight (:type built-in)
  :preface
  (setq sentence-end-double-space t)
  (setq sentence-end-without-period nil))

Cross reference (xref.el)

xref provides helpful commands for code navigation and discovery.

(use-package xref
  :straight (:type built-in)
  :config
  (setq xref-file-name-display 'project-relative)
  (setq xref-search-program 'ripgrep))

Interactive diff, patch, or merge conflict (ediff.el)

This package provides a convenient way of simultaneous browsing through the differences between a pair (or a triple) of files or buffers. The files being compared, file-A, file-B, and file-C (if applicable) are shown in separate windows (side by side, one above the another, or in separate frames), and the differences are highlighted as you step through them. You can also copy difference regions from one buffer to another (and recover old differences if you change your mind).

(use-package ediff
  :config
  (setq ediff-keep-variants nil)
  (setq ediff-make-buffers-readonly-at-startup nil)
  (setq ediff-merge-revisions-with-ancestor t)
  (setq ediff-show-clashes-only t)
  (setq ediff-split-window-function 'split-window-horizontally)
  (setq ediff-window-setup-function 'ediff-setup-windows-plain)
  ;; Tweak those for safer identification and removal
  (setq ediff-combination-pattern
        '("<<<<<<< ale-ediff-combine Variant A" A
          ">>>>>>> ale-ediff-combine Variant B" B
          "####### ale-ediff-combine Ancestor" Ancestor
          "======= ale-ediff-combine End"))
  (defun ale-ediff-flush-combination-pattern ()
    "Remove my custom `ediff-combination-pattern' markers.
This is a quick-and-dirty way to get rid of the markers that are
left behind by `smerge-ediff' when combining the output of two
diffs.  While this could be automated via a hook, I am not yet
sure this is a good approach."
    (interactive)
    (flush-lines ".*ale-ediff.*" (point-min) (point-max) nil)))

Input method (rime.el)

(use-package rime
  :after-call post-self-insert-hook
  :custom-face
  (rime-preedit-face ((t (:inherit lazy-highlight))))
  :bind
  (("S-SPC" . toggle-input-method)
   :map rime-active-mode-map
   ("C-`" . rime-send-keybinding)
   ("C-k" . rime-send-keybinding)
   ("<C-i>" . rime-send-keybinding)
   ("C-o" . rime-send-keybinding)
   ("C-a" . rime-send-keybinding)
   ("C-e" . rime-send-keybinding)
   ("<escape>" . (lambda () (interactive) (execute-kbd-macro (kbd "C-g"))))
   ([tab] . rime-send-keybinding))
  :config
  (setq default-input-method "rime")
  (setq rime-disable-predicates '(meow-normal-mode-p
                                  meow-motion-mode-p
                                  meow-keypad-mode-p
                                  rime-predicate-after-alphabet-char-p))
  (setq rime-inline-predicates '(rime-predicate-space-after-cc-p
                                 rime-predicate-current-uppercase-letter-p))
  (setq rime-show-candidate 'posframe)
  (setq rime-posframe-style 'vertical)
  (setq rime-posframe-properties '(:internal-border-width 10 :lines-truncate t))
  (setq rime-title "")
  (setq rime-candidate-num-format-function #'ale-rime-candidate-num-fmt))

Autoload

;;;###autoload
(defadvice! rime-return-ad (fn &rest args)
  "Make return key (commit script text) compatible with vterm."
  :around #'rime-return
  (interactive)
  (if (eq major-mode 'vterm-mode)
      (progn
        (let ((input (rime-lib-get-input)))
          (execute-kbd-macro (kbd "<escape>"))
          (toggle-input-method)
          (dotimes (i (length input))
            (execute-kbd-macro (kbd (substring input i (+ i 1)))))
          (toggle-input-method)))
    (apply fn args)))

;;;###autoload
(defun ale-rime-candidate-num-fmt (num select-labels)
  "Format for the number before each candidate."
  (if select-labels
      (format "%s " (nth (1- num) select-labels))
    (format "%d. " num)))

Snippet (tempel.el)

(use-package tempel
  :straight (tempel :host github :repo "minad/tempel")
  :after-call post-self-insert-hook
  :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand
         ("M-*" . tempel-insert))
  :hook
  ;; Optionally make the Tempel templates available to Abbrev,
  ;; either locally or globally. `expand-abbrev' is bound to C-x '.
  ;; (prog-mode . tempel-abbrev-mode)
  ;; (tempel-global-abbrev-mode)
  (prog-mode . ale-tempel-setup-capf)
  (text-mode . ale-tempel-setup-capf))

Autoload

;;;###autoload
(defadvice! tempel-condition-ad (modes plist)
  "Return non-nil if one of MODES matches and the PLIST condition is satisfied."
  :override #'tempel--condition-p
  (and
   (cl-loop
    for m in modes thereis
    (or (eq m #'fundamental-mode)
        (derived-mode-p m)
        (when-let* (((derived-mode-p 'org-mode))
                    (element (org-element-context))
                    ((eq 'src-block (car-safe element))))
          (if-let* ((lang (plist-get (cadr element) :language))
                    (mode (org-src-get-lang-mode lang))
                    ((fboundp mode)))
              mode
            #'fundamental-mode))))
   (or (not (plist-member plist :condition))
       (save-excursion
         (save-restriction
           (save-match-data
             (eval (plist-get plist :condition) 'lexical)))))))

;;;###autoload
(defun ale-tempel-setup-capf ()
  ;; Add the Tempel Capf to `completion-at-point-functions'.
  ;; `tempel-expand' only triggers on exact matches. Alternatively use
  ;; `tempel-complete' if you want to see all matches, but then you
  ;; should also configure `tempel-trigger-prefix', such that Tempel
  ;; does not trigger too often when you don't expect it. NOTE: We add
  ;; `tempel-expand' *before* the main programming mode Capf, such
  ;; that it will be tried first.
  (setq-local completion-at-point-functions
              (cons #'tempel-complete completion-at-point-functions)))

Templates

All the Tempo syntax elements are fully supported. The syntax elements are described in detail in the docstring of tempo-define-template in tempo.el. We document the important ones here:

  • “string” Inserts a string literal.
  • p Inserts an unnamed placeholder field.
  • n Inserts a newline.
  • > Indents with indent-according-to-mode.
  • r Inserts the current region.
  • r> The region, but indented.
  • n> Inserts a newline and indents.
  • & Insert newline if there is only whitespace between line start and point.
  • % Insert newline if there is only whitespace between point and line end.
  • o Like % but leaves the point before newline.
  • (s NAME) Inserts a named field.
  • (p PROMPT <NAME> <NONINS>) Insert an optionally named field with a prompt. The PROMPT is displayed directly in the buffer as default value. If NOINSERT is non-nil, no field is inserted. Then the minibuffer is used for prompting and the value is bound to NAME.
  • (r PROMPT <NAME> <NOINSERT>) Insert region or act like (p ...).
  • (r> PROMPT <NAME> <NOINSERT>) Act like (r ...), but indent region.

Furthermore Tempel supports syntax extensions:

  • (p FORM <NAME> <NONINS>) Like p described above, but FORM is evaluated.
  • (FORM ...) Other Lisp forms are evaluated. Named fields are lexically bound.

Use caution with templates which execute arbitrary code!

fundamental-mode ;; Available everywhere

(today (format-time-string "%Y-%m-%d"))

prog-mode

(fixme (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "FIXME ")
(todo (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "TODO ")
(bug (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "BUG ")
(hack (if (derived-mode-p 'emacs-lisp-mode) ";; " comment-start) "HACK ")

latex-mode

(begin "\\begin{" (s env) "}" > n> r> "\\end{" (s env) "}")
(frac "\\frac{" p "}{" p "}")
(enumerate "\\begin{enumerate}\n\\item " r> n> "\\end{enumerate}")
(itemize "\\begin{itemize}\n\\item " r> n> "\\end{itemize}")

lisp-mode emacs-lisp-mode ;; Specify multiple modes

(lambda "(lambda (" p ")" n> r> ")")

emacs-lisp-mode

(use "(use-package " p n> ":demand t" n> ":config" n> p ")")
(cond "(cond (" p ")" n> "()" n> "()" ")")
(lambda "(lambda (" p ")" n> r> ")")
(var "(defvar " p "\n  \"" p "\")")
(const "(defconst " p "\n  \"" p "\")")
(custom "(defcustom " p "\n  \"" p "\"" n> ":type '" p ")")
(face "(defface " p " '((t :inherit " p "))\n  \"" p "\")")
(group "(defgroup " p " nil\n  \"" p "\"" n> ":group '" p n> ":prefix \"" p "-\")")
(macro "(defmacro " p " (" p ")\n  \"" p "\"" n> r> ")")
(fun "(defun " p " (" p ")\n  \"" p "\"" n> r> ")")
(let "(let (" p ")" n> r> ")")
(star "(let* (" p ")" n> r> ")")
(rec "(letrec (" p ")" n> r> ")")
(command "(defun " p " (" p ")\n  \"" p "\"" n> "(interactive)" n> r> ")")

eshell-mode

(for "for " (p "i") " in " p " { " p " }")
(while "while { " p " } { " p " }")
(until "until { " p " } { " p " }")
(if "if { " p " } { " p " }")
(if-else "if { " p " } { " p " } { " p " }")
(unless "unless { " p " } { " p " }")
(unless-else "unless { " p " } { " p " } { " p " }")

text-mode

(cut "--8<---------------cut here---------------start------------->8---" n r n
     "--8<---------------cut here---------------end--------------->8---" n)
(asciibox "+-" (make-string (length str) ?-) "-+" n
          "| " (s str)                       " |" n
          "+-" (make-string (length str) ?-) "-+" n)
(rot13 (p "plain text" text) n "----" n (rot13 text))
(calc (p "taylor(sin(x),x=0,3)" formula) n "----" n (format "%s" (calc-eval formula)))

rst-mode

(title (make-string (length title) ?=) n (p "Title: " title) n (make-string (length title) ?=) n)

java-mode

(class "public class " (p (file-name-base (or (buffer-file-name) (buffer-name)))) " {" n> r> n "}")

c-mode :condition (re-search-backward "^\\S-*$" (line-beginning-position) 'noerror)

(inc "#include <" (p (concat (file-name-base (or (buffer-file-name) (buffer-name))) ".h")) ">")
(incc "#include \"" (p (concat (file-name-base (or (buffer-file-name) (buffer-name))) ".h")) "\"")

org-mode

(title "#+title: " p n "#+author: Alex Lu" n "#+language: en" n n)
(quote "#+begin_quote" n> r> n> "#+end_quote")
(example "#+begin_example" n> r> n> "#+end_example")
(center "#+begin_center" n> r> n> "#+end_center")
(comment "#+begin_comment" n> r> n> "#+end_comment")
(verse "#+begin_verse" n> r> n> "#+end_verse")
(src "#+begin_src " p n> r> n> "#+end_src")
(elisp "#+begin_src emacs-lisp" n> r> n "#+end_src" :post (tempel-done))

;; Local Variables:
;; mode: lisp-data
;; outline-regexp: "[a-z]"
;; End:

Pair insertion (eletric.el)

Emacs labels as electric any behaviour that involves contextual auto-insertion of characters.

  • Indent automatically.
  • If electric-pair-mode is enabled (which I might do manually), insert quotes and brackets in pairs. Only do so if there is no alphabetic character after the cursor.
  • To get those numbers, evaluate (string-to-char CHAR) where CHAR is the one you are interested in. For example, get the literal tab’s character with `(string-to-char “\t”)’.
  • While inputting a pair, inserting the closing character will just skip over the existing one, rather than add a new one.
  • Do not skip over whitespace when operating on pairs. Combined with the above point, this means that a new character will be inserted, rather than be skipped over. I find this better, because it prevents the point from jumping forward, plus it allows for more natural editing.
  • The rest concern the conditions for transforming quotes into their curly equivalents. I keep this disabled, because curly quotes are distinct characters. It is difficult to search for them. Just note that on GNU/Linux you can type them directly by hitting the “compose” key and then an angled bracket (< or >) followed by a quote mark.
(use-package electric
  :config
  (advice-add 'electric-pair-post-self-insert-function :around
              (lambda (fn &rest args) (let ((mark-active nil)) (apply fn args))))
  (setq electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit)
  (setq electric-pair-preserve-balance t)
  (setq electric-pair-pairs
        '((8216 . 8217)
          (8220 . 8221)
          (171 . 187)))
  (setq electric-pair-skip-self 'electric-pair-default-skip-self)
  (setq electric-pair-skip-whitespace nil)
  (setq electric-pair-skip-whitespace-chars '(9 10 32))
  (setq electric-quote-context-sensitive t)
  (setq electric-quote-paragraph t)
  (setq electric-quote-string nil)
  (setq electric-quote-replace-double t)
  (electric-indent-mode 1)
  (electric-pair-mode 1)
  (electric-quote-mode -1)
  :hook
  (org-mode . ale-electric-inhibit-<)
  (minibuffer-setup . (lambda () (unless (eq this-command 'eval-expression) (electric-pair-mode 0))))
  (minibuffer-exit . (lambda () (electric-pair-mode 1))))

Autoload

;;;###autoload
(defun ale-electric-inhibit-< ()
  (setq-local electric-pair-inhibit-predicate
              `(lambda (c) (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c)))))

Parentheses (paren.el / rainbow-delimiters.el)

Configure the mode that highlights matching delimiters or parentheses. I consider this of utmost importance when working with languages such as elisp.

Summary of what these do:

  • Activate the mode upon startup.
  • Show the matching delimiter/parenthesis if on screen, else show nothing. It is possible to highlight the expression enclosed by the delimiters, by using either mixed or expression. The latter always highlights the entire balanced expression, while the former will only do so if the matching delimiter is off screen.
  • show-paren-when-point-in-periphery lets you highlight parentheses even if the point is in their vicinity. This means the beginning or end of the line, with space in between. I used that for a long while and it server me well. Now that I have a better understanding of Elisp, I disable it.
  • Do not highlight a match when the point is on the inside of the parenthesis.
  • Use rainbow color for delimiters
(use-package paren
  :straight (:type built-in)
  :config
  (setq show-paren-style 'parenthesis)
  (setq show-paren-when-point-in-periphery nil)
  (setq show-paren-when-point-inside-paren nil)
  (show-paren-mode))

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

Prettify symbols (prog-mode.el)

(use-package prog-mode
  :straight nil
  :hook (prog-mode . prettify-symbols-mode)
  :config
  (setq-default prettify-symbols-alist
                '(("lambda" . )
                  ("<-" . ?←)
                  ("->" . ?→)
                  ("->>" . ?↠)
                  ("=>" . ?⇒)
                  ("/=" . ?≠)
                  ("!=" . ?≠)
                  ("==" . ?≡)
                  ("<=" . ?≤)
                  (">=" . ?≥)
                  ("=<<" . (?= (Br . Bl) ?≪))
                  (">>=" . (?≫ (Br . Bl) ?=))
                  ("<=<" . ?↢)
                  (">=>" . ?↣)))
  (setq prettify-symbols-unprettify-at-point 'right-edge))

User interface

Tabs as workspaces (tab-bar.el)

(use-package tab-bar
  :straight (:type built-in)
  :config
  (setq tab-bar-new-tab-choice "~/"))

Transient commands (transient.el)

transient.el implements support for powerful keyboard-driven menus. Such menus can be used as simple visual command dispatchers. More complex menus take advantage of infix arguments, which are somewhat similar to prefix arguments, but are more flexible and discoverable. This package is in-built with Emacs 28.

(use-package transient
  :config
  (setq transient-default-level 7)
  (setq transient-show-popup -0.2)
  (transient-bind-q-to-quit)
  (setq transient-display-buffer-action '(display-buffer-below-selected))
  :bind
  (nil
   :map transient-map
   ("<escape>" . transient-quit-all)
   :map transient-sticky-map
   ("ESC" . transient-quit-all)))

Theme

modus-vivendi is a built-in theme in emacs (version >= 28) created by Protesilaos Stavrou.

(straight-use-package `(modus-themes ,@(when EMACS29+ '(:type built-in))))
(use-package modus-themes
  :hook (after-init . (lambda () (load-theme 'modus-vivendi t)))
  :config
  (setq! modus-themes-links '(no-underline)))

Fonts (fonts.el)

Here are some recommended fonts for programming or general text editing.

  • Victor Mono
  • Sarasa Mono SC
  • Fira Code Retina

A list of my favorite CJK fonts.

  • LXGW WenKai Mono
  • HarmonyOS Sans SC Light
  • Smartisan Compact CNS
  • 青鸟华光简报宋二
  • FZSuXinShiLiuKaiS-R-GB
(setq ale-font-size 140)
(setq ale-default-font "Victor Mono")
(setq ale-fixed-font "Sarasa Mono SC")
(setq ale-variable-font "Sarasa Mono SC")
(setq ale-zh-font "LXGW WenKai Mono")
(if (daemonp)
    (add-hook 'after-make-frame-functions #'ale-font-setup)
  (add-hook 'emacs-startup-hook #'ale-font-setup))

Autoload

(defvar ale-font-size 140)
(defvar ale-default-font "Victor Mono")
(defvar ale-fixed-font "Sarasa Mono SC")
(defvar ale-variable-font "Sarasa Mono SC")
(defvar ale-zh-font "LXGW WenKai Mono")
(defvar ale-zh-font-scale 1.0)

;;;###autoload
(defun ale-font-setup (&optional frame)
  "Setup default/fixed-pitch/variable-pitch/zh-font."
  (interactive)
  (with-selected-frame (or frame (selected-frame))
    (custom-theme-set-faces
     'user
     '(font-lock-keyword-face ((t (:slant italic))))
     '(font-lock-variable-name-face ((t (:weight demibold))))
     '(font-lock-function-name-face ((t (:weight demibold)))))
    (when-let ((default ale-default-font)
               (fixed-pitch ale-fixed-font)
               (variable-pitch ale-variable-font)
               (zh-font (font-spec :family ale-zh-font)))
      (custom-theme-set-faces
       'user
       `(default ((t (:font ,(font-spec :family default) :height ,ale-font-size))))
       `(fixed-pitch ((t (:font ,(font-spec :family fixed-pitch) :height ,ale-font-size))))
       `(variable-pitch ((t (:font ,(font-spec :family variable-pitch) :height ,ale-font-size)))))
      (unless (equal zh-font (font-spec :family variable-pitch))
        (setq face-font-rescale-alist (list (cons zh-font ale-zh-font-scale))))
      (dolist (charset '(kana han cjk-misc bopomofo))
        (set-fontset-font (frame-parameter nil 'font) charset zh-font))
      (unless EMACS28+
        (set-fontset-font t 'symbol (font-spec :family "Noto Color Emoji"))))))

;;;###autoload
(defun ale-font-cn-set-title (beg end)
  (interactive "r")
  (remove-overlays beg end)
  (let ((ov (make-overlay beg end)))
    (overlay-put ov 'display '(height 1.5))))

;;;###autoload
(defun ale-font-cn-set-quote (beg end)
  (interactive "r")
  (remove-overlays beg end)
  (let ((ov (make-overlay beg end)))
    (overlay-put ov 'face 'font-lock-comment-face)))

Mode line (mode-line.el)

The following infos in modeline are provided:

Left
  • Meow current state (INSERT/NORMAL…)
  • Buffer info (icon, name, modified state)
  • Macro recording state
Right
  • Current line / column
  • Input method

Besides, it’s easy to define your own mode line segments with ale-define-modeline, and don’t forget to put your newly defined segments in ale-mode-line-format.

(use-package emacs
  :init
  (ale-ml-mode))

Autoload

(defcustom ale-ml-format
  '(:left (bar editing-state buffer-info macro-rec) :right (position input-method))
  "Mode line SEGMENTs aligned to left/right respectively.
The SEGMENTs are defined by `ale-ml-define'."
  :set
  (lambda (k v)
    (cl-labels ((expand (l)
                  (cl-loop for s in l collect
                           `(:eval (ale-ml--suffix ',(intern (format "ale-%s-ml" s)))))))
      (let ((fmt-left (or (expand (plist-get v :left)) mode-line-format))
            (fmt-right (expand (plist-get v :right))))
        (set k `((:eval (let ((str-right (format-mode-line ',fmt-right)))
                          (concat (format-mode-line ',fmt-left)
                                  (propertize " " 'display
                                              `((space :align-to (- (+ right right-fringe right-margin)
                                                                    ,(string-width str-right)))))
                                  str-right)))))))))

(defcustom ale-ml-height 25
  "Doc.")

(defvar ale-ml-selected-window nil)

(defun ale-ml--window-active ()
  "Return t if mode line is in active window."
  (unless (and (bound-and-true-p mini-frame-frame)
               (and (frame-live-p mini-frame-frame)
                    (frame-visible-p mini-frame-frame)))
    (and ale-ml-selected-window (eq (ale-ml--get-current-window) ale-ml-selected-window))))

(defun ale-ml--get-current-window (&optional frame)
  "Get the current window but should exclude the child windows."
  (if (and (fboundp 'frame-parent) (frame-parent frame))
      (frame-selected-window (frame-parent frame))
    (frame-selected-window frame)))

(defun ale-ml-record-selected-window-h (&rest _)
  "Update `ale-ml-selected-window' on redisplay."
  (let ((win (ale-ml--get-current-window)))
    (setq ale-ml-selected-window
          (if (minibuffer-window-active-p win)
              (minibuffer-selected-window)
            win))))

(add-hook 'pre-redisplay-functions #'ale-ml-record-selected-window-h)

(defun ale-ml--suffix (ml-func)
  "If ML-FUNC return a non-empty string, append a space to it."
  (when-let (str (funcall ml-func)) (concat str " ")))

(cl-defmacro ale-ml-define (name &optional docstr &rest body)
  "Define a mode line segment NAME with BODY and DOCSTR."
  (declare (indent defun) (doc-string 2))
  (let ((ml (intern (format "ale-%s-ml" name)))) `(defun ,ml () ,docstr ,@body)))

(ale-ml-define bar
  (when (and (display-graphic-p) (image-type-available-p 'pbm))
    (propertize
     " " 'display
     (ignore-errors
       (create-image
        (concat (format "P1\n%i %i\n" 2 ale-ml-height)
                (make-string (* 2 ale-ml-height) ?1) "\n")
        'pbm t :foreground "None" :ascent 'center)))))

(ale-ml-define position
  (concat (propertize "l " 'face 'font-lock-keyword-face)
          (propertize "%l " 'face 'font-lock-doc-face)
          (propertize "c " 'face 'font-lock-keyword-face)
          (propertize "%c" 'face 'font-lock-doc-face)))

(ale-ml-define editing-state (when (bound-and-true-p meow-mode) (meow-indicator)))

(ale-ml-define buffer-info
  (concat
   (and buffer-file-name (all-the-icons-icon-for-file
                          buffer-file-name :height 0.75 :v-adjust 0.02))
   " "
   (propertize "%b"
               'face (cond ((and buffer-file-name (buffer-modified-p))
                            'marginalia-modified)
                           ((ale-ml--window-active) 'bold)
                           (t 'mode-line-inactive))
               'mouse-face 'mode-line-highlight
               'help-echo "Buffer name\nmouse-1: Previous buffer\nmouse-3: Next buffer"
               'local-map mode-line-buffer-identification-keymap)))

(ale-ml-define macro-rec
  (when (or defining-kbd-macro executing-kbd-macro)
    (propertize "KM" 'face 'warning)))

(ale-ml-define input-method (when (bound-and-true-p rime-mode) (rime-lighter)))

(defvar ale-ml--default-mode-line mode-line-format "Store the default mode-line format")

;;;###autoload
(define-minor-mode ale-ml-mode
  "Toggle `ale-ml-mode' on or off."
  :global t
  (if ale-ml-mode
      (setq-default mode-line-format ale-ml-format)
    (setq-default mode-line-format ale-ml--default-mode-line)))

Frame margin (fringe.el)

  • Redefine word wrap arrow at fringe
  • Create a 20 pixel margin for emacs frame
(define-fringe-bitmap 'right-curly-arrow  [])
(define-fringe-bitmap 'left-curly-arrow  [])
(add-to-list 'default-frame-alist '(internal-border-width . 10))
(add-to-list 'default-frame-alist '(left-fringe . 1))
(add-to-list 'default-frame-alist '(right-fringe . 1))
(fringe-mode '(1 . 1))

Pixel scrolling (pixel-scroll.el)

Pixelwise scrolling in emacs. This was added in emacs version > 29, you need to add --with-xinput2 in build flags to enable this feature.

(when (boundp 'pixel-scroll-precision-mode)
  (pixel-scroll-precision-mode 1))

Vscode style icons (vscode-icon.el)

Similar to all-the-icons.el, vscode-icon is a icon library which provides VSCode style icons with image format.

(use-package vscode-icon
  :config
  (push '("jpg" . "image") vscode-icon-file-alist)
  (push '("7z" . "zip") vscode-icon-file-alist)
  (push '("mkv" . "video") vscode-icon-file-alist)
  (push '("epub" . "storybook") vscode-icon-file-alist))

A colorful dired (diredfl.el)

Additional syntax highlighting in dired buffer.

(use-package diredfl
  :hook (dired-mode . diredfl-mode)
  :config
  (set-face-attribute 'diredfl-dir-name nil :bold t))

Interactive query replace (anzu.el)

anzu.el provides a minor mode which displays ‘current match/total matches’ in the mode-line in various search modes. This makes it easy to understand how many matches there are in the current buffer for your search query.

(use-package anzu
  :after-call isearch-mode
  :bind
  ([remap query-replace] . anzu-query-replace)
  ([remap query-replace-regexp] . anzu-query-replace-regexp)
  :config
  (global-anzu-mode +1))

Alternative isearch UI (isearch-mb.el)

This package provides an alternative isearch UI based on the minibuffer. This allows editing the search string in arbitrary ways without any special maneuver; unlike standard isearch, cursor motion commands do not end the search. Moreover, the search status information in the echo area and some keybindings are slightly simplified.

(use-package isearch-mb
  :after-call isearch-mode
  :config
  (isearch-mb-mode)
  (add-to-list 'isearch-mb--with-buffer #'consult-isearch-history)
  (add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace)
  :bind
  (nil
   :map isearch-mb-minibuffer-map
   ([remap previous-matching-history-element] . consult-isearch-history)))

Window position (transpose-frame.el)

The transpose-frame library defines a set of commands for shifting the layout of Emacs windows. Rather than me describing how these work, I strongly encourage you to read the “Commentary” section in the source code. Do it with M-x find-library transpose-frame.

(use-package transpose-frame)

Distraction-free writing (writeroom-mode.el)

(use-package writeroom-mode
  :after-call find-file-hook
  :hook
  (after-init . global-writeroom-mode)
  (org-mode . ale-visual-fill-center-text)
  :config
  (setq writeroom-width 128
        writeroom-bottom-divider-width 0
        writeroom-fringes-outside-margins t
        writeroom-fullscreen-effect nil
        writeroom-major-modes '(text-mode prog-mode conf-mode special-mode Info-mode)
        writeroom-major-modes-exceptions '(process-menu-mode proced-mode)
        writeroom-maximize-window nil
        writeroom-mode-line t))

Autoload

;;;###autoload
(defun ale-visual-fill-center-text ()
  "Centering text."
  (interactive)
  (setq-local visual-fill-column-width 120)
  (setq-local visual-fill-column-center-text t)
  (visual-fill-column-mode 1))

Key bindings hint (which-key.el)

(use-package which-key
  :init
  (which-key-mode 1 ))

Hunk indicator (git-gutter.el)

(use-package git-gutter
  :config
  ;; (custom-set-variables
  ;;  '(git-gutter:modified-sign "⏽")
  ;;  '(git-gutter:added-sign "⏽")
  ;;  '(git-gutter:deleted-sign "⏽"))
  )

Utils

Buffer list (ibuffer.el)

ibuffer.el ships with Emacs and it provides a drop-in replacement for list-buffers. Compared to its counterpart, it allows for granular control over the buffer list and is more powerful overall.

(use-package ibuffer
  :init
  (advice-add 'list-buffers :override 'ibuffer)
  :bind
  (nil
   :map ibuffer-mode-map
   ("M-o" . nil)
   ("* f" . ibuffer-mark-by-file-name-regexp)
   ("* g" . ibuffer-mark-by-content-regexp)
   ("* n" . ibuffer-mark-by-name-regexp)
   ("s n" . ibuffer-do-sort-by-alphabetic)
   ("/ g" . ibuffer-filter-by-content))
  :config
  (setq ibuffer-expert t)
  (setq ibuffer-display-summary nil)
  (setq ibuffer-use-other-window nil)
  (setq ibuffer-show-empty-filter-groups nil)
  (setq ibuffer-movement-cycle nil)
  (setq ibuffer-default-sorting-mode 'filename/process)
  (setq ibuffer-use-header-line t)
  (setq ibuffer-default-shrink-to-minimum-size nil)
  ;; (setq ibuffer-never-show-predicates '("^ \\*.*"))
  (setq ibuffer-formats
        '((mark modified read-only locked " "
                (name 30 30 :left :elide)
                " "
                (size 9 -1 :right)
                " "
                (mode 16 16 :left :elide)
                " " filename-and-process)
          (mark " " (name 16 -1) " " filename)))
  (setq ibuffer-saved-filter-groups nil)
  (setq ibuffer-old-time 48)
  (add-hook 'ibuffer-mode-hook (lambda () (interactive) (hl-line-mode) (ibuffer-update 0))))

Keyboard version right-click (embark.el)

This package provides a sort of right-click contextual menu for Emacs, accessed through the embark-act command (which you should bind to a convenient key), offering you relevant actions to use on a target determined by the context.

(use-package embark
  :after-call dired-after-readin-hook minibuffer-setup-hook
  :bind
  (("C-." . embark-act)
   :map minibuffer-local-map ("C-." . embark-act) ("C-," . embark-become)
   :map embark-collect-mode-map ("C-." . embark-act))
  :config
  (use-package embark-consult :demand t)
  (ale-embark-keymaps 1)
  (setq embark-quit-after-action t))

Autoload

(autoload 'consult-grep "consult")
(autoload 'consult-line "consult")
(autoload 'consult-imenu "consult-imenu")
(autoload 'consult-outline "consult")

(defvar ale-embark-become-general-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "g") 'consult-grep)
    map)
  "General custom cross-package `embark-become' keymap.")

(defvar ale-embark-become-line-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "l") 'consult-line)
    (define-key map (kbd "i") 'consult-imenu)
    (define-key map (kbd "s") 'consult-outline) ; as my default is 'M-s s'
    map)
  "Line-specific custom cross-package `embark-become' keymap.")

(defvar embark-become-file+buffer-map)
(autoload 'project-switch-to-buffer "project")
(autoload 'project-find-file "project")

(defvar ale-embark-become-file+buffer-map
  (let ((map (make-sparse-keymap)))
    (set-keymap-parent map embark-become-file+buffer-map)
    (define-key map (kbd "B") 'project-switch-to-buffer)
    (define-key map (kbd "F") 'project-find-file)
    map)
  "File+buffer custom cross-package `embark-become' keymap.")

(defvar embark-become-keymaps)

;;;###autoload
(define-minor-mode ale-embark-keymaps
  "Add or remove keymaps from Embark.
This is based on the value of `ale-embark-add-keymaps'
and is meant to keep things clean in case I ever wish to disable
those so-called 'extras'."
  :init-value nil
  :global t
  (let ((maps '(ale-embark-become-general-map
                ale-embark-become-line-map
                ale-embark-become-file+buffer-map)))
    (if ale-embark-keymaps
        (dolist (map maps)
          (cl-pushnew map embark-become-keymaps))
      (setq embark-become-keymaps
            (dolist (map maps)
              (delete map embark-become-keymaps))))))

Window motions (ace-window.el)

Index based window motions.

(use-package ace-window
  :bind
  ("M-o" . ace-select-window)
  :config
  (setq aw-keys '(?a ?r ?s ?t ?n ?e ?i ?o)))

Vterm (vterm.el)

(use-package vterm
  :config
  (ale-vterm-mux-mode)
  (setq vterm-max-scrollback 5000)
  (set-face-attribute 'vterm-color-white nil :foreground "#cccccc")
  (set-face-attribute 'vterm-color-black nil :foreground "#111111")
  (advice-add 'meow-insert-exit :around #'ale-vterm-escape-advisor)
  (advice-add 'meow-kill-whole-line :around #'ale-vterm-kill-whole-line-advisor)
  :bind
  (("M-`" . ale-vterm-toggle)
   :map vterm-mode-map
   ("M-`" . ale-vterm-toggle)
   ("M--" . ale-vterm-new)
   ("M-'" . vterm-send-M-apostrophe)
   ("M-\"" . vterm-send-M-quote)
   ("M-/" . vterm-send-M-/)
   ("M-." . ale-vterm-next)
   ("M-," . ale-vterm-prev)
   ("M-RET" . vterm-send-M-return)
   ("s-n" . vterm-next-prompt)
   ("s-p" . vterm-previous-prompt)
   ("S-SPC" . nil)
   ("S-<escape>" . (lambda () (interactive) (meow-normal-mode) (meow--update-cursor)))
   ("<C-i>" . vterm-send-C-i)
   ("C-;" . vterm-send-C-semicolon)
   ("C-<delete>" . vterm-send-C-delete)
   ("C-<return>" . vterm-send-F5)))

Autoload

(require 'vterm)

(defcustom ale-vterm-position-alist
  '((always . ((window-height . 0.2) (side . bottom))))
  "doc")

(defcustom ale-vterm-kill-whole-line-cmd
  'vterm-send-C-u
  "Command for kill whole line in vterm buffer."
  :type 'symbol :group 'vterm)

(defvar ale-vterm-buffers nil
  "The list of non-dedicated vterm buffers.")

(defvar ale-vterm-index 0
  "The index of current vterm buffer.")

;;;###autoload
(defun ale-vterm-escape-advisor (fn &rest args)
  (if (derived-mode-p 'vterm-mode)
      (vterm-send-escape)
    (apply fn args)))

;;;###autoload
(defun ale-vterm-kill-whole-line-advisor (fn &rest args)
  (if (derived-mode-p 'vterm-mode)
      (funcall ale-vterm-kill-whole-line-cmd)
    (apply fn args)))

(defun ale-vterm--disable-side-window (fn &rest args)
  "Prevent vterm size adjust break selection."
  (unless (and (region-active-p)
               (derived-mode-p 'vterm-mode))
    (apply fn args)))

;;;###autoload
(defun vterm-send-C-delete ()
  (interactive)
  (vterm-send-key "<delete>" nil nil 0))

;;;###autoload
(defun vterm-send-C-semicolon ()
  (interactive)
  (vterm-send-key ";" nil nil 0))

;;;###autoload
(defun vterm-send-M-return ()
  (interactive)
  (vterm-send-escape)
  (vterm-send-return))

;;;###autoload
(defun vterm-send-M-/ ()
  (interactive)
  (vterm-send-key "/" nil 0 nil))

;;;###autoload
(defun vterm-send-F5 ()
  (interactive)
  (vterm-send-key "<f5>" nil nil nil))

;;;###autoload
(defun vterm-send-M-apostrophe ()
  (interactive)
  (vterm-send-key "'" nil 0 nil))

;;;###autoload
(defun vterm-send-M-quote ()
  (interactive)
  (vterm-send-key "\"" nil 0 nil))

(defun ale-vterm--get-win-params ()
  "Parse `ale-vterm-position-alist' to get vterm display parameters."
  `(("^\\*vterm.*"
     (display-buffer-in-side-window)
     (window-parameters . ((mode-line-format . none)))
     ,@(cl-loop for (pred . pos) in ale-vterm-position-alist
                when (funcall pred)
                return pos))))

;;;###autoload
(defun ale-vterm-toggle (&optional project-root)
  "Toggle vterm.
If called with prefix argument, create a new vterm buffer with
project root directory as `default-directory'."
  (interactive "P")
  (if (eq major-mode 'vterm-mode)
      (delete-window)
    (let* ((display-buffer-alist (ale-vterm--get-win-params))
           (buf (nth ale-vterm-index ale-vterm-buffers))
           (pr-root (or (ignore-errors (project-root (project-current))) default-directory))
           (default-directory (if project-root pr-root default-directory))
           (index (if buf (ale-vterm--get-index buf) 0)))
      (add-to-list 'ale-vterm-buffers (vterm index))
      (ale-vterm--insert))))

(defun ale-vterm--get-index (buf)
  (let* ((name (buffer-name buf)))
    (string-match "\\*vterm\\*\<\\([0-9]+\\)\>" name)
    (string-to-number (cl-subseq name (match-beginning 1) (match-end 1)))))

(declare-function meow-insert "meow-command")
(declare-function evil-insert-state "evil")
(defun ale-vterm--insert ()
  (cond
   ((featurep 'meow)
    (meow-insert))
   ((featurep 'evil)
    (evil-insert-state))))

;;;###autoload
(defun ale-vterm-new ()
  "Create new vterm buffer."
  (interactive)
  (let ((new-index (1+ (ale-vterm--get-index (car ale-vterm-buffers))))
        (display-buffer-alist (ale-vterm--get-win-params)))
    (add-to-list 'ale-vterm-buffers (vterm new-index))
    (ale-vterm--insert)))

;;;###autoload
(defun ale-vterm-next (&optional arg)
  "Select next vterm buffer.
Create new one if no vterm buffer exists."
  (interactive "P")
  (let* ((curr-index (cl-position (current-buffer) ale-vterm-buffers))
         (new-index (+ curr-index (or arg -1)))
         (buf (nth new-index ale-vterm-buffers)))
    (when buf
      (switch-to-buffer buf)
      (setq ale-vterm-index new-index))))

;;;###autoload
(defun ale-vterm-prev (&optional arg)
  "Select previous vterm buffer."
  (interactive "p")
  (ale-vterm-next arg))

(defun ale-vterm--kill-buffer ()
  "Remove killed buffer from `ale-vterm-buffers'.

Used as a hook function added to `kill-buffer-hook'."
  (let* ((buf (current-buffer))
         (name (buffer-name buf)))
    (when (string-prefix-p "*vterm" name)
      (delq! buf ale-vterm-buffers))))

;;;###autoload
(define-minor-mode ale-vterm-mux-mode
  "Show/hide multiple vterm windows under control."
  :global t
  :group 'vterm
  (if ale-vterm-mux-mode
      (progn
        (advice-add 'display-buffer-in-side-window :around 'ale-vterm--disable-side-window)
        (add-hook 'kill-buffer-hook #'ale-vterm--kill-buffer))
    (advice-remove 'display-buffer-in-side-window 'ale-vterm--disable-side-window)
    (remove-hook 'kill-buffer-hook #'ale-vterm--kill-buffer)))

Git porcelain (magit.el)

(use-package magit
  :config
  (setq magit-display-buffer-function 'magit-display-buffer-same-window-except-diff-v1)
  (setq magit-define-global-key-bindings nil)
  (setq git-commit-summary-max-length 68)
  (setq git-commit-known-pseudo-headers
        '("Signed-off-by"
          "Acked-by"
          "Modified-by"
          "Cc"
          "Suggested-by"
          "Reported-by"
          "Tested-by"
          "Reviewed-by"))
  (setq git-commit-style-convention-checks
        '(non-empty-second-line
          overlong-summary-line))
  (setq magit-diff-refine-hunk t)
  (setq magit-repository-directories '(("~/Code" . 1) ("~" . 1)))
  :bind (("C-M-g" . magit-status-here)
         :map magit-diff-section-base-map
         ("<C-return>" . magit-diff-visit-file-other-window)))

Helpful (helpful.el)

Helpful.el provides a better help buffer. Here are some tweaks I made for this package and built-in help buffer:

  • disable auto jump to other end when cycle through buttons never
  • open new window when invoking helpful-visit-references. auto
  • focus newly opened help buffer (same behaviour as helpful.el)
(use-package helpful
  :bind
  (("C-h K" . #'describe-keymap)  ; overrides `Info-goto-emacs-key-command-node'
   ([remap describe-function] . #'helpful-callable)
   ([remap describe-symbol] . #'helpful-symbol)
   ([remap describe-key] . #'helpful-key)
   :map helpful-mode-map
   ("M-n" . (lambda () (interactive) (forward-button 1 nil 1 t)))
   ("M-p" . (lambda () (interactive) (backward-button 1 nil 1 t)))))

Autoload

(defvar ale-helpful-initialized nil)

;;;###autoload
(defun ale-helpful-mode-hook ()
  ;; FIXME: A better way?
  (setq ale-helpful-initialized nil)
  (advice-add 'find-file :before
              (lambda (&rest _)
                (when (and (not ale-helpful-initialized) (derived-mode-p 'helpful-mode))
                  (switch-to-buffer "*scratch*")
                  (switch-to-prev-buffer)
                  (setq ale-helpful-initialized t))))
  (visual-line-mode))

Emacs Manual (info.el)

(use-package info
  :straight (:type built-in)
  :bind
  (nil
   :map Info-mode-map
   ("n" . next-line)
   ("p" . previous-line)
   ("C-n" . Info-next)
   ("C-p" . Info-prev)
   ("M-n" . forward-paragraph)
   ("M-p" . backward-paragraph)))

Man page (man.el)

(use-package man
  :straight (:type built-in)
  :bind
  (nil
   :map Man-mode-map
   ("q" . kill-this-buffer))
  :config
  (setq Man-notify-method 'newframe))

Ripgrep (rg.el)

A search package based on the ripgrep command line tool. It allows you to interactively create searches, doing automatic searches based on the editing context, refining and modifying search results and much more. It is also highly configurable to be able to fit different users’ needs.

(use-package rg
  :config
  (ale-rg-setup)
  :bind
  (nil
   :map grandview-prog-map
   ("p" . rg-project-all-files-no-ask)))

Autoload

The purpose of the function ale-rg-setup is to avoid executing rg-define-toggle/search directly in use-package, because this macro introduces some deps that we don’t need at startup.

;;;###autoload
(defun ale-rg-setup ()
  "Define toggles in `rg-mode'."
  (rg-define-toggle "--context 3" (kbd "C"))
  (rg-define-toggle "-A 5" (kbd "A"))
  (rg-define-search rg-project-all-files-no-ask
    :dir project :files "all"))

Writable grep (wgrep.el)

With wgrep we can directly edit the results of a grep and save the changes to all affected buffers. In principle, this is the same as what the built-in occur offers. We can use it to operate on a list of matches by leveraging the full power of Emacs’ editing capabilities (e.g. keyboard macros, query and replace a regexp .etc).

(use-package wgrep
  :config
  (setq wgrep-auto-save-buffer t)
  (setq wgrep-change-readonly-file t)
  :bind
  (nil
   :map wgrep-mode-map
   ("M-n" . next-error-no-select)
   ("M-p" . previous-error-no-select)))

Pdf reader (pdf-tools.el)

(use-package pdf-tools
  :if HAS-GUI
  :after-call dired-after-readin-hook minibuffer-setup-hook
  :config
  (pdf-tools-install)
  (setq-default pdf-view-display-size 'fit-page)
  ;; automatically annotate highlights
  (setq pdf-annot-activate-created-annotations t)
  ;; turn off cua so copy works
  (add-hook 'pdf-view-mode-hook (lambda () (cua-mode 0)))
  ;; more fine-grained zooming
  (setq pdf-view-resize-factor 1.1)
  ;; keyboard shortcuts
  :bind
  (nil
   :map pdf-view-mode-map
   ("C-s" . isearch-forward)
   ("h" . pdf-annot-add-highlight-markup-annotation)
   ("t" . 'pdf-annot-add-text-annotation)
   ("D" . 'pdf-annot-delete)))

Epub reader (nov.el)

(use-package shrface
  :after nov
  :config
  (shrface-basic)
  (shrface-trial)
  (add-to-list 'shr-external-rendering-functions
               '(span . shrface-tag-span))
  (shrface-default-keybindings) ; setup default keybindings
  (setq shrface-href-versatile t))

(use-package nov
  :init
  (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
  (add-hook 'nov-mode-hook 'ale-nov-setup)
  :config
  (advice-add 'nov-render-title :override #'ignore)
  (setq nov-shr-rendering-functions '((img . nov-render-img)
                                      (title . nov-render-title)
                                      (b . shr-tag-b)))
  (setq nov-shr-rendering-functions
        (append nov-shr-rendering-functions
                shr-external-rendering-functions))
  (defun ale-nov-setup ()
    (require 'shrface)
    (shrface-mode)))

Murl (murl.el)

(bind-keys
 :map grandview-apps-map
 ("m" . murl-open))

Autoload

(require 'json)

(defvar murl-list-file (expand-file-name "~/.cache/murl/main_list.json"))

(defun murl--playlist ()
  (append (json-read-file murl-list-file) nil))

(defun murl--get-attr (title attr)
  (cl-dolist (i (murl--playlist))
    (when (string= title (cdr (assq 'title i)))
      (cl-return (cdr (assq attr i))))))

;;;###autoload
(defun murl-open (&optional no-hist)
  "Select video or stream to play in mpv."
  (interactive "P")
  (unless no-hist
    (let* ((clip (condition-case nil (current-kill 0 t) (error ""))))
      (set-text-properties 0 (length clip) nil clip)
      (when-let* ((is-url (string-prefix-p "http" clip))
                  (json (shell-command-to-string (concat "murl -P 1088 json '" clip "'")))
                  (valid (string-prefix-p "{" json))
                  (obj (json-read-from-string json))
                  (playlist (murl--playlist)))
        (cl-pushnew obj playlist :test 'equal)
        (with-temp-buffer
          (insert (json-encode (vconcat playlist)))
          (json-pretty-print-buffer)
          (write-region (point-min) (point-max) murl-list-file)))))
  (let* ((cands-raw (mapcar (lambda (i) (cdr (assq 'title i))) (murl--playlist)))
         (annotation (lambda (s) (marginalia--documentation (murl--get-attr s 'url))))
         (cands (+minibuffer-append-metadata annotation cands-raw))
         (title (completing-read "murls: " cands))
         (sub (murl--get-attr title 'sub)))
    (call-process "murl" nil 0 nil "-r" "-f" "-P" "1088" "-s" sub (murl--get-attr title 'url))))

Dictionary (fanyi.el)

(use-package fanyi
  :bind
  (nil
   :map grandview-apps-map
   ("t" . fanyi-dwim))
  :custom
  (fanyi-providers '(fanyi-etymon-provider
                     fanyi-longman-provider)))

Forges (forge.el)

(use-package forge)

Programming Languages

Rust

(use-package rust-mode)

C|C++

(use-package ob-C
  :straight (:type built-in)
  :after-call org-mode-hook
  :commands (org-babel-execute:C
             org-babel-expand:C
             org-babel-execute:cpp
             org-babel-expand:cpp)
  :config
  (add-to-list 'org-structure-template-alist '("cc" . "src C"))
  (add-to-list 'org-structure-template-alist '("cp" . "src cpp")))

Python

(use-package python
  :straight (:type built-in)
  :config
  (setq python-indent-offset 4)
  (setq python-indent-guess-indent-offset-verbose nil))
(use-package ob-python
  :straight (:type built-in)
  :after-call org-mode-hook
  :commands (org-babel-execute:python)
  :config
  (add-to-list 'org-structure-template-alist '("py" . "src python")))

Lua

(use-package lua-mode
  :config
  (setq lua-indent-level 2))

Yaml

(use-package yaml-mode)

JavaScript

(use-package js
  :straight (:type built-in)
  :config
  (setq js-indent-level 2))

(use-package web-mode
  :config
  (setq web-mode-content-types-alist '(("jsx" . "\\.js[x]?\\'")))
  :hook
  (web-mode . (lambda ()
                (emmet-mode)
                (setq web-mode-markup-indent-offset 2)
                (setq web-mode-code-indent-offset 2)
                (setq web-mode-script-padding 0)))
  :mode (("\\.vue\\'" . web-mode)
         ("\\.jsx?$" . web-mode)))
(use-package ob-js
  :straight (:type built-in)
  :after-call org-mode-hook
  :commands (org-babel-execute:js)
  :config
  (add-to-list 'org-structure-template-alist '("js" . "src js")))

Bash|Zsh

(use-package sh-script
  :straight (:type built-in)
  :config
  (setq sh-basic-offset 2))
(use-package ob-shell
  :straight (:type built-in)
  :after-call org-mode-hook
  :commands (org-babel-execute:bash)
  :config
  (add-to-list 'org-structure-template-alist '("sh" . "src bash")))

HTML

(use-package emmet-mode)

LaTeX

(use-package ob-latex
  :straight (:type built-in)
  :after-call org-mode-hook
  :commands (org-babel-execute:latex org-babel-expand:latex)
  :config
  (add-to-list 'org-structure-template-alist '("la" . "src latex")))

Makefile

(use-package ob-makefile
  :straight (:type built-in)
  :after-call org-mode-hook
  :commands (org-babel-execute:makefile)
  :config
  (add-to-list 'org-structure-template-alist '("mk" . "src makefile")))

DevTools

LSP (lsp.el)

(use-package lsp-mode
  :hook ((sh-mode
          lua-mode
          python-mode
          web-mode
          typescript-mode
          rust-mode)
         . lsp-deferred)
  :config
  (setq lsp-eldoc-hook nil)
  (setq lsp-enable-snippet nil)
  (setq lsp-signature-auto-activate t)
  (setq lsp-signature-function 'lsp-signature-posframe)
  (setq lsp-signature-doc-lines 20)
  (setq lsp-server-install-dir (expand-file-name (locate-user-emacs-file "lsp")))
  (setq lsp-headerline-breadcrumb-enable nil)
  (setq lsp-lua-completion-keyword-snippet "Disable")
  (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\envs\\'"))

Extensions

(use-package lsp-tailwindcss
  :after (lsp-mode web-mode)
  :preface
  (setq lsp-tailwindcss-add-on-mode t))

(use-package lsp-pyright
  :hook (python-mode . ale-lsp-pyright-import-venv)
  :config
  (when (executable-find "python3")
    (setq lsp-pyright-python-executable-cmd "python3")))

(use-package lsp-ui
  :config
  (setq lsp-ui-doc-position 'bottom)
  (setq lsp-ui-doc-show-with-cursor t)
  (setq lsp-ui-doc-show-with-mouse nil)
  :hook
  (lsp-mode . lsp-ui-mode))

Autoload

;;;###autoload
(defadvice! ale-lsp-volar--vue-project-p (workspace-root)
  "Make 'lsp-volar' support nuxt project."
  :override #'lsp-volar--vue-project-p
  (when-let* ((package-json (f-join workspace-root "package.json"))
              (exist (f-file-p package-json))
              (config (json-read-file package-json))
              (dependencies (alist-get 'dependencies config)))
    (or (alist-get 'vue dependencies) (alist-get 'nuxt dependencies))))

;;;###autoload
(defun ale-lsp-pyright-import-venv ()
  "Fix `import' resolution with projects with virtual env like conda."
  (require 'lsp-pyright)
  (let* ((pr-root (lsp-workspace-root))
         (py-ver "python3.9")
         (extra-path (concat pr-root "/envs/lib/" py-ver "/site-packages")))
    (setq lsp-pyright-extra-paths (vector extra-path))))

Virturl environment (conda.el)

(use-package conda
  :after-call find-file-hook
  :config
  (setq conda-anaconda-home "/opt/miniconda/")
  (setq conda-env-home-directory "/opt/miniconda/")
  (setq conda-message-on-environment-switch t)
  :bind
  (nil
   :map grandview-prog-map
   ("C" . ale-conda-activate-for-buffer))
  :hook
  (conda-postactivate . (lambda ()
                          (interactive)
                          (require 'jupyter)
                          (jupyter-available-kernelspecs t))))

Autoload

;;;###autoload
(defun ale-conda-activate-for-buffer ()
  "Enhanced `conda-env-activate-for-buffer'.
It support local `conda-env-subdirectory' such as `./envs'."
  (interactive)
  (let* ((filename (buffer-file-name))
         (yml-file (and filename (conda--find-env-yml (f-dirname filename))))
         (yml-path (and yml-file (f-dirname yml-file)))
         (prefix-env (concat yml-path "/" conda-env-subdirectory "/")))
    (cond ((or (null filename) (null yml-file))
           (conda-env-activate "base"))
          ((bound-and-true-p conda-project-env-path)
           (conda-env-activate conda-project-env-path))
          ((file-exists-p prefix-env)
           (unless (string= prefix-env (or conda-env-current-name ""))
             (conda-env-activate-path prefix-env)))
          (t
           (let ((env-name (conda-env-name-to-dir (conda--get-name-from-env-yml yml-file))))
             (when (and env-name (not (equal env-name conda-env-current-name)))
               (conda-env-activate env-name)))))))

Jupyter (jupyter.el)

(use-package jupyter
  :config
  ;; See https://github.com/nnicandro/emacs-jupyter/issues/380.
  (defun jupyter-ansi-color-apply-on-region (begin end)
    (ansi-color-apply-on-region begin end t))
  (require 'ob-jupyter)
  (org-babel-jupyter-override-src-block "python")
  (setq org-babel-default-header-args:python '((:async . "yes") (:kernel . "python3"))))

Colorizer (rainbow-mode.el)

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

Formatter (reformatter.el)

reformatter.el (created by Purcell) lets you easily define an idiomatic command to reformat the current buffer using a command-line program, together with an optional minor mode which can apply this command automatically on save.

(use-package reformatter
  :config
  (reformatter-define lua-format
    :program "stylua"
    :args '("--indent-width" "2" "-")
    :lighter " styLua")
  (reformatter-define python-format
    :program "black"
    :args '("-")
    :lighter " blackFMT")
  :bind
  (nil
   :map grandview-apps-map
   ("f" . ale-format-buffer)))

Autoload

;;;###autoload
(defun ale-format-buffer ()
  (interactive)
  (let* ((mode-name (string-remove-suffix "-mode" (format "%s" major-mode)))
         (func-name (intern (format "%s-format-buffer" mode-name))))
    (if (functionp func-name)
        (funcall func-name)
      (user-error
       (format
        "No available formatter for %s. Use `reformatter-define' to create it."
        major-mode)))))

Syntax checker (flymake.el)

(use-package flymake
  :straight (:type built-in)
  :hook ((prog-mode . ale-flymake-enable)
         (text-mode . flymake-mode))
  :bind
  (nil
   :map grandview-prog-map
   ("N" . 'flymake-goto-next-error)
   ("P" . 'flymake-goto-prev-error)
   ("S" . 'flymake-start)))

Autoload

;;;###autoload
(defun ale-flymake-enable ()
  "Inhibit `flymake-mode' under certain circumstances."
  (unless (or (equal default-directory
                     (or (file-name-directory grandview-org-file)
                         user-emacs-directory))
              (eq (current-buffer) (get-buffer "*scratch*")))
    (flymake-mode +1)))

REST client (restclient.el)

(use-package restclient)

Scratch buffers (scratch.el)

ale-scratch produces a org-mode buffer with right header-args for current jupyter kernel. Use it with SPC f s, doing it with a prefix argument (C-u) will prompt for a major mode instead. Simple yet super effective!

(bind-keys
 :map grandview-apps-map
 ("s" . ale-scratch))

Autoload

(defun ale-scratch--list-modes ()
  "List known major modes."
  (cl-loop for sym the symbols of obarray
           for name = (symbol-name sym)
           when (and (functionp sym)
                     (not (member sym minor-mode-list))
                     (string-match "-mode$" name)
                     (not (string-match "--" name)))
           collect (substring name 0 -5)))

;;;###autoload
(defun ale-scratch (query-for-mode)
  "Create or switch to an org-mode scratch buffer.
A jupyter session is attached to the buffer.
If called with prefix arg, prompt for a major mode for the buffer."
  (interactive "P")
  (let ((buf (get-buffer "*ALE-scratch*")))
    (unless buf
      (let* ((new-buf (generate-new-buffer "*ALE-scratch*"))
             (jpt-session (or (bound-and-true-p conda-env-current-name) "base"))
             (text (concat "#+PROPERTY: header-args:python :session " jpt-session))
             (mode "org"))
        (with-current-buffer new-buf
          (when query-for-mode
            (setq mode (completing-read "Mode: " (ale-scratch--list-modes) nil t nil nil))
            (setq text (format "Scratch buffer for: %s" mode)))
          (insert text)
          (funcall (intern (concat mode "-mode")))
          (setq-local org-image-actual-width '(1024))
          (unless (string= mode "org") (comment-region (point-min) (point-max)))
          (insert "\n\n")
          (setq buf new-buf))))
    (pop-to-buffer buf)))

About this file

COPYING

Copyright (c) 2020-2022 Alex Lu <alexluigit@gmail.com>

This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this file. If not, see http://www.gnu.org/licenses/.

File local variables

(defun grandview--org-eldoc-funcall (_callback &rest _ignored)
  "Fix `elisp-eldoc-funcall' in `org-mode'."
  (when (eq (org-element-type (org-element-at-point)) 'src-block)
    (let* ((sym-info (elisp--fnsym-in-current-sexp))
           (fn-sym (car sym-info)))
      (when (fboundp fn-sym)
        (message "%s: %s"
                 (propertize (format "%s" fn-sym) 'face 'font-lock-function-name-face)
                 (apply #'elisp-get-fnsym-args-string sym-info))))))

(defun grandview-setup-literate-file ()
  "Setup for `grandview-org-file'."
  (delq! t completion-at-point-functions)
  (appendq! completion-at-point-functions '(elisp-completion-at-point t))
  (setq-local eldoc-documentation-functions
              '(elisp-eldoc-var-docstring grandview--org-eldoc-funcall))
  (setq-local ale-org-id-auto t))

Here are the local variables in this file.

  • completion-at-point-functions: make autocompletion works in source code block
  • eldoc-documentation-functions: make eldoc works the same way as in a normal elisp file
  • ale-org-id-auto: generate :CUSTOM_ID for every heading on saving.

About

原非大观。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Emacs Lisp 100.0%