Ecosyste.ms: Awesome

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

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

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

Last synced: 3 months ago
JSON representation

原非大观。

Lists

README

        

#+TITLE: Emacs Grandview
#+AUTHOR: Alex Lu
#+EMAIL: [email protected]
#+PROPERTY: header-args :mkdirp yes
#+STARTUP: showall

* Overview
:PROPERTIES:
:CUSTOM_ID: Overview-4853848f
:END:

** Introduction
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-0a365d38
:END:

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

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

*** User config
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-User_config-c93b584e
:END:

=user.el= is the file to place your:

1. *grandview* specfic options which include:
+ ~grandview-cache-dir~: Cache directory for grandview.
+ ~grandview-org-file~: Path for grandview main config .org file.
+ ~grandview-gc-cons-threshold~: The value for ~gc-cons-threshold~.
+ ~grandview-theme~: The default theme, it should be a theme name.
+ ~grandview-font-size~: Font size being applied.
+ ~grandview-default-font~: The default font, which should included in
~font-family-list~.
+ ~grandview-fixed-font~: The =fixed-pitch= font, which should included in
~font-family-list~.
+ ~grandview-variable-font~: The =variable-pitch= font, which should included in
~font-family-list~.
+ ~grandview-CJK-font~: The font used for CJK characters.
2. other arbitrary codes to load before loading the main config
3. packages expecting further evaluation or testing

*** This =.org= file
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-This_=.org=_file-59a7ea61
:END:

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

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

*** Screenshots
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-Screenshots-36873671
:END:

