Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/alexluigit/emacs-grandview

原非大观。
https://github.com/alexluigit/emacs-grandview

Last synced: 3 days ago
JSON representation

原非大观。

Awesome Lists containing this project

README

        

# -*- eval: (grandview-org-local-setup); -*-
#+AUTHOR: Alex Lu
#+EMAIL: [email protected]
#+PROPERTY: header-args :mkdirp yes
#+STARTUP: showall
#+html:
-----
* Introduction
:PROPERTIES:
:CUSTOM_ID: h:1AECDD3A-9714-41E1-BA21-8BCAFED96CD3
:END:

This repository, *emacs-grandview*, showcases my personal Emacs configuration for
educational purposes and experimentation. While you're welcome to use it as a
basis for your own setup, I'm not maintaining it as a Emacs distribution.
Therefore, I'm unlikely to merge pull requests except for documentation
improvements or changes I find beneficial. Feel free to ask any questions you
may have.

If you're viewing this document on GitHub, the "table of contents" button in the
upper-right corner of the README container box can help you navigate its
sections. Welcome to my kitchen sink.

** Features
:PROPERTIES:
:CUSTOM_ID: h:E84BE1F8-9771-4C99-9B79-B68D1F830798
:END:

+ Modal editing based on =meow=, not =evil= at all.
+ Manage all your config in a single file.
+ Use built-in emacs packages whenever applicable.
+ Light-weighted & lighting fast startup speed.
+ ... etc.

** Usage Instructions
:PROPERTIES:
:CUSTOM_ID: h:93E5268D-CFDD-4DBF-A793-0CC5812DE181
:END:

This section details the loading procedure of *grandview* and provides
instructions for using this configuration.

*** This file (*grandview.org*)
:PROPERTIES:
:CUSTOM_ID: h:95B6B174-9877-4DCB-B298-86B3B889A11F
:END:

This file holds *both* source code and documentation of this project. See:
[[https://en.wikipedia.org/wiki/Literate_programming][Literate programming]]. If you are new to =org-mode=, check out the video: [[https://www.youtube.com/watch?v=0-brF21ShRk][Org mode
Intro]], or read their documentation: https://orgmode.org/. Try move the cursor
to one of the headings in this file and press =Tab= to fold / unfold the node
would be a good start.

After modifying code blocks in this file, you can apply changes immediately with
~M-x restart-emacs~. Upon restart, your old configuration will be replaced, and
Emacs will apply the latest configuration. This behavior mirrors that of a
regular Emacs configuration, even if not using literate programming. However,
before restarting, ensure all parentheses are balanced to avoid startup issues.

The elisp source blocks in this file is tangled to =grandview.el= which is then
evaluated by Emacs. Sections named *Extras* are tangled to separate files, and
corresponding autoloads are generated. Specifically, the =grandview-tangle=
function collects all *;;;###autoload* markers in code blocks and generates
=loaddefs= for them. You also don't need to manually enter the tangle path for the
*Extras* section, =grandview-tangle= will do it automatically.

To explicitly assign a library name to an *Extras* code block, use the format
*Extras ::: LIB_NAME*. This declares the feature *LIB_NAME* (without loading it),
which can then be required in other Elisp blocks using =(require 'LIB_NAME)=.

The incredibly useful =org-toggle-comment= command (bound to =SPC c ;= by default)
makes configuration management a breeze. When debugging problematic code within
one or more sections, simply comment out those sections and restart Emacs. The
configurations within those sections will be ignored, as they won't be tangled
during the startup process.

Here's an example of a typical tangled directory structure (files in *autoloads*
directory may vary depending on your configuration):

#+begin_example
tree ~/.cache/emacs/grandview

├── autoloads
│ ├── +eglot.el
│ ├── +eletric.el
│ ├── +files.el
│ ├── +font.el
│ ├── +frame-cursor-dim.el
│ ├── +frame-opacity.el
│ ├── +meow.el
│ ├── +minibuffer.el
│ ├── +mode-line.el
│ ├── +orderless.el
│ ├── +org-id.el
│ ├── +org-tree-slide.el
│ ├── +org.el
│ ├── +project.el
│ ├── +reformatter.el
│ ├── +rg.el
│ ├── +rime.el
│ ├── +scratch.el
│ ├── +simple.el
│ ├── +tabspaces.el
│ ├── +vc.el
│ ├── +visual-fill-column.el
│ ├── +vterm.el
│ ├── +vue.el
│ └── +window-monocle.el
├── grandview-custom.el
├── grandview-loaddefs.el
├── grandview-loaddefs.md5
├── grandview-macros.el
├── grandview.el
└── grandview.el.md5
#+end_example

These files are automatically generated and should not be edited manually. More
details are in the [[#h:4E1A86EF-3A26-4308-A263-1447D5D3987E][Load tangled elisp file]] section.

*** Private config | grandview options (*grandview.private.el*)
:PROPERTIES:
:CUSTOM_ID: h:9102F88E-A57C-48D2-A606-FF01002E4D98
:END:

=grandview.private.el= is the place to put your *grandview* specific configuration or
other private settings to load before loading =grandview.el=. See [[#h:0EBF6C16-D48B-4BE3-85B6-39FC96982410][Customization
options]].

Here is an example of =grandview.private.el=:

#+begin_src emacs-lisp :tangle no
;; adjust font size
(setq grandview-custom-font-size 150)

;; use this font as fixed font
(setq grandview-custom-fixed-font "Victor Mono")

;; set this theme
(setq grandview-custom-theme 'modus-operandi)

;; use http proxy
(setopt grandview-custom-env-alist
(pushnew! grandview-custom-env-alist
'("HTTP_PROXY" . "http://127.0.0.1:1080")
'("HTTPS_PROXY" . "http://127.0.0.1:1080")))

;; set initial frame opacity
(setq +frame-opacity 80)

;; enable copilot when programming
(add-hook 'prog-mode-hook 'copilot-mode)

;; ... other customizations
#+end_src

** Installation
:PROPERTIES:
:CUSTOM_ID: h:B45EA3C7-3DE3-42A1-AAD6-45FC9A5A6FBB
:END:

You might need these commands when using =emacs-grandview=.

|-------------+-----------------------------|
| Package | Description |
|-------------+-----------------------------|
| fd | find command, but faster |
| rg | grep command, but faster |
| git | Version control backend |
| gls (macOS) | GNU's ls, in built on Linux |
| pass | Simple password store |
|-------------+-----------------------------|

Back up your existing Emacs configuration, if any:

#+begin_src shell :tangle no
cp -r ~/.emacs.d ~/.emacs.d.bak
#+end_src

Clone the repo to *~/.config* directly:

#+begin_src shell :tangle no
git clone https://www.github.com/alexluigit/emacs-grandview ~/.config/emacs
#+end_src

Alternatively, create a symlink:

#+begin_src shell :tangle no
git clone https://www.github.com/alexluigit/emacs-grandview /path/to/dotfiles
ln -sf /path/to/dotfiles/grandview ~/.config/emacs
#+end_src

Now you can launch it:

#+begin_src shell :tangle no
# on macOS
open -a Emacs.app

# on Linux
emacs
#+end_src

Emacs may take some time to install packages on its first launch.

During the native compile stage, which can take several minutes, the UI may
become unresponsive and choppy. Please be patient and do not worry.

After the initial setup, you can restart Emacs using the *rem* shell command,
which is explained below.

** Restart script
:PROPERTIES:
:CUSTOM_ID: h:2DCD6D3B-6759-4E78-AF50-5B23FA77C603
:END:

Emacs offers =unload-feature= and =remove-hook= to mitigate the side effects of
packages or user configurations. However, a full Emacs restart, often achieved
through the =restart-emacs= command, remains the simpler solution. When Emacs
freezes, preventing access to keybindings, a shell script to restart Emacs from
the terminal becomes invaluable. Consequently, I created the following CLI
named =rem= (for "restart-emacs"). If you've followed the previous instructions,
this script is already installed. It will be located at *~/.local/bin/rem*, so
ensure *~/.local/bin* is included in your *PATH*.

These flags are supported by this script:

+ ~-r~: Before restarting, re-tangle this org file.
+ ~-p ~: Before restarting, delete cache of package *NAME*.
+ ~-P~: Before restarting, delete all package caches.
+ ~-d~: pass =--debug-init= flag to emacs, for debugging purpose.

#+name: grandview-cache
#+begin_src emacs-lisp :var type="main" :tangle no
(pcase type
("main" (format "%s" grandview--cache))
("pkg-repos" (format "%s" package-user-dir)))
#+end_src

#+begin_src bash :tangle "~/.local/bin/rem" :shebang "#!/usr/bin/env bash" :noweb yes
PKG="" REPOs=false RESET=false DEBUG="" BIN="emacs"
[[ $(uname) == "Darwin" ]] && BIN="/Applications/Emacs.app/Contents/MacOS/Emacs"

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

emacs_cmd="$BIN $DEBUG >/dev/null 2>&1 & disown"
kill -9 $(pgrep -x '[Ee]macs') 2>/dev/null
[[ -n $PKG ]] && rm -rf <>/$PKG 2>/dev/null
$RESET && rm -rf '<>' 2>/dev/null
$REPOs && rm -rf '<>' 2>/dev/null
eval $emacs_cmd
#+end_src

** COPYING
:PROPERTIES:
:CUSTOM_ID: h:3CC0C8EB-75AE-4CDE-803B-A904AD026E65
:END:

#+begin_quote
Copyright (c) 2020-2025 Alex Lu

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 .
#+end_quote

* THE CORE
:PROPERTIES:
:CUSTOM_ID: h:FB2572C2-4DDC-44A2-92C0-D8754305B2A7
:END:

This section contains the initialization files and core modules for *grandview*.
Here, you'll find configurations for the modal editing, user interface,
including themes and fonts.

** The early-init file (*early-init.el*)
:PROPERTIES:
:CUSTOM_ID: h:610D1070-5D79-466F-A5A5-7068396F2D41
:END:

It is sometimes necessary to have customizations take effect during Emacs
startup earlier than the normal init file is processed. Such customizations can
be put in the early init file. This file is loaded before the package system
and GUI is initialized, so in it you can customize variables that affect the
package initialization process.

By setting =user-emacs-directory= to =~/.cache/emacs=, we prevent modules like
=comp.c= and =package.el= from cluttering our primary Emacs directory, where
=grandview.org= is located. This configuration also (almost) ensures that
subsequently loaded packages store their cache within =~/.cache/emacs=.

These are some general setups for frames initialization and the basics of the
toolkit. In short, I disabled most of UI elements to keep things minimal.

Be cautious when experimenting with *grandview*'s initialization files,
specifically *early-init.el* and *init.el*. These two files, along with other
output files, are automatically generated and updated each time Emacs restarts.
If you manually delete *init.el* or *early-init.el*, or improperly edit their
corresponding code blocks, Emacs will lose track of *grandview.org* and start with
an empty profile. In such cases, you will need to manually create and edit
these two files.

#+begin_src emacs-lisp :tangle (expand-file-name "early-init.el" (file-name-directory user-init-file))
;;; early-init.el --- tangled from grandview.org -*- lexical-binding: t -*-

;;; Commentary:
;; This file is auto-generated.
;; Do *NOT* edit this file directly. Edit relevant sections in `grandview.org' instead.
;; Do not delete this file, otherwise you'll have to retangle it manually.

;;; Code:

(setq
gc-cons-threshold most-positive-fixnum ; Inhibit garbage collection during startup
native-comp-async-report-warnings-errors 'silent ; Do not report native-comp warnings
user-emacs-directory (expand-file-name "~/.cache/emacs/") ; No littering
custom-file (concat user-emacs-directory "custom.el") ; Place all "custom" code in a temporary file
package-user-dir (locate-user-emacs-file "elpa") ; Use `user-emacs-directory' we set above
package-quickstart nil ; Prevent package.el loading packages prior to their init-file
package-enable-at-startup t ; no need to call `package-initialize'
package-install-upgrade-built-in nil ; do not update built-in lib
package-archives
'(("gnu-elpa" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/"))
package-archive-priorities '(("melpa" . 3) ("gnu-elpa" . 2) ("nongnu" . 1))
auto-mode-case-fold nil ; Use case-sensitive `auto-mode-alist' for performance
fast-but-imprecise-scrolling t ; More performant rapid scrolling over unfontified regions
ffap-machine-p-known 'reject ; Don't ping things that look like domain names
frame-inhibit-implied-resize t ; Inhibit frame resizing for performance
frame-resize-pixelwise t ; Allow resizing frame pixelwise
idle-update-delay 1.0 ; slow down UI updates down
read-process-output-max (* 1024 1024) ; Increase how much is read from processes in a single chunk.
process-adaptive-read-buffering nil
redisplay-skip-fontification-on-input t ; Inhibits it for better scrolling performance.
command-line-x-option-alist nil ; Remove irreleant command line options for faster startup
select-active-regions 'only ; Emacs hangs when large selections contain mixed line endings.
auto-save-list-file-prefix nil ; Disable auto-save
create-lockfiles nil ; Disable lockfiles
make-backup-files nil ; Disable backup files
vc-follow-symlinks t ; Do not ask about symlink following
use-short-answers t ; y/n for yes/no
ring-bell-function #'ignore ; Do NOT ring the bell
ad-redefinition-action 'accept ; Disable warnings from legacy advice system
inhibit-compacting-font-caches t ; Don't compact font caches during gc
inhibit-startup-message t ; Reduce noise at startup
inhibit-startup-echo-area-message user-login-name
inhibit-default-init t
initial-scratch-message nil)

(load custom-file 'noerror 'silent) ; Load user's customization
(tool-bar-mode -1) ; Disable toolbar
(tooltip-mode -1) ; Disable tooltips
(menu-bar-mode -1) ; Disable menu bar
(scroll-bar-mode -1) ; Disable scroll bar
#+end_src

** The initialization file (*init.el*)
:PROPERTIES:
:CUSTOM_ID: h:DF24DB4D-F3B6-47B2-AE76-F5728FF1CFB4
:END:

The =init.el= file is responsible for:

+ Defining the =grandview-tangle= function
+ Tangling =grandview.org= and loading the resulting Emacs Lisp file.

*** Locate *grandview*'s files
:PROPERTIES:
:CUSTOM_ID: h:8236B22C-818F-48D5-A1E6-2D4579863C2F
:END:

You can find all the paths managed by *grandview* here.

#+begin_src emacs-lisp :tangle (expand-file-name "init.el" (file-name-directory user-init-file))
;;; init.el --- tangled from grandview.org -*- lexical-binding: t -*-

;;; Commentary:
;; This file is auto-generated.
;; Do *NOT* edit this file directly. Edit relevant sections in `grandview.org' instead.
;; Do not delete this file, otherwise you'll have to retangle it manually.

;;; Code:

(defvar grandview--cache
(expand-file-name "grandview/" user-emacs-directory))

(defvar grandview--def-dir
(expand-file-name "autoloads/" grandview--cache))

(defun grandview--path (type)
"Get grandview's init path according to TYPE."
(pcase type
('g (expand-file-name "grandview.org"
(file-name-directory user-init-file)))
('private (expand-file-name "grandview.private.el"
(file-name-directory user-init-file)))
('main (expand-file-name "grandview.el" grandview--cache))
('main-md5 (expand-file-name "grandview.el.md5" grandview--cache))
('def-el (expand-file-name "grandview-loaddefs.el" grandview--cache))
('def-md5 (expand-file-name "grandview-loaddefs.md5" grandview--cache))))

(defun grandview--readfile (path)
"Return the decoded text in PATH as single-byte string."
(with-temp-buffer
(set-buffer-multibyte nil)
(setq buffer-file-coding-system 'binary)
(when (file-exists-p path) (insert-file-contents-literally path))
(buffer-substring-no-properties (point-min) (point-max))))
#+end_src

*** The *grandview-tangle* function
:PROPERTIES:
:CUSTOM_ID: h:9A75C0EA-7484-4B28-894C-B9A5415F5294
:END:

These are 2 helper functions for =grandview-tangle=.

+ =grandview--loaddefs-gen= ::

This function compares the MD5 hash of the previously generated *loaddefs* file
located in =grandview--def-dir= with the MD5 hash of the latest version of
*loaddefs*. If the hashes do not match, indicating changes in one or more
*Extras* code blocks, the function regenerates =grandview-loaddefs.el=. This
prevents unnecessary calls to =loaddefs-generate=.

+ =grandview--put-tangle-path= ::

This function ensures the tangle path attribute for each *Extras* code block is
put correctly.

#+begin_src emacs-lisp :tangle (expand-file-name "init.el" (file-name-directory user-init-file))
(defun grandview--put-tangle-path (file-name)
"Prepare metadata for `grandview-tangle' in FILE-NAME."
(with-current-buffer (find-file-noselect file-name)
(goto-char (point-min))
(save-excursion
(widen)
(cl-labels
((tangle-path-template ()
":tangle (expand-file-name \"%s\" grandview--def-dir)")
(put-tangle-path (pkg)
(let* ((pfx+? (ignore-errors (string= "+" (substring pkg 0 1))))
(.el? (ignore-errors
(string= ".el" (substring pkg -3))))
(f-name (concat (if pfx+? "" "+") pkg (if .el? "" ".el")))
(path (format (tangle-path-template) f-name)))
(org-entry-put nil "header-args:emacs-lisp" path))))
(org-map-entries
(lambda ()
(org-with-point-at (point)
(when-let* ((heading (org-get-heading))
((string-prefix-p "Extras" heading)))
(cond
((string-prefix-p "Extras ::: " heading)
(put-tangle-path
(cadr (string-split heading " ::: "))))
((string= heading "Extras")
(let* ((title (save-excursion
(org-up-heading-safe) (org-get-heading)))
(memo (ignore-errors
(substring
title (1+ (string-match "(\\(.*\\))" title))
(match-end 1)))))
(put-tangle-path
(or memo (replace-regexp-in-string
(regexp-quote " ") "_" title t t))))))))))))
(save-buffer)
(kill-current-buffer)))

(defun grandview--loaddefs-gen (&optional force)
"Generate autoload files for Grandview.
Only do it when FORCE or contents in autoload directory changed."
(let* ((autoload-md5 (grandview--path 'def-md5))
(old-md5 (grandview--readfile autoload-md5))
(all-el-files (directory-files-recursively
grandview--def-dir "\\.el$"))
(files-as-str (with-temp-buffer
(dolist (file all-el-files)
(insert-file-contents file))
(buffer-string)))
(new-md5 (secure-hash 'md5 files-as-str)))
(when (or force (not (string= old-md5 new-md5)))
(let ((generate-autoload-file nil) (inhibit-message t))
(loaddefs-generate grandview--def-dir
(grandview--path 'def-el)))
(with-temp-buffer
(erase-buffer)
(insert new-md5)
(write-region (point-min) (point-max) autoload-md5)))))
#+end_src

The =grandview-tangle= function, defined here, employs the same MD5 checking
mechanism as =grandview--loaddefs-gen=. This ensures that changes to the
=grandview.org= file are captured by =grandview-tangle= upon killing Emacs.

It is not an interactive command that users can call with =M-x=; instead, it is
called automatically when needed. You can safely ignore it.

#+begin_src emacs-lisp :tangle (expand-file-name "init.el" (file-name-directory user-init-file))
(defun grandview-tangle (&optional force)
"Tangle grandview.org to grandview.el.
The tanglement only happens when the hashed md5 string changed after
last change or FORCE is non nil."
(let* ((g-org (grandview--path 'g))
(main-el (grandview--path 'main))
(md5-file (grandview--path 'main-md5))
(old-md5 (grandview--readfile md5-file))
(new-md5 (secure-hash 'md5 (grandview--readfile g-org)))
find-file-hook kill-buffer-hook write-file-functions
enable-local-variables)
(when (or force (not (string= old-md5 new-md5)))
(require 'ob-tangle)
(grandview--put-tangle-path g-org)
(with-temp-buffer
(erase-buffer)
(insert new-md5)
(write-region (point-min) (point-max) md5-file))
(let (org-confirm-babel-evaluate)
(org-babel-tangle-file g-org main-el)
(with-temp-buffer
(insert-file-contents main-el)
(goto-char (point-max))
(insert "\n(provide 'grandview)\n;;; grandview.el ends here")
(write-region nil nil main-el)))
(cl-loop for lib in (directory-files-recursively
grandview--def-dir "\\.el$")
for base = (file-name-base lib)
for end-s =
(format "\n(provide '%s)\n;;; %s.el ends here" base base)
do (with-temp-buffer
(insert ";;; -*- lexical-binding: t -*-\n\n")
(insert-file-contents lib)
(goto-char (point-max))
(insert end-s)
(write-region nil nil lib)))
(grandview--loaddefs-gen force))))
#+end_src

*** Load tangled .el files
:PROPERTIES:
:CUSTOM_ID: h:4E1A86EF-3A26-4308-A263-1447D5D3987E
:END:

If the directory =grandview--cache= does not exist (either due to first-time use
or deletion, such as with =rem -r=), tangling =grandview.org= and loading the
resulting Emacs Lisp files will occur. Otherwise, it assumes all necessary
files are generated and can be required directly. Additionally, the
=grandview-tangle= function is hooked to =kill-emacs-hook=. This means the md5 check
happens whenever Emacs is killed, ensuring tangling occurs whenever the content
of =grandview.org= changes and the latest configuration is used upon restart. This
design avoids tangling =grandview.org= every time Emacs launches, which can be
slow due to the heaviness of =org.el=.

#+begin_src emacs-lisp :tangle (expand-file-name "init.el" (file-name-directory user-init-file))
(let ((raise-err-when-debug-init (not (getenv-internal "DEBUG")))
file-name-handler-alist)
(unless (file-exists-p grandview--cache)
(make-directory grandview--def-dir t)
(grandview-tangle t)) ; "Initiate spin!" -- Joseph Cooper
(add-to-list 'load-path grandview--cache)
(add-to-list 'load-path grandview--def-dir)
(add-hook 'kill-emacs-hook #'grandview-tangle -90)
(require 'grandview-macros nil raise-err-when-debug-init)
(require 'grandview-custom nil raise-err-when-debug-init)
(when-let* ((private-conf (grandview--path 'private))
((file-exists-p private-conf)))
(load private-conf raise-err-when-debug-init 'silent))
(require 'grandview-loaddefs nil raise-err-when-debug-init)
(push '(grandview-org-local-setup) safe-local-eval-forms)
(require 'grandview nil raise-err-when-debug-init)
(setq gc-cons-threshold 134217728))
#+end_src

** Pre-load setups
:PROPERTIES:
:CUSTOM_ID: h:976B3AE1-6291-4123-9A3C-A19BC284FA51
:END:

Setting up the environment variables, package manager, etc.

#+begin_src emacs-lisp
;;; grandview.el --- tangled from grandview.org -*- lexical-binding: t -*-
;; Generated by the `grandview-tangle' function.
;; Package-Requires: ((emacs "30"))
;; This file is not part of GNU Emacs.

;;; Commentary:
;; Auto generated file, do not edit.

;;; Code:

#+end_src

*** Local variables in *grandview.org*
:PROPERTIES:
:CUSTOM_ID: h:70CB6487-B425-4BE3-8E1B-1FE655056EFC
:END:

Here are the local setups for this file (*grandview.org*).

+ make *eldoc* works the same way as in a normal elisp file.
When making changes to elisp code blocks, it's recommended to use
=org-edit-special= (=SPC c '=) to edit the code in a new window with full language
support. However, for small changes where you want symbol documentation in
the minibuffer, this "hack" can be useful.

+ Do not add spaces at line beginning in src blocks.
Basically setting ~org-edit-src-content-indentation~ to 0.

+ Enable ~auto-fill-mode~.
+ Center the text via ~+visual-fill-column-center-mode~.
+ Ensure the *CUSTOM_ID* attribute is attached to every section when saving.

I used the built-in function =add-file-local-variable-prop-line= to append local
variables or eval forms to *grandview.org*. This is the recommended method.

#+begin_src emacs-lisp
(defun grandview--org-eldoc-funcall (_callback &rest _ignored)
"Adapt `elisp-eldoc-funcall' in `grandviwe.org'."
(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-org-local-setup ()
"Adaptive setups for `grandview.org'."
(setq-local eldoc-documentation-functions
'(elisp-eldoc-var-docstring grandview--org-eldoc-funcall))
(when (fboundp '+org-id-headlines)
(add-hook 'before-save-hook #'+org-id-headlines nil t))
(when (fboundp '+visual-fill-column-center-mode)
(setq-local visual-fill-column-width 120)
(+visual-fill-column-center-mode))
;; keep this line as errors may occur before the `org-src' config block
(setq-local org-edit-src-content-indentation 0)
(auto-fill-mode))
#+end_src

*** Customization options
:PROPERTIES:
:CUSTOM_ID: h:0EBF6C16-D48B-4BE3-85B6-39FC96982410
:END:

In addition, several custom options were introduced in this file:

+ ~grandview-custom-theme~: Default theme for grandview.
+ ~grandview-custom-font-size~: Font size being applied.
+ ~grandview-custom-default-font~: The default font.
+ ~grandview-custom-fixed-font~: The =fixed-pitch= font.
+ ~grandview-custom-variable-font~: The =variable-pitch= font.
+ ~grandview-custom-headline-sizes~: Height scaling of 1-4 level of headlines.
+ ~grandview-custom-cjk-font~: Font for Chinese / Japanese / Korean characters.
+ ~grandview-custom-env-alist~: Use these environment variables in GUI emacs.

Despite we can set environment variables on the fly using =setenv= function and it
works fine in most of the scenarios, it is more reliable to set them before
loading any packages. Some people prefer using the package ~exec-path-from-shell~
to load environment variables from SHELL, but calling external process is quite
slow, so I'm better off do it the regular way.

#+begin_src emacs-lisp :tangle (expand-file-name "grandview-custom.el" grandview--cache)
;;; grandview-custom.el --- environment variables in emacs -*- lexical-binding: t -*-

;;; Commentary:

;;; Auto generated by function `grandview-tangle', do not edit.

;;; Code:

(defcustom grandview-custom-theme 'doom-one
"Default theme for grandview."
:group 'grandview :type 'theme)

(defcustom grandview-custom-font-size 150
"Default font size for grandview."
:group 'grandview :type 'number)

(defcustom grandview-custom-default-font "Iosevka Nerd Font Mono"
"Default font for grandview."
:group 'grandview :type 'string)

(defcustom grandview-custom-fixed-font "Sarasa Mono SC"
"Default fixed font for grandview."
:group 'grandview :type 'string)

(defcustom grandview-custom-variable-font "Sarasa Mono SC"
"Default variable font for grandview."
:group 'grandview :type 'string)

(defcustom grandview-custom-cjk-font "LXGW WenKai Mono"
"Default CJK font (Chinese/Japanese/Korean) for grandview."
:group 'grandview :type 'string)

(defcustom grandview-custom-headline-sizes
'(1.2 1.15 1.1 1.05)
"Height scaling of level-1 to level-4 headings.
This option controls the `:height' attribute of headlines for relevant
faces in `outline' and `org'."
:group 'org :type 'list)

(defcustom grandview-custom-env-alist
(let ((x-cfg (expand-file-name "~/.config"))
(x-data (expand-file-name "~/.local/share")))
`(("XDG_CONFIG_HOME" . ,x-cfg)
("XDG_DATA_HOME" . ,x-data)
("GNUPGHOME" . ,(expand-file-name "gnupg" x-data))
("PASSWORD_STORE_DIR" . ,(expand-file-name "pass" x-data))
("SSH_AUTH_SOCK" . ,(expand-file-name "gnupg/S.gpg-agent.ssh" x-data))
("PATH" . ,(string-join
(list (expand-file-name "python/bin" x-data)
"/opt/homebrew/bin" "/opt/homebrew/sbin"
"/usr/local/bin" "/usr/bin" "/bin" "/usr/sbin" "/sbin"
(expand-file-name "~/.local/bin")) ":"))))
"Use these environment variables in GUI emacs."
:group 'grandview :type 'alist
:set (lambda (sym env-alist)
(set-default sym env-alist)
(pcase-dolist (`(,name . ,value) env-alist)
(setenv name value)
(when (string-equal "PATH" name)
(setq exec-path (append (parse-colon-path value)
(list exec-directory)))
(setq-default eshell-path-env value)))))

(provide 'grandview-custom)
;;; grandview-custom.el ends here
#+end_src

*** Pre-defined keymaps
:PROPERTIES:
:CUSTOM_ID: h:E5232E0D-670F-487F-BA8C-50396998807A
:END:

Here I defined a few top-level keymaps for you to use in the following
configurations. When used with =meow=, they are bind as different prefixes under
leader key (=SPC=). See [[#h:95E86308-81E7-4A9F-B457-1EE8F8F80896][Meow > Keybindings > Leader]]

+ =grandview-file-map=: File manipulations, dired, etc.
+ =grandview-mct-map=: [M]inibuffer and [C]ompletion in [T]andem.
+ =grandview-prog-map=: Commands relate to programming.
+ =grandview-app-map=: Useful application / utils such as set frame opacity, etc.

And a few child keymaps.

+ =+config-prefix-map=: Commands for managing Emacs config, under =grandview-app-map=
+ =+find-file-prefix-map=: Locate files in specified places, under =grandview-file-map=
+ =+file-laf-prefix-map=: Adjust look and feel of buffer/file, under =grandview-file-map=

Initially, the =+config-prefix-map= includes several commands:

+ =grandview-org= and =grandview-private-el= ::
allow you to quickly locate =grandview.org= and =grandview.private.el=, respectively.

+ =emacs-init-time= ::
show the time takes for Emacs to finish initialization

+ =restart-emacs= ::
built-in command to restart Emacs

#+begin_src emacs-lisp
(defvar-keymap grandview-file-map
:doc "File and directory manipulations.")

(defvar-keymap grandview-mct-map
:doc "Search everything using minibuffer.")

(defvar-keymap grandview-prog-map
:doc "Keymap for programming.")

(defvar-keymap grandview-app-map
:doc "Useful applications and utilities."
"d" #'toggle-debug-on-error
"=" #'count-words)

(defvar-keymap +config-prefix-map
:doc "Handy commands for configuring `emacs-grandview'."
"g" #'grandview-org
"p" #'grandview-private-el
"i" #'emacs-init-time
"r" #'restart-emacs)

(defvar-keymap +find-file-prefix-map
:doc "Keymap for locating files")

(defvar-keymap +file-laf-prefix-map
:doc "Keymap for comamands to adjust look and feel of file or buffers."
;; recenter current [l]ine
"l" #'recenter)

(defun grandview-org ()
"Edit grandview's org document."
(interactive)
(find-file (grandview--path 'g)))

(defun grandview-private-el ()
"Edit grandview's private config."
(interactive)
(find-file (grandview--path 'private)))

(define-key grandview-file-map (kbd "f") +find-file-prefix-map)
(define-key grandview-file-map (kbd "l") +file-laf-prefix-map)
(define-key grandview-app-map (kbd "g") +config-prefix-map)
#+end_src

*** Macros for configuring packages
:PROPERTIES:
:CUSTOM_ID: h:3407EAC6-5A0B-4B67-8237-DAFF12B63F63
:END:

A few handy macros were defined to be used in elisp code blocks of this =.org=
file, you'll see them appear in this file a couple of times. These macros are
also accessible in =grandview.private.el=.

+ =appendq!=: append lists to a symbol.
+ =delq!=: delete an element from a list.
+ =pushnew!=: add items to a list if they aren't there.
+ =prependq!=: prepend lists to somewhere.
+ =defadvice!=: Drop-in replacement for =advice-add=.
+ =bind!=: Less typing when binding keys.

#+begin_src lisp :tangle (expand-file-name "grandview-macros.el" grandview--cache)
;;; grandview-macros.el --- handy macros for configuring packages -*- lexical-binding: t -*-

;;; Commentary:

;;; Auto generated by function `grandview-tangle', do not edit.

;;; Code:

;;; Copied from `doom-emacs'
(defmacro appendq! (sym &rest lists)
"Append LISTS to SYM in place."
`(setq ,sym (append ,sym ,@lists)))

(defmacro delq! (elt list)
"`delq' ELT from LIST in-place."
`(setq ,list (delq ,elt ,list)))

(defmacro pushnew! (place &rest values)
"Push VALUES sequentially into PLACE, if they aren't already present.
This is a variadic `cl-pushnew'."
(let ((var (make-symbol "result")))
`(dolist (,var (list ,@values) (with-no-warnings ,place))
(cl-pushnew ,var ,place :test #'equal))))

(defmacro prependq! (sym &rest lists)
"Prepend LISTS to SYM in place."
`(setq ,sym (append ,@lists ,sym)))

(defmacro defadvice! (symbol arglist &optional docstring &rest body)
"Define an advice called SYMBOL and add it to PLACES.
ARGLIST is as in `defun'. WHERE is a keyword as passed to `advice-add', and
PLACE is the function to which to add the advice, like in `advice-add'.
DOCSTRING and BODY are as in `defun'.
\(fn SYMBOL ARGLIST &optional DOCSTRING &rest [WHERE PLACES...] BODY\)"
(declare (doc-string 3) (indent defun))
(unless (stringp docstring)
(push docstring body)
(setq docstring nil))
(let (where-alist)
(while (keywordp (car body))
(push `(cons ,(pop body)
(let ((l ,(pop body))) (if (proper-list-p l) l (list l))))
where-alist))
`(progn
(defun ,symbol ,arglist ,docstring ,@body)
(dolist (targets (list ,@(nreverse where-alist)))
(dolist (target (cdr targets))
(advice-add target (car targets) #',symbol))))))

;;; Copied from Prot
(defmacro bind! (keymap &rest definitions)
"Expand key binding DEFINITIONS for the given KEYMAP.
DEFINITIONS is a sequence of string and command pairs."
(declare (indent 1))
(unless (zerop (% (length definitions) 2))
(error "Uneven number of key+command pairs"))
(let ((keys (seq-filter #'stringp definitions))
;; We do accept nil as a definition: it unsets the given key.
(commands (seq-remove #'stringp definitions)))
`(when-let* (((keymapp ,keymap))
(map ,keymap))
,@(mapcar
(lambda (pair)
(let* ((key (car pair))
(command (cdr pair)))
(unless (and (null key) (null command))
`(define-key map (kbd ,key) ,command))))
(cl-mapcar #'cons keys commands)))))

(provide 'grandview-macros)
;;; grandview-macros.el ends here
#+end_src

*** Package manager
:PROPERTIES:
:CUSTOM_ID: h:F8A8595A-6466-49D8-902B-3CA74C9B0657
:END:

As the author of =use-package= stated, it is *not* a package manager. Instead,
it's designed to isolate package configurations within your Emacs init file,
promoting both performance and organization.

Despite this, we stick to use it in conjunction with =package.el= as our package
management solution for several reasons:

+ Both are built into Emacs (we are using emacs30).
+ =use-package= can control package loading order with the ~:after~ keyword.
+ =use-package= now supports installing packages from source using the ~:vc~ keyword.
+ Our package configurations are maintained separately from =use-package=
declarations, so using them in this way is acceptable for our needs.

To further enhance its lazy-loading capabilities, we're incorporating the
~:after-call~ keyword definition from Doom Emacs.

#+begin_src elisp :tangle no
;; The keyword should be used like this:
(use-package some-package :after-call SYMBOL|LIST)
#+end_src

#+begin_src emacs-lisp
(with-eval-after-load 'use-package-core
;; `use-package' adds syntax highlighting for the `use-package' macro, but
;; Emacs 26+ already highlights macros, so it's redundant.
(font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords)

(push :after-call use-package-deferring-keywords)
(setq use-package-keywords
(use-package-list-insert :after-call use-package-keywords :after))

(defvar grandview--deferred-packages-alist '(t))
(setq
use-package-compute-statistics init-file-debug
use-package-verbose init-file-debug
use-package-minimum-reported-time (if init-file-debug 0 0.1)
use-package-expand-minimally (not noninteractive)
use-package-vc-prefer-newest t
use-package-always-defer t)

(defun grandview--log (string &optional label)
"Log STRING out.
Optional LABEL can be a symbol or string, use GRANDVIEW by default."
(let* ((label-str (cond ((and label (symbolp label)) (symbol-name label))
((stringp label) label)
(t "GRANDVIEW")))
(label (propertize label-str 'face 'font-lock-builtin-face)))
(prog1 nil (message "%s" (format "%s: %s" label string)))))

(defalias 'use-package-normalize/:after-call #'use-package-normalize-symlist)
(defun use-package-handler/:after-call (name _keyword hooks rest state)
(if (plist-get state :demand)
(use-package-process-keywords name rest state)
(let ((fn (make-symbol (format "grandview--after-call-%s-h" name))))
(use-package-concat
`((fset ',fn
(lambda (&rest _)
(grandview--log (format "package「%s」lazy loaded" ',name))
(condition-case e
;; If `default-directory' is a directory that doesn't
;; exist or is unreadable, Emacs throws up file-missing
;; errors, so we set it to a directory we know exists and
;; is readable.
(let ((default-directory user-emacs-directory))
(require ',name))
((debug error)
(message "Failed to load deferred package %s: %s" ',name e)))
(when-let* ((deferral-list
(assq ',name grandview--deferred-packages-alist)))
(dolist (hook (cdr deferral-list))
(advice-remove hook #',fn)
(remove-hook hook #',fn))
(delq! deferral-list grandview--deferred-packages-alist)
(unintern ',fn nil)))))
(let (forms)
(dolist (hook hooks forms)
(push (if (string-match-p "-\\(?:functions\\|hook\\)$" (symbol-name hook))
`(add-hook ',hook #',fn)
`(advice-add #',hook :before #',fn))
forms)))
`((unless (assq ',name grandview--deferred-packages-alist)
(push '(,name) grandview--deferred-packages-alist))
(nconc (assq ',name grandview--deferred-packages-alist)
'(,@hooks)))
(use-package-process-keywords name rest state))))))
#+end_src

** Modal Editing On Wish
:PROPERTIES:
:CUSTOM_ID: h:CDB8D834-C18F-4B3E-BAE8-98BBDFE5095B
:END:

*grandview*'s modal editing is powered by *meow.el* (Modal Editing On Wish), a
contribution by *@DogLooksGood*! Meow includes a built-in tutorial, accessible
via =M-x meow-tutor=. For more information about meow or modal editing in
general, visit [[https://www.github.com/DoglooksGood/meow][meow]].

Unlike *evil-mode*, which emulates the entirety of Vim within Emacs, meow focuses
on bringing modal editing to vanilla Emacs. Compared to *Vim* or *evil-mode*, meow
is minimalist, lightweight, and powerful. However, I missed some utilities from
my Vim days, so I use two additional packages in tandem with meow. So far, they
work together flawlessly.

+ *avy.el* ::

With *Avy*, you can move point to any position in Emacs – even in a different
window – using very few keystrokes. For this, you look at the position where
you want point to be, invoke Avy, and then enter the sequence of characters
displayed at that position. This package is similar to *easymotion.vim* in Vim.

+ *embrace.el* ::

This package provides symbol pair insertion, modification, and deletion,
similar to *surround.vim* in Vim. I've forked it to make it more customizable,
allowing you to use keystrokes like ~er~ to add a pair of parentheses around the
selected region (assuming your ~e~ key is bound to =embrace-add=).

Keybindings are not included here due to their length and should be configured
separately; see [[#h:95E86308-81E7-4A9F-B457-1EE8F8F80896][Keybindings]].

#+begin_src emacs-lisp
(use-package meow
:ensure t
:after-call pre-command-hook
:autoload meow-define-keys)
(use-package avy :ensure t :after meow)
(use-package embrace
:vc (:url "https://github.com/alexluigit/embrace.el")
:after meow)

(setq avy-timeout-seconds 0.4)
(setq avy-all-windows nil)
(setq avy-keys '(?a ?r ?s ?t ?n ?e ?i ?o))

(setq embrace-default-pairs
'((?r . ("(" . ")"))
(?R . ("( " . " )"))
(?c . ("{" . "}"))
(?C . ("{ " . " }"))
(?\[ . ("[" . "]"))
(?\] . ("[ " . " ]"))
(?a . ("<" . ">"))
(?A . ("< " . " >"))
(?s . ("\"" . "\""))
(?\' . ("\'" . "\'"))
(?` . ("`" . "`"))))
#+end_src

*** Keybindings
:PROPERTIES:
:CUSTOM_ID: h:95E86308-81E7-4A9F-B457-1EE8F8F80896
:END:

This section defines the core keybindings for =emacs-grandview=. Feel free to
customize them to your preferences. It's recommended to avoid commenting out
this section as it contains essential keybindings.

**** NORMAL
:PROPERTIES:
:CUSTOM_ID: h:B82246F0-CABC-48E4-AE8F-FDAAD6CFDC4F
:END:

A key difference between *Emacs* and *Vim* lies in their keybinding styles, beyond
modal editing. *Vim*, including *evil-mode*, often assigns frequently used commands
to home row keys (e.g., =j= for "move to next line"). This approach, while widely
accepted, clashes with Emacs, especially for users of non-standard keyboard
layouts like Dvorak. *Vim's* keybindings are fundamentally incompatible with
Emacs for these reasons:

+ *Vim's* philosophy contradicts Emacs's established keybinding conventions :::

While =k= for "previous line" might seem logical in isolation, in Emacs, "k"
often signifies "kill," as in =C-k= for =kill-line= or =C-c k= for
=kill-current-buffer=. This consistent concept, developed over Emacs's
history, explains why =org-kill-note-or-show-branches= and
=org-next-visible-heading= are bound to =C-c C-k= and =C-c C-n=, not the reverse.
Adapting *Vim* to *Emacs* requires endless keybinding translations across
packages.

+ The *hjkl* paradigm is keyboard-layout dependent and a flawed starting point :::

Even with a standard QWERTY layout, the natural hand position places the
fingers on *jkl;* rather than *hjkl*. This creates a dilemma: either maintain
the standard hand position and awkwardly stretch the index finger to reach
"h," or adjust the hand position to match *hjkl*. Neither option is ideal.

Emacs's keybinding style, conversely, emphasizes a clear relationship between
the key and the command. For example, =C-n= for =next-line= uses =n= in a mnemonic
way that aids understanding and recall.

The following =meow-normal-state-keymap= aligns with this principle, using =n/p= for
=meow-next/prev= and =i/o= for moving the cursor forward/backward one character
(mnemonic: move [i]n and [o]ut).

#+begin_src emacs-lisp
(meow-define-keys 'normal
'("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)
'("" . ignore)
'("" . meow-cancel-selection)
'(";" . meow-pop-selection)
'("," . meow-inner-of-thing)
'("." . meow-bounds-of-thing)
'("<" . meow-beginning-of-thing)
'(">" . meow-end-of-thing)
'("%" . +meow-insert-at-indentation)
'("-" . negative-argument)
'("=" . meow-query-replace)
'("+" . meow-query-replace-regexp)
'("^" . meow-last-buffer)
'("'" . meow-reverse)
'("a" . meow-append)
'("A" . meow-insert)
'("b" . meow-back-symbol)
'("B" . meow-block)
'("c" . meow-change)
'("C" . meow-change-save)
'("d" . meow-delete)
'("D" . meow-backward-delete)
;; TODO
;; '("e" . +meow-enclose-thing)
;; '("E" . +meow-surround-thing)
'("e" . embrace-add)
'("E" . embrace-commander)
'("f" . meow-next-symbol)
'("F" . meow-find)
'("g" . meow-grab)
'("G" . meow-sync-grab)
'("h" . mark-paragraph)
'("i" . meow-right)
'("I" . meow-right-expand)
'("j" . +meow-join-line)
'("J" . meow-join)
'("k" . meow-kill)
'("K" . meow-C-k)
'("l" . meow-line)
'("L" . +meow-mark-inner-line)
'("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" . quit-window)
'("Q" . meow-kmacro-lines)
'("r" . repeat)
'("R" . undo-redo)
'("s" . meow-search)
'("S" . meow-pop-search)
'("t" . avy-goto-char-timer)
'("T" . meow-till)
'("u" . meow-undo)
'("U" . meow-undo-in-selection)
'("v" . meow-visit)
'("w" . meow-next-word)
'("W" . meow-back-word)
'("x" . meow-save)
'("X" . meow-save-append)
'("y" . meow-yank)
'("Y" . meow-yank-pop)
'("z" . meow-pop-to-mark)
'("Z" . meow-unpop-to-mark))

(with-eval-after-load 'consult
(meow-define-keys 'normal
'("/" . consult-line)
'("V" . consult-global-mark)))
#+end_src

**** INSERT
:PROPERTIES:
:CUSTOM_ID: h:CE42BEF3-7ED7-46DF-B4AC-B32AAFBAE529
:END:

In normal mode, we use =i= / =o= key to move cursor forward or backward 1 character.
To make it consistent, we bind =C-i= and =C-o= to do the same job globally.

#+begin_src emacs-lisp
(bind! global-map
"S-" #'kill-whole-line
"" #'forward-char ; for emacs -nw
"" #'forward-char
"C-o" #'backward-char)
#+end_src

**** LEADER
:PROPERTIES:
:CUSTOM_ID: h:5D69FCEA-9C0B-4EB9-8C03-E0AF1A4EB46C
:END:

The ability to use =SPC= as a *leader* key is a key advantage of modal editing.
While a sequence like =SPC x f= requires three keystrokes, which may seem longer
than =C-x C-f=, it's objectively easier than holding the control key while
pressing another letter. Overusing modifier keys (Ctrl, Shift, Alt, etc.) in
shortcuts increases the risk of repetitive strain injury (RSI). Maximizing the
use of a leader key can help reduce RSI, especially for those who spend long
hours at the keyboard.

#+begin_src emacs-lisp
(meow-define-keys 'leader
'("0" . delete-window)
'("1" . delete-other-windows)
'("2" . split-window-below)
'("3" . split-window-right)
'(";" . comment-line)
'("," . beginning-of-buffer)
'("." . end-of-buffer)
'("r" . eval-expression) ; [r]un something
'("i" . ibuffer)
'("q" . tab-close)
(cons "4" ctl-x-4-map)
(cons "5" ctl-x-5-map)
(cons "7" ctl-x-r-map)
(cons "8" ctl-x-x-map)
(cons "TAB" tab-prefix-map)
(cons "a" grandview-app-map)
(cons "f" grandview-file-map)
(cons "p" grandview-prog-map)
(cons "t" grandview-mct-map) ; complete [t]ext
(cons "w" window-prefix-map))

(define-key grandview-file-map (kbd "p") project-prefix-map)

(with-eval-after-load 'consult
(meow-define-keys 'leader '("SPC" . consult-buffer)))
#+end_src

**** MOTION
:PROPERTIES:
:CUSTOM_ID: h:8F6D148E-53F1-4FA2-A362-7607F82AA103
:END:

+ Do not use =ESC= key as a prefix.
+ When *consult* is available, bind =/= as =consult-line= globally.

#+begin_src emacs-lisp
(meow-define-keys 'motion '("" . ignore))

(with-eval-after-load 'consult
(meow-define-keys 'motion '("/" . consult-line)))
#+end_src

*** Configuration
:PROPERTIES:
:CUSTOM_ID: h:410E6AA7-4C2A-4D99-AE39-A385CB40FCCD
:END:

Here I'm adjusting some meow's behavior that I'm not quite used to:

+ Disable meow's region expand mechanism and its visual hint.
+ When region is active, =meow-yank= behaves the same as =meow-replace=.
+ When region is inactive, =meow-save= saves a line.
+ When region is inactive, =meow-kill= kills a line.
+ When region is inactive, =meow-cancel-selection= moves cursor to the first
non-whitespace character on this line.
+ When region is inactive, =meow-reverse= moves cursor to the end of line.

#+begin_src emacs-lisp
(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) (?h . ?h)))
(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 . 2))
(setq meow-cursor-type-default 'hbar)
(setq meow-selection-command-fallback
'((meow-change . meow-change-char)
(meow-kill . meow-kill-whole-line)
(meow-cancel-selection . back-to-indentation)
(meow-pop-selection . meow-pop-grab)
(meow-beacon-change . meow-beacon-change-char)
(meow-reverse . move-end-of-line)
(meow-save . +meow-save-line)))
(setq meow-char-thing-table
'((?r . round) (?b . square) (?c . curly) (?s . string) (?e . symbol) (?w . window)
(?B . buffer) (?p . paragraph) (?d . defun) (?i . indent) (?. . sentence) (?l . line)))

(with-eval-after-load 'meow (require '+meow))

;; Finally, enable meow globally.
(meow-global-mode)
#+end_src

*** Extras ::: +meow.el
:PROPERTIES:
:CUSTOM_ID: h:3050E51B-BD81-4574-B257-232C450D998B
:header-args:emacs-lisp: :tangle (expand-file-name "+meow.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(defadvice! meow-yank-ad (fn &rest _)
"When region is active, do `meow-replace', otherwise just `meow-yank'."
:around #'meow-yank
(if (region-active-p) (meow-replace) (funcall fn)))

(defadvice! meow-search-ad (&rest _)
"Center point after search."
:after #'meow-search
(recenter))

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

(defadvice! meow-inhibit-highlight-num-positions-ad (&rest _)
"Do not show cursor movement indices."
:override #'meow--maybe-highlight-num-positions
(ignore))

(defun +meow-join-line ()
"Join the current line with the line beneath it."
(interactive)
(delete-indentation 1))

(defun +meow-mark-inner-line ()
"Mark inner line and move cursor to bol."
(interactive)
(save-window-excursion
(move-end-of-line 1)
(set-mark-command nil)
(back-to-indentation)))

(defun +meow-insert-at-indentation ()
"Insert at first non-whitespace point at current line."
(interactive)
(back-to-indentation)
(meow-insert))

(defun +meow-save-line ()
"Fallback command for `meow-save'."
(interactive)
(let ((beg (if (eobp)
(line-beginning-position 0)
(line-beginning-position)))
(end (line-beginning-position 2)))
(kill-ring-save beg end)))
#+end_src

** User interface basics
:PROPERTIES:
:CUSTOM_ID: h:110DCD38-DDFE-40D6-BC73-7DC3C229A0E7
:END:

This section contain configs in relation to basic user interface such as scroll
behavior, font settings, theme, etc.

*** Themes
:PROPERTIES:
:CUSTOM_ID: h:19F22F8C-F10D-48E0-8157-41D4836D2F6A
:END:

Recommended themes (using their package names):

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

- =doom-themes=
A megapack of popular themes, including aesthetic extensions
for popular packages.

- =ef-themes=
Yet another theme suite developed by Prot.

- =color-theme-sanityinc-tomorrow=
SanityInc tomorrow theme.

#+begin_src emacs-lisp
(use-package modus-themes :ensure nil)
(use-package ef-themes :ensure t)
(use-package doom-themes :ensure t)
(use-package color-theme-sanityinc-tomorrow :ensure t)

(with-eval-after-load 'ef-themes
(setq ef-themes-to-toggle '(ef-day ef-winter)
ef-themes-mixed-fonts t
ef-themes-variable-pitch-ui t))

(with-eval-after-load 'modus-themes
(setq modus-themes-common-palette-overrides
'((underline-link unspecified)
(underline-link-visited unspecified)
(underline-link-symbolic unspecified))))

(with-eval-after-load 'outline
(+theme-outline-headline-setup)
(add-hook 'enable-theme-functions #'+theme-outline-headline-setup))

(add-hook 'after-init-hook (lambda () (load-theme grandview-custom-theme t)))
#+end_src

**** Extras ::: +theme.el
:PROPERTIES:
:CUSTOM_ID: h:E3066C60-3548-40B4-BEB2-1C05972EBCA0
:header-args:emacs-lisp: :tangle (expand-file-name "+theme.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +theme-outline-headline-setup (&optional _theme)
"Setup headline heights for `outline'."
(cl-loop for idx in '(1 2 3 4)
for face = (intern (format "outline-%s" idx))
for height = (nth (1- idx) grandview-custom-headline-sizes)
do (set-face-attribute face nil :height height)))
#+end_src

*** Fonts and icons
:PROPERTIES:
:CUSTOM_ID: h:B50A7C8A-7553-46CF-92B8-56865009C681
:END:

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=

Please make sure the font you choose can be recognized by Emacs, you can varify
that by evaluating:

#+begin_src emacs-lisp :tangle no
(member "Sarasa Mono SC" (font-family-list))
#+end_src

Setup fonts.

#+begin_src emacs-lisp
(+font-setup)
(with-eval-after-load '+font
;; add the hook AFTER `grandview-custom-theme' is enabled, it is just faster
(add-hook 'enable-theme-functions #'+font-setup))
#+end_src

***** Extras ::: +font.el
:PROPERTIES:
:CUSTOM_ID: h:38943ED3-0842-4327-9998-3E331FAA76DB
:header-args:emacs-lisp: :tangle (expand-file-name "+font.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +font-setup (&optional _theme)
"Setup default/fixed-pitch/variable-pitch/zh-font."
(cl-loop
with font-families = (font-family-list)
for font in (list grandview-custom-default-font
grandview-custom-fixed-font
grandview-custom-variable-font)
for name in '(default fixed-pitch variable-pitch)
if (member font font-families) do
(set-face-attribute
name nil :font font :height grandview-custom-font-size)
else do (message "Font `%s' is not available" font)
finally
(progn
(set-face-attribute
'font-lock-keyword-face nil :slant 'italic)
(set-face-attribute
'font-lock-variable-name-face nil :weight 'demibold)
(set-face-attribute
'font-lock-function-name-face nil :weight 'demibold)
(if (member grandview-custom-cjk-font font-families)
(dolist (charset '(kana han cjk-misc bopomofo))
(set-fontset-font
t charset (font-spec :family grandview-custom-cjk-font)))
(message "Font `%s' is not available" grandview-custom-cjk-font)))))

;;;###autoload
(defun +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 +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)))
#+end_src

**** Icon library (nerd-icons.el)
:PROPERTIES:
:CUSTOM_ID: h:14F5303D-F1A6-48D0-83AF-E4F8F34EC946
:END:

#+begin_src emacs-lisp
(use-package nerd-icons :ensure t)

(require 'nerd-icons)
(setq nerd-icons-font-family "Iosevka Nerd Font")
(when (memq system-type '(ms-dos cygwin windows-nt))
(setq nerd-icons-font-family "Iosevka NF"))
#+end_src

*** Frame display
:PROPERTIES:
:CUSTOM_ID: h:F715736C-9299-489F-96A4-C43C649C883A
:END:

- Remove title bar on macOS
- Enter fullscreen automatically on macOS
- Run garbage collector when emacs is out focus

#+begin_src emacs-lisp
(when (eq system-type 'darwin)
(add-to-list 'default-frame-alist '(undecorated . t))
(add-to-list 'default-frame-alist '(fullscreen . maximized)))

(add-hook 'window-configuration-change-hook #'+frame-opacity-auto)

(add-function :after after-focus-change-function
(lambda () (unless (frame-focus-state)
(garbage-collect))))

(bind! grandview-app-map "f" #'toggle-frame-maximized)
#+end_src

**** Frame margin (fringe.el)
:PROPERTIES:
:CUSTOM_ID: h:55DA6FDA-C0E0-44A3-8335-DD6D5D5C18E1
:END:

+ Redefine word wrap arrow at fringe
+ Create a 20 pixel margin for emacs frame

#+begin_src emacs-lisp
(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))
#+end_src

**** Frame opacity
:PROPERTIES:
:CUSTOM_ID: h:DEEA194F-9E63-45BF-98FA-0E5A44D03023
:END:

This module provides the =+frame-opacity-set= command to change the frame opactity
on the fly. It is useful when I want to see other application's windows that
are beneath emacs frames.

#+begin_src emacs-lisp
(bind! grandview-app-map "o" #'+frame-opacity-set)
#+end_src

***** Extras ::: +frame-opacity.el
:PROPERTIES:
:header-args:emacs-lisp: :tangle (expand-file-name "+frame-opacity.el" grandview--def-dir)
:CUSTOM_ID: h:AC3B6E27-5E40-4B4C-A40C-E1DD1CEE0E61
:END:

#+begin_src emacs-lisp
(defcustom +frame-opacity 100
"Default frame opacity."
:group 'grandview
:type 'integer)

(defun +frame-opacity--adjust (opacity)
(pcase system-type
('darwin (set-frame-parameter nil 'alpha `(,opacity ,opacity)))
(_ (set-frame-parameter nil 'alpha-background opacity))))

;;;###autoload
(defun +frame-opacity-auto ()
"Setup frame opacity according to current major-mode."
(+frame-opacity--adjust +frame-opacity))

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

**** Less distracting cursor
:PROPERTIES:
:CUSTOM_ID: h:A926706F-208A-445E-9ED3-018000FF921C
:END:

In some scenario the blinking cursor is quite annoying. Use
=+frame-cursor-dim-mode= to temporarily use a almost invisible cursor.

***** Extras ::: +frame-cursor-dim.el
:PROPERTIES:
:header-args:emacs-lisp: :tangle (expand-file-name "+frame-cursor-dim.el" grandview--def-dir)
:CUSTOM_ID: h:FDEE294E-7DB1-4D72-B7D4-C062203E31D8
:END:

#+begin_src emacs-lisp
(defvar +frame-cursor-saved-color
(frame-parameter nil 'cursor-color))

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

;;;###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))
(setq-local cursor-type meow-cursor-type-default)
(blink-cursor-mode +1)
(set-cursor-color +frame-cursor-saved-color)))
#+end_src

*** Scrolling behavior
:PROPERTIES:
:CUSTOM_ID: h:33978C23-2375-4C80-9D5B-E78BCCB7F53E
:END:

Some tweaks for scrolling behaviors:

+ Show current key strokes in echo area after 0.25s
+ Disable bidirectional text scanning for a modest performance boost.
+ 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).

#+begin_src emacs-lisp
(setq-default bidi-display-reordering 'left-to-right)
(setq-default bidi-paragraph-direction 'left-to-right)
(setq bidi-inhibit-bpa t)
(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)
#+end_src

*** Transient commands (transient.el)
:PROPERTIES:
:CUSTOM_ID: h:6C19254F-E941-4E51-A443-3F8A7D796589
:END:

=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 inbuilt with Emacs 28+.

#+begin_src emacs-lisp
(use-package transient :ensure nil)

(with-eval-after-load 'transient
(setq transient-enable-popup-navigation nil)
(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))
(define-key transient-map (kbd "") 'transient-quit-all)
(define-key transient-sticky-map (kbd "ESC") 'transient-quit-all))
#+end_src

** Use Emacs like a Desktop Environment
:PROPERTIES:
:CUSTOM_ID: h:E630E73C-36CF-4530-AB9F-C45176D23EFD
:END:

*** Password management (epg.el)
:PROPERTIES:
:CUSTOM_ID: h:AF952C02-1FB3-4AED-B612-E7F92F0DF00B
:END:

I use *gnupg* as my password manager and ssh authentication tool, integrate them
with emacs is rather simple, no external library required.

#+begin_src emacs-lisp
(use-package epg :ensure nil)
(use-package password-store :ensure nil)

(setq auth-sources '("~/.local/share/authinfo.gpg"))
(setq epg-pinentry-mode 'loopback)
#+end_src

In addition, setup *gpg-agent* like this:

#+begin_src conf :tangle no
# $GNUPGHOME/gpg-agent.conf

allow-emacs-pinentry
# on Mac OS
pinentry-program /usr/local/bin/pinentry-mac
#+end_src

see: [[https://emacs.stackexchange.com/questions/32881/enabling-minibuffer-pinentry-with-emacs-25-and-gnupg-2-1-on-ubuntu-xenial][Ref]]

*** Buffer management (ibuffer.el)
:PROPERTIES:
:CUSTOM_ID: h:0A906F33-AF6F-4265-813E-78B8D76B0FA5
:END:

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

#+begin_src emacs-lisp
(use-package ibuffer :ensure nil)

(with-eval-after-load 'ibuffer
(add-hook 'ibuffer-mode-hook
(lambda () (hl-line-mode) (ibuffer-update 0)))
(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)
(bind! ibuffer-mode-map
"* 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))
#+end_src

*** Window management
:PROPERTIES:
:CUSTOM_ID: h:1A9091D5-B8A4-4135-9415-9823ABF0AF3C
:END:

Manage windows in Emacs.

**** Window placement
:PROPERTIES:
:CUSTOM_ID: h:72ECA0DF-D43C-4E28-92A9-CB830B99B85A
:END:

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.

=SPC w o= and =s-o= to switch focus other window.

#+begin_src emacs-lisp
(use-package window :ensure nil)

(with-eval-after-load 'window
(setq-default cursor-in-non-selected-windows nil)
(setq highlight-nonselected-windows nil)
(setq display-buffer-alist
`(("\\*\\(Flymake\\|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))
("\\*compilation\\*"
(display-buffer-in-side-window)
(window-height . 0.2)
(side . 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))
;; there are situations when we want to switch windows while typing
(define-key global-map (kbd "s-o") 'other-window)

(bind! window-prefix-map
"k" #'delete-window
"1" #'delete-other-windows
"f" #'fit-window-to-buffer
"o" #'other-window)
#+end_src

**** Adjust window size
:PROPERTIES:
:CUSTOM_ID: h:16A1B935-F687-42D1-A3D1-EED84A7A493D
:END:

=window.el= provides 4 commands to adjust the size of a window. Prefix them with
1-9 to do the adjustment multiple times at once. Also don't forget that =r= in
normal mode can =repeat= the last command, which may save a lot of key strokes for
you.

#+begin_src emacs-lisp
(bind! window-prefix-map
"-" #'shrink-window
"=" #'enlarge-window
"," #'shrink-window-horizontally
"." #'enlarge-window-horizontally)
#+end_src

**** Window reorder (windmove.el)
:PROPERTIES:
:CUSTOM_ID: h:D0621A73-5618-478E-B8C9-59A798B3BA15
:END:

Directional window-selection routines. Thanks to =meow-keypad-mode=, the actual
keystroke for this binding is =SPC w d/u/r/l= (check out *KEYPAD AND MOTION MODE*
section in =meow-tutor=).

#+begin_src emacs-lisp
(use-package windmove :ensure nil :after-call split-window)

(bind! window-prefix-map
"t" #'toggle-window-dedicated ; restore this command
"d" #'windmove-swap-states-down
"u" #'windmove-swap-states-up
"r" #'windmove-swap-states-right
"l" #'windmove-swap-states-left)
#+end_src

**** Maximize | restore window size
:PROPERTIES:
:CUSTOM_ID: h:5BE7214A-B950-4F9B-951E-C45882829C0F
:END:

A command to maximize / restore a window.

#+begin_src emacs-lisp
(define-key window-prefix-map (kbd "w") #'+window-monocle-mode)
#+end_src

***** Extras ::: +window-monocle.el
:PROPERTIES:
:CUSTOM_ID: h:7BAA0631-CBD3-4BED-BB6B-D6BE35BDBA73
:header-args:emacs-lisp: :tangle (expand-file-name "+window-monocle.el" grandview--def-dir)
:END:

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

;;;###autoload
(define-minor-mode +window-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 +window-monocle--saved-window-configuration)
(buf (current-buffer)))
(if (one-window-p)
(when config
(set-window-configuration config))
(setq +window-monocle--saved-window-configuration (current-window-configuration))
(when (window-parameter nil 'window-side) (delete-window))
(delete-other-windows)
(switch-to-buffer buf))))
#+end_src

*** Running Emacs as server process (server.el)
:PROPERTIES:
:CUSTOM_ID: h:0CA633D0-896A-4396-BF65-4C6807A5FE16
:END:

This library allows Emacs to operate as a server for other processes.

During init, we enable Emacs as a server unless there is a running server.
Emacs opens up a socket for communication with clients. If there are no client
buffers to edit, =server-edit= acts like =(switch-to-buffer (other-buffer))=.

When some other program runs "the editor" to edit a file, "the editor" can be
the Emacs client program =emacsclient=. This program transmits the file names to
Emacs through the server subprocess, and Emacs visits them and lets you edit
them.

Note that any number of clients may dispatch files to Emacs to be edited.

#+begin_src emacs-lisp
(use-package server :ensure nil :after-call after-init-hook)

(with-eval-after-load 'server
(require 'server)
(unless (server-running-p) (server-start))
(select-frame-set-input-focus (selected-frame)))
#+end_src

*** COMMENT Persistent sessions (desktop.el)
:PROPERTIES:
:CUSTOM_ID: h:FD3E91E4-0457-47EA-B98E-E8D0ECD7524F
:END:

#+begin_src emacs-lisp
(desktop-save-mode 1)
#+end_src

* Completion framework
:PROPERTIES:
:CUSTOM_ID: h:50E17583-1B94-4453-83B8-6E05260EA03D
:END:

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.

Auto-completion, especially in the minibuffer, is essential for this setup.
While not a core component of *grandview* — other completion UIs like Ivy can be
substituted without breaking the setup — this section should generally not be
disabled. Debugging Emacs often involves manually entering commands in the
minibuffer, making this functionality crucial. Feel free to adjust the
configurations within this section as needed.

** Setup minibuffer
:PROPERTIES:
:CUSTOM_ID: h:CDA5E090-037A-4CCB-BA10-215137E0DB8B
:END:
*** Minibuffer and completion functions (minibuffer.el)
:PROPERTIES:
:CUSTOM_ID: h:97388408-518C-4774-968C-23C71CFE8599
:END:

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.

#+begin_src emacs-lisp
(use-package minibuffer :ensure nil)
(use-package minibuf-eldef :ensure nil)

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

(bind! minibuffer-local-map
"S-" 'kill-whole-line
"" 'exit-minibuffer
"" 'forward-char
"" 'forward-char
"C-o" 'backward-char)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:2DA0D02F-765C-47AE-8118-A4CA24FE9CB5
:header-args:emacs-lisp: :tangle (expand-file-name "+minibuffer.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###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)))))
#+end_src

*** Minibuffer history (savehist.el)
:PROPERTIES:
:CUSTOM_ID: h:F88C364F-4C13-4C6E-B9A1-20F6CFBDC24F
:END:

Keeps a record of actions involving the minibuffer.

#+begin_src emacs-lisp
(use-package savehist :ensure nil :after-call minibuffer-setup-hook)

(with-eval-after-load 'savehist
(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))
#+end_src

** Completion in minibuffer
:PROPERTIES:
:CUSTOM_ID: h:40B8F500-A2B8-4569-9B32-B8D36369F518
:END:
*** Vertical completion candidates (vertico.el)
:PROPERTIES:
:CUSTOM_ID: h:0ABB48B0-A43D-4F71-9B2E-5B0C885B82FD
:END:

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.

#+begin_src emacs-lisp
(use-package vertico :ensure t :after-call pre-command-hook)

(with-eval-after-load 'vertico (vertico-mode 1))
#+end_src

*** Match candidates made easy (orderless.el)
:PROPERTIES:
:CUSTOM_ID: h:C20C20F0-CA95-4CC3-BCF3-42525A72A99F
:END:

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.

#+begin_src emacs-lisp
(use-package pinyinlib :ensure t)
(use-package orderless :ensure t)

(with-eval-after-load 'vertico
(autoload 'pinyinlib-build-regexp-string "pinyinlib")
(setq completion-styles '(orderless partial-completion basic))
(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)))
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:60693C7D-8538-4B1C-83B2-E79C21E72A20
:header-args:emacs-lisp: :tangle (expand-file-name "+orderless.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(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 suffix."
(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))))
#+end_src

*** Useful commands using completion (consult.el)
:PROPERTIES:
:CUSTOM_ID: h:BD952A54-C221-4AE9-BEEC-4B0F24272F33
:END:

Consult implements a set of =consult-= 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.

The purpose for setting =consult-async-min-input= to 2 is to adapt for Chinese
word, which usually consists of only 2 characters.

As of keybindings, I bind some frequently used consult commands onto the
=grandview-mct-map= directly for quick access, while making sure the others are
accessiable under its child keymap =+consult-prefix-=map=.

#+begin_src emacs-lisp
(use-package consult :ensure t :after-call vertico-mode)

(with-eval-after-load 'consult
(setq completion-in-region-function #'consult-completion-in-region)
(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-line-numbers-widen t)
(setq consult-async-min-input 2)
(setq consult-async-input-debounce 0.5)
(setq consult-async-input-throttle 0.8)
(setq consult-narrow-key ">")

(bind! grandview-mct-map
"e" 'consult-compile-error
"f" 'consult-flymake
"r" 'consult-ripgrep
"k" 'consult-kmacro
"i" 'consult-imenu
"o" 'consult-outline
"y" 'consult-yank-from-kill-ring
"m" 'consult-bookmark)

(defvar-keymap +consult-prefix-map
:doc "Keymap for `consult' commands."
"a" 'consult-org-agenda
"h" 'consult-org-heading
"l" 'consult-line
"L" 'consult-line-multi
"m" 'consult-mark
"v" 'consult-global-mark ; [v]isited
"e" 'consult-compile-error
"f" 'consult-flymake
"k" 'consult-kmacro
"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
"c" 'consult-complex-command
"C" 'consult-mode-command
"M" 'consult-minor-mode-menu)

(define-key grandview-mct-map (kbd "t") +consult-prefix-map))
#+end_src

*** Candidate annotation (marginalia.el)
:PROPERTIES:
:CUSTOM_ID: h:A08A5E53-E279-4950-AA96-09022CF50A0F
:END:

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.

#+begin_src emacs-lisp
(use-package marginalia :ensure t)

(with-eval-after-load 'vertico
(marginalia-mode)
(setq marginalia-align 'left))
#+end_src

** Completion in regular buffers
:PROPERTIES:
:CUSTOM_ID: h:69DD7D9B-3D1E-4CB4-933B-7E83FDC30538
:END:
*** Completion overlay region function (corfu.el)
:PROPERTIES:
:CUSTOM_ID: h:908C6FBC-B6C2-43D3-B696-859B32DAF6ED
:END:

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

#+begin_src emacs-lisp
(use-package corfu :ensure t :after-call self-insert-command)

(with-eval-after-load 'corfu
(setq corfu-auto t)
(setq corfu-auto-delay 0.05)
(setq corfu-cycle t)
(setq corfu-preselect 'prompt)
(setq corfu-on-exact-match nil)
(global-corfu-mode))
#+end_src

*** Completion at point Extensions (cape.el)
:PROPERTIES:
:CUSTOM_ID: h:E3567F74-AFCB-43EE-9A86-7FBF740CFDE3
:END:

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

#+begin_src emacs-lisp
(use-package cape :ensure t :after-call meow-insert-mode-hook)

(with-eval-after-load 'cape
(setq cape-dict-file "/usr/share/dict/words")

(when (>= emacs-major-version 30)
(setopt text-mode-ispell-word-completion nil))

(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-keyword)
(add-hook 'completion-at-point-functions #'cape-dict)

(define-key grandview-mct-map (kbd "c") cape-prefix-map))
#+end_src

*** Snippet system (tempel.el)
:PROPERTIES:
:CUSTOM_ID: h:B5025BD7-D74F-4DCF-BA55-1B1E8BD42106
:END:

*Tempel* provides a basic template/snippet system, using a format compatible with
Emacs' *Tempo* library.

"Tempel is a tiny template package for Emacs, which uses the syntax of the Emacs
Tempo library. Tempo is an ancient temple of the church of Emacs. It is 30 years
old, but still in good shape since it successfully resisted change over the
decades. However it may look a bit dusty here and there. Therefore we present
Tempel, a new implementation of Tempo with inline expansion and integration with
recent Emacs facilities. Tempel takes advantage of the standard
completion-at-point-functions mechanism which is used by Emacs for in-buffer
completion."

-- From https://github.com/minad/tempel

#+begin_src emacs-lisp
(use-package tempel :ensure t :after-call self-insert-command)

(defvar-keymap +tempel-prefix-map
:doc "Kepmap for tempel commands."
"i" #'tempel-insert
"k" #'tempel-kill
"a" #'tempel-abort)

;; [s]nippet
(define-key grandview-mct-map (kbd "s") +tempel-prefix-map)

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

(add-hook 'prog-mode-hook '+tempel-setup-capf)
(add-hook 'text-mode-hook '+tempel-setup-capf)

(with-eval-after-load 'eglot (eglot-tempel-mode))
#+end_src

**** Integrate with eglot (eglot-tempel)
:PROPERTIES:
:CUSTOM_ID: h:67E4ADAF-79BB-444B-B187-8C0B22D64ED4
:END:

*Eglot-tempel* is introduced to translate snippets from other systems into the
*Tempel* format, enabling seamless integration with eglot's autocompletion.

#+begin_src emacs-lisp
(use-package eglot-tempel
:vc (:url "https://github.com/fejfighter/eglot-tempel"))

(with-eval-after-load 'eglot (eglot-tempel-mode))
#+end_src

*** Templates
:PROPERTIES:
:CUSTOM_ID: h:160AE5D1-274F-432D-B5BF-DA1DE00A889F
:END:

This section defines snippet templates for different file types with optional
conditions. 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 )~ 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 )~ Insert region or act like ~(p ...)~.
- ~(r> PROMPT )~ Act like ~(r ...)~, but indent region.

Furthermore *Tempel* supports syntax extensions:

- ~(p FORM )~ Like ~p~ described above, but ~FORM~ is evaluated.
- ~(FORM ...)~ Other Lisp forms are evaluated. Named fields are lexically bound.
- ~q~ Quits the containing template when jumped to.

Use caution with templates which execute arbitrary code!

We use separate code blocks for templates of different programming languages and
text formats to make them easier to locate within the document.

**** Universal
:PROPERTIES:
:CUSTOM_ID: h:1AAF20BE-D986-45F7-AE1C-E5206FE96D72
:END:

#+begin_src lisp :tangle (expand-file-name "templates" user-emacs-directory)
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 ")
#+end_src

**** Text
:PROPERTIES:
:CUSTOM_ID: h:76034888-E06F-469B-AFE1-F803830381C6
:END:

#+begin_src lisp :tangle (expand-file-name "templates" user-emacs-directory)
text-mode

(box "┌─" (make-string (length str) ?─) "─┐" n
"│ " (s str) " │" n
"└─" (make-string (length str) ?─) "─┘" n)
(abox "+-" (make-string (length str) ?-) "-+" n
"| " (s str) " |" n
"+-" (make-string (length str) ?-) "-+" n)
(cut "--8<---------------cut here---------------start------------->8---" n r n
"--8<---------------cut here---------------end--------------->8---" 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)))
(table (p (read-number "Rows: ") rows noinsert)
(p (read-number "Cols: ") cols noinsert)
"| " (p " ") (* (1- cols) " | " (p " ")) " |" n
"|" (* cols "----|") n
(* rows "| " (p " ") (* (1- cols) " | " (p " ")) " |" n))

rst-mode

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

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}")
#+end_src

**** Org mode
:PROPERTIES:
:CUSTOM_ID: h:CB189D93-B839-4E32-AFF2-7AED445FD52D
:END:

#+begin_src lisp :tangle (expand-file-name "templates" user-emacs-directory)
org-mode :when (and (org-in-src-block-p)
(string= (org-element-property
:language (org-element-context))
"emacs-lisp"))

(defk "(define-key " p " (kbd \"" p "\") " p ")")
(use- "(use-package " p " ensure: " p ")")
(lambda "(lambda (" p ")" n> r> ")")
(autoload ";;;###autoload")

org-mode

(caption "#+caption: ")
(drawer ":" p ":" n r ":end:")
(begin "#+begin_" (s name) n> r> n "#+end_" name)
(quote "#+begin_quote" n> r> n "#+end_quote")
(sidenote "#+begin_sidenote" n> r> n "#+end_sidenote")
(marginnote "#+begin_marginnote" n> r> n "#+end_marginnote")
(example "#+begin_example" n> r> n "#+end_example")
(center "#+begin_center" n> r> n "#+end_center")
(ascii "#+begin_export ascii" n> r> n "#+end_export")
(html "#+begin_export html" n> r> n "#+end_export")
(latex "#+begin_export latex" n> r> n "#+end_export")
(comment "#+begin_comment" n> r> n "#+end_comment")
(verse "#+begin_verse" n> r> n "#+end_verse")
(src "#+begin_src " q n r n "#+end_src")
(gnuplot "#+begin_src gnuplot :var data=" (p "table") " :file " (p "plot.png") n r n "#+end_src" :post (org-edit-src-code))
(elisp "#+begin_src emacs-lisp" n r n "#+end_src")
(eelisp "#+begin_src emacs-lisp" n r n "#+end_src" :post (org-edit-src-code))
(inlsrc "src_" p "{" q "}")
(title "#+title: " p n "#+author: Alex Lu" n "#+language: en")
#+end_src

**** Lisp | Emacs Lisp
:PROPERTIES:
:CUSTOM_ID: h:8E3BDD9B-929E-4210-AE34-15803E5E95EB
:END:

#+begin_src lisp :tangle (expand-file-name "templates" user-emacs-directory)
lisp-mode emacs-lisp-mode ;; Specify multiple modes

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

emacs-lisp-mode

(use- "(use-package " p ")")
(lambda "(lambda (" p ")" n> r> ")")
(autoload ";;;###autoload")
(point "(point)")
(defv "(defvar " p "\n \"" p "\")")
(local "(defvar-local " 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> ")")
(alias "(defalias '" p " '" p ")")
(defun "(defun " p " (" p ")\n \"" p "\"" n> r> ")")
(iflet "(if-let* (" p ")" n> r> ")")
(whenlet "(when-let* (" p ")" n> r> ")")
(whilelet "(while-let (" p ")" n> r> ")")
(andlet "(and-let* (" p ")" n> r> ")")
(cond "(cond (" p ")" n> "()" n> "()" ")")
(pcase "(pcase " (p "scrutinee") n "(" q "))" >)
(lett "(let (" p ")" n> r> ")")
(lettt "(let* (" p ")" n> r> ")")
(pcaselet "(pcase-let (" p ")" n> r> ")")
(pcaselett "(pcase-let* (" p ")" n> r> ")")
(letrec "(letrec (" p ")" n> r> ")")
(dotimes "(dotimes (" p ")" n> r> ")")
(dolist "(dolist (" p ")" n> r> ")")
(loop "(cl-loop for " p " in " p " do" n> r> ")")
(command "(defun " p " (" p ")\n \"" p "\"" n> "(interactive" p ")" n> r> ")")
(header ";;; " (file-name-nondirectory (or (buffer-file-name) (buffer-name)))
" --- " p " -*- lexical-binding: t -*-" n
";;; Commentary:" n ";;; Code:" n n)
(provide "(provide '" (file-name-base (or (buffer-file-name) (buffer-name))) ")" n
";;; " (file-name-nondirectory (or (buffer-file-name) (buffer-name)))
" ends here" n)
(package (i header) r n n (i provide))

eshell-mode

(for "for " (p "i") " in " p " { " q " }")
(while "while { " p " } { " q " }")
(until "until { " p " } { " q " }")
(if "if { " p " } { " q " }")
(ife "if { " p " } { " p " } { " q " }")
(unl "unless { " p " } { " q " }")
(unle "unless { " p " } { " p " } { " q " }")
#+end_src

**** C
:PROPERTIES:
:CUSTOM_ID: h:9640E798-2D69-47B7-95C4-A0B78A49E934
:END:

#+begin_src lisp :tangle (expand-file-name "templates" user-emacs-directory)
c-mode :when (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")) "\"")
#+end_src

* File management
:PROPERTIES:
:CUSTOM_ID: h:2F4217E3-BF28-4F5D-B8F6-389562DF634A
:END:
** Working with directories
:PROPERTIES:
:CUSTOM_ID: h:92C67806-B5D1-47BB-87D9-3E62A5D6615B
:END:
*** Dired - DIRectory EDitor (dired.el)
:PROPERTIES:
:CUSTOM_ID: h:6A72D4DB-61C0-418E-98C2-56FE0DB68E91
:END:

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

#+begin_src emacs-lisp
(use-package dired :ensure nil :after-call pre-command-hook)

(when (eq system-type 'darwin) (setq insert-directory-program "gls"))
(setq dired-listing-switches
"-l --almost-all --human-readable --time-style=long-iso --group-directories-first --no-group")

(with-eval-after-load 'dired
(setq mouse-1-click-follows-link nil)
(setq dired-mouse-drag-files t) ; added in Emacs 29
(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-bind-info nil)
(setq dired-bind-man nil)
(setq dired-clean-confirm-killing-deleted-buffers nil)
(setq dired-do-revert-buffer t)
(setq dired-auto-revert-buffer #'dired-directory-changed-p)
(bind! dired-mode-map
"" 'dired-mouse-find-file ; right click for opening file / entering dir
"" 'dired-do-shell-command ; side button for shell command execution
"/" 'dired-goto-file
"," 'dired-create-directory
"." 'dired-create-empty-file
"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))
#+end_src

**** Extra Dired functionality
:PROPERTIES:
:CUSTOM_ID: h:C992F838-A4BB-4AA5-9331-147D081D8FE5
:END:

At load time *dired-x.el* will install itself and bind some Dired keys. Some
*dired.el* and *dired-aux.el* functions have extra features if *dired-x* is loaded.

#+begin_src emacs-lisp
(use-package dired-x :ensure nil)
(use-package dired-aux :ensure nil)

(with-eval-after-load 'dired-x
(setq dired-omit-files (concat dired-omit-files "\\|^\\..*$")))
#+end_src

**** Font lock rules for Dired buffers (diredfl.el)
:PROPERTIES:
:CUSTOM_ID: h:89FB8551-2C36-42FD-B4DE-CF2EB706D18E
:END:

*Diredfl* enhances dired with additional font-lock rules for a more colorful
display. We've customized the ~diredfl-dir-name~ face to render directory entries
in bold, distinguishing them from file entries.

#+begin_src emacs-lisp
(use-package diredfl :ensure t)

(with-eval-after-load 'dired
(add-hook 'dired-mode-hook 'diredfl-mode))

(with-eval-after-load 'diredfl
(set-face-attribute 'diredfl-dir-name nil :bold t)
(add-hook 'enable-theme-functions
(lambda (_theme)
(set-face-attribute 'diredfl-dir-name nil :bold t))))
#+end_src

*** Batch rename files made easy (wdired.el)
:PROPERTIES:
:CUSTOM_ID: h:39A09526-7F38-4DA8-BE81-DC02D32FDC58
:END:

Bulk renaming files like a breeze.

#+begin_src emacs-lisp
(use-package wdired :ensure nil)

(with-eval-after-load 'dired
(setq wdired-allow-to-change-permissions t)
(setq wdired-create-parent-directories t)
(define-key dired-mode-map (kbd "i") 'wdired-change-to-wdired-mode))
#+end_src

*** Browse images using Dired (image-dired.el)
:PROPERTIES:
:CUSTOM_ID: h:398861A3-9FBA-43EA-A54F-3C06C4902C7D
:END:

=image-dired= allows us to browse and manipulate images using Dired.

+ show bigger sized thumbnail image, we are in the 21st century
+ do not display original image in other window when flag/mark files
It's very slow, for image preview, I use =dirvish= instead.
+ tweak the keybindings to my preferences

#+begin_src emacs-lisp
(use-package image-dired :ensure nil)

(with-eval-after-load 'image-dired
(setq image-dired-thumb-size 256)
(setq image-dired-marking-shows-next nil)
(bind! image-dired-thumbnail-mode-map
"n" 'image-dired-next-line
"p" 'image-dired-previous-line
"i" 'image-dired-forward-image
"o" 'image-dired-backward-image))
#+end_src

*** A polished dired with batteries included (dirvish.el)
:PROPERTIES:
:CUSTOM_ID: h:AAFF178E-C115-4A55-8B8F-BE287515CDCE
:END:

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
[[https://github.com/ranger/ranger][ranger]], [[https://github.com/vifm/vifm][vifm]], [[https://github.com/gokcehan/lf][lf]] since Emacs is more than a terminal emulator.

#+begin_src emacs-lisp
;; (use-package dirvish :ensure t)

;; for maintenance purpose
(use-package dirvish
:vc (:url "https://github.com/alexluigit/dirvish" :lisp-dir "extensions/")
:init
(autoload 'dirvish-layout-toggle "dirvish-extras")
(add-to-list 'load-path "~/.cache/emacs/elpa/dirvish")
(require 'dirvish)
(require 'dirvish-extras))

(with-eval-after-load 'dirvish
(dirvish-override-dired-mode)
(dirvish-side-follow-mode)
(dirvish-peek-mode))
#+end_src

**** Look and feel
:PROPERTIES:
:CUSTOM_ID: h:D63746B7-A408-41A8-8AA7-9043B27C6CA8
:END:

Here are some customizations about the appearance of *dirvish*.

+ show file attributes on file lines.
+ display necessary information on mode line and header line.
+ other prettifications

#+begin_src emacs-lisp
(with-eval-after-load 'dirvish
(add-hook 'dirvish-setup-hook 'dirvish-emerge-mode)
(add-hook 'dirvish-directory-view-mode-hook 'diredfl-mode)
(setq dirvish-attributes
'(vc-state file-size git-msg subtree-state nerd-icons collapse file-time))
(setopt dirvish-subtree-state-style 'nerd)
(setq dirvish-mode-line-format '(:left (sort symlink) :right (vc-info yank index)))
(setq dirvish-header-line-height '(25 . 35))
(setq dirvish-mode-line-height 21)
(setq dirvish-side-width 38)
(setq dirvish-header-line-format '(:left (path) :right (free-space)))
(setq dirvish-path-separators (list " " " " "  ")))
#+end_src

**** Quick access
:PROPERTIES:
:CUSTOM_ID: h:579DBE58-EA09-4EF3-9016-75D2E91C366E
:END:

These are quick access entries for the ~dirvish-quick-access~ command, which can
be called inside or outside of *Dirvish*.

#+begin_src emacs-lisp
(with-eval-after-load 'dirvish-quick-access
(setopt dirvish-quick-access-entries
'(("o" "~/" "Home")
("d" "/opt/dotfiles/" "Dotfiles")
("u" "~/.cache/emacs/" "Emacs cache")
("p" "~/Code/" "Code")
("n" "~/Downloads/" "Downloads")
("w" "~/Pictures/wallpaper/" "Wallpaper")
("m" "/mnt/" "Mounted Drives")
("s" "/mnt/HDD/Share/" "Shared files")
("a" "🔍\\\.org$📁~/Documents/📁" "All Notes") ; this requires `dirvish-fd'
("t" "~/.local/share/Trash/files/" "Trash")
("W" "/smb:alex%[email protected]:share/"))))
#+end_src

**** Shortcuts
:PROPERTIES:
:CUSTOM_ID: h:88A15EC2-44EA-4201-8017-0219988B8F6F
:END:

A child keymap, =dirvish-prefix-map=, is created under `grandview-file-map` to
open *Dirvish* in different forms.

*Dirvish* improves file viewing, navigation, and management by adding features and
commands. I bind these to =dirvish-mode-map=, which inherits from =dired-mode-map=,
for convenient access.

#+Begin_src emacs-lisp
(defvar-keymap +dirvish-prefix-map
:doc "Keymap for `dirvish' commands."
"e" 'dirvish ; dir [e]ditor
"f" 'dirvish-fd
"n" 'dirvish-side
"a" 'dirvish-quick-access
"j" 'dirvish-fd-jump)
(define-key grandview-file-map (kbd "e") +dirvish-prefix-map)
(bind! mode-specific-map "e" 'dirvish-dwim)

(with-eval-after-load 'dirvish
(bind! dirvish-mode-map
"" 'dirvish-subtree-toggle-or-open ; left click for expand/collapse dir or open file
"s-n" 'dirvish-history-go-forward
"s-p" 'dirvish-history-go-backward
"h" 'dirvish-history-jump
"^" 'dirvish-history-last
"TAB" 'dirvish-subtree-toggle
"a" 'dirvish-quick-access
"f" 'dirvish-file-info-menu
"v" 'dirvish-vc-menu
"*" 'dirvish-mark-menu
"N" 'dirvish-narrow
"s-e" 'dirvish-emerge-menu
"s-t" 'dirvish-layout-toggle
"s-s" 'dirvish-setup-menu
"s-j" 'dirvish-fd-jump
"s" 'dirvish-quicksort
"l" 'dirvish-ls-switches-menu
"y" 'dirvish-yank-menu))
#+end_src

*** Project management (project.el)
:PROPERTIES:
:CUSTOM_ID: h:29675BEC-05AD-4591-8D74-B7284820D7FC
:END:

In Emacs, a *project* is a collection of files and directories sharing a common
root, identified by a special file or directory. By default, ~.git/~ directories
are recognized as project roots due to built-in support for Git via =vc.el=.

To switch projects, use =SPC p p= (=project-switch-project=). This command presents
a list of registered projects and an option to choose a new directory. If a
directory with a recognizable root is selected, it's added to the project
list. After selecting a project, a list of common actions appears; these are
defined by the =project-switch-commands= user option and accessed through =SPC p=
followed by a final key (e.g., =SPC p /= for =project-dired=, as configured in the
=project-prefix-map=).

If any =project.el= command is invoked outside a project, it will first prompt for
a project before executing its action. For example, =project-find-file= will
prompt for a project, switch to it, and then prompt for a file within that
project.

When inside a project, project-level commands become available. For instance,
=SPC p f= (=project-find-file=) searches for a file inside the current project, and
=SPC p b= (=project-switch-to-buffer=) switches to a buffer belonging to that
project. Refer to the =project-prefix-map= for a complete list of available
commands.

#+begin_src emacs-lisp
(use-package project :ensure nil)

(with-eval-after-load 'project
(setopt project-vc-merge-submodules nil)
(setopt project-switch-commands
'((project-find-file "Find file")
(project-find-dir "Find directory")
(project-dired "Dired at root")
(project-vc-dir "VC-project"))))

(define-key mode-specific-map (kbd "n") '+project-find-or-new-file)

(bind! project-prefix-map
"/" '+project-dired
"p" '+project-switch)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:0C70EF8E-00DB-4201-8110-83FD08E51D68
:header-args:emacs-lisp: :tangle (expand-file-name "+project.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(require 'project)

(defun +project--switch (directory &optional command)
"Do the work of `project-switch-project' in the given DIRECTORY.
With optional COMMAND, run it in DIRECTORY."
(let ((command (or (when (functionp command) command)
(if (symbolp project-switch-commands)
project-switch-commands
(project--switch-project-command))))
(buffer (current-buffer)))
(unwind-protect
(progn
(setq-local project-current-directory-override directory)
(call-interactively command))
(with-current-buffer buffer
(kill-local-variable 'project-current-directory-override)))))

;;;###autoload
(defun +project-find-or-new-file (&optional arg)
"A extended version of `project-find-file' with ARG.
When called without prefix or with `C-u' - 2, it works the same as
`project-find-file'; When prefix with `C-u' - 3, call `make-empty-file'."
(interactive "P")
(let ((prefix-arg (prefix-numeric-value arg)))
(cond
((not arg) (project-find-file))
((= prefix-arg 2) (call-interactively 'project-find-file))
((= prefix-arg 3) (call-interactively 'make-empty-file)))))

;;;###autoload
(defun +project-switch (directory)
"Switch to project DIRECTORY.
If DIRECTORY exists in a frame, select it. Otherwise switch to
the project in DIRECTORY using `project-dired'."
(interactive (list (funcall project-prompter)))
(project--remember-dir directory)
(let ((name (file-name-nondirectory (directory-file-name directory))))
(if (member name (mapcar #'car (make-frame-names-alist)))
(select-frame-by-name name)
(+project--switch directory 'project-dired))))
#+end_src

** Extra file types
:PROPERTIES:
:CUSTOM_ID: h:71694E57-F59D-44AB-8F9D-4D28CAA9F24D
:END:
*** Image viewer (image-mode.el)
:PROPERTIES:
:CUSTOM_ID: h:DFD26558-B786-42E9-A1B3-5E9A22ADB73C
:END:

View images inside emacs. Just rebind some keys here.

#+begin_src emacs-lisp
(use-package image-mode :ensure nil)

(with-eval-after-load 'image-mode
(bind! image-mode-map
"," 'image-bob
"." 'image-eob
"-" 'image-decrease-size
"=" 'image-increase-size
"n" 'image-next-line
"p" 'image-previous-line
"C-n" 'image-next-file
"C-p" 'image-previous-file
"i n" 'image-next-file
"i p" 'image-previous-file))
#+end_src

*** Pdf reader (pdf-tools.el)
:PROPERTIES:
:CUSTOM_ID: h:8F56919D-146C-4EE0-AECF-5512A9B8F571
:END:

#+begin_src emacs-lisp
(use-package pdf-tools :ensure t :after-call doc-view-mode-hook)

(with-eval-after-load 'pdf-tools
(pdf-tools-install)
(setq-default pdf-view-display-size 'fit-page)
(setq pdf-annot-activate-created-annotations t) ; automatically annotate highlights
(add-hook 'pdf-view-mode-hook (lambda () (cua-mode 0))) ; turn off cua so copy works
(setq pdf-view-resize-factor 1.1) ; more fine-grained zooming
(bind! 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))
#+end_src

*** Epub reader (nov.el)
:PROPERTIES:
:CUSTOM_ID: h:9A0AC633-4FD9-4F1B-9246-023CF0711BA1
:END:

#+begin_src emacs-lisp
(use-package nov :ensure t)

(push '("\\.epub\\'" . nov-mode) auto-mode-alist)

(with-eval-after-load 'nov
(setq nov-shr-rendering-functions
'((img . nov-render-img)
(title . nov-render-title)
(b . shr-tag-b))))
#+end_src

*** Working with remote files (tramp.el)
:PROPERTIES:
:CUSTOM_ID: h:04FB6C89-69E6-416F-BE7D-916308165779
:END:

#+begin_src emacs-lisp
(use-package tramp :ensure nil)

(with-eval-after-load 'tramp
(add-to-list 'tramp-connection-properties
(list (regexp-quote "/ssh:buzz:") "direct-async-process" t))
(setq tramp-verbose 0)
(setq tramp-auto-save-directory (locate-user-emacs-file "tramp/"))
(setq tramp-chunksize 2000)
(setopt tramp-use-ssh-controlmaster-options nil))
#+end_src

** Automated actions
:PROPERTIES:
:CUSTOM_ID: h:93EFC399-993E-4F72-976E-5F793476071B
:END:
*** Auto refresh file content (autorevert.el)
:PROPERTIES:
:CUSTOM_ID: h:0F47CCEA-83F8-4A20-A7FA-E6EB7478ED57
:END:

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

#+begin_src emacs-lisp
(use-package autorevert :ensure nil :after-call after-find-file)

(with-eval-after-load 'autorevert
(setq auto-revert-verbose t)
(global-auto-revert-mode))
#+end_src

*** Record recent opened files (recentf.el)
:PROPERTIES:
:CUSTOM_ID: h:9500079C-EA85-46E9-A8E3-084F611A4DDA
:END:

*recentf.el* keeps a record of recently opened files.

The =recentf-cleanup= command, as its name suggest, cleans up the recent files
list. That is, remove duplicates, non-kept, and excluded files. In order to
keep our recent file list always up-to-date, running it once whenever the mode
is activate, which is the default behavior, is not enough. Hence, we set it to
do the cleaning task every 5 minutes.

#+begin_src emacs-lisp
(use-package recentf :ensure nil :after-call meow-keypad-mode-hook)

(with-eval-after-load 'recentf
(setq recentf-max-saved-items 300)
(setq recentf-auto-cleanup 300)
(recentf-mode 1))
#+end_src

*** Restore cursor position in files (saveplace.el)
:PROPERTIES:
:CUSTOM_ID: h:0963A7D0-58A0-428E-9277-7296E80C43EE
:END:

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.

#+begin_src emacs-lisp
(use-package saveplace :ensure nil :after-call find-file-hook)

(with-eval-after-load 'saveplace
(setq save-place-file (locate-user-emacs-file "saveplace"))
(setq save-place-forget-unreadable-files t)
(save-place-mode 1))
#+end_src

*** Automatically save modified files (auto-save-visited-mode)
:PROPERTIES:
:CUSTOM_ID: h:027CF6BF-B43E-4403-BA46-E2BCB639DA3B
:END:

With =auto-save-visited-mode= enabled, modified visited file buffers are
automatically saved. The =auto-save-visited-interval= variable controls the
frequency of these checks. In this configuration, modification checks occur
every 5 minutes.

#+begin_src emacs-lisp
(use-package files :ensure nil)

(setq auto-save-default nil)
(auto-save-visited-mode)
(setopt auto-save-visited-interval 300)
#+end_src

** Utilities
:PROPERTIES:
:CUSTOM_ID: h:79DC4C05-4541-498C-918D-5769455C429C
:END:

This section introduces several file operation utilities.

*** Find file with sudo | Rename file
:PROPERTIES:
:CUSTOM_ID: h:5CABCC01-3381-455A-A2A6-3E8DBD847537
:END:

+ ~+files-sudo-find~: open file with sudo privilege
+ ~+files-rename-file-and-buffer~: rename current visited file and buffer name

These two commands, ~+files-sudo-find~ and ~+files-rename-file-and-buffer~, are
closely related because a failed file rename is often due to a permission issue.

#+begin_src emacs-lisp
(bind! grandview-file-map
"w" 'save-buffer ; [SPC x s] is painful to press
"k" 'kill-current-buffer
"r" '+files-rename-file-and-buffer
"s" '+files-sudo-find)
#+end_src

**** Extras ::: +files.el
:PROPERTIES:
:CUSTOM_ID: h:CA90D351-61A4-408C-9BC5-6349E0679FA0
:header-args:emacs-lisp: :tangle (expand-file-name "+files.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +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 +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)))))
#+end_src

*** Find files in predefined locations
:PROPERTIES:
:CUSTOM_ID: h:05500C75-1E86-4560-90A0-3B78A110400D
:END:

A few commands for quick file access.

+ ~+files-open-grandview-config~: open =grandview.org= (this file)
+ ~+files-find-dotfiles~: find and open dotfiles
+ ~+files-find-user-files~: find files in user predefined locations

#+begin_src emacs-lisp
(use-package files :ensure nil)

(setq large-file-warning-threshold 50000000)
(setq permanently-enabled-local-variables '(lexical-binding encoding))
(setq confirm-kill-processes nil)

(bind! +find-file-prefix-map
"." '+files-find-dotfiles
"u" '+files-find-user-files)
#+end_src

**** Extras ::: +files.el
:PROPERTIES:
:CUSTOM_ID: h:89CC46D7-7265-4442-BBFE-6627EFE1EB9C
:header-args:emacs-lisp: :tangle (expand-file-name "+files.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(defcustom +files-dotfiles-repo "/opt/dotfiles/"
"Path for user dotfiles."
:group 'grandview :type 'string)

(defcustom +files-user-dirs-alist
'(((prompt . "Photos") (path . "~/Pictures/"))
((prompt . "Desktop") (path . "~/Desktop"))
((prompt . "GPT chats") (path . "~/Documents/chats"))
((prompt . "Notes") (path . "~/Documents/notes"))
((prompt . "Downloads") (path . "~/Downloads/")))
"Doc."
:group 'grandview :type '(repeat list))

(defun +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 +files-find-dotfiles ()
"Open files in dotfiles repo."
(interactive)
(unless +files-dotfiles-repo
(user-error "`+files-dotfiles-repo' is undefined"))
(+files--in-directory +files-dotfiles-repo " Dotfiles: "))

;;;###autoload
(defun +files-find-user-files ()
"Open files in directories defined in `+files-user-dirs-alist'."
(interactive)
(let* ((cands-raw
(mapcar (lambda (i) (cdr (assq 'prompt i))) +files-user-dirs-alist))
(get-item (lambda (s field)
(cl-dolist (i +files-user-dirs-alist)
(when (string= s (cdr (assq 'prompt 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)))
(+files--in-directory path (concat title ": "))))
#+end_src

* Editing enhancement
:PROPERTIES:
:CUSTOM_ID: h:401D8E76-D334-4E49-A670-8F04B6EA5B27
:END:

GNU Emacs is an extensible, customizable, free/libre text editor (and more).
This section introduces several built-in packages that provide fundamental text
editing commands and features.

** Editor basics
:PROPERTIES:
:CUSTOM_ID: h:0EFB6EF1-C52F-4EC8-8257-F3C21300AC0A
:END:
*** Keyboard orientation (keyboard.c)
:PROPERTIES:
:CUSTOM_ID: h:46996B4B-A490-4981-B583-51ED036305CD
:END:

Historically, terminal utilities like *bash* and *vim* cannot distinguish between
certain keystrokes. For instance, they treat =C-i= and =Tab= as the same, as well
as =C-m= and =Return=. By default, Emacs follows this convention. However, Emacs
is capable of differentiating these keystrokes. In a GUI environment, I've
remapped the =?C-i= key sequence, allowing Emacs to recognize =Tab= and =C-i= as
distinct keycodes. My terminal emulator, Alacritty, is configured to translate
=C-i= to ==. This enables various commands from different terminal applications
(like shell and vim) to be bound to =C-i= through the == intermediary.

To understand the underlying mechanisms, you may want to explore
=input-decode-map= and [[https://en.wikipedia.org/wiki/Escape_sequence][escape sequences]].

#+begin_src emacs-lisp
(when (display-graphic-p) (define-key input-decode-map [?\C-i] [C-i]))
#+end_src

Here is how I do with my alacritty configuration:

#+begin_src toml :tangle no
[keyboard]
bindings = [
# ...
# Ctrl-i => F7
{ chars = "\u001B[18~", key = "I", mods = "Control" }
# ...
]
#+end_src

Modifier settings for macOS, we use the default though.

#+begin_src emacs-lisp
(setq ns-command-modifier 'super)
(setq ns-alternate-modifier 'meta)
#+end_src

*** Tab for Indentation (indent.el)
:PROPERTIES:
:CUSTOM_ID: h:1EE47211-84CB-4880-8D62-592999C7441D
:END:

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.

#+begin_src emacs-lisp
(setq-default tab-always-indent t)
(setq-default tab-first-completion 'word-or-paren-or-punct) ; Emacs 27
(setq-default indent-tabs-mode nil)
#+end_src

*** Line wrap and sentences (fill.el)
:PROPERTIES:
:CUSTOM_ID: h:065A996E-9860-46BE-BC54-B12EC8414752
:END:

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.

#+begin_src emacs-lisp
(use-package fill :ensure nil)

(add-hook 'text-mode-hook 'turn-on-auto-fill)

(setq-default fill-column 80)
(setq colon-double-space nil)
(setq adaptive-fill-mode t)
(setq sentence-end-double-space t)
(setq sentence-end-without-period nil)
#+end_src

*** Built-in editing utilities (simple.el)
:PROPERTIES:
:CUSTOM_ID: h:FB996162-B528-4AD8-A114-660D515D6F23
:END:

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

+ Unbind =SPC= in /*messages*/ buffer since we use it as the leader key
+ Add a command to toggle the letter case of the word under cursor
+ Add a command to mark the inner line
+ Bind =M-n/p= | =s-n/p= globally for page scrolling | paragraph navigation.

#+begin_src emacs-lisp
(use-package simple :ensure nil :after meow)

;; these bindings work globally.
(bind! global-map
"S-SPC" #'toggle-input-method
"s-u" #'+simple-toggle-letter-case
"M-n" #'scroll-up-command
"M-p" #'scroll-down-command
"s-n" #'forward-paragraph
"s-p" #'backward-paragraph)

(with-eval-after-load 'simple
(require '+simple)
(define-key messages-buffer-mode-map (kbd "SPC") nil)
(setq next-error-message-highlight t)) ; added in Emacs 28.1
#+end_src

**** Extras ::: +simple.el
:PROPERTIES:
:CUSTOM_ID: h:C1A34439-1A4C-4A32-9FEE-2A22CF785336
:header-args:emacs-lisp: :tangle (expand-file-name "+simple.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(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)))

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

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

;; Copied from `xah-fly-keys'
(defun +simple-toggle-letter-case ()
"Toggle the letter case of current word or selection.
Always cycle in this order: Init Caps, ALL CAPS, all lower."
(interactive)
(let ((deactivate-mark nil) xbeg xend)
(if (region-active-p)
(setq xbeg (region-beginning) xend (region-end))
(save-excursion
(skip-chars-backward "[:alnum:]")
(setq xbeg (point))
(skip-chars-forward "[:alnum:]")
(setq xend (point))))
(when (not (eq last-command this-command))
(put this-command 'state 0))
(cond
((equal 0 (get this-command 'state))
(upcase-initials-region xbeg xend)
(put this-command 'state 1))
((equal 1 (get this-command 'state))
(upcase-region xbeg xend)
(put this-command 'state 2))
((equal 2 (get this-command 'state))
(downcase-region xbeg xend)
(put this-command 'state 0)))))
#+end_src

*** Long line text (so-long.el)
:PROPERTIES:
:CUSTOM_ID: h:F19F714D-1B7B-47A1-AE22-5DF737822996
:END:

Emacs versions 27 and later include =global-so-long-mode=, which brings consistent
performance when working with buffers containing very long lines. This mode
allows the active major mode to adapt gracefully. While the definition of "very
long" is configurable via =M-x find-library so-long=, the default settings work
well for most users and require no additional customization.

=so-long-mode= Files with long lines have huge performance impact for file preview
(in our case =dirvish.el=), so we ensure the =global-so-long-mode= is enabled before
=dired= is loaded.

#+begin_src emacs-lisp
(use-package so-long :ensure nil :after-call dired-mode-hook)

(with-eval-after-load 'so-long (global-so-long-mode))
#+end_src

** Search | Query
:PROPERTIES:
:CUSTOM_ID: h:29B6B90B-9E1A-481E-A987-FEBF09AAFBBC
:END:
*** Interactive query replace (anzu.el)
:PROPERTIES:
:CUSTOM_ID: h:81C344C7-6981-4526-A34B-6EBAB2590CF0
:END:

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

#+begin_src emacs-lisp
(use-package anzu
:ensure t
:after-call isearch-mode meow-query-replace)

(with-eval-after-load 'anzu
(global-anzu-mode)
(define-key global-map [remap query-replace] 'anzu-query-replace)
(define-key global-map [remap query-replace-regexp] 'anzu-query-replace-regexp))
#+end_src

*** Alternative isearch UI (isearch-mb.el)
:PROPERTIES:
:CUSTOM_ID: h:EFA1CC25-8309-4639-A358-8F2B1BC1AE79
:END:

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.

#+begin_src emacs-lisp
(use-package isearch-mb
:ensure t
:after-call isearch-mode meow-query-replace)

(with-eval-after-load 'isearch-mb
(isearch-mb-mode)
(add-to-list 'isearch-mb--with-buffer #'consult-isearch-history)
(add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace)
(define-key isearch-mb-minibuffer-map
[remap previous-matching-history-element] 'consult-isearch-history))
#+end_src

*** Cross reference (xref.el)
:PROPERTIES:
:CUSTOM_ID: h:9409CEEF-F5E0-46FF-9AF0-1159A5778FE1
:END:

*xref* is a pluggable emacs subsystem to quickly find where identifiers are
defined and referenced, and for quick renaming, etc.

Every module (such as *eglot*) that plugs into xref must provide a constructor function that
returns a backend value and a set of methods:

- ~xref-backend-identifier-at-point~
- ~xref-backend-identifier-completion-table~
- ~xref-backend-definitions~
- ~xref-backend-references~
- ~xref-backend-apropos~

For users, there are some commands that are available to users by default, the
most frequently used are:

- ~xref-find-definitions~ (=M-.=)
- ~xref-go-back~ (=M-,=)
- ~xref-find-references~ (=M-?=)
- ...etc.

I remapped =M-/= to ~xref-find-references~, which is more accessible than the
default, so that the three adjacent keybindings =SPC m ,= | =SPC m .= | =SPC m /= all
relate to the concept of *xref*, making them easier to understand and use.

#+begin_src emacs-lisp
(use-package xref :ensure nil)

(define-key global-map (kbd "M-/") 'xref-find-references)

(setq xref-prompt-for-identifier nil)
(setq xref-file-name-display 'project-relative)
(setq xref-search-program 'ripgrep)
#+end_src

*** Ripgrep (rg.el)
:PROPERTIES:
:CUSTOM_ID: h:DCD3F63B-2054-45A3-9579-34B921641468
:END:

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.

Some additions I've added to rg transient:

+ =C= key to toggle =--context 3= flag
show 3 lines of text before and after the keyword
+ =A= key to toggle =-A 5= flag
like =--context=, but only show text after the keyword
+ press =SPC o s= to search in current project, no ask for confirmation

#+begin_src emacs-lisp
(use-package rg :ensure t :after-call find-file-hook)

(with-eval-after-load 'rg
(+rg-setup)
(define-key grandview-prog-map (kbd "s") 'rg-project-all-files-no-ask))
#+end_src

**** Writable grep (wgrep.el)
:PROPERTIES:
:CUSTOM_ID: h:39DE729A-046C-4CD1-B3A5-C807D366061E
:END:

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

#+begin_src emacs-lisp
(use-package wgrep :ensure t)

(with-eval-after-load 'wgrep
(setq wgrep-auto-save-buffer t)
(setq wgrep-change-readonly-file t))
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:EF6C06C9-5720-41F2-970C-8E221EA70B92
:header-args:emacs-lisp: :tangle (expand-file-name "+rg.el" grandview--def-dir)
:END:

The purpose of wrapping =rg-define-toggle/search= is to avoid executing them
immediately, because these macros introduce dependencies that we don't need at
startup.

#+begin_src emacs-lisp
;;;###autoload
(defun +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 "*"))
#+end_src

** Visual feedback
:PROPERTIES:
:CUSTOM_ID: h:5E45BE4B-5234-4A29-93F7-6D7BCF1F13DA
:END:
*** COMMENT Display line nubmers
:PROPERTIES:
:CUSTOM_ID: h:E9F856B8-86A8-45E2-8E80-7C8B45B0C7C2
:END:

I don't rely on line numbers that much, so I usually enable them manually via
=M-x display-line-numbers-mode=.

#+begin_src emacs-lisp
(use-package display-line-numbers-mode :ensure nil)

(add-hook 'prog-mode-hook 'display-line-numbers-mode)
#+end_src

*** Show parentheses (paren.el)
:PROPERTIES:
:CUSTOM_ID: h:6443408B-A49C-4D29-B14F-998CDB289F76
:END:

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.

#+begin_src emacs-lisp
(setq show-paren-style 'parenthesis)
(setq show-paren-when-point-in-periphery nil)
(setq show-paren-when-point-inside-paren nil)
(show-paren-mode)
#+end_src

*** Prettify symbols
:PROPERTIES:
:CUSTOM_ID: h:C5B6527C-E772-40DA-9142-F0AACA0B2AB9
:END:

#+begin_src emacs-lisp
(add-hook 'prog-mode-hook 'prettify-symbols-mode)

(setq-default prettify-symbols-alist
'(("<-" . ?←)
("->" . ?→)
("->>" . ?↠)
("=>" . ?⇒)
("/=" . ?≠)
("!=" . ?≠)
("==" . ?≡)
("<=" . ?≤)
(">=" . ?≥)
("=<<" . (?= (Br . Bl) ?≪))
(">>=" . (?≫ (Br . Bl) ?=))
("<=<" . ?↢)
(">=>" . ?↣)))
(setq prettify-symbols-unprettify-at-point 'right-edge)
#+end_src

** Automation
:PROPERTIES:
:CUSTOM_ID: h:F7A967C9-D685-4B40-9EDB-FF2C334546D7
:END:
*** Pair insertion (eletric.el)
:PROPERTIES:
:CUSTOM_ID: h:CF2FA7CE-78D4-4941-B286-F1062994900C
:END:

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.

#+begin_src emacs-lisp
(use-package electric :ensure nil)

(add-hook 'org-mode-hook '+electric-inhibit-<)
(add-hook 'minibuffer-setup-hook
(lambda () (unless (eq this-command 'eval-expression)
(electric-pair-mode 0))))
(add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1)))

(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)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:AED70F01-7494-4652-B763-76FA41DF7902
:header-args:emacs-lisp: :tangle (expand-file-name "+eletric.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defadvice! electric-pair-post-self-insert-a (fn &rest args)
"Doc."
:around #'electric-pair-post-self-insert-function
(let ((mark-active nil)) (apply fn args)))

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

*** Rime client for Emacs (rime.el)
:PROPERTIES:
:CUSTOM_ID: h:D97101B7-E8EB-4C7B-B097-E1E2ADCF72F9
:END:

#+begin_src emacs-lisp
(use-package rime :ensure t)

(setq default-input-method "rime")
(with-eval-after-load 'rime
(when (eq system-type 'darwin)
(setq rime-librime-root (expand-file-name "librime" user-emacs-directory))
(setq rime-emacs-module-header-root "/opt/homebrew/include"))
(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 #'+rime-candidate-num-fmt)
(set-face-attribute 'rime-preedit-face nil :inherit 'lazy-highlight)
(define-key rime-active-mode-map [tab] 'rime-send-keybinding)
(bind! rime-active-mode-map
"C-`" 'rime-send-keybinding
"C-k" 'rime-send-keybinding
"" 'rime-send-keybinding
"C-o" 'rime-send-keybinding
"C-a" 'rime-send-keybinding
"C-e" 'rime-send-keybinding
"" (lambda () (interactive) (execute-kbd-macro (kbd "C-g")))))
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:6B8B3E46-1951-4840-B3B3-4667AD69C2FA
:header-args:emacs-lisp: :tangle (expand-file-name "+rime.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defadvice! rime-return-a (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 ""))
(toggle-input-method)
(dotimes (i (length input))
(execute-kbd-macro (kbd (substring input i (+ i 1)))))
(toggle-input-method)))
(apply fn args)))

;;;###autoload
(defun +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)))
#+end_src

*** Remove whitespaces on saving (whitespace.el)
:PROPERTIES:
:CUSTOM_ID: h:CBCE7DD3-399D-4B34-B1D7-97F4FD0BE1F2
:END:

This built-in package handles blanks (TAB, (HARD) SPACE and NEWLINE).

#+begin_src emacs-lisp
(use-package whitespace :ensure nil :after-call basic-save-buffer)

(add-hook 'before-save-hook #'+whitespace-cleanup-before-saving)
#+end_src

**** Extras
:PROPERTIES:
:header-args:emacs-lisp: :tangle (expand-file-name "+whitespace.el" grandview--def-dir)
:CUSTOM_ID: h:5D70CEE9-84B3-4374-9398-75D3518CA876
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +whitespace-cleanup-before-saving ()
"Cleanup whitespaces for local actual files."
(when-let* ((file buffer-file-name)
((not (file-remote-p file)))
((file-exists-p file)))
(whitespace-cleanup)))
#+end_src

** Using org-mode
:PROPERTIES:
:CUSTOM_ID: h:D30BE4AD-1214-42E5-8634-A76D5CF5F9E7
:END:
*** Org (org.el)
:PROPERTIES:
:CUSTOM_ID: h:4EDF19C6-565A-4AD5-937E-E8749F4C1129
:END:

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

#+begin_src emacs-lisp
(use-package org :ensure nil)

(with-eval-after-load 'org
(+org-headline-setup)
(add-hook 'enable-theme-functions #'+org-headline-setup)
(add-hook 'org-mode-hook 'variable-pitch-mode)
(add-hook 'org-mode-hook 'org-indent-mode)
(add-hook 'org-tab-first-hook 'org-end-of-line)
(setq org-tag-column 0)
(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))
#+end_src

**** Extras ::: +org.el
:PROPERTIES:
:CUSTOM_ID: h:D1E76232-405A-40C8-87B1-B553DBB57F43
:header-args:emacs-lisp: :tangle (expand-file-name "+org.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(require 'org-faces)

(defadvice! org-toggle-comment-ad (fn &rest args)
"Drop-in replacement for `org-toggle-comment'.
This allows `org-toggle-comment' to toggle comment for all the
entries with the same level in the active region while behaves
the same when the region is inactive. This is useful for
debugging code blocks in a org config file."
:around #'org-toggle-comment
(if (region-active-p)
(progn
(exchange-point-and-mark)
(let ((end (region-end)) last-point)
(while (< (point) end)
(setq last-point (point))
(apply fn args)
(org-forward-heading-same-level 1)
(when (eq last-point (point))
(org-forward-element)))))
(apply fn args)))

(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 +org-headline-setup (&optional _theme)
"Setup headline heights for org-mode."
(cl-loop for idx in '(1 2 3 4)
for face = (intern (format "org-level-%s" idx))
for height = (nth (1- idx) grandview-custom-headline-sizes)
do (set-face-attribute face nil :height height)))
#+end_src

*** Identifiers for org entries (org-id.el)
:PROPERTIES:
:CUSTOM_ID: h:F86F6861-E18B-4790-9B87-28F1A8B10465
:END:

#+begin_src emacs-lisp
(use-package org-id :ensure nil)

(setq org-link-context-for-files t)
(setq org-link-keep-stored-after-insertion nil)
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:7269F6A0-67A5-4672-A32C-70711E30F3C5
:header-args:emacs-lisp: :tangle (expand-file-name "+org-id.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(require 'org-id)

;; Original idea:
;; .
(defun +org-id--get ()
"Get the CUSTOM_ID of the current entry.
If the entry already has a CUSTOM_ID, return it as-is, else create a new
one."
(let* ((pos (point))
(id (org-entry-get pos "CUSTOM_ID")))
(if (and id (stringp id) (string-match-p "\\S-" id))
id
(setq id (org-id-new "h"))
(org-entry-put pos "CUSTOM_ID" id)
id)))

;;;###autoload
(defun +org-id-headlines ()
"Add missing CUSTOM_ID to all headlines in current file."
(interactive)
(org-map-entries #'+org-id--get))

;;;###autoload
(defun +org-id-headline ()
"Add missing CUSTOM_ID to headline at point."
(interactive)
(+org-id--get))
#+end_src

*** Literate programming (ob.el)
:PROPERTIES:
:CUSTOM_ID: h:A02ABD13-186B-410D-B78D-D64CA5C7ED9F
:END:

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

#+begin_src emacs-lisp
(use-package ob :ensure nil)
(use-package ob-C :ensure nil)
(use-package ob-js :ensure nil)
(use-package ob-shell :ensure nil)
(use-package ob-latex :ensure nil)
(use-package ob-makefile :ensure nil)
(use-package ob-csharp :vc (:url "https://github.com/samwdp/ob-csharp"))

(with-eval-after-load 'org
(autoload 'org-babel-execute:C "ob-C")
(autoload 'org-babel-expand:C "ob-C")
(autoload 'org-babel-execute:cpp "ob-C")
(autoload 'org-babel-expand:cpp "ob-C")
(autoload 'org-babel-execute:csharp "ob-csharp")
(autoload 'org-babel-execute:python "ob-python")
(autoload 'org-babel-execute:js "ob-js")
(autoload 'org-babel-execute:bash "ob-shell")
(autoload 'org-babel-expand:latex "ob-latex")
(autoload 'org-babel-execute:latex "ob-latex")
(autoload 'org-babel-execute:makefile "ob-makefile")
(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"))))
#+end_src

*** Source code block (org-src.el)
:PROPERTIES:
:CUSTOM_ID: h:34643643-4664-4119-AE64-FE72967C4F2C
:END:

#+begin_src emacs-lisp
(use-package org-src :ensure nil)

(with-eval-after-load 'org-src
(setq org-edit-src-content-indentation 0)
(setq org-src-window-setup 'split-window-right)
(push '("conf-unix" . conf-unix) org-src-lang-modes))
#+end_src

*** Reveal invisible org elements (org-appear.el)
:PROPERTIES:
:CUSTOM_ID: h:C3F87A9F-23E5-4B3E-A4BF-E2637C7C8050
:END:

#+begin_src emacs-lisp
(use-package org-appear :ensure t)

(add-hook 'org-mode-hook 'org-appear-mode)
(setq org-appear-autolinks t)
(setq org-hide-emphasis-markers t)
#+end_src

*** Modern org style
:PROPERTIES:
:CUSTOM_ID: h:A8059D5E-D3A9-4345-88E4-883F3F099547
:END:

=org-modern= adds some styling to your Org buffer, which gives it a modern look.

#+begin_src emacs-lisp
(use-package org-modern :ensure t)

(setq org-modern-star 'replace)

(add-hook 'org-mode-hook 'org-modern-mode)
(add-hook 'org-agenda-finalize-hook 'org-modern-agenda)
#+end_src

*** Habit (org-habit.el)
:PROPERTIES:
:CUSTOM_ID: h:413F6EA4-0A0F-4101-8EC3-2BBBDFC33C38
:END:

#+begin_src emacs-lisp
(use-package org-habit :ensure nil)

(with-eval-after-load 'org
(appendq! org-modules '(org-habit))
(setq org-habit-graph-column 60))
#+end_src

*** Slide (org-tree-slide.el)
:PROPERTIES:
:CUSTOM_ID: h:D1DB04F4-C2FF-42C3-ACCC-25D4189C6B30
:END:

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

#+begin_src emacs-lisp
(use-package org-tree-slide :ensure t)

(with-eval-after-load 'org
(define-key org-mode-map (kbd "C-c |") 'org-tree-slide-mode))

(with-eval-after-load 'org-tree-slide
(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-prettify-slide-h)
(bind! org-tree-slide-mode-map
"" 'org-tree-slide-move-previous-tree
"" 'org-tree-slide-move-next-tree))
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:13354F6E-FF7A-43DE-8DC4-43AF25D0DEA9
:header-args:emacs-lisp: :tangle (expand-file-name "+org-tree-slide.el" grandview--def-dir)
:CUSTOM_ID: Org_mode-Slide_(org-tree-slide.el)-Autoload-7ff9d878
:END:

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

;;;###autoload
(defadvice! +org-tree-slide-simple-header-a (blank-lines)
"Set the header with overlay.
Some number of BLANK-LINES will be shown below the header."
:override #'org-tree-slide--set-slide-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-prettify-slide-h ()
"Set up the org window for presentation."
(cond (org-tree-slide-mode
(text-scale-set +org-tree-slide-text-scale)
(+frame-cursor-dim-mode +1)
(+window-monocle-mode +1)
(ignore-errors (org-latex-preview '(4))))
(t
(text-scale-set 0)
(+window-monocle-mode -1)
(+frame-cursor-dim-mode -1)
(org-clear-latex-preview)
(org-mode))))
#+end_src

* Programming
:PROPERTIES:
:CUSTOM_ID: h:2E097771-0569-462C-AB7A-4FA52F97E771
:END:
** Services
:PROPERTIES:
:CUSTOM_ID: h:CAED6558-08CD-4081-B9A9-9159B780C69F
:END:
*** Tree sitter integration (treesit.el)
:PROPERTIES:
:CUSTOM_ID: h:9A2141F6-3085-4E4D-958C-466E4A16D9BE
:END:

=treesit= (built-in with Emacs29+) is an Emacs binding for Tree-sitter, an
incremental parsing system.

It aims to be the foundation for a new breed of Emacs packages that understand
code structurally. For example:

- Faster, fine-grained code highlighting.
- More flexible code folding.
- Structural editing (like Paredit, or even better) for non-Lisp code.
- More informative indexing for imenu.

After setting the ~treesit-language-source-alist~, try =M-x
treesit-install-language-grammar=.

See: https://www.masteringemacs.org/article/how-to-get-started-tree-sitter for
details.

#+begin_src emacs-lisp
(setq treesit-language-source-alist
'((bash "https://github.com/tree-sitter/tree-sitter-bash")
(cmake "https://github.com/uyha/tree-sitter-cmake")
(c-sharp "https://github.com/tree-sitter/tree-sitter-c-sharp")
(css "https://github.com/tree-sitter/tree-sitter-css")
(elisp "https://github.com/Wilfred/tree-sitter-elisp")
(go "https://github.com/tree-sitter/tree-sitter-go")
(html "https://github.com/tree-sitter/tree-sitter-html")
(javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
(jsdoc "https://github.com/tree-sitter/tree-sitter-jsdoc" "master" "src")
(json "https://github.com/tree-sitter/tree-sitter-json")
(lua "https://github.com/tree-sitter-grammars/tree-sitter-lua")
(make "https://github.com/alemuller/tree-sitter-make")
(markdown "https://github.com/ikatyang/tree-sitter-markdown")
(python "https://github.com/tree-sitter/tree-sitter-python")
(toml "https://github.com/tree-sitter/tree-sitter-toml")
(tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
(yaml "https://github.com/ikatyang/tree-sitter-yaml")
(clojure "https://github.com/sogaiu/tree-sitter-clojure")
(rust "https://github.com/tree-sitter/tree-sitter-rust")
(vue "https://github.com/ikatyang/tree-sitter-vue")))

(setq treesit-font-lock-level 2)
;; (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
#+end_src

*** Client for LSP servers (eglot.el)
:PROPERTIES:
:CUSTOM_ID: h:D1EA3177-9AE1-44EE-8C71-E45FE5BF3AB5
:END:

*eglot* is the built-in language server protocol client in Emacs29+.

It's much simper and faster than *lsp-mode*, also it works well with all the
in-built emacs packages such as flymake, eldoc, etc.

We use =emacs-lsp-booster= (see: https://github.com/blahgeek/emacs-lsp-booster) to
speed up the json parsing, make sure you have the binary installed in your *PATH*.

Huge thanks to @blahgeek and @jdtsmith!

#+begin_src emacs-lisp
(use-package eglot :ensure nil)
(use-package markdown-mode :ensure t)
(use-package eglot-booster :vc (:url "https://github.com/jdtsmith/eglot-booster"))

(with-eval-after-load 'eglot
(setq eglot-autoshutdown t)
(setq eglot-send-changes-idle-time 0)
(add-hook 'eglot-mode-hook '+eglot-setup-eldoc)
(setq eglot-booster-io-only t)
(add-hook 'eglot-mode-hook 'eglot-booster-mode))

(defvar-keymap +eglot-prefix-map
:doc "Keymap for `eglot' commands."
"a" 'eglot-code-actions
"t" 'eglot-find-typeDefinition
"d" 'eglot-find-declaration
"i" 'eglot-find-implementation
"r" 'eglot-rename)

(define-key grandview-prog-map (kbd "e") +eglot-prefix-map)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:BE13EC8F-A1C9-47C2-8EBC-CB34CA7E9546
:header-args:emacs-lisp: :tangle (expand-file-name "+eglot.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +eglot-setup-eldoc ()
"Show docs for errors first, then signatures and others."
(setq-local eldoc-documentation-functions
'(flymake-eldoc-function
eglot-signature-eldoc-function
eglot-hover-eldoc-function)))
#+end_src

*** Syntax checker (flymake.el)
:PROPERTIES:
:CUSTOM_ID: h:06C35376-E12F-4587-BD8E-B0EC021E2F08
:END:

#+begin_src emacs-lisp
(use-package flymake :ensure nil)

(add-hook 'prog-mode-hook 'flymake-mode)

(defvar-keymap +flymake-prefix-map
:doc "Keymap for `flymake' commands."
"n" 'flymake-goto-next-error
"p" 'flymake-goto-prev-error
"s" 'flymake-start)

(define-key grandview-prog-map (kbd "f") +flymake-prefix-map)
#+end_src

*** Compiler (compile.el)
:PROPERTIES:
:CUSTOM_ID: h:D6A16E5A-D417-4585-843F-92F206900B84
:END:

Run compiler as inferior of Emacs, parse error messages.

#+begin_src emacs-lisp
(bind! grandview-file-map
"c" 'compile
"C" 'recompile)
#+end_src

*** Formatter (reformatter.el)
:PROPERTIES:
:CUSTOM_ID: h:6E21C502-9ADA-452C-B4B9-C10D88CD42CB
:END:

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

#+begin_src emacs-lisp
(use-package reformatter :ensure t)

(define-key +file-laf-prefix-map (kbd "r") '+reformatter-format-buffer)

(with-eval-after-load 'reformatter
(reformatter-define lua-format
:program "stylua"
:args '("--indent-width" "2" "-")
:lighter " styLua")
(reformatter-define python-format
:program "black"
:args '("-")
:lighter " blackFMT")
(reformatter-define vue-format
:program "eslint_d"
:args (list "--stdin" "--fix-to-stdout" "--stdin-filename" (buffer-file-name))
:working-directory (+reformatter-eslint-root)
:lighter " eslint")
(reformatter-define typescript-format
:program "eslint_d"
:args (list "--stdin" "--fix-to-stdout" "--stdin-filename" (buffer-file-name))
:working-directory (+reformatter-eslint-root)
:lighter " eslint"))
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:91FB5726-4A78-403A-8DF6-3D0B2644C6B4
:header-args:emacs-lisp: :tangle (expand-file-name "+reformatter.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +reformatter-eslint-root ()
"Up traverse filesystem for a directory containing eslint config."
(locate-dominating-file
default-directory
(lambda (directory)
(seq-find
(lambda (project-marker)
(file-exists-p (expand-file-name project-marker directory)))
'("eslint.config.js" "eslint.config.mjs" "eslint.config.cjs" "package.json")))))

;;;###autoload
(defun +reformatter-format-buffer ()
(interactive)
(let* ((mode-name
(string-remove-suffix
"-mode" (string-remove-suffix "-ts-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)))))
#+end_src

** Visual hints
:PROPERTIES:
:CUSTOM_ID: h:2C3D76D9-164E-43DF-BDA0-ED29F7CEC433
:END:
*** "Rainbow Parentheses" (rainbow-delimiters.el)
:PROPERTIES:
:CUSTOM_ID: h:5E41D973-4C55-41F7-98D7-7EDD69FBA923
:END:

=rainbow-delimiters= is a "rainbow parentheses"-like mode which highlights
parentheses, brackets, and braces according to their depth. Each successive
level is highlighted in a different color. This makes it easy to spot matching
delimiters, orient yourself in the code, and tell which statements are at a
given level.

#+begin_src emacs-lisp
(use-package rainbow-delimiters :ensure t)

(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
#+end_src

*** Color name colorizer (rainbow-mode.el)
:PROPERTIES:
:CUSTOM_ID: h:A5576BEA-6543-4DF6-AAAF-A2F689D64BA7
:END:

This minor mode sets background color to strings that match color names,
e.g. #0000ff is displayed in white with a blue background.

#+begin_src emacs-lisp
(use-package rainbow-mode :ensure t)

(add-hook 'prog-mode-hook 'rainbow-mode)
#+end_src

*** Show diagnostic info at point (eldoc.el)
:PROPERTIES:
:CUSTOM_ID: h:88EA4FBD-F7DC-436C-8515-8095306CE126
:END:

For languages like emacs-lisp, this package shows function arglist or variable
docstring in echo area. For other programming languages, thanks to the
=eldoc-documentation-functions'= hook it provides, any help or diagnostic info
that are relevent to the symbol (whatever it is) under the cursor will be
displayed in the minibuffer.

If the information string is more than one line, by default eldoc will
automatically expand and shrink the minibuffer, which I don't like very much.
So I turn =eldoc-echo-area-use-multiline-p= off.

#+begin_src emacs-lisp
(setq eldoc-echo-area-use-multiline-p nil)
#+end_src

** DevTools
:PROPERTIES:
:CUSTOM_ID: h:2D8763CA-4A2A-4D6E-AFF7-C23521A67265
:END:
*** Jupyter (jupyter.el)
:PROPERTIES:
:CUSTOM_ID: h:76B9895A-72F7-4BB4-B46C-2F087FCA81E8
:END:

#+begin_src emacs-lisp
(use-package jupyter :ensure t)

(with-eval-after-load 'jupyter
(require 'ob-jupyter)
(org-babel-jupyter-override-src-block "python")
(setq org-babel-default-header-args:python
'((:async . "yes") (:kernel . "python3"))))
#+end_src

*** REST client (verb.el)
:PROPERTIES:
:CUSTOM_ID: h:A25206AC-05E6-4F4D-A161-C295484936FF
:END:

*Verb* is a package for Emacs which allows you to organize and send HTTP requests.

The package introduces a new minor mode, Verb mode, which works as an extension
to Org mode. The core idea is to organize specifications for HTTP requests
using Org's tree structure. Properties defined in the child headings extend or
sometimes override properties defined in the parent headings - this way, it is
easy to define many HTTP request specifications without having to repeat common
components as URL hosts, authentication headers, ports, etc. *Verb* tries to
combine the usefulness of Org mode with the common functionality provided by
other HTTP clients. However, very little knowledge of Org mode is needed to use
Verb.

#+begin_src emacs-lisp
(use-package verb :ensure t)

(with-eval-after-load 'org
(define-key grandview-prog-map (kbd "p") verb-command-map))
#+end_src

*** Scratch buffer (scratch)
:PROPERTIES:
:CUSTOM_ID: h:13503906-8D66-4DF7-93A3-EA726D9C1771
:END:

=+scratch= command 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!

#+begin_src emacs-lisp
(define-key grandview-app-map (kbd "s") '+scratch)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:DB49C042-A133-4B4B-9773-36016E5009B1
:header-args:emacs-lisp: :tangle (expand-file-name "+scratch.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(defun +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 +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 "*Grandview-scratch*")))
(unless buf
(let* ((new-buf (generate-new-buffer "*Grandview-scratch*"))
(jpt-session "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: " (+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)))
#+end_src

** Programming languages
:PROPERTIES:
:CUSTOM_ID: h:D0B53A0B-18C5-4246-A9EA-227DF576E301
:END:

Specfic configuartions for different programming languages.

*** Rust
:PROPERTIES:
:CUSTOM_ID: h:55CECA8C-5AF1-4A69-94C8-EE19FB31AEB3
:END:

#+begin_src emacs-lisp
(use-package rust-ts-mode :ensure nil)
#+end_src

*** Csharp
:PROPERTIES:
:CUSTOM_ID: h:FE82B63A-051F-4017-AF2D-39CAE2055730
:END:

Sharper is a Transient-based menu for the dotnet CLI. It aims to cover the most
common scenarios. To invoke it, type =M-x sharper-main-transient= in a .cs file.

#+begin_src emacs-lisp
(use-package csharp-mode :ensure nil)
(use-package sharper :ensure t)

(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-ts-mode))
(add-to-list 'auto-mode-alist '("\\.[a]?xaml\\'" . nxml-mode))

(with-eval-after-load 'csharp-mode
(setq csharp-ts-mode-indent-offset 2)
(require 'sharper)
;; A temporary fix for dotnet-sdk installation path on Apple Silicon machines
(when (and (eq system-type 'darwin)
(string-match "aarch64-.*" system-configuration))
(setenv "DYLD_FALLBACK_LIBRARY_PATH" "/usr/local/lib:/usr/local/lib64:/opt/homebrew/lib")
(setenv "DOTNET_ROOT" "/usr/local/share/dotnet")))
#+end_src

*** Python
:PROPERTIES:
:CUSTOM_ID: h:EA0C4A6F-7802-4918-BE58-74B92F26071F
:END:

#+begin_src emacs-lisp
(use-package python :ensure nil)

(add-to-list 'auto-mode-alist '("\\.py\\'" . python-ts-mode))

(setq python-indent-offset 2)
(setq python-indent-guess-indent-offset-verbose nil)
#+end_src

*** Lisp | Elisp
:PROPERTIES:
:CUSTOM_ID: h:578FBB21-6FDF-4845-8F05-A300039FF08E
:END:

#+begin_src emacs-lisp
(setq lisp-indent-offset nil)
#+end_src

*** Lua
:PROPERTIES:
:CUSTOM_ID: h:9531A99F-8F6E-4B98-9EE2-F152AACD6134
:END:

#+begin_src emacs-lisp
(use-package lua-ts-mode :ensure nil)

(add-to-list 'auto-mode-alist '("\\.lua\\'" . lua-ts-mode))

(setq lua-indent-level 2)
#+end_src

*** Yaml
:PROPERTIES:
:CUSTOM_ID: h:013A24A9-6CC4-48B5-8681-EF53A79CDEC0
:END:

#+begin_src emacs-lisp
(use-package yaml-ts-mode :ensure nil)

(add-to-list 'auto-mode-alist '("\\.y[a]?ml\\'" . yaml-ts-mode))
#+end_src

*** Javascript | Typescript
:PROPERTIES:
:CUSTOM_ID: h:9EB7B53E-7346-4236-A5BF-67594F996D3F
:END:

#+begin_src emacs-lisp
(use-package js :ensure nil)
(use-package typescript-ts-mode :ensure nil)

(add-to-list 'auto-mode-alist '("\\.js\\'" . js-ts-mode))
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))

(add-hook 'typescript-ts-mode-hook #'eglot-ensure)
(add-hook 'js-ts-mode-hook #'eglot-ensure)
(setq js-indent-level 2)
#+end_src

*** Web template (web-mode.el)
:PROPERTIES:
:CUSTOM_ID: h:D9E48633-7EE3-42CB-B0CA-C9CC6F71E7D6
:END:

=web-mode.el= is an emacs major mode for editing web templates aka HTML files
embedding parts (CSS/JavaScript) and blocks (pre rendered by client/server side
engines). It is compatible with many template engines: PHP, JSP, ASP, Django,
React/JSX, Angularjs, ejs, etc.

The default indent level and text padding (to the left edge) is a bit strange to
me, so I change them to my liking.

#+begin_src emacs-lisp
(use-package web-mode :ensure t)

(with-eval-after-load 'web-mode
(setq
web-mode-markup-indent-offset 2
web-mode-css-indent-offset 2
web-mode-code-indent-offset 2
web-mode-part-padding 0
web-mode-script-padding 0
web-mode-style-padding 0))
#+end_src

**** vue
:PROPERTIES:
:CUSTOM_ID: h:FDC89DB8-32D0-4794-898D-E273028D0FB8
:END:

Template tags in vue's template can become very complicated, that's the reason I
still use =web-mode= to edit .vue files, after all it comes with a lot of features
which =vue-ts-mode= (see: [[https://github.com/8uff3r/vue-ts-mode][vue-ts-mode]]) failed to provide for now. Maybe I'll
switch to =vue-ts-mode= in the future.

#+begin_src emacs-lisp
(define-derived-mode vue-mode web-mode "Vue")
(add-to-list 'auto-mode-alist '("\\.vue\\'" . vue-mode))

(add-hook 'vue-mode-hook #'eglot-ensure)

(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs +vue-eglot-option))

(with-eval-after-load 'copilot
(add-to-list
'copilot-indentation-alist '(vue-mode web-mode-code-indent-offset)))
#+end_src

***** Extras
:PROPERTIES:
:CUSTOM_ID: h:3711C8CE-5305-4F82-9905-0A8DB2F843A8
:header-args:emacs-lisp: :tangle (expand-file-name "+vue.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defcustom +vue-eglot-option
'((vue-mode typescript-ts-mode) .
("vue-language-server" "--stdio"
:initializationOptions
(:typescript
(:tsdk
"/opt/homebrew/lib/node_modules/typescript/lib"
:languageFeatures
(:completion
(:defaultTagNameCase
"both" :defaultAttrNameCase "kebabCase"
:getDocumentNameCasesRequest nil
:getDocumentSelectionRequest nil)
:diagnostics (:getDocumentVersionRequest nil))
:documentFeatures
(:documentFormatting
(:defaultPrintWidth
100 :getDocumentPrintWidthRequest nil)
:documentSymbol t :documentColor t))
:vue (:hybridMode :json-false))))
"Lsp client options for vue - typescript project.
The string followed by `:tsdk' is the path to typescript sdk library,
which may vary across in different OS, usually you can get the path by
evaluate the code below:
`(expand-file-name \"lib\" (string-trim-right
(shell-command-to-string \"npm list -g -p typescript | head -n1\")))'
It basically instructs npm to find the path by itself. Run this npm
query is quite expensive, which makes emacs lag for a second everytime a
new vue typescript project is opened. Hence, set it manually seems a
better option."
:type 'eglot-server-program)
#+end_src

*** Css
:PROPERTIES:
:CUSTOM_ID: h:FB19DD40-5FE1-4020-A5A4-35F622F1015F
:END:

#+begin_src emacs-lisp
(use-package css-mode :ensure nil)

(add-to-list 'auto-mode-alist '("\\.css\\'" . css-ts-mode))

(setq css-indent-offset 2)
#+end_src

*** Bash | Zsh
:PROPERTIES:
:CUSTOM_ID: h:688926D3-73C7-4916-A001-759600DCC64C
:END:

#+begin_src emacs-lisp
(use-package sh-script :ensure nil)

(setq sh-basic-offset 2)
#+end_src

* Utilities & Libraries
:PROPERTIES:
:CUSTOM_ID: h:6019F888-2F43-40CD-89C6-03D542D48FA4
:END:

Emacs is notable for its integration with many common tools. Not only can you
invoke them from within the editor, Emacs usually helps you use their output
more effectively.

** Get help in Emacs
:PROPERTIES:
:CUSTOM_ID: h:B5C5A759-2EF0-4B56-8E18-B1AD5E3D3D96
:END:

This section details how to access help information within Emacs, including
relevant configurations and guides.

*** Emacs Manual (info.el)
:PROPERTIES:
:CUSTOM_ID: h:59DC4309-57FF-4762-8ECC-96826FC36D96
:END:

The keybinding =SPC h i= (or =C-h i= if meow-keypad-mode is not enabled) opens the
Info documentation tree, which covers almost all topics about Emacs itself and
installed packages (provided corresponding .info documents are available). I've
added some keybindings to simplify navigation within this tree.

#+begin_src emacs-lisp
(use-package info :ensure nil)

(with-eval-after-load 'info
(bind! Info-mode-map
"n" 'next-line
"p" 'previous-line
"C-n" 'Info-next
"C-p" 'Info-prev))
#+end_src

*** Man page (man.el)
:PROPERTIES:
:CUSTOM_ID: h:E15D7EFF-09E4-481B-8B4C-8AE08F46A65E
:END:

=man.el= lets you view command man pages directly within Emacs, instead of using
the command-line tool. The powerful document navigation and text manipulation
tools we've already set up make reading manuals much easier.

#+begin_src emacs-lisp
(use-package man :ensure nil)

(setq Man-notify-method 'newframe)
(with-eval-after-load 'man
(define-key Man-mode-map (kbd "q") 'kill-this-buffer))
#+end_src

*** Helpful (helpful.el)
:PROPERTIES:
:CUSTOM_ID: h:9B7AA6E6-C7BE-41CD-B523-CB34BB185E0A
:END:

=helpful.el= provides a better help buffer. It's always a good thing for us to be
able to read the source code of the symbol we are looking at since it tells more
information than its docstring. Again, because we use =meow=, the actual prefix
to enable these shortcuts is =SPC h=. For example, =SPC h s= invokes
=helpful-symbol=.

#+begin_src emacs-lisp
(use-package helpful :ensure t)

(bind! help-map
"/" 'helpful-symbol
"v" 'helpful-variable
"K" 'describe-keymap
"C-f" 'helpful-callable
"k" 'helpful-key)
#+end_src

*** Find libraries (find-func.el)
:PROPERTIES:
:CUSTOM_ID: h:386D803D-CA14-4ACE-A7A6-F5B137E7C353
:END:

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 Emacs
itself as long as the ~find-function-C-source-directory~ is set properly.

#+begin_src emacs-lisp
(use-package find-func :ensure nil :after-call minibuffer-setup-hook)

(define-key +find-file-prefix-map (kbd "l") 'find-library)
#+end_src

** Vterm - performant terminal emulator in Emacs
:PROPERTIES:
:CUSTOM_ID: h:B49FA4E0-EB36-4734-AF25-54D6E6EFEBA6
:END:

Emacs-libvterm (=vterm=) is a powerful terminal emulator that leverages an
external library (libvterm) loaded as a dynamic module. Using compiled code
instead of Emacs Lisp, allows vterm to be fast, fully capable, and seamlessly
handle large outputs.

#+begin_src emacs-lisp
(use-package vterm :ensure t)

(setopt vterm-keymap-exceptions '("C-c" "C-h"))

(defun +vterm-setup-window (window)
"Select WINDOW and set its fringes."
(select-window window)
(set-window-fringes window 10))

(push '("^\\*vterm.*"
(display-buffer-in-side-window)
(body-function . +vterm-setup-window)
;; TODO: useful mode-line for vterm buffers
;; (window-parameters . ((mode-line-format . none)))
(window-width . 0.5)
(side . right))
display-buffer-alist)

(with-eval-after-load 'vterm
(setq vterm-max-scrollback 5000))
#+end_src

*** Poorman's terminal multiplexer
:PROPERTIES:
:CUSTOM_ID: h:B68077F6-09F1-4D8D-B5EE-7B1DCB0F44CD
:END:

*+vterm-stack* provides a stack-based navigation for vterm buffers, allowing you
to easily switch between them within current tab (managed by *tab-bar*), create
new ones, and toggle them.

+ =+vterm-stack-new=: Creates a new vterm instance in current tab.
+ =+vterm-stack-next= | =+vterm-stack-prev=: Navigate between vterm instances of
current tab.
+ =+vterm-stack-toggle=: Toggles a vterm side window. If used with a prefix
argument, the new vterm instance is created from the current project root.

#+begin_src emacs-lisp
(defvar-keymap +vterm-prefix-map
:doc "Keymap for vterm commands."
"t" #'+vterm-stack-toggle
"n" #'+vterm-stack-next
"p" #'+vterm-stack-prev
"o" #'+vterm-stack-new)

(define-key grandview-app-map (kbd "t") +vterm-prefix-map)

(with-eval-after-load 'tab-bar
(require '+vterm-stack)
(add-hook '+vterm-stack-new-buffer-hook #'meow-insert)
(add-hook 'tab-bar-tab-post-open-functions #'+vterm-stack-tab-post-open-h)
(add-hook 'tab-bar-tab-pre-close-functions #'+vterm-stack-tab-pre-close-h))
#+end_src

**** Extras ::: +vterm-stack.el
:PROPERTIES:
:CUSTOM_ID: h:B05AAF84-FA1E-42FC-8C55-C7C9AD951D7E
:header-args:emacs-lisp: :tangle (expand-file-name "+vterm-stack.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(require 'vterm)

(defvar +vterm-stack-list '((:current nil :stack nil))
"The list of non-dedicated vterm buffer stacks.
The structure is:
'((:current BUF-ID :stack ((BUF-ID . BUF) (BUF-ID . BUF) ...))
(:current BUF-ID :stack ((BUF-ID . BUF) (BUF-ID . BUF) ...))
...)")

(defvar +vterm-stack-new-buffer-hook nil
"List of hooks to call when a new vterm stack buffer created.")

(defun +vterm-stack-kill-buffer-h ()
"Remove killed buffer from `+vterm-stack-list'.
Used as a hook function added to `kill-buffer-hook'."
(let* ((buf (current-buffer))
(tab-index (cl-position-if
(lambda (data)
(member buf (mapcar #'cdr (plist-get data :stack))))
+vterm-stack-list))
(tab-data (nth tab-index +vterm-stack-list))
(tab-stack (plist-get tab-data :stack))
(id (car (rassq buf tab-stack)))
(updated-stack (delq (assoc id tab-stack) tab-stack))
(next (caar updated-stack))
(curr (plist-get tab-data :current)))
(when tab-data
(setf (nth tab-index +vterm-stack-list)
`(:current ,(cond ((not next) nil) ((equal id curr) next) (t curr))
:stack ,updated-stack)))))

;;;###autoload
(defun +vterm-stack-new (&optional name)
"Create new vterm buffer and insert it to `+vterm-stack-list'.
Optional NAME to specify buffer name."
(interactive (list (read-string "Name for new vterm: ")))
(let* ((buf-name (if (or (not name) (equal name ""))
(make-temp-name "")
name))
(buf (vterm (format "*vterm*%s" buf-name)))
(tab-index (tab-bar--current-tab-index))
(tab-data (nth tab-index +vterm-stack-list))
(tab-stack (plist-get tab-data :stack)))
(if tab-data
(setf (nth tab-index +vterm-stack-list)
`(:current ,buf-name
:stack ,(cons (cons buf-name buf) tab-stack)))
(add-to-list
'+vterm-stack-list
(list :current buf-name :stack (list (cons buf-name buf)))))
(with-current-buffer buf
(run-hooks '+vterm-stack-new-buffer-hook)
(add-hook 'kill-buffer-hook #'+vterm-stack-kill-buffer-h nil t))))

(defun +vterm-stack-next (&optional reverse)
"Select next vterm buffer in `+vterm-stack-list'.
When REVERSE, select the previous one."
(interactive "P")
(when-let* (((derived-mode-p 'vterm-mode))
(curr-buf (current-buffer))
(tab-index (tab-bar--current-tab-index))
(tab-data (nth tab-index +vterm-stack-list))
(tab-stack (plist-get tab-data :stack))
(curr-id (cl-position curr-buf tab-stack :key #'cdr))
(next-id (+ curr-id (if reverse 1 -1)))
(next-pair (when (>= next-id 0)
(nth next-id tab-stack)))
(next-buf (cdr next-pair)))
(switch-to-buffer next-buf)
(vterm-goto-char (point))
(setf (nth tab-index +vterm-stack-list)
`(:current ,(car next-pair) :stack ,tab-stack))))

(defun +vterm-stack-prev ()
"Select previous vterm buffer in `+vterm-stack-list'."
(interactive)
(+vterm-stack-next 'reverse))

;;;###autoload
(cl-defun +vterm-stack-toggle (&optional in-place)
"Toggle current vterm buffer recorded in `+vterm-stack-list'.
If IN-PLACE, create a new vterm buffer with `default-directory',
otherwise create a new vterm buffer with project root directory as
`default-directory'."
(interactive "P")
(when (derived-mode-p 'vterm-mode)
(cl-return-from +vterm-stack-toggle (delete-window)))
(cl-loop for w in (window-list (selected-frame))
when (with-selected-window w (derived-mode-p 'vterm-mode))
do (cl-return-from +vterm-stack-toggle (select-window w)))
(let* ((pr-root (or (ignore-errors (project-root (project-current)))
default-directory))
(default-directory (if in-place default-directory pr-root))
(tab-index (tab-bar--current-tab-index))
(tab-data (nth tab-index +vterm-stack-list))
(buf-id (plist-get tab-data :current))
(tab-stack (plist-get tab-data :stack)))
(when-let* ((buf (cdr (assoc buf-id tab-stack))))
(cl-return-from +vterm-stack-toggle (display-buffer buf)))
(+vterm-stack-new "default")))

(defun +vterm-stack-tab-post-open-h (tab)
"Re-index TAB indices in `+vterm-stack-list'."
(when-let* ((idx (tab-bar--tab-index tab)) ((> idx 0)))
(push '(:current nil :stack nil) (nthcdr idx +vterm-stack-list))))

(defun +vterm-stack-tab-pre-close-h (tab _last-tab-p)
"Re-index TAB indices in `+vterm-stack-list'."
(cl-loop for idx from 0
for stack in +vterm-stack-list
do (progn (when (equal (tab-bar--tab-index tab) idx)
(setf (nth idx +vterm-stack-list) nil)))
finally (setq +vterm-stack-list (remove nil +vterm-stack-list))))
#+end_src

*** Integrate with meow
:PROPERTIES:
:CUSTOM_ID: h:5A93DE31-FD9B-4DF0-BF49-3434E722A5DB
:END:

*Vterm* consumes all inputs other than its exceptions, making it not integrate
well with meow. This can be solved by using a custom keymap in normal mode with
standard bindings and using the default =vterm-mode-map= in =meow-insert-mode=.

#+begin_src emacs-lisp
(add-hook 'vterm-mode-hook #'+vterm-meow-setup)

(with-eval-after-load 'vterm
;; bring back ESC taken by `meow-insert-mode-map'
(define-key vterm-mode-map (kbd "C-c ESC") #'vterm-send-escape))
#+end_src

**** Extras ::: +vterm-meow.el
:PROPERTIES:
:header-args:emacs-lisp: :tangle (expand-file-name "+vterm-meow.el" grandview--def-dir)
:CUSTOM_ID: h:F8C913CE-EC1D-4CF5-8562-8FAE30296AF0
:END:

Adopted from https://github.com/accelbread/meow-vterm

#+begin_src emacs-lisp
(require 'vterm)
(require 'meow)

(defvar +vterm-meow-normal-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "RET") #'vterm-send-return)
(define-key map [remap yank] #'vterm-yank)
(define-key map [remap xterm-paste] #'vterm-xterm-paste)
(define-key map [remap yank-pop] #'vterm-yank-pop)
(define-key map [remap mouse-yank-primary] #'vterm-yank-primary)
(define-key map [remap self-insert-command] #'vterm--self-insert)
(define-key map [remap beginning-of-defun] #'vterm-previous-prompt)
(define-key map [remap end-of-defun] #'vterm-next-prompt)
map)
"Keymap for vterm in normal mode.")

(defun +vterm-meow-insert-enter ()
"Enable vterm default binding in insert and set cursor."
(use-local-map vterm-mode-map)
(vterm-goto-char (point)))

(defun +vterm-meow-insert-exit ()
"Use regular bindings in normal mode."
(use-local-map +vterm-meow-normal-mode-map))

;;;###autoload
(defun +vterm-meow-setup ()
"Configure insert mode for vterm."
(add-hook 'meow-insert-enter-hook #'+vterm-meow-insert-enter nil t)
(add-hook 'meow-insert-exit-hook #'+vterm-meow-insert-exit nil t)
(use-local-map +vterm-meow-normal-mode-map))
#+end_src

*** SHELL | REPL commands
:PROPERTIES:
:CUSTOM_ID: h:D8D2AB9A-1BC8-4B15-AA1E-BB0938E4B3AD
:END:

The terminal can't differentiate between certain shortcuts, like ~/~ and ~C-/~,
preventing them from being bound to separate commands. This is unfortunate
because they are very easy to press. To work around this, we can use function
keys (~F1~ - ~F12~) as an intermediary, mapping these indistinguishable shortcuts to
different function keys, and then binding those function keys to the desired
commands. See [[#h:46996B4B-A490-4981-B583-51ED036305CD][Keyboard orientation]] and my [[http://github.com/alexluigit/zenish][zsh profile]] for details.

#+begin_src emacs-lisp
(with-eval-after-load 'vterm
(require '+vterm-commands)
(bind! +vterm-prefix-map "s" #'+vterm-send-region)
(bind! vterm-mode-map
"S-SPC" nil
"S-" #'+vterm-send-F5
"C-/" #'+vterm-send-F6
"" #'+vterm-send-F7
"C-" #'+vterm-send-F8
"C-," #'+vterm-send-F10
"C-." #'+vterm-send-F11
"C-;" #'+vterm-send-F12))
#+end_src

**** Extras ::: +vterm-commands.el
:PROPERTIES:
:header-args:emacs-lisp: :tangle (expand-file-name "+vterm-commands.el" grandview--def-dir)
:CUSTOM_ID: h:F8C913CE-EC1D-4CF5-8562-8FAE30296AF0
:END:

#+begin_src emacs-lisp
(defun +vterm-send-F5 ()
"Send F5 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-F6 ()
"Send F6 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-F7 ()
"Send F7 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-F8 ()
"Send F8 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-F10 ()
"Send F10 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-F11 ()
"Send F11 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-F12 ()
"Send F12 to shell."
(interactive)
(vterm-send-key "" nil nil nil))

(defun +vterm-send-region (beg end)
"Send the region delimited by BEG and END to current inferior."
(interactive "r\n")
(cl-loop
with str = (buffer-substring-no-properties beg end)
for w in (window-list (selected-frame))
when (with-selected-window w (derived-mode-p 'vterm-mode))
return (with-current-buffer (window-buffer w)
(vterm-send-string str)
(vterm-send-return))))
#+end_src

** AI
:PROPERTIES:
:CUSTOM_ID: h:3F098843-7B8B-4BC0-B729-314C0B40D296
:END:
*** Github Copilot client (copilot.el)
:PROPERTIES:
:CUSTOM_ID: h:29D887A7-7848-4AD7-AB6C-B1E6CE0073B0
:END:

#+begin_src emacs-lisp
(use-package copilot
:vc (:url "https://github.com/copilot-emacs/copilot.el"))

(define-key grandview-prog-map (kbd "c") 'copilot-mode)

(with-eval-after-load 'copilot
(bind! copilot-completion-map
"" 'copilot-accept-completion
"TAB" 'copilot-accept-completion
"S-TAB" 'copilot-accept-completion-by-line))
#+end_src

*** Interact with GPTs (gptel)
:PROPERTIES:
:CUSTOM_ID: h:D088502B-CC12-4E4B-A994-6B248AEB453F
:END:

=gptel= is a great package from *@karthink*. It looks very promising as a LLM chat
client. The only tweak I made about it is to let gptel knows how to place the
newly opened chat window:
1. the chat buffer should never interrupt the existing window layout, i.e,
always create a new window for the chat.
2. always put the chat window at rightmost of the frame.

It's also worth mentioning that the acquisition of apikey for the chat client is
handled by the =password-store= library, it expects that your apikey is
accessiable by the =pass= command.

#+begin_src emacs-lisp
(use-package gptel :ensure t)

(with-eval-after-load 'gptel
(setq
gptel-display-buffer-action
`(display-buffer-in-direction
(body-function . ,#'select-window) (direction . rightmost))
gptel-directives
'((default . "You are a large language model living in Emacs and a helpful assistant.")
(programming . "You are a large language model and a careful programmer. Provide code and only code as output without any additional text, prompt, note. Provide only updated part of the code unless 1. it's for a new file 2. it's a rewrite 3. I asked for full codebase explicitly in prompt.")
(writing . "You are a large language model and a writing assistant. Respond concisely.")
(chat . "You are a large language model and a conversation partner. Respond concisely."))
gptel-default-mode 'org-mode
gptel-model 'gemini-2.0-flash-exp
gptel-backend
(gptel-make-gemini "Gemini"
:key (lambda () (require 'password-store) (password-store-get "gemini_apikey"))
:stream t))
(add-hook 'gptel-mode-hook #'visual-line-mode))

(defvar-keymap +gptel-prefix-map
:doc "Summon gpt models."
"n" #'gptel ; start [n]ew dialog
"a" #'gptel-add
"f" #'gptel-add-file
"s" #'gptel-send
"r" #'gptel-rewrite
"t" #'gptel-org-set-topic
"p" #'gptel-org-set-properties
"o" #'gptel-menu ; change [o]ptions
"m" #'gptel-mode)

;; a key for AI
(bind! grandview-app-map "a" +gptel-prefix-map)
#+end_src

** Version Control
:PROPERTIES:
:CUSTOM_ID: h:805C7D86-52FA-4C21-9180-A408F38D5B95
:END:
*** Built-in vc library (vc.el)
:PROPERTIES:
:CUSTOM_ID: h:052B95EE-5D87-4A0C-99D4-FFF2D0D40087
:END:

=vc.el= provides seamless integration of version control systems within Emacs,
allowing you to perform common version control operations directly from the
editor. Press =SPC a v= to access the available commands.

Extras:
+ Rename the buffer of =vc-print-root-log= to mention the project.

#+begin_src emacs-lisp
(use-package vc :ensure nil)

(define-key grandview-app-map (kbd "v") vc-prefix-map)
#+end_src

**** Extras
:PROPERTIES:
:header-args:emacs-lisp: :tangle (expand-file-name "+vc.el" grandview--def-dir)
:CUSTOM_ID: h:931E886F-B8C7-42BD-A1F1-16DA75406593
:END:

#+begin_src emacs-lisp
;;;###autoload
(defadvice! +vc-rename-root-log-a (&rest _)
"Rename the buffer of `vc-print-root-log' to mention the project."
:after #'vc-print-root-log
(when-let* ((root (vc-root-dir))
((progn (project-known-project-roots)
(consp project--list)))
((member root (mapcar #'car project--list))))
(rename-buffer (format "*vc-root-log: %s*" root))))
#+end_src

*** Git porcelain (magit.el)
:PROPERTIES:
:CUSTOM_ID: h:2F3C245E-4DE8-46F4-9168-0B45BED441E1
:END:

Magit is a text-based Git user interface that puts an unmatched focus on
streamlining workflows. Commands are invoked using short mnemonic key sequences
that take the cursor's position in the highly actionable interface into account
to provide context-sensitive behavior.

#+begin_src emacs-lisp
(use-package magit :ensure t)

(define-key grandview-app-map (kbd "m") 'magit-status-here)
(with-eval-after-load 'magit
(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)))
(define-key magit-diff-section-base-map
(kbd "") 'magit-diff-visit-file-other-window))
#+end_src

**** Work with Github from the comfort of Magit (forge.el)
:PROPERTIES:
:CUSTOM_ID: h:DCD08E96-5E66-4791-83EF-601C6C7420C3
:END:

#+begin_src emacs-lisp
(use-package forge :after magit :ensure t)
#+end_src

*** Interactive diff, patch, or merge conflict (ediff.el)
:PROPERTIES:
:CUSTOM_ID: h:C990DF9D-D266-4514-A1B6-B86F262036CA
:END:

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

#+begin_src emacs-lisp
(use-package ediff :ensure nil)

(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
'("<<<<<<< grandv-ediff-combine Variant A" A
">>>>>>> grandv-ediff-combine Variant B" B
"####### grandv-ediff-combine Ancestor" Ancestor
"======= grandv-ediff-combine End"))
#+end_src

*** Hunk indicator (git-gutter.el)
:PROPERTIES:
:CUSTOM_ID: h:0969690B-0AF3-46AE-8A9E-F61150723E9F
:END:

#+begin_src emacs-lisp
(use-package git-gutter :ensure t)

(setq git-gutter:modified-sign "⏽"
git-gutter:added-sign "⏽"
git-gutter:deleted-sign "⏽")
#+end_src

** Interface enhancement
:PROPERTIES:
:CUSTOM_ID: h:6762E5C6-D007-452E-8328-47EF50515A8B
:END:
*** Workspaces
:PROPERTIES:
:CUSTOM_ID: h:AF5996B4-03F2-439A-8EA9-4087A2C152C9
:END:
**** Frame-local tabs (tab-bar.el)
:PROPERTIES:
:CUSTOM_ID: h:772C6B34-A85D-4F75-A9DE-A38B5B4DFB75
:END:

=tab-bar-mode= allows you to create multiple tabs within each Emacs frame, similar
to the tabbed interface found in standard text editors and browsers. However,
Emacs tabs are more powerful: each one saves a complete window configuration,
not just a single buffer. This means you can arrange your window into multiple
buffers, save that layout as a tab with a name, and then switch to a different
tab with a completely different arrangement of buffers.

The customizations I made here are just for visual appeal.

#+begin_src emacs-lisp
(use-package tab-bar :ensure nil)

(bind! tab-prefix-map
"n" #'tab-new ; `n' -> [n]ew feels much natural
"2" #'tab-duplicate)

(setq
tab-bar-new-tab-choice "*scratch*"
tab-bar-separator " "
tab-bar-close-button-show nil
tab-bar-close-last-tab-choice 'tab-bar-mode-disable
tab-bar-tab-name-format-function #'+tab-bar-tab-name-format
tab-bar-format '(+tab-bar-format-menu-bar
tab-bar-format-tabs
tab-bar-separator
tab-bar-format-align-right
tab-bar-format-global))
#+end_src

***** Extras
:PROPERTIES:
:CUSTOM_ID: h:FF6995C8-B5C2-4B1A-A1D6-1E1780E1F582
:header-args:emacs-lisp: :tangle (expand-file-name "+tab-bar.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +tab-bar-format-menu-bar ()
"Produce the Menu button for the tab bar that shows the menu bar."
`((menu-bar menu-item (propertize " 𝝺 " 'face 'cursor)
tab-bar-menu-bar :help "Menu Bar")))

;;;###autoload
(defun +tab-bar-tab-name-format (tab index)
"Format name of TAB with INDEX."
(propertize (concat " " (tab-bar-tab-name-format-default tab index) " ")
'face (funcall tab-bar-tab-face-function tab)))
#+end_src

**** Buffer-isolated workspaces (tabspaces.el)
:PROPERTIES:
:CUSTOM_ID: h:8853080E-7EA8-4666-A69C-7E9684F097C1
:END:

*Tabspaces* leverages *tab-bar.el* and *project.el* (both built into emacs 27+) to
create buffer-isolated workspaces (or "tabspaces") that also integrate with your
version-controlled projects.

Because *tabspaces* is built upon *tab-bar*, it's logical to bind its commands to
the =tab-prefix-map= (~SPC TAB~). ~SPC TAB TAB~ provides access to them.

The =+tabspaces-integrate-consult= function would seamlessly integrate workspace
buffers into =consult-buffer=, displaying workspace buffers by default and all
buffers when narrowing using =b=. Note that you can also see all project related
buffers and files just by narrowing with =p= in a default consult setup.

The =+tabspaces-startup-workspaces= variable lets you define one or more
workspaces that will be created when Emacs starts.

#+begin_src emacs-lisp
(use-package tabspaces :ensure t :after-call tab-bar-mode-hook)

(when +tabspaces-startup-workspaces
(add-hook 'server-after-make-frame-hook #'+tabspaces-init-frame))

(with-eval-after-load 'tabspaces
(require '+tabspaces)
(tabspaces-mode 1)

(setq tabspaces-keymap-prefix nil
tabspaces-default-tab "Default"
tabspaces-include-buffers '("*scratch*" "*Messages*")
tabspaces-remove-to-default t)

(define-key tab-prefix-map (kbd "TAB") tabspaces-command-map)

(with-eval-after-load 'consult (+tabspaces-integrate-consult)))
#+end_src

***** Extras
:PROPERTIES:
:CUSTOM_ID: h:FF6995C8-B5C2-4B1A-A1D6-1E1780E1F582
:header-args:emacs-lisp: :tangle (expand-file-name "+tabspaces.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(defcustom +tabspaces-startup-workspaces nil
"Workspaces being created at startup."
:group 'grandview :type 'alist)

(defun +tabspaces-init-frame ()
"Setup tabspaces when a frame is created as well."
(cl-loop
with dir-fn = (if (fboundp 'dirvish) #'dirvish #'dired)
for (name . path) in +tabspaces-startup-workspaces
do (if (member name (tabspaces--list-tabspaces))
(progn (tab-bar-switch-to-tab name)
(when (and (not (get-buffer name)) path)
(apply dir-fn path)))
(tab-bar-new-tab)
(tab-bar-rename-tab name)
(when path (apply dir-fn path)))
finally do (tab-bar-select-tab 1)))

(defun +tabspaces-integrate-consult ()
"Integrate `tabspaces' with `consult'."
(consult-customize consult--source-buffer :hidden t :default nil)
(defvar consult--source-workspace
(list :name "Workspace Buffers"
:narrow ?w
:history 'buffer-name-history
:category 'buffer
:state #'consult--buffer-state
:default t
:items (lambda () (consult--buffer-query
:predicate #'tabspaces--local-buffer-p
:sort 'visibility
:as #'buffer-name)))
"Set Workspace buffer list for consult buffer.")
(add-to-list 'consult-buffer-sources 'consult--source-workspace))
#+end_src

*** Composable mode line
:PROPERTIES:
:CUSTOM_ID: h:9C974B19-F47B-4758-87BD-A889965D7239
:END:

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
~+mode-line-define-segment~, and don't forget to put your newly defined segments
in ~+mode-line-format~.

#+begin_src emacs-lisp
(+mode-line-mode)
#+end_src

**** Extras ::: +mode-line.el
:PROPERTIES:
:CUSTOM_ID: h:E77C4AD0-D38E-43D5-AFAC-C3135D88A444
:header-args:emacs-lisp: :tangle (expand-file-name "+mode-line.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
(defun +mode-line--fmt-setter (k fmt)
"Setter for `+mode-line-format'."
(cl-labels ((expand (l)
(cl-loop for s in l collect
`(:eval (+mode-line--suffix
',(intern (format "+mode-line-%s-seg" s)))))))
(let ((fmt-left (or (expand (plist-get fmt :left)) mode-line-format))
(fmt-right (expand (plist-get fmt :right))))
(set k `((:eval
(let* ((str-right (format-mode-line ',fmt-right))
(spec `((space :align-to
(- (+ right right-fringe right-margin)
,(string-width str-right))))))
(concat (format-mode-line ',fmt-left)
(propertize " " 'display spec)
str-right))))))))

(defcustom +mode-line-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 `+mode-line-define'."
:set #'+mode-line--fmt-setter)

(defcustom +mode-line-height 21
"Modeline's height in pixel.")

(defvar +mode-line-selected-window nil)

(defun +mode-line-disable-locally ()
"Do not show mode line in this buffer."
(setq mode-line-format nil))

(defun +mode-line--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 +mode-line-selected-window
(eq (+mode-line--get-current-window) +mode-line-selected-window))))

(defun +mode-line--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 +mode-line-record-selected-window-h (&rest _)
"Update `+mode-line-selected-window' on redisplay."
(let ((win (+mode-line--get-current-window)))
(setq +mode-line-selected-window
(if (minibuffer-window-active-p win)
(minibuffer-selected-window)
win))))

(add-hook 'window-selection-change-functions #'+mode-line-record-selected-window-h)

(defun +mode-line--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 +mode-line-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 "+mode-line-%s-seg" name))))
`(defun ,ml () ,docstr ,@body)))

(+mode-line-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 +mode-line-height)
(make-string (* 2 +mode-line-height) ?1) "\n")
'pbm t :foreground "None" :ascent 'center)))))

(+mode-line-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)))

(+mode-line-define editing-state
(when (bound-and-true-p meow-mode) (meow-indicator)))

(+mode-line-define buffer-info
(concat
(and buffer-file-name (nerd-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)
((+mode-line--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)))

(+mode-line-define macro-rec
(when (or defining-kbd-macro executing-kbd-macro)
(propertize "KM" 'face 'warning)))

(+mode-line-define input-method
(when (bound-and-true-p rime-mode) (rime-lighter)))

(defvar +mode-line--default-mode-line mode-line-format
"Store the default mode-line format")

;;;###autoload
(define-minor-mode +mode-line-mode
"Toggle `+mode-line-mode' on or off."
:global t
(if +mode-line-mode
(progn
(setq-default mode-line-format +mode-line-format)
(add-hook 'compilation-mode-hook #'+mode-line-disable-locally))
(setq-default mode-line-format +mode-line--default-mode-line)
(remove-hook 'compilation-mode-hook #'+mode-line-disable-locally)))
#+end_src

*** Right-click with keyboard (embark.el)
:PROPERTIES:
:CUSTOM_ID: h:80BF3F35-E8B5-4710-88AD-3942261EF07B
:END:

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

#+begin_src emacs-lisp
(use-package embark :ensure t :after-call display-buffer minibuffer-setup-hook)
(use-package embark-consult :ensure t :after embark)

(with-eval-after-load 'embark
(setq embark-quit-after-action t)
(define-key global-map (kbd "C-.") 'embark-act)
(define-key minibuffer-local-map (kbd "C-.") 'embark-act)
(define-key minibuffer-local-map (kbd "C-,") 'embark-become))
#+end_src

*** Highlight focused window (auto-dim-other-buffers.el)
:PROPERTIES:
:CUSTOM_ID: h:1F76247C-9DAE-4BA7-B6B0-CEF3A8870EFA
:END:

Just dim other windows out so that we know better which window is the focused
one. We only load this package after we have more than 1 windows.

#+begin_src emacs-lisp
(use-package auto-dim-other-buffers :ensure t :after-call split-window)

(with-eval-after-load 'auto-dim-other-buffers
(auto-dim-other-buffers-mode))
#+end_src

*** Eldoc in childframe (eldoc-box.el)
:PROPERTIES:
:CUSTOM_ID: h:93A4474C-DD68-4017-BA27-0E45E369804F
:END:

This package displays ElDoc documentations in a childframe. The childframe is
selectable and scrollable with mouse, even though the cursor is hidden.

#+begin_src emacs-lisp
(use-package eldoc-box :ensure t :after-call flymake-mode)

;; [d]oc
(with-eval-after-load 'eldoc-box
(define-key grandview-prog-map (kbd "d") 'eldoc-box-help-at-point))
#+end_src

*** Pixel scrolling (pixel-scroll.el)
:PROPERTIES:
:CUSTOM_ID: h:E59DA6AB-6266-4CFA-BA46-97A4A8382F11
:END:

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

#+begin_src emacs-lisp
(when (boundp 'pixel-scroll-precision-mode)
(pixel-scroll-precision-mode 1))
#+end_src

*** Pulse highlight on demand (pulsar.el)
:PROPERTIES:
:CUSTOM_ID: h:F45A1F5E-7445-4427-B27F-CAF6EB415744
:END:

=pulsar.el= is another great package from Prot. This is a small package that
temporarily highlights the current line after a given function is invoked.

What we do here is to notify =pulsar= that I want the highlight also happens in
the following scenario:

+ On switching to a different window, e.g. after invoking =windmove-left=.
+ On navigating through errors / warnings, e.g. after invoking =next-error=.

#+begin_src emacs-lisp
(use-package pulsar :ensure t)

(pulsar-global-mode)
(with-eval-after-load 'pulsar
(appendq! pulsar-pulse-functions
(list 'flymake-goto-next-error 'flymake-goto-prev-error
'meow-save 'meow-replace 'meow-search
'meow-pop-to-mark 'moew-unpop-to-mark)))
#+end_src

*** Visual line wrap (visual-fill-column.el)
:PROPERTIES:
:CUSTOM_ID: h:2C9638FC-CCDD-436D-9401-2C9142785F33
:END:

=visual-fill-column-mode= is a small Emacs minor mode that mimics the effect of
=fill-column= in =visual-line-mode=. Instead of wrapping lines at the window edge,
which is the standard behaviour of =visual-line-mode=, it wraps lines at
=fill-column= (or =visual-fill-column-width=, if set). This is accomplished by
widening the margins, which narrows the text area. When the window size
changes, the margins are adjusted automatically.

When =visual-fill-column-center-text= is set, the text is centered in the window.
This can also be used in combination with =auto-fill-mode= instead of
=visual-line-mode=, or in programming modes.

#+begin_src emacs-lisp
(use-package visual-fill-column :ensure t)

;; [c]enter the text
(define-key +file-laf-prefix-map (kbd "c") '+visual-fill-column-center-mode)
#+end_src

**** Extras
:PROPERTIES:
:CUSTOM_ID: h:FC7300FE-855C-4AC5-9272-689829C7D4D7
:header-args:emacs-lisp: :tangle (expand-file-name "+visual-fill-column.el" grandview--def-dir)
:END:

#+begin_src emacs-lisp
;;;###autoload
(define-minor-mode +visual-fill-column-center-mode
"A thin wrapper on `visual-line-fill-column-mode'.
Use this mode to activate and deactivate `visual-line-mode' and
`visual-fill-column-mode' and text centering in conjunction."
:init-value nil :lighter nil :global nil
(if +visual-fill-column-center-mode
(progn
(setq-local visual-fill-column-center-text t)
(visual-line-fill-column-mode 1))
(setq-local visual-fill-column-center-text nil)
(visual-line-fill-column-mode -1)))
#+end_src

** Knowledge management
:PROPERTIES:
:CUSTOM_ID: h:0C192736-DDAD-4638-8611-AB8A58B3BA3B
:END:
*** Note taking tool (denote.el)
:PROPERTIES:
:CUSTOM_ID: h:B392C4E0-C2DF-4B59-B164-A106F05EA1A1
:END:

#+begin_src emacs-lisp
(use-package denote :ensure t)
#+end_src