**** File manager
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-Screenshots-File_manager-b2a4921d
:END:
[[https://user-images.githubusercontent.com/16313743/169456875-ed5af1e7-57cd-4203-96e9-9038119721b9.png][https://user-images.githubusercontent.com/16313743/169456875-ed5af1e7-57cd-4203-96e9-9038119721b9.png]]

**** IDE
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-Screenshots-IDE-093ea33d
:END:
[[https://user-images.githubusercontent.com/16313743/169660050-b66d09b7-617e-46a0-a2a9-138c570d1336.png][https://user-images.githubusercontent.com/16313743/169660050-b66d09b7-617e-46a0-a2a9-138c570d1336.png]]

**** Org-mode
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-Screenshots-Org-mode-7fb34810
:END:
[[https://user-images.githubusercontent.com/16313743/169660518-bb1fd05c-089a-41db-830d-43784ae14d6d.png][https://user-images.githubusercontent.com/16313743/169660518-bb1fd05c-089a-41db-830d-43784ae14d6d.png]]

**** Lightning fast startup
:PROPERTIES:
:CUSTOM_ID: Overview-Introduction-Screenshots-Lightning_fast_startup-0c29acd2
:END:
[[https://user-images.githubusercontent.com/16313743/169660620-c5f7bef5-499a-4ea2-8a39-3e0f2801cb24.png][https://user-images.githubusercontent.com/16313743/169660620-c5f7bef5-499a-4ea2-8a39-3e0f2801cb24.png]]

** Installation
:PROPERTIES:
:CUSTOM_ID: Overview-Installation-e1935e0a
:END:

Make sure to backup your own emacs config before installation.

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

*** Dependencies
:PROPERTIES:
:CUSTOM_ID: Overview-Installation-Dependencies-423bdf47
:END:

Here are the dependencies of this project.

|-------------------+-----------------------------+----------|
| Package | Description | Required |
|-------------------+-----------------------------+----------|
| =fd= | A modern =find= | |
| =rg= | A modern =grep= | |
| =git= | Version control | |
| =noto-fonts-emoji= | Font for emojis | optional |
| =words= | English words completion | optional |
| =font-victor-mono= | Default fixed pitch font | optional |
| =ttf-sarasa-gothic= | Default variable pitch font | optional |
| =xdotool= | Automation tool for X11 | optional |
|-------------------+-----------------------------+----------|

*** Tangling
:PROPERTIES:
:CUSTOM_ID: Overview-Installation-Tangling-2622c756
:END:

Clone the repo and tangle the config.

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

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

* Restart Emacs
:PROPERTIES:
:CUSTOM_ID: *Restart_it!*-780054ff
:END:

Although Emacs already provided functionalities like ~unload-feature~ and
~remove-hook~ to eliminate the side effects of certain packages or user
configurations, most of the time it's still easier to reload your Emacs
configurations through a complete restart. Luckily, Emacs 30+ comes with this
feature out of the box, that is the ~restart-emacs~ command. However, when Emacs
hangs up, we can not expect it to evaluate any elisp code, in this case, a shell
script that helps us to restart Emacs can be very handy.

** Source
:PROPERTIES:
:CUSTOM_ID: *Restart_it!*-CLI_source-a2764225
:END:

Here is the source code for the =rem= (acronym for ~restart-emacs~) command. The
first elisp code block is consumed by the second code block to inject a few
values such as file path into the actual shell script.

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

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

#+begin_src bash :tangle "~/.local/bin/rem" :shebang "#!/usr/bin/env bash" :noweb yes
_is_ime_rime () { [[ -d ~/.config/rime ]] || [[ -d ~/.local/share/fcitx5/rime ]]; }

ELCs=false REPOs=false RESET=false DEBUG="" INIT_DIR=""
BIN="emacs"
[[ $(uname) == "Darwin" ]] && BIN="/Applications/Emacs.app/Contents/MacOS/Emacs"

[[ "$GRANDVIEW_HOME" ]] && INIT_DIR="--init-directory $GRANDVIEW_HOME"

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

emacs_cmd="$BIN --bg-daemon $DEBUG $INIT_DIR"
notify-send "Restarting emacs..." 2>/dev/null
kill -9 $(pgrep -x '[Ee]macs') 2>/dev/null
$RESET && rm -rf '<>' 2>/dev/null
$ELCs && rm -rf '<>' 2>/dev/null
$REPOs && rm -rf '<>' 2>/dev/null
_is_ime_rime && eval GTK_IM_MODULE=emacs XMODIFIERS=@im=emacs $emacs_cmd \
|| eval $emacs_cmd
command -v xdotool >/dev/null 2>&1 && xdotool set_desktop 0 2>&1
emacsclient -cn
#+end_src

** Usage
:PROPERTIES:
:CUSTOM_ID: *Restart_it!*-CLI_usage-63ad000f
:END:

Here are the available flags of this command.

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

* *TEXT EDITING*
:PROPERTIES:
:CUSTOM_ID: *Text_editing*-2d55608c
:END:

** Basic editing (simple.el)
:PROPERTIES:
:CUSTOM_ID: *Text_editing*-Basic_editing_(simple.el)-32cbfccd
: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
- Recenter the screen and highlight the keywords after we call ~next/previous-error~

#+begin_src emacs-lisp
(once '(:packages simple)
(bind-key "SPC" nil messages-buffer-mode-map)
(add-hook 'next-error-hook #'recenter)
(setq next-error-message-highlight t)) ; added in Emacs 28.1
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+simple.el"
:CUSTOM_ID: *Text_editing*-Basic_editing_(simple.el)-Autoload-40b8d1b5
:END:

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

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

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

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

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

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

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

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

** Modal editing (meow.el)
:PROPERTIES:
:CUSTOM_ID: *Text_editing*-Modal_editing_(meow.el)-c67b850b
:END:

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

You may noticed that I didn't include any keybindings of meow here, that's
because it can be very lengthy and should be configured separately, see
[[#*Keybindings*-36788c8a][Keybindings]] for details.

If you want to know more about meow or modal editing in general, check out [[https://www.github.com/DoglooksGood/meow][meow]].

#+begin_src emacs-lisp
(straight-use-package 'meow)
(require 'meow)

(meow-global-mode)

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

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+meow.el"
:CUSTOM_ID: *Text_editing*-Modal_editing_(meow.el)-Autoload-862833de
:END:

#+begin_src emacs-lisp
;;;###autoload
(defadvice! meow-search-ad (&rest _)
"Do not highlight number positions."
:after #'meow-search
(recenter))

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

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

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

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

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

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

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

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

** Quick goto char (avy.el)
:PROPERTIES:
:CUSTOM_ID: *Text_editing*-Quick_goto_char_(avy.el)-caa75f98
:END:

Jump to any visible text.

#+begin_src emacs-lisp
(straight-use-package 'avy)

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

** Symbol pairs (embrace.el)
:PROPERTIES:
:CUSTOM_ID: *Text_editing*-Symbol_pairs_(embrace.el)-31c59fb0
:END:

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

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

#+begin_src emacs-lisp
(straight-use-package
'(embrace :type git :host github :repo "cute-jumper/embrace.el"
:fork (:host github :repo "alexluigit/embrace.el")))

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

** Tab for Indentation (indent.el)
:PROPERTIES:
:CUSTOM_ID: *TEXT_EDITING*-Tab_for_Indentation-67ef1cad
: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

* *USER INTERFACE*
:PROPERTIES:
:CUSTOM_ID: *User_interface*-c85993d8
:END:

** Basics
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Basics-cf25db16
:END:

Show current key strokes in echo area after 0.25s

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

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

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

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

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

** Theme
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Theme-35a328fb
:END:

Recommended themes (using their package names):

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

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

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

- =apropospriate-theme=
Apropospriate theme.

- =lambda-themes=
Lambda themes.

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

- =mindre-theme=
Mindre theme.

#+begin_src emacs-lisp
(straight-use-package `(modus-themes ,@(when (> emacs-major-version 28) '(:type built-in))))
(straight-use-package 'ef-themes)
(straight-use-package '(lambda-themes :host github :repo "lambda-emacs/lambda-themes"))
(straight-use-package 'apropospriate-theme)
(straight-use-package 'doom-themes)
(straight-use-package 'color-theme-sanityinc-tomorrow)
(straight-use-package 'mindre-theme)
(once '(:hooks after-init-hook)
(setq modus-themes-common-palette-overrides
'((underline-link unspecified)
(underline-link-visited unspecified)
(underline-link-symbolic unspecified)))
(setq ef-themes-to-toggle '(ef-summer ef-winter)
ef-themes-mixed-fonts t
ef-themes-variable-pitch-ui t)
(load-theme grandview-theme t))
#+end_src

** Fonts (fonts.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Fonts_(fonts.el)-073ed5c4
: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=
- =青鸟华光简报宋二=
- =FZSuXinShiLiuKaiS-R-GB=

#+begin_src emacs-lisp
(if (daemonp)
(add-hook 'after-make-frame-functions #'+font-setup)
(+font-setup))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+fonts.el"
:CUSTOM_ID: User_interface_extras-Fonts_(fonts.el)-Autoload-647703e6
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +font-setup (&optional frame)
"Setup default/fixed-pitch/variable-pitch/zh-font."
(cl-loop with font-families = (font-family-list frame)
for font in (list grandview-default-font grandview-fixed-font
grandview-variable-font)
for name in '(default fixed-pitch variable-pitch)
for fspec = (font-spec :family font)
if (member font font-families) do
(custom-theme-set-faces
'user `(,name ((t (:font ,fspec :height ,grandview-font-size)))))
else do (message "Font `%s' is not available" font)
finally
(progn
(custom-theme-set-faces
'user
'(font-lock-keyword-face ((t (:slant italic))))
'(font-lock-variable-name-face ((t (:weight demibold))))
'(font-lock-function-name-face ((t (:weight demibold)))))
(if (member grandview-CJK-font font-families)
(dolist (charset '(kana han cjk-misc bopomofo))
(set-fontset-font t charset (font-spec :family grandview-CJK-font)))
(message "Font `%s' is not available" grandview-CJK-font))
(unless (> emacs-major-version 27)
(set-fontset-font t 'symbol (font-spec :family "Noto Color Emoji"))))))

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

** Buffer management (ibuffer.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Buffer_management_(ibuffer.el)-98eec7f5
: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
(straight-use-package '(ibuffer :type built-in))

(once '(:packages 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-keys :map ibuffer-mode-map
("M-o" . nil)
("* f" . ibuffer-mark-by-file-name-regexp)
("* g" . ibuffer-mark-by-content-regexp)
("* n" . ibuffer-mark-by-name-regexp)
("s n" . ibuffer-do-sort-by-alphabetic)
("/ g" . ibuffer-filter-by-content)))
#+end_src

** Window commands (window.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Window_commands_(window.el)-23CE9BA0
:END:

*** Window selection (windmove.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Window_commands_(window.el)-Window_selection_(windmove.el)-05B96B74
:END:

Directional window-selection routines.

#+begin_src emacs-lisp
(straight-use-package '(windmove :type built-in))

(bind-keys :map global-map
("M-o" . other-window)
("M-N" . windmove-down)
("M-P" . windmove-up)
("M-I" . windmove-right)
("M-O" . windmove-left))
#+end_src

*** Window placement (window.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Window_commands_(window.el)-Window_placement-79B7247E
: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.

#+begin_src emacs-lisp
(once '(:hooks window-configuration-change-hook)
(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))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+window.el"
:CUSTOM_ID: *User_interface*-Window_placement_(window.el)-Autoload-544e5f03
:END:

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

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

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

** Frame parameters (frame.el)
:PROPERTIES:
:CUSTOM_ID: *User_interface*-Frame_(frame.el)-0b197c36
:END:

- Remove title bar on macOS
- Enter fullscreen automatically on macOS
- Adjust frame opacity dynamically

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

(when (> emacs-major-version 28)
(add-hook 'window-configuration-change-hook #'+frame-opacity-auto))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+frame.el"
:CUSTOM_ID: *User_interface*-Frame_(frame.el)-Autoload-e5168c36
: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-cursor-dim-mode'."
:group 'cursor :type 'string)

(defcustom +frame-opacity (if (eq system-type 'gnu/linux) 80 90)
"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))))))

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

** COMMENT Persistent sessions (desktop.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Persistent_sessions_(desktop.el)-8aa71a74
:END:

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

** Icon library (nerd-icons.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Icon_library_(nerd-icons.el)-E848DA41
:END:

#+begin_src emacs-lisp
(straight-use-package
'(nerd-icons :repo "rainstormstudio/nerd-icons.el"
:host github
:files (:defaults "data")))
#+end_src

** Nerd fonts (nerd-fonts.el)
:PROPERTIES:
:CUSTOM_ID: *USER_INTERFACE*-Nerd_fonts_(nerd-fonts.el)-c1c1aede
:END:

#+begin_src emacs-lisp
(straight-use-package
'(nerd-fonts :host github :repo "twlz0ne/nerd-fonts.el"))

;; (once '(:hooks pre-command-hook)
(require 'nerd-fonts)
;; )
#+end_src

* *COMPLETION FRAMEWORK*
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-c9eb4c39
: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.

** Minibuffer and completion functions (minibuffer.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Minibuffer_and_completion_functions_(minibuffer.el)-3122e308
: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
(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)
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+minibuffer.el"
:CUSTOM_ID: *Completion_framework*-Minibuffer_and_completion_functions_(minibuffer.el)-Autoload-838b8348
: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: *Completion_framework*-Minibuffer_history_(savehist.el)-f2b413ed
:END:

Keeps a record of actions involving the minibuffer.

#+begin_src emacs-lisp
(once '(:hooks minibuffer-setup-hook)
(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

** Vertical completion candidates (vertico.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Vertical_completion_candidates_(vertico.el)-9b700386
: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
(straight-use-package 'vertico)

(once '(:hooks pre-command-hook)
(vertico-mode 1))
#+end_src

** Match candidates made easy (orderless.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Match_candidates_made_easy_(orderless.el)-2eb67ad3
: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
(straight-use-package 'pinyinlib)
(straight-use-package 'orderless)

(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

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+orderless.el"
:CUSTOM_ID: *Completion_framework*-Match_candidates_made_easy_(orderless.el)-Autoload-b4e5dd4a
: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 prefix."
(when (string-suffix-p "=" pattern)
`(orderless-literal . ,(substring pattern 0 -1))))

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

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

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

** Useful commands using completion (consult.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Useful_commands_using_completion_(consult.el)-98e66a86
: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.

#+begin_src emacs-lisp
(straight-use-package 'consult)

(once '(:packages vertico)
(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 3)
(setq consult-async-input-debounce 0.5)
(setq consult-async-input-throttle 0.8)
(setq consult-narrow-key ">")
(bind-keys :map grandview-mct-map
("/" . consult-line-multi)
("t" . consult-mark)
("T" . consult-global-mark)
("a" . consult-apropos)
("e" . consult-compile-error)
("r" . consult-ripgrep)
("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)
("m" . consult-bookmark)
("c" . consult-complex-command)
("C" . consult-mode-command)
("M" . consult-minor-mode-menu)))

(once '(:packages meow)
(bind-key "/" 'consult-line meow-normal-state-keymap))
#+end_src

** Candidate annotation (marginalia.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Candidate_annotation_(marginalia.el)-abeb1224
: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
(straight-use-package 'marginalia)

(once '(:packages vertico)
(marginalia-mode)
(setq marginalia-align 'left))
#+end_src

** Completion overlay region function (corfu.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Completion_overlay_region_function_(corfu.el)-8760cb44
: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
(straight-use-package 'corfu)

(once '(:before self-insert-command)
(setq! corfu-auto t)
(setq! corfu-auto-delay 0.05)
(setq! corfu-auto-prefix 2)
(setq! corfu-cycle t)
(setq! corfu-preselect 'prompt)
(setq! corfu-on-exact-match nil)
(global-corfu-mode)
(bind-keys :map corfu-map
("TAB" . corfu-next)
([tab] . corfu-next)
("S-TAB" . corfu-previous)
([backtab] . corfu-previous)
("M-n" . nil)
("M-p" . nil)))
#+end_src

** Completion at point Extensions (cape.el)
:PROPERTIES:
:CUSTOM_ID: *Completion_framework*-Completion_at_point_Extensions_(cape.el)-4194e04b
:END:

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

#+begin_src emacs-lisp
(straight-use-package 'cape)

(once '(:before self-insert-command)
(setq! cape-dict-file "/usr/share/dict/words")
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
(add-to-list 'completion-at-point-functions #'cape-keyword)
(add-to-list 'completion-at-point-functions #'cape-ispell)
(add-to-list 'completion-at-point-functions #'cape-dict)
(define-prefix-command '+cape-map)
(defvar +cape-prefix-map (make-sparse-keymap))
(defalias '+cape-map +cape-prefix-map)
(bind-keys :map global-map
("C-M-/" . +cape-map) ; remapped `dabbrev-completion'
:map +cape-prefix-map
("c" . completion-at-point) ; capf
("t" . complete-tag) ; etags
("d" . cape-dabbrev) ; or dabbrev-completion
("f" . cape-file)
("k" . cape-keyword)
("s" . cape-symbol)
("a" . cape-abbrev)
("i" . cape-ispell)
("l" . cape-line)
("w" . cape-dict)
("\\" . cape-tex)
("_" . cape-tex)
("^" . cape-tex)
("&" . cape-sgml)
("r" . cape-rfc1345)))
#+end_src

* Keybindings
:PROPERTIES:
:CUSTOM_ID: *Keybindings*-36788c8a
:END:

This section contains all core keybindings of *Grandview*.

** Orientation
:PROPERTIES:
:CUSTOM_ID: Keybindings-Orientation-D2EEBCF8
:END:

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

#+begin_src emacs-lisp
;; (define-key input-decode-map [?\C-i] [C-i])
(add-hook 'after-make-frame-functions
(lambda (f) (with-selected-frame f
(define-key input-decode-map [?\C-i] [C-i]))))
#+end_src

macOS specific settings.

#+begin_src emacs-lisp
(setq mac-command-modifier 'meta)
(setq mac-option-modifier 'super)
#+end_src

** INSERT
:PROPERTIES:
:CUSTOM_ID: *Keybindings*-INSERT-3d96728e
:END:

#+begin_src emacs-lisp
(once '(:packages meow)
(bind-keys :map meow-insert-state-keymap
("M-" . meow-kill-whole-line)
("" . meow-right)
("C-o" . meow-left)))
#+end_src

** NORMAL
:PROPERTIES:
:CUSTOM_ID: *Keybindings*-NORMAL-e105b916
:END:

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

** LEADER
:PROPERTIES:
:CUSTOM_ID: *Keybindings*-LEADER-ab120692
:END:

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

(bind-keys :map grandview-files-map
("w" . save-buffer) ; [SPC x s] in Colemak is painful to press
("g" . grandview-config)
:map grandview-apps-map
("d" . toggle-debug-on-error)
("o" . +frame-opacity-set)
("=" . count-words)
("m" . +show-messages))
#+end_src

** GLOBAL
:PROPERTIES:
:CUSTOM_ID: *Keybindings*-GLOBAL-4055cc3f
:END:

All major bindings work globally.

#+begin_src emacs-lisp
(bind-keys :map global-map
("" . +simple-pop-local-mark-ring)
("M-SPC" . +monocle-mode) ; replaced `just-one-space'
("S-SPC" . toggle-input-method)
("M-u" . +toggle-letter-case)
("" . +simple-pop-local-mark-ring)
("C-o" . pop-global-mark)
("s-n" . scroll-up-command)
("s-p" . scroll-down-command)
("M-o" . other-window)
("M-n" . forward-paragraph)
("M-p" . backward-paragraph)
:map tab-prefix-map
("w" . other-window)
:map minibuffer-local-map
("" . exit-minibuffer)
("M-" . meow-kill-whole-line)
("" . forward-char)
("" . forward-char)
("C-o" . backward-char)
:map meow-insert-state-keymap
("" . meow-right)
:map image-map
("o" . nil)
("w" . image-save))
#+end_src

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

#+begin_src emacs-lisp
(when (featurep 'meow)
(meow-motion-overwrite-define-key '("" . +meow-escape)))
#+end_src

* File management
:PROPERTIES:
:CUSTOM_ID: File_management-28279792
:END:

** File/Directory handling functions (files.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Find_files_(files.el)-9a84d0e0
:END:

+ Save modified buffers automatically
+ Utilities:
- ~+files-find-dotfiles~
- ~+files-sudo-find~
- ~+files-rename-file-and-buffer~
- ~+files-find-user-files~

#+begin_src emacs-lisp
(setq large-file-warning-threshold 50000000)
(setq permanently-enabled-local-variables '(lexical-binding encoding))
(setq auto-save-default nil)
(setq +files-user-dirs-alist
'(((title . " Shows") (path . "/mnt/HDD/Share"))
((title . " Coding") (path . "/mnt/HDD/Dev"))
((title . " Books") (path . "/mnt/HDD/Book"))
((title . " Videos") (path . "/mnt/HDD/Video"))
((title . " Notes") (path . "~/Documents/notes"))
((title . " Photos") (path . "~/Pictures"))
((title . " Downloads") (path . "~/Downloads"))))
(setq confirm-kill-processes nil)
(auto-save-visited-mode)

(bind-keys
:map grandview-files-map
("." . +files-find-dotfiles)
("s" . +files-sudo-find)
("r" . +files-rename-file-and-buffer)
("u" . +files-find-user-files))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+files.el"
:CUSTOM_ID: File_management-Find_files_(files.el)-Autoload-d23acbab
:END:

#+begin_src emacs-lisp
(defcustom +files-dotfiles-repo (getenv "DOTPATH")
"Doc."
:group 'grandview :type 'string)

(defcustom +files-user-dirs-alist
'(((title . " Photos") (path . "~/Pictures/"))
((title . " Videos") (path . "~/Video/"))
((title . " 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-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-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-sudo-find ()
"Reopen current file as root."
(interactive)
(let ((file (buffer-file-name)))
(find-file (if (file-writable-p file)
file
(concat "/sudo::" file)))))

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

** Find libraries (find-func.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Find_libraries_(find-func.el)-032214e2
: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 Emwacs
itself as long as the ~find-function-C-source-directory~ is set properly.

#+begin_src emacs-lisp
(straight-use-package 'find-func)

(when (eq system-type 'gnu/linux)
(setq find-function-C-source-directory "~/Code/emacs/src"))
(bind-keys :map grandview-files-map
("l" . find-library))
#+end_src

** Recent files (recentf.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Recent_files_(recentf.el)-ef26c355
:END:

Keep a record of all recently opened files.

#+begin_src emacs-lisp
(straight-use-package '(recentf :type built-in))

(once '(:before after-find-file)
(setq recentf-max-saved-items 100)
(recentf-mode 1))
#+end_src

** Restore file place (saveplace.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Restore_file_place_(saveplace.el)-34b7fe81
: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
(straight-use-package '(saveplace :type built-in))

(once '(:hooks find-file-hook)
(setq save-place-file (locate-user-emacs-file "saveplace"))
(setq save-place-forget-unreadable-files t)
(save-place-mode 1))
#+end_src

** Auto refresh file content (autorevert.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Auto_refresh_file_content_(autorevert.el)-fa9dac07
: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
(straight-use-package '(autorevert :type built-in))

(once '(:hooks find-file-hook)
(setq auto-revert-verbose t)
(global-auto-revert-mode))
#+end_src

** Dired (dired.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Dired_(dired.el)-257fe80d
: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
(straight-use-package '(dired :type built-in))
(straight-use-package '(dired-x :type built-in))
(straight-use-package '(dired-aux :type built-in))
(straight-use-package 'diredfl)

(add-hook 'dired-mode-hook 'diredfl-mode)
(add-hook 'dirvish-directory-view-mode-hook 'diredfl-mode)
(once '(:packages 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))))

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

(once '(:before dired-noselect dired-jump dirvish-curr)
(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-keys :map dired-mode-map
("/" . 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)))

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

** Writable Dired (wdired.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Writable_Dired_(wdired.el)-51253ea7
:END:

Bulk renaming files like a breeze.

#+begin_src emacs-lisp
(straight-use-package '(wdired :type built-in))

(once '(:packages dired)
(setq wdired-allow-to-change-permissions t)
(setq wdired-create-parent-directories t)
(bind-keys :map dired-mode-map
("i" . wdired-change-to-wdired-mode)))
#+end_src

** Use dired to browse and manipulate images (image-dired.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Use_dired_to_browse_and_manipulate_images_(image-dired.el)-6cc60efc
:END:

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

+ show bigger sized thumbnail image, we are in the 21st century
+ no not display original image in other window when flag/mark files
- it's very slow
- if I want to view the bigger image, I use =dirvish= instead
+ tweak the keybindings to my preferences

#+begin_src emacs-lisp
(straight-use-package '(image-dired :type built-in))

(once '(:packages image-dired)
(setq image-dired-thumb-size 256)
(setq image-dired-marking-shows-next nil)
(bind-keys :map 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: File_management-A_better_dired_(dirvish.el)-cff45a67
: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
(straight-use-package '(dirvish :type git :repo "alexluigit/dirvish" :depth full))

(dirvish-override-dired-mode)
(dirvish-side-follow-mode)
(dirvish-peek-mode)
(add-hook 'dirvish-setup-hook 'dirvish-emerge-mode)
(setq dirvish-attributes
'(vc-state file-size git-msg subtree-state nerd-icons collapse file-time))
(setq! 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-side-width 38)
(setq dirvish-header-line-format '(:left (path) :right (free-space)))
(setq dirvish-path-separators (list " " " " "  "))
(once '(:hooks pre-command-hook)
(setq! 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/📁" "AllNotes")
("t" "~/.local/share/Trash/files/" "Trash")
("W" "/smb:alex%[email protected]:share/")))
(bind-keys :map 'dirvish-mode-map
("" . dirvish-subtree-toggle-or-open) ; left click for expand/collapse dir or open file
("" . dired-mouse-find-file-other-window) ; middle click for opening file / entering dir in other window
("" . dired-mouse-find-file) ; right click for opening file / entering dir
("" . dired-do-shell-command) ; side button for shell command execution
("SPC" . consult-buffer)
("M-n" . dirvish-history-go-forward)
("M-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)
("M-e" . dirvish-emerge-menu)
("M-t" . dirvish-layout-toggle)
("M-s" . dirvish-setup-menu)
("M-j" . dirvish-fd-jump)
([remap dired-sort-toggle-or-edit] . dirvish-quicksort)
([remap dired-do-redisplay] . dirvish-ls-switches-menu)
([remap dired-do-copy] . dirvish-yank-menu)
:map mode-specific-map
("e" . dirvish-dwim)
:map grandview-files-map
("e" . dirvish)
("f" . dirvish-fd)
("n" . dirvish-side)
("o" . dirvish-quick-access)
("b" . dirvish-fd-jump)))
#+end_src

** Project management (project.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Project_management_(project.el)-0b5bec24
:END:

#+begin_src emacs-lisp
(straight-use-package '(project :type built-in))

(setq project-switch-commands
'((project-find-file "File" ?\r)
(+project-find-subdir "Subdir" ?s)
(project-dired "Dired" ?d)
(+project-retrieve-tag "Tag switch" ?t)
(+project-magit-status "Magit" ?m)
(+project-commit-log "Log VC" ?l)))
(setq +project-commit-log-limit 25)

(bind-keys :map project-prefix-map
("l" . +project-commit-log)
("m" . +project-magit-status)
("s" . +project-find-subdir)
("t" . +project-retrieve-tag))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+project.el"
:CUSTOM_ID: File_management-Project_management_(project.el)-Autoload-49a0fbb3
:END:

#+begin_src emacs-lisp
(require 'cl-lib)
(require 'project)
(require 'vc)

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

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

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

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

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

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

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

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

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

(autoload 'magit-status "magit")

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

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

** Working with remote files (tramp.el)
:PROPERTIES:
:CUSTOM_ID: File_management-Working_with_remote_files_(tramp.el)-c1834af3
:END:

#+begin_src emacs-lisp
(straight-use-package '(tramp :type built-in))

(once '(:packages 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)
(setq! tramp-use-ssh-controlmaster-options nil))
#+end_src

* Org mode
:PROPERTIES:
:CUSTOM_ID: Org_mode-dd9abcb7
:END:

** Org (org.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Org_(org.el)-4769227e
: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
(straight-use-package '(org :type built-in))

(once '(:packages org)
(add-hook 'org-mode-hook '+org-font-setup)
(add-hook 'org-mode-hook 'org-indent-mode)
(add-hook 'org-tab-first-hook 'org-end-of-line)
(setq org-adapt-indentation nil)
(setq org-hide-leading-stars t)
(setq org-startup-folded t)
(setq org-confirm-babel-evaluate nil)
(setq org-ellipsis " ▾")
(setq org-agenda-start-with-log-mode t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-image-actual-width nil)
(setq org-display-remote-inline-images 'download)
(bind-keys :map grandview-org-map ("o" . consult-org-heading)
:map org-mode-map
("C-c l" . org-insert-last-stored-link)))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+org.el"
:CUSTOM_ID: Org_mode-Org_(org.el)-Autoload-e4ba486e
:END:

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

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

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

** Identifiers for org entries (org-id.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Identifiers_for_org_entries_(org-id.el)-3fb8bcab
:END:

#+begin_src emacs-lisp
(straight-use-package '(org-id :type built-in))

(add-hook 'org-mode-hook '+org-id-update)
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+org-id.el"
:CUSTOM_ID: Org_mode-Identifiers_for_org_entries_(org-id.el)-Autoload-2963b014
:END:

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

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

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

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

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

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

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

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

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

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

** Literate programming (ob.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Literate_programming_(ob.el)-67a48331
:END:

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

#+begin_src emacs-lisp
(straight-use-package '(ob :type built-in))
(straight-use-package '(ob-C :type built-in))
(straight-use-package '(ob-js :type built-in))
(straight-use-package '(ob-shell :type built-in))
(straight-use-package '(ob-latex :type built-in))
(straight-use-package '(ob-makefile :type built-in))
(straight-use-package '(ob-csharp :host github :repo "samwdp/ob-csharp"))

(once '(:packages 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: Org_mode-Source_code_block_(org-src.el)-21f82cd1
:END:

#+begin_src emacs-lisp
(straight-use-package '(org-src :type built-in))

(once '(:packages org-src)
(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: Org_mode-Reveal_invisible_org_elements_(org-appear.el)-a14e0c4c
:END:

#+begin_src emacs-lisp
(straight-use-package 'org-appear)

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

** Modern org style (org-modern.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Modern_org_style_(org-modern.el)-dd02a702
:END:

#+begin_src emacs-lisp
(straight-use-package 'org-modern)

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

** COMMENT Visual alignment (valign.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Visual_alignment_(valign.el)-f097ffdb
:END:

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

#+begin_src emacs-lisp
(straight-use-package 'valign)

(add-hook 'org-mode-hook 'valign-mode)
(setq valign-fancy-bar t)
#+end_src

** Habit (org-habit.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Habit_(org-habit.el)-4b78bc2e
:END:

#+begin_src emacs-lisp
(straight-use-package '(org-habit :type built-in))

(once '(:packages org)
(appendq! org-modules '(org-habit))
(setq org-habit-graph-column 60))
#+end_src

** COMMENT Notebook (org-roam.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Wiki_(org-roam.el)-acf43c6a
:END:

#+begin_src emacs-lisp
(straight-use-package 'org-roam)

(once '(:hooks pre-command-hook)
(setq org-roam-node-display-template
(concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
(setq! org-roam-directory (file-truename "~/Documents/roam"))
(setq! org-roam-completion-everywhere t)
(autoload 'org-roam-buffer-toggle "org-roam-mode")
(bind-keys :map grandview-org-map
("l" . org-roam-buffer-toggle)
("f" . org-roam-node-find)
("g" . org-roam-graph)
("i" . org-roam-node-insert)
("c" . org-roam-capture)
("j" . org-roam-dailies-capture-today))
(org-roam-db-autosync-mode))
#+end_src

** Slide (org-tree-slide.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Slide_(org-tree-slide.el)-9fcc4f61
:END:

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

#+begin_src emacs-lisp
(straight-use-package 'org-tree-slide)

(bind-key "S" 'org-tree-slide-mode grandview-org-map)

(once '(:packages 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-hide-elements-h)
(add-hook 'org-tree-slide-play-hook #'+org-tree-slide-hide-elements-h)
(add-hook 'org-tree-slide-mode-hook #'+org-tree-slide-prettify-slide-h)
(bind-keys :map 'org-tree-slide-mode-map
("" . org-tree-slide-move-previous-tree)
("" . org-tree-slide-move-next-tree)))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+org-tree-slide.el"
: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-hide-elements-h ()
"Hide org constructs defined in `+org-tree-hide-elements'."
(dolist (reg +org-tree-hide-elements)
(save-excursion
(goto-char (point-min))
(while (re-search-forward reg nil t)
(org-flag-region (match-beginning 1)
(match-end 0) org-tree-slide-mode t)))))

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

** COMMENT Org exporter backend for Hugo (ox-hugo.el)
:PROPERTIES:
:CUSTOM_ID: Org_mode-Org_exporter_backend_for_Hugo_(ox-hugo.el)-5ebd8b86
:END:

#+begin_src emacs-lisp
(straight-use-package ox-hugo)
#+end_src

* Text editing
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-d41696e6
:END:

** Long line text (so-long.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Long_line_text_(so-long.el)-812460aa
:END:

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

#+begin_src emacs-lisp
(straight-use-package '(so-long :type built-in))

(once '(:hooks find-file-hook) (global-so-long-mode))
#+end_src

** Fill line (fill.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Fill_line_(fill.el)-9df783fa
: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
(straight-use-package '(fill :type built-in))

(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

** Cross reference (xref.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Cross_reference_(xref.el)-1a6bcfb6
:END:

*xref* provides helpful commands for code navigation and discovery.

#+begin_src emacs-lisp
(straight-use-package '(fill :type built-in))

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

** Interactive diff, patch, or merge conflict (ediff.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Interactive_diff,_patch,_or_merge_conflict_(ediff.el)-c015b8a2
: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
(straight-use-package '(ediff :type built-in))

(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

*** Autoload
:PROPERTIES:
:CUSTOM_ID: Text_editing-Interactive_diff,_patch,_or_merge_conflict_(ediff.el)-Autoload-d752df39
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+ediff.el"
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +ediff-flush-combination-pattern ()
"Remove my custom `ediff-combination-pattern' markers.
This is a quick-and-dirty way to get rid of the markers that are
left behind by `smerge-ediff' when combining the output of two
diffs. While this could be automated via a hook, I am not yet
sure this is a good approach."
(interactive)
(flush-lines ".*grandv-ediff.*" (point-min) (point-max) nil))
#+end_src

** Input method (rime.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Input_method_(rime.el)-af0e2cfb
:END:

#+begin_src emacs-lisp
(straight-use-package 'rime)

(setq default-input-method "rime")
(once '(:packages rime)
(when (eq system-type 'darwin)
(setq! rime-librime-root (expand-file-name "librime" user-emacs-directory)))
(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)
(custom-theme-set-faces
'user '(rime-preedit-face ((t (:inherit lazy-highlight)))))
(bind-keys :map 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"))))
([tab] . rime-send-keybinding)))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+rime.el"
:CUSTOM_ID: Text_editing_extras-Input_method_(rime.el)-Autoload-8f669619
: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

** Snippet (tempel.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing-Snippet_(tempel.el)-79179e2c
:END:

#+begin_src emacs-lisp
(straight-use-package 'tempel)

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

*** Autoload
:PROPERTIES:
:CUSTOM_ID: Text_editing-Snippet_(tempel.el)-Autoload-3abbbd2b
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+tempel.el"
:END:

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

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

*** Templates
:PROPERTIES:
:CUSTOM_ID: Text_editing-Snippet_(tempel.el)-Templates-d217a113
:END:

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.

Use caution with templates which execute arbitrary code!

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

latex-mode

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

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

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

emacs-lisp-mode

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

eshell-mode

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

text-mode

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

rst-mode

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

java-mode

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

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

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

org-mode

(title "#+title: " p n "#+author: Alex Lu" n "#+language: en" n n)
(quote "#+begin_quote" n> r> n> "#+end_quote")
(example "#+begin_example" n> r> n> "#+end_example")
(center "#+begin_center" n> r> n> "#+end_center")
(comment "#+begin_comment" n> r> n> "#+end_comment")
(verse "#+begin_verse" n> r> n> "#+end_verse")
(src "#+begin_src " p n> r> n> "#+end_src")
(elisp "#+begin_src emacs-lisp" n> r> n "#+end_src")
(cc "#+begin_src C" n> r> n "#+end_src")
(cpp "#+begin_src cpp" n> r> n "#+end_src")
(csharp "#+begin_src csharp" n> r> n "#+end_src")
(python "#+begin_src python" n> r> n "#+end_src")
(js "#+begin_src js" n> r> n "#+end_src")
(bash "#+begin_src bash" n> r> n "#+end_src")
(latex "#+begin_src latex" n> r> n "#+end_src")

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

** Pair insertion (eletric.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Pair_insertion_(eletric.el)-e2bb1bf7
: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
(straight-use-package '(electric :type built-in))

(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

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+eletric.el"
:CUSTOM_ID: Text_editing_extras-Pair_insertion_(eletric.el)-Autoload-1ff20547
: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

** Parentheses (paren.el / rainbow-delimiters.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Parentheses_(paren.el__rainbow-delimiters.el)-ae05e47a
: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.
- Use rainbow color for delimiters

#+begin_src emacs-lisp
(straight-use-package 'rainbow-delimiters)

(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)

(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 (prog-mode.el)
:PROPERTIES:
:CUSTOM_ID: Text_editing_extras-Prettify_symbols_(prog-mode.el)-70abfe03
: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

* User interface
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-9ff97526
:END:

** Makes windows without focus less prominent (auto-dim-other-buffers.el)
:PROPERTIES:
:CUSTOM_ID: User_interface-Make_(auto-dim-other-buffers.el)-73a27abb
:END:

#+begin_src emacs-lisp
(straight-use-package 'auto-dim-other-buffers)

(auto-dim-other-buffers-mode)
#+end_src

** Workspaces (tabspaces.el)
:PROPERTIES:
:CUSTOM_ID: *User_interface*-Tabs_as_workspaces_(tab-bar.el)-a66cb3df
:END:

Use the inbuilt =tab-bar.el= and the package =tabspaces.el= to create workspaces.

- General tab bar customization
- Enable ~tabspaces-mode~
- Create some default workspaces
- Filter buffers for consult tabspaces
- Rename the first tab (generated by tab-bar itself) to =Default=

#+begin_src emacs-lisp
(straight-use-package 'tabspaces)

(require 'tabspaces)
(tabspaces-mode 1)
(add-hook 'server-after-make-frame-hook #'+tabspaces-setup)

(setq 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 #'+tabspaces-tab-name-format-comfortable
tab-bar-format '(+tabspaces-format-menu-bar
tab-bar-format-tabs
tab-bar-separator
tab-bar-format-align-right
tab-bar-format-global)
tabspaces-keymap-prefix nil
tabspaces-default-tab "Default"
tabspaces-include-buffers '("*scratch*" "*Messages*")
tabspaces-remove-to-default t)

(once '(:packages tabspaces)
(bind-keys :map tab-prefix-map
("C" . tabspaces-clear-buffers)
("R" . tabspaces-remove-selected-buffer)
("k" . tabspaces-kill-buffers-close-workspace)
("s" . tabspaces-switch-or-create-workspace)))

(once '(:packages 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

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+tabspaces.el"
:CUSTOM_ID: User_interface-Workspaces_(tabspaces.el)-Autoload-14379110
:END:

#+begin_src emacs-lisp
(defcustom +tabspaces-startup-workspaces nil
"Workspaces being created at startup."
:group 'grandview :type 'alist)

;;;###autoload
(defun +tabspaces-format-menu-bar ()
"Produce the Menu button for the tab bar that shows the menu bar."
`((menu-bar menu-item (propertize " 𝝺" 'face 'tab-bar)
tab-bar-menu-bar :help "Menu Bar")))

;;;###autoload
(defun +tabspaces-tab-name-format-comfortable (tab i)
(propertize (concat " " (tab-bar-tab-name-format-default tab i) " ")
'face (funcall tab-bar-tab-face-function tab)))

(defun +tabspaces-create-workspace (name &optional path)
"Setup the workspace with name NAME and startup path PATH."
(if (member name (tabspaces--list-tabspaces))
(progn (tab-bar-switch-to-tab name)
(when (and (not (get-buffer name)) path) (dirvish path)))
(tab-bar-new-tab)
(tab-bar-rename-tab name)
(when path (dirvish path))))

;;;###autoload
(defun +tabspaces-setup ()
"Setup tabspaces when a frame is created as well."
(cl-loop for (name . path) in +tabspaces-startup-workspaces
do (+tabspaces-create-workspace name path))
(tab-bar-select-tab 1)
(tab-bar-rename-tab "Default"))
#+end_src

** Transient commands (transient.el)
:PROPERTIES:
:CUSTOM_ID: User_interface-Transient_commands_(transient.el)-0bc5bb81
: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
(once '(:packages 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))
(bind-keys :map transient-map
("" . transient-quit-all)
:map transient-sticky-map
("ESC" . transient-quit-all)))
#+end_src

** Mode line (mode-line.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Modeline_(mode-line.el)-70f41922
: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

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+mode-line.el"
:CUSTOM_ID: User_interface_extras-Modeline_(mode-line.el)-Autoload-750e010a
: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 30
"Doc.")

(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

** Frame margin (fringe.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Frame_margin_(fringe.el)-ef1b50cd
: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

** Pixel scrolling (pixel-scroll.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Pixel_scrolling_(pixel-scroll.el)-1de8ceca
: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 or after select functions (pulsar.el)
:PROPERTIES:
:CUSTOM_ID: User_interface-Pulse_highlight_on_demand_or_after_select_functions_(pulsar.el)-3d92b459
:END:

Another great package from Prot!

#+begin_src emacs-lisp
(straight-use-package 'pulsar)

(pulsar-global-mode)
(once '(:packages pulsar)
(appendq! pulsar-pulse-functions
(list 'windmove-left 'windmove-right
'windmove-up 'windmove-down
'previous-window-any-frame
'next-window-any-frame '+meow-save)))
#+end_src

** VSCode style icons (vscode-icon.el)
:PROPERTIES:
:CUSTOM_ID: User_interface-Vscode_style_icons_(vscode-icon.el)-0f3dbf81
:END:

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

#+begin_src emacs-lisp
(straight-use-package 'vscode-icon)

(once '(:packages vscode-icon)
(push '("jpg" . "image") vscode-icon-file-alist)
(push '("7z" . "zip") vscode-icon-file-alist)
(push '("mkv" . "video") vscode-icon-file-alist)
(push '("epub" . "storybook") vscode-icon-file-alist))
#+end_src

** Interactive query replace (anzu.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Interactive_query_replace_(anzu.el)-01352108
: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
(straight-use-package 'anzu)

(once '(:hooks pre-command-hook)
(global-anzu-mode)
(bind-key [remap query-replace] 'anzu-query-replace)
(bind-key [remap query-replace-regexp] 'anzu-query-replace-regexp))
#+end_src

** Alternative isearch UI (isearch-mb.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Alternative_isearch_UI_(isearch-mb.el)-44065c12
: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
(straight-use-package 'isearch-mb)

(isearch-mb-mode)

(once '(:packages isearch-mb)
(add-to-list 'isearch-mb--with-buffer #'consult-isearch-history)
(add-to-list 'isearch-mb--after-exit #'anzu-isearch-query-replace)
(bind-keys :map isearch-mb-minibuffer-map
([remap previous-matching-history-element] . consult-isearch-history)))
#+end_src

** Distraction-free writing (writeroom-mode.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Distraction-free_writing_(writeroom-mode.el)-2ea3c297
:END:

#+begin_src emacs-lisp
(straight-use-package 'writeroom-mode)

(once '(:before pre-command-hook)
(setq writeroom-width 128
writeroom-bottom-divider-width 0
writeroom-fringes-outside-margins t
writeroom-fullscreen-effect nil
writeroom-major-modes '(text-mode prog-mode conf-mode special-mode Info-mode)
writeroom-major-modes-exceptions '(process-menu-mode proced-mode)
writeroom-maximize-window nil
writeroom-mode-line t)
(global-writeroom-mode))

(add-hook 'org-mode-hook #'+visual-fill-center-text)
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+writeroom-mode.el"
:CUSTOM_ID: User_interface_extras-Distraction-free_writing_(writeroom-mode.el)-Autoload-a4284246
:END:

#+begin_src emacs-lisp
;;;###autoload
(defun +visual-fill-center-text ()
"Centering text."
(interactive)
(setq-local visual-fill-column-width 120)
(setq-local visual-fill-column-center-text t)
(visual-fill-column-mode 1))
#+end_src

** Key bindings hint (which-key.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Key_bindings_hint_(which-key.el)-2c4b84b2
:END:

#+begin_src emacs-lisp
(straight-use-package 'which-key)

(once '(:hooks pre-command-hook) (which-key-mode))
#+end_src

** Hunk indicator (git-gutter.el)
:PROPERTIES:
:CUSTOM_ID: User_interface_extras-Hunk_indicator_(git-gutter.el)-644ef61a
:END:

#+begin_src emacs-lisp
(straight-use-package 'git-gutter)

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

* Utils
:PROPERTIES:
:CUSTOM_ID: Utils-8191d3b4
:END:

** Keyboard version right-click (embark.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Keyboard_version_right-click_(embark.el)-70bed6c5
:END:

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

#+begin_src emacs-lisp
(straight-use-package 'embark)
(straight-use-package 'embark-consult)

(once '(:hooks pre-command-hook)
(setq embark-quit-after-action t)
(bind-keys :map global-map
("C-." . embark-act)
:map minibuffer-local-map
("C-." . embark-act)
("C-," . embark-become)))
#+end_src

** Vterm (vterm.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Vterm_(vterm.el)-b5cc6784
:END:

#+begin_src emacs-lisp
(straight-use-package 'vterm)

(bind-key "M-`" '+vterm-toggle)

(once '(:packages vterm)
(+vterm-mux-mode)
(setq vterm-max-scrollback 5000)
(add-hook 'vterm-copy-mode-hook
(lambda () (if vterm-copy-mode (meow-normal-mode) (meow-insert-mode))))
(bind-keys :map vterm-mode-map
("M-`" . +vterm-toggle)
("M--" . +vterm-new)
("M-'" . vterm-send-M-apostrophe)
("M-\"" . vterm-send-M-quote)
("M-/" . vterm-send-M-/)
("M-." . +vterm-next)
("M-," . +vterm-prev)
("M-RET" . vterm-send-M-return)
("s-n" . vterm-next-prompt)
("s-p" . vterm-previous-prompt)
("S-SPC" . nil)
("S-" . vterm-copy-mode)
("C-" . vterm-send-F5)
:map vterm-copy-mode-map
("S-" . vterm-copy-mode-done)))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+vterm.el"
:CUSTOM_ID: Utils-Vterm_(vterm.el)-Autoload-45112bf1
:END:

#+begin_src emacs-lisp
(require 'vterm)

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

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

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

;;;###autoload
(defadvice! +vterm-escape-a (fn &rest args)
:around #'meow-insert-exit
(if (derived-mode-p 'vterm-mode)
(vterm-send-escape)
(apply fn args)))

;;;###autoload
(defadvice! +vterm-kill-whole-line-a (fn &rest args)
:around #'meow-kill-whole-line
(if (derived-mode-p 'vterm-mode)
(vterm-send-key "" nil nil nil)
(apply fn args)))

;;;###autoload
(defadvice! +vterm-backword-char-a (fn &rest args)
:around #'meow-right
(if (derived-mode-p 'vterm-mode)
(vterm-send-key "" nil nil nil)
(apply fn args)))

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

** Git porcelain (magit.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Git_porcelain_(magit.el)-2bd86a5c
:END:

#+begin_src emacs-lisp
(straight-use-package 'magit)

(bind-key "C-M-g" 'magit-status-here)
(once '(:packages 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)))
(bind-keys :map magit-diff-section-base-map
("" . magit-diff-visit-file-other-window)))
#+end_src

** Helpful (helpful.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Helpful_(helpful.el)-a60f78d4
:END:

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

- disable auto jump to other end when cycle through buttons never
- open new window when invoking =helpful-visit-references=. auto
- focus newly opened help buffer (same behaviour as helpful.el)

#+begin_src emacs-lisp
(straight-use-package 'helpful)

(bind-keys :map global-map
("C-h K" . describe-keymap) ; overrides `Info-goto-emacs-key-command-node'
([remap describe-function] . helpful-callable)
([remap describe-symbol] . helpful-symbol)
([remap describe-key] . helpful-key))

(once '(:packages helpful)
(bind-keys :map helpful-mode-map
("M-n" . (lambda () (interactive) (forward-button 1 nil 1 t)))
("M-p" . (lambda () (interactive) (backward-button 1 nil 1 t)))))
#+end_src

** Emacs Manual (info.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Emacs_Manual_(info.el)-d5c1f4c5
:END:

#+begin_src emacs-lisp
(straight-use-package '(info :type built-in))

(once '(:packages info)
(bind-keys :map Info-mode-map
("n" . next-line)
("p" . previous-line)
("C-n" . Info-next)
("C-p" . Info-prev)
("M-n" . forward-paragraph)
("M-p" . backward-paragraph)))
#+end_src

** Man page (man.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Man_page_(man.el)-7c4bbdc4
:END:

#+begin_src emacs-lisp
(straight-use-package '(man :type built-in))

(setq Man-notify-method 'newframe)
(once '(:packages man)
(bind-keys :map Man-mode-map ("q" . kill-this-buffer)))
#+end_src

** Ripgrep (rg.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Ripgrep_(rg.el)-e391a6c2
: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 p p= to search in current project, no ask for confirmation

#+begin_src emacs-lisp
(straight-use-package 'rg)

(once '(:hooks find-file-hook)
(+rg-setup) ; for lazy loading
(bind-key "r" 'rg-project-all-files-no-ask grandview-prog-map))
#+end_src

*** Autoload
:PROPERTIES:
:CUSTOM_ID: Utils-Ripgrep_(rg.el)-Autoload-c1d779af
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+rg.el"
: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

** Writable grep (wgrep.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Writable_grep_(wgrep.el)-63076505
: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
(straight-use-package 'wgrep)

(once '(:packages wgrep)
(setq wgrep-auto-save-buffer t)
(setq wgrep-change-readonly-file t)
(bind-keys :map wgrep-mode-map
("M-n" . next-error-no-select)
("M-p" . previous-error-no-select)))
#+end_src

** Pdf reader (pdf-tools.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Pdf_reader_(pdf-tools.el)-d13f78a6
:END:

#+begin_src emacs-lisp
(straight-use-package 'pdf-tools)

(once '(:hooks find-file-hook)
(pdf-tools-install))

(once '(:packages pdf-tools)
(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-keys :map pdf-view-mode-map
("C-s" . isearch-forward)
("h" . pdf-annot-add-highlight-markup-annotation)
("t" . 'pdf-annot-add-text-annotation)
("D" . 'pdf-annot-delete)))
#+end_src

** Epub reader (nov.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Epub_reader_(nov.el)-cd2820eb
:END:

#+begin_src emacs-lisp
(straight-use-package 'nov)
(push '("\\.epub\\'" . nov-mode) auto-mode-alist)
(setq nov-shr-rendering-functions '((img . nov-render-img)
(title . nov-render-title)
(b . shr-tag-b)))
#+end_src

** Murl (murl.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Murl_(murl.el)-6b2c63b5
:END:

#+begin_src emacs-lisp
(bind-keys
:map grandview-apps-map
("l" . murl-open)) ; "l" for live streaming
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+murl.el"
:CUSTOM_ID: Utils-Murl_(murl.el)-Autoload-4a8c3e5e
:END:

#+begin_src emacs-lisp
(require 'json)

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

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

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

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

** Dictionary (fanyi.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Dictionary_(fanyi.el)-00d4d402
:END:

#+begin_src emacs-lisp
(straight-use-package 'fanyi)

(bind-key "t" 'fanyi-dwim grandview-apps-map)
(once '(:packages fanyi)
(setq! fanyi-providers '(fanyi-etymon-provider fanyi-longman-provider)))
#+end_src

** Forges (forge.el)
:PROPERTIES:
:CUSTOM_ID: Utils-Forges_(forge.el)-a5135e77
:END:

#+begin_src emacs-lisp
(straight-use-package 'forge)
#+end_src

* Programming Languages
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-1f5deb4e
:END:

** Rust
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-Rust-71634a9b
:END:

#+begin_src emacs-lisp
(straight-use-package 'rust-mode)
#+end_src

** Csharp
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-Csharp-701DFE28
: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
(straight-use-package 'sharper)
(once '(:hooks csharp-mode-hook)
(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 "DOTNET_ROOT" "/usr/local/share/dotnet"))

(add-to-list 'auto-mode-alist '("\\.[a]?xaml\\'" . nxml-mode))
#+end_src

** Python
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-Python-a11554d3
:END:

#+begin_src emacs-lisp
(straight-use-package '(python :type built-in))

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

** Lua
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-Lua-73137a0a
:END:

#+begin_src emacs-lisp
(straight-use-package 'lua-mode)

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

** Yaml
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-Yaml-a3da110e
:END:

#+begin_src emacs-lisp
(straight-use-package 'yaml-mode)
#+end_src

** JavaScript
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-JavaScript-e274ea41
:END:

#+begin_src emacs-lisp
(straight-use-package '(js :type built-in))
(straight-use-package 'web-mode)

(add-hook 'web-mode-hook
(lambda ()
(emmet-mode)
(setq web-mode-markup-indent-offset 2)
(setq web-mode-code-indent-offset 2)
(setq web-mode-script-padding 0)))
(add-to-list 'auto-mode-alist '("\\.vue\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.jsx?$" . web-mode))

(setq js-indent-level 2)
(setq web-mode-content-types-alist '(("jsx" . "\\.js[x]?\\'")))
#+end_src

** Bash|Zsh
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-Bash|Zsh-88916c15
:END:

#+begin_src emacs-lisp
(straight-use-package '(sh-script :type built-in))

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

** HTML
:PROPERTIES:
:CUSTOM_ID: Programming_Languages-HTML-38c2c5f7
:END:

#+begin_src emacs-lisp
(straight-use-package 'emmet-mode)
#+end_src

* DevTools
:PROPERTIES:
:CUSTOM_ID: DevTools-f401ccff
:END:

** Compiler (compile.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Compiler-842C3947
:END:

Run compiler as inferior of Emacs, parse error messages.

#+begin_src emacs-lisp
(bind-keys :map grandview-prog-map
("p" . compile)
("P" . recompile))
#+end_src

** LSP (lsp.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-LSP_(lsp.el)-9884b2d1
:END:

#+begin_src emacs-lisp
(straight-use-package 'lsp-mode)

(dolist (lang '(sh lua python web typescript rust csharp))
(define-prefix-command '+lsp-map)
(bind-keys :map mode-specific-map ("l" . +lsp-map))
(add-hook (intern (format "%s-mode-hook" lang)) 'lsp-deferred))

(once '(:hooks lsp-mode-hook)
(defalias '+lsp-map lsp-command-map))

(once '(:packages lsp-mode)
(setq lsp-completion-provider :none) ;; we use Corfu!
(setq lsp-enable-imenu nil)
(setq lsp-eldoc-hook nil)
(setq lsp-enable-snippet nil)
(setq lsp-signature-auto-activate t)
(setq lsp-signature-function 'lsp-signature-posframe)
(setq lsp-signature-doc-lines 20)
(setq lsp-server-install-dir (expand-file-name (locate-user-emacs-file "lsp")))
(setq lsp-headerline-breadcrumb-enable t)
(setq lsp-lua-completion-keyword-snippet "Disable")
(add-hook 'lsp-mode-hook #'+lsp-setup-orderless-h)
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\envs\\'"))
#+end_src

*** Extensions
:PROPERTIES:
:CUSTOM_ID: DevTools-LSP_(lsp.el)-Extensions-c1cee41d
:END:

#+begin_src emacs-lisp
(straight-use-package 'lsp-tailwindcss)
(straight-use-package 'lsp-pyright)
(straight-use-package 'lsp-ui)

(add-hook 'python-mode-hook '+lsp-pyright-import-venv)
(add-hook 'lsp-mode-hook 'lsp-ui-mode)
(setq lsp-tailwindcss-add-on-mode t)
(when (executable-find "python3")
(setq lsp-pyright-python-executable-cmd "python3"))
(setq lsp-ui-doc-position 'bottom)
(setq lsp-ui-doc-show-with-cursor t)
(setq lsp-ui-doc-show-with-mouse t)
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+lsp.el"
:CUSTOM_ID: DevTools-LSP_(lsp.el)-Autoload-7b67a356
:END:

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

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

;;;###autoload
(defun +lsp-setup-orderless-h ()
"Configure orderless for `lsp-mode'."
(setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
'(orderless)))
#+end_src

** COMMENT Fastest LSP client (lsp-bridge.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Fastest_LSP_client_(lsp-bridge.el)-b41ad894
:END:

#+begin_src emacs-lisp
(straight-use-package
'(lsp-bridge :host github :repo "manateelazycat/lsp-bridge"
:files (:defaults "acm/*.el" "acm/*.py" "*.py")
:post-build ((make-symbolic-link
(expand-file-name "acm/icons" default-directory)
(expand-file-name "straight/build/lsp-bridge/"
straight-base-dir)))))

(once '(:hooks find-file-hook)
(global-lsp-bridge-mode))
#+end_src

** Virturl environment (conda.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Virturl_environment_(conda.el)-89e49db2
:END:

#+begin_src emacs-lisp
(straight-use-package 'conda)

(setq conda-anaconda-home "/opt/miniconda/")
(setq conda-env-home-directory "/opt/miniconda/")
(setq conda-message-on-environment-switch t)
(bind-key "C" '+conda-activate-for-buffer grandview-prog-map)
(add-hook 'conda-postactivate-hook
(lambda () (require 'jupyter) (jupyter-available-kernelspecs t)))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+conda.el"
:CUSTOM_ID: DevTools-Virturl_environment_(conda.el)-Autoload-075d685c
:END:

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

** Jupyter (jupyter.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Jupyter_(jupyter.el)-3e47b9a1
:END:

#+begin_src emacs-lisp
(straight-use-package 'jupyter)

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

** Colorizer (rainbow-mode.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Colorizer_(rainbow-mode.el)-446765b7
:END:

#+begin_src emacs-lisp
(straight-use-package 'rainbow-mode)
(add-hook 'prog-mode-hook 'rainbow-mode)
#+end_src

** Formatter (reformatter.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Formatter_(reformatter.el)-f22ebd0e
: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
(straight-use-package 'reformatter)

(bind-key "f" '+format-buffer grandview-apps-map)
(once '(:packages reformatter)
(reformatter-define lua-format
:program "stylua"
:args '("--indent-width" "2" "-")
:lighter " styLua")
(reformatter-define python-format
:program "black"
:args '("-")
:lighter " blackFMT"))
#+end_src

*** Autoload
:PROPERTIES:
:CUSTOM_ID: DevTools-Formatter_(reformatter.el)-Autoload-a38e5186
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+reformatter.el"
:END:

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

** Syntax checker (flymake.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Syntax_checker_(flymake.el)-6459d9de
:END:

#+begin_src emacs-lisp
(straight-use-package '(flymake :type built-in))

(add-hook 'prog-mode-hook '+flymake-enable)
(add-hook 'text-mode-hook 'flymake-mode)
(bind-keys :map grandview-prog-map
("e" . flymake-goto-next-error)
("E" . flymake-goto-prev-error)
("S" . flymake-start))
#+end_src

*** Autoload
:PROPERTIES:
:CUSTOM_ID: DevTools-Syntax_checker_(flymake.el)-Autoload-12553acc
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+flymake.el"
:END:

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

** REST client (restclient.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-REST_client_(restclient.el)-59960c1f
:END:

#+begin_src emacs-lisp
(straight-use-package 'restclient)
#+end_src

** Scratch buffers (scratch.el)
:PROPERTIES:
:CUSTOM_ID: DevTools-Scratch_buffers_(scratch.el)-362d86c2
: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
(bind-keys :map grandview-apps-map
("s" . +scratch))
#+end_src

*** Autoload
:PROPERTIES:
:header-args:emacs-lisp: :tangle "/tmp/grandview/autoloads/+scratch.el"
:CUSTOM_ID: DevTools-Scratch_buffers_(scratch.el)-Autoload-44ca32c9
: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 (or (bound-and-true-p conda-env-current-name) "base"))
(text (concat "#+PROPERTY: header-args:python :session "
jpt-session))
(mode "org"))
(with-current-buffer new-buf
(when query-for-mode
(setq mode (completing-read
"Mode: " (+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

* About this file
:PROPERTIES:
:CUSTOM_ID: About_this_file-c37d0fab
:END:

** COPYING
:PROPERTIES:
:CUSTOM_ID: About_this_file-COPYING-81f2aab2
:END:

#+begin_quote
Copyright (c) 2020-2022 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

** File local variables
:PROPERTIES:
:CUSTOM_ID: About_this_file-File_local_variables-4f09d29d
:END:

Here are the local variables in this file.

+ ~eldoc-documentation-functions~: make eldoc works the same way as in a normal elisp file
+ ~org-edit-src-content-indentation~: do not add spaces at line beginning in src blocks
+ ~+org-id-auto~: generate =:CUSTOM_ID= for every heading on saving.

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

(defun grandview-setup-literate-file ()
"Setup for `grandview-org-file'."
(setq-local eldoc-documentation-functions
'(elisp-eldoc-var-docstring grandview--org-eldoc-funcall))
(setq-local org-edit-src-content-indentation 0)
(setq-local +org-id-auto t))
#+end_src

# Local Variables:
# eval: (ignore-errors (grandview-setup-literate-file)))
# End: