- Core Configurations
- User Configurations
- Part 1: Beginning of A Journey
- Part 2: Decorating Irkalla
- Part 3: Expanding Our Utility Box
- Emacs Everywhere: Call Forth Emacs Frame
- ESUP: Trace Performance Hiccups
- Helpful:
C-h
On Steroids - Recentf: Controlling Recent Files
- Pyim: Chinese, Japanese & Korean
- Shackle: Hardcode Placement of Popup Buffers
- Popper: Pop-up Related Buffers On Demand
- Dired: Built-in Emacs File Manager
- Which Key Was It Again???
- Consult: Search & Navigation Commands
- Embark: Mini-Buffer Actions
- Vertico: Mini-Buffer On Steroids
- Marginalia: Annotate The Mini-Buffer
- Terminal Work & Emulation Within Emacs
- Readers: Life Without Reading Sucks
- Communication With The Outside World
- Browsing The Web
- Part 4: Editing Environment
- EVIL: VIM Layer For Emacs
- EditorConf: Respect Project Config
- Direnv: Loads Pre-Defined Directory Environment
- Turn Into Executable File On Save
- Display Number Line
- Highlight Matching Parentheses
- Colorful Indentations Levels
- Colorful Hex/RGB Colors
- Highlight Current Line
- Window Frame Dividers
- Whitespaces: Management of Invisible Chars
- Ligatures: Alternative Way To Display SYMB
- Soft-Wrap & Center The Displayed Content
- Elec-Pair: Insert Matching SYMB
- Autorevert: Watch Files -> Update On Change
- Maintaining A Cleaner History
- Separate Emacs Clipboard From System
- Ediff: Display File Difference(s)
- Regional Specific Manipulations
- Magit: Git Porcelain Inside Emacs
- Beframe: Isolate Emacs Frames
- Desktop: Emacs Session Management
- Project: Management of Random Ideas
- :NIXPKGS: Jinx: Elegant Spell Checker
- Part 5: Documentation Languages
- Part 6: Completion & Debugging
- Orderless: Completion Candidate Matching
- Breadcrumb: Location Indicator For Larger Projects
- Apheleia: Code Formatter
- Corfu: Elegant Completion UI
- Cape: Controlling Suggestion Output
- Yasnippet: Completion Through Abbreviations
- Eldoc: Documentation of SYMB
- Tree-Sitter: Parser Generator Tool
- Flymake: Built-in Buffer Diagnostics
- Dape: Debug Adapter Protocol
- Part 7: Language Server Protocols
- C: Embracing the Challenges of Low-Level Code
- Emacs Lisp: The Mother-tongue of Emacs
- Haskell: Purely Functional
- Lua: Embedded Scripting Language
- Nix: Purely Functional DSL
- Python: High Level Language
- Rust: Statically & Strongly Typed
- Typst: A Less Bloated LaTeX Alt.
- Web Development: Bloating The Web With Funky Code
- Zig: Imperative & Statically Compiled
- Part 8: Miscellaneous Bits
- Part 9: Concluding Our Endless Package Hunt!
;;; early-init.el --- Welcome To Irkalla Emacs -*- lexical-binding: t; -*-
;; Copyright (C) 2023-2023 Icy-Thought
;; Author: Icy-Thought <[email protected]>
;; Keywords: internal
;; URL: https://icy-thought.github.io/
;;; Commentary:
;; Configurations which ought to be loaded during Irkalla Emacs early initliazation process.
;;; Code:
(defgroup ikralla nil
"Irkalla might as well become a cult at this point."
:link '(url-link "https://github.com/Icy-Thought/emacs.d")
:group 'emacs)
(defcustom irkalla/underworld (file-truename "~/Workspace/public/emacs.d")
"Underworld, the land where Irkalla resides within."
:type 'string
:group 'irkalla)
(defconst IS-REPRO
(and (eq system-type 'gnu/linux)
(with-temp-buffer
(insert-file-contents "/etc/os-release")
(re-search-forward (rx bos "ID=" (| "nixos" "guix") eos) nil t))))
(setq-default default-frame-alist
(append '((fullscreen . nil)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(vertical-scroll-bars . nil)))
initial-frame-alist (copy-alist default-frame-alist)
user-emacs-directory "~/.config/emacs")
(setq-default custom-file (expand-file-name "etc/custom.el" user-emacs-directory))
(if (file-exists-p custom-file)
(load custom-file 'noerror 'nomessage)
(with-temp-buffer (write-file custom-file)))
(with-current-buffer "*scratch*" (emacs-lock-mode 'kill))
(with-current-buffer "*Messages*" (emacs-lock-mode 'kill))
(when (boundp 'read-process-output-max)
(setq-default process-adaptive-read-buffering nil
read-process-output-max
(or (ignore-errors (with-temp-buffer
(insert-file-contents "/proc/sys/fs/pipe-max-size")
(string-to-number (buffer-string))))
(* 1024 1024))))
(when (featurep 'native-compile)
;; :NOTE| Retain native compilation cache files in ~/.cache/emacs directory
(let ((path (expand-file-name "var/eln-cache/" user-emacs-directory)))
(setq-default native-comp-eln-load-path (list path)
native-compile-target-directory path))
;; :NOTE| Prevent unwanted runtime builds + reduce noise
(setq-default native-comp-deferred-compilation nil
native-comp-async-report-warnings-errors nil))
(setq-default gc-cons-percentage 0.6
gc-cons-threshold most-positive-fixnum)
;; :NOTE| Reduce GC threshold for propper garbage collection
(add-hook 'after-init-hook
(lambda () (setopt gc-cons-threshold (* 5 1024 1024))))
Source: https://reddit.com/r/emacs/comments/3kqt6e/2_easy_little_known_steps_to_speed_up_emacs_start
(put 'file-name-handler-alist 'original-value
(default-toplevel-value 'file-name-handler-alist))
(set-default-toplevel-value 'file-name-handler-alist nil)
;; Restore `file-name-handler-alist' after startup while conserving the potential new elements
(add-hook 'emacs-startup-hook
(lambda ()
(setopt file-name-handler-alist
(delete-dups
(append file-name-handler-alist
(get 'file-name-handler-alist 'original-value))))) 99)
(setq-default ad-redefinition-action 'accept
debug-on-error init-file-debug
jka-compr-verbose init-file-debug)
(setq-default auto-mode-case-fold nil
blink-cursor-mode nil
echo-keystrokes 0.02
fast-but-imprecise-scrolling t
inhibit-startup-screen t
menu-bar-mode nil
mode-line-format nil
scroll-bar-mode nil
tool-bar-mode nil
use-dialog-box nil
use-file-dialog nil)
Reduce startup blabber aggressively instead of partial reduction through the variables.
(fset 'display-startup-echo-area-message #'ignore)
(fset 'display-startup-screen #'ignore)
(setq-default frame-inhibit-implied-resize t
frame-resize-pixelwise t
idle-update-delay 1.0
inhibit-compacting-font-caches t
redisplay-skip-fontification-on-input t)
(setq-default auto-save-list-file-prefix nil
create-lockfiles nil
package-enable-at-startup nil
use-short-answers t
vc-follow-symlinks t)
(setq-default initial-major-mode 'fundamental-mode
initial-scratch-message nil
load-prefer-newer t
ring-bell-function 'ignore
select-active-regions 'only
select-enable-clipboard nil)
We might as well set our default Emacs environment to UTF-8
.
(prefer-coding-system 'utf-8)
(set-charset-priority 'unicode)
(set-default-coding-systems 'utf-8)
(set-language-environment "UTF-8")
(set-locale-environment "en_US.UTF-8")
;;; early-init.el ends here
;;; init.el --- Core: laboratory of Irkalla -*- lexical-binding: t; -*-
;; Copyright (C) 2023-2023 Icy-Thought
;; Author: Icy-Thought <[email protected]>
;; Keywords: internal
;; URL: https://icy-thought.github.io/
;;; Commentary:
;; The main file where I include my (increasing?) Emacs modules & configurations.
;;; Code:
Since Elpaca struggles to find the development build version of my Emacs, it is necessary for me to define the elpaca-core-date
for it to function as intended. The reason for this is because Nix disables the emacs-build-time
for the reproducible builds to work as designed.
(unless (<= emacs-major-version 29)
(defvar elpaca-core-date (list (string-to-number (format-time-string "%Y%m%d")))))
(defvar elpaca-installer-version 0.8)
(defvar elpaca-directory (expand-file-name "var/elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil :depth 1
:files (:defaults "elpaca-test.el" (:exclude "extensions"))
:build (:not elpaca--activate-package)))
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(when (< emacs-major-version 28) (require 'subr-x))
(condition-case-unless-debug err
(if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (apply #'call-process `("git" nil ,buffer t "clone"
,@(when-let* ((depth (plist-get order :depth)))
(list (format "--depth=%d" depth) "--no-single-branch"))
,(plist-get order :repo) ,repo))))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(load "./elpaca-autoloads")))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
Tell Evil
to refrain from taking over the keybindings when inside elpaca-ui-mode
.
(with-eval-after-load 'evil
(evil-make-intercept-map elpaca-ui-mode-map))
(elpaca elpaca-use-package
(elpaca-use-package-mode)
(setopt use-package-always-ensure t
use-package-compute-statistics t))
Another neat thing that I could do with the help of macros, is to add a use-feature
macro that helps reduce the burden of writing use-package
+ :ensure nil
and instead compress it into (use-feature)
!
(defmacro use-feature (name &rest args)
"Similar to `use-package', but for built-in packages.
NAME and ARGS are in `use-package'."
(declare (indent defun))
`(use-package ,name
:ensure nil
,@args))
;; :NOTE| Magit complains a lot about Transient...
(setopt elpaca-ignored-dependencies
(delq 'transient elpaca-ignored-dependencies))
(elpaca-wait)
(defun irkalla/read-secret-file (filename)
"Fetch content of secrets file generated by agenix."
(with-temp-buffer
(insert-file-contents (expand-file-name filename "/run/agenix"))
(string-trim-right (buffer-string))))
(use-package no-littering :demand t
:config
(setq no-littering-etc-directory (expand-file-name "etc" user-emacs-directory)
no-littering-var-directory (expand-file-name "var" user-emacs-directory))
(with-eval-after-load 'recentf
(add-to-list 'recentf-exclude (recentf-expand-file-name no-littering-var-directory))
(add-to-list 'recentf-exclude (recentf-expand-file-name no-littering-etc-directory)))
(with-eval-after-load 'files
(setopt auto-save-file-name-transforms
`((".*" ,(no-littering-expand-var-file-name "auto-save/") t))
backup-directory-alist
`((".*" . ,(no-littering-expand-var-file-name "backups/"))))))
(use-package pretty-hydra
:config
(cl-defun pretty-hydra-title (title &optional icon-type icon-name
&key face height v-adjust)
(let ((face (or face `(:inherit hydra-face-pink :height 1.2 :slant italic)))
(height (or height 1.2))
(v-adjust (or v-adjust 0.0)))
(concat
(when (and (display-graphic-p) icon-type icon-name)
(let ((f (intern (format "nerd-icons-%s" icon-type))))
(when (fboundp f)
(concat (apply f (list icon-name :face face :height height :v-adjust v-adjust))
" "))))
(propertize title 'face face)))))
To prevent Elpaca from complaining about the missing :prett-hydra
use-package keyword, we have to tell it to first wait and then proceed with the remaining configuration.
(elpaca-wait)
(use-package hydra-posframe
:ensure (:host github :repo "Ladicle/hydra-posframe")
:after (pretty-hydra)
:config (hydra-posframe-mode)
:custom
(hydra-posframe-border-width 2)
(hydra-posframe-parameters '((left-fringe . 25) (right-fringe . 25))))
(pretty-hydra-define main-hydra
(:title (pretty-hydra-title "──「 Phylum Cnidaria 」──" 'mdicon "nf-md-graph")
:color teal :quit-key "q")
("Main"
(("o" launcher-hydra/body "Launcher")
("m" elpaca-hydra/body "Elpaca"))
"Control"
(("b" buffer-hydra/body "Buffer"))
"Action"
(("z" zone "Zooning out..."))))
Then we have another essential Hydra that we will tie our editing related commands to, editor-hydra
.
(pretty-hydra-define editor-hydra
(:title (pretty-hydra-title "──「 Chrysaora Melanaster 」──" 'mdicon "nf-md-graph_outline")
:color teal :quit-key "q")
("Programming"
(("RET" (if (derived-mode-p 'prog-mode)
(call-interactively #'project-compile)
(message "Buffer /= PROG buffer...")) "Compile"))
"Action"
(("b" eval-buffer "Eval Buf."))))
(pretty-hydra-define visual-editor-hydra
(:title (pretty-hydra-title "──「 (Visual) Chrysaora Melanaster 」──" 'mdicon "nf-md-graph_outline")
:color teal :quit-key "q")
("Action"
(("e" eval-region "Eval Region"))))
Time to add a couple of keybindings to call the newly created Hydras!
(with-eval-after-load 'evil
(evil-global-set-key 'normal (kbd "SPC") 'main-hydra/body)
(evil-global-set-key 'normal (kbd ",") 'editor-hydra/body)
(evil-global-set-key 'visual (kbd ",") 'visual-editor-hydra/body))
(pretty-hydra-define buffer-hydra
(:title (pretty-hydra-title "──「 Main: Buffer(s) 」──" 'octicon "nf-oct-repo_template")
:color teal :quit-key "q")
("Buffer"
(("s" scratch-buffer "Scratch")
("j" next-buffer "Next")
("k" previous-buffer "Previous"))))
(pretty-hydra-define launcher-hydra
(:title (pretty-hydra-title "──「 Main: Launcher(s) 」──" 'codicon "nf-cod-rocket")
:color teal :quit-key "q")
("EWW Browse"
(("w" (eww-browse-url "https://en.wikipedia.org") "Wikipedia"))))
(with-eval-after-load 'elpaca
(pretty-hydra-define elpaca-hydra
(:title (pretty-hydra-title "──「 Main: Elpaca 」──" 'pomicon "nf-pom-clean_code")
:color teal :quit-key "q")
("Main"
(("p" elpaca-manager "Elpaca manager")
("r" elpaca-rebuild "Rebuild package")
("i" elpaca-info "Package info"))
"Fetch"
(("f" elpaca-fetch "Specific package")
("e" elpaca-fetch-all "All packages"))
"Update"
(("m" elpaca-merge "Specific package")
("a" elpaca-merge-all "All packages")))))
(use-feature windmove
:config
(windmove-default-keybindings)
(windmove-default-keybindings 'meta))
Enabling winner-mode
is necessary if you want to create a “zoom” feature for your Emacs configuration. And by that I mean the ability to zoom into one split window and later zoom out to the previous configuration. But for us to zoom-out, it is required from our Emacs configuration to memorize the previous state and this is where winner-mode
comes in!
(use-feature winner
:hook (winner-mode))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define window-hydra
(:title (pretty-hydra-title "──「 Base: Frame Management 」──" 'mdicon "nf-md-dock_window")
:color teal :quit-key "q")
("Windows"
(("f" delete-other-windows "Focus Window")
("u" winner-undo "Restore Old Windows")
("r" winner-redo "Redo Window Change"))))
(pretty-hydra-define+ main-hydra ()
("Control"
(("w" window-hydra/body "Window")))))
(use-feature emacs
:hook (elpaca-after-init . pixel-scroll-precision-mode)
:config (setopt scroll-preserve-screen-position t))
(use-feature time
:custom
(display-time-24hr-format t)
(display-time-day-and-date t))
Add the Irkalla theme directory to Emacs load-path
. Necessary for Autothemer to recognize my customized themes.
(let ((themes-dir (expand-file-name "themes" irkalla/underworld)))
(when (file-directory-p themes-dir)
(add-to-list 'load-path themes-dir)
(add-to-list 'load-path (expand-file-name "template" themes-dir))))
(add-to-list 'custom-theme-load-path
(expand-file-name "themes" irkalla/underworld))
What remains of us is to load the theme of our choice.
(use-package autothemer :demand t
:init (load-theme 'kanagawa-wave t))
(use-package fontaine
:preface (defvar irkalla/default-font-family "VictorMono Nerd Font")
:hook (enable-theme-functions . fontaine-apply-current-preset)
:init (fontaine-set-preset 'default)
:custom
(fontaine-presets
`((default)
(reading
:variable-pitch-family "Cardo"
:variable-pitch-height 185
:variable-pitch-slant normal
:variable-pitch-weight regular)
(presentation
:default-height 175
:default-weight semibold)
(t
:default-family ,irkalla/default-font-family
:default-height 145
:default-weight semibold
:fixed-pitch-family ,irkalla/default-font-family
:fixed-pitch-height 145
:fixed-pitch-slant normal
:variable-pitch-family ,irkalla/default-font-family
:variable-pitch-height 1.00
:variable-pitch-slant italic))))
We also want to create a mode for the reading
preset to activate/deactivate from a Hydra.
(defun irkalla/manuscript-toggle ()
"Toggle buffer appearance for a touch of sophistication."
(if (eq (symbol-value 'fontaine-current-preset) 'regular)
(fontaine-set-preset 'reading)
(fontaine-set-preset 'regular)))
(define-minor-mode irkalla/manuscript-mode
"Paint our buffers with the ancient manuscript style."
:group 'irkalla
:global nil
(irkalla/manuscript-toggle))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ window-hydra ()
("Main"
(("t" fontaine-set-preset "Fontaine Preset")
("m" irkalla/manuscript-mode "Manuscript Mode" :toggle t)))))
Displaying all languages with the same font is cursed and lucky enough Emacs provides a proper way to deal with such cursed behavior! With the help of set-fontset-font
we can inform Emacs about the fonts we’d like it to use when displaying content written in a given language.
(use-feature face-remap
:hook (text-mode . variable-pitch-mode)
:bind (("C-0" . (lambda () (interactive) (text-scale-increase 0.0)))
("C-+" . (lambda () (interactive) (text-scale-increase 0.5)))
("C--" . (lambda () (interactive) (text-scale-decrease 0.5))))
:config
(set-fontset-font t 'arabic (font-spec :family "Scheherazade New") nil 'prepend)
(set-fontset-font t 'han (font-spec :family "Sarasa Mono CL") nil 'prepend)
(set-fontset-font t 'symbol (font-spec :family "Noto Color Emoji") nil 'append))
(use-feature font-lock
:custom-face
(font-lock-builtin-face ((t (:slant italic))))
(font-lock-comment-face ((t (:slant italic))))
(font-lock-doc-face ((t (:slant italic))))
(font-lock-function-name-face ((t (:slant italic :weight bold))))
(font-lock-keyword-face ((t (:slant italic))))
(font-lock-preprocessor-face ((t (:weight bold))))
(font-lock-string-face ((t (:slant italic))))
:custom (font-lock-maximum-decoration t))
(use-feature emacs
:hook (emacs-lisp-mode . prettify-symbols-mode)
:config (setopt prettify-symbols-unprettify-at-point 'right-edge))
(use-package nerd-icons :demand t
:custom
(nerd-icons-font-family
(when (featurep 'fontaine)
(plist-get (fontaine--get-preset-properties 'default) :default-family)))
(nerd-icons-scale-factor 1.05))
(use-package nerd-icons-completion
:after (nerd-icons)
:config (nerd-icons-completion-mode))
(use-package svg-tag-mode
:hook ((prog-mode text-mode) . svg-tag-mode)
:config
<<svg-tag-constants>>
<<svg-tag-patterns>>)
(defconst date-re "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}")
(defconst time-re "[0-9]\\{2\\}:[0-9]\\{2\\}")
(defconst day-re "[A-Za-z]\\{3\\}")
(defconst day-time-re (format "\\(%s\\)? ?\\(%s\\)?" day-re time-re))
<<svg-tag-common>>
<<svg-tag-progress>>
(add-hook 'org-mode-hook
(lambda ()
<<svg-tag-org>>
<<svg-tag-progress>>
(svg-tag-mode)))
(setopt svg-tag-tags
;; :TODO| Reduce to a more general solution
`((,(rx (group ":" (| "todo" "TODO") "|" (1+ any)))
. ((lambda (tag) (svg-tag-make tag :face 'org-todo :inverse t :crop-left t :beg 6))))
(,(rx (group ":" (| "todo" "TODO") "|"))
. ((lambda (tag) (svg-tag-make tag :face 'org-todo :inverse nil :margin 0 :crop-right t :beg 1 :end -1))))
;; :WARN| Heads-up for whatever insane thing found below.
(,(rx (group ":" (| "warn" "WARN") "|" (1+ any)))
. ((lambda (tag) (svg-tag-make tag :face 'org-warning :inverse t :crop-left t :beg 7))))
(,(rx (group ":" (| "warn" "WARN") "|"))
. ((lambda (tag)
(svg-tag-make tag :face 'org-warning :inverse nil :margin 0 :crop-right t :beg 1 :end -1))))
;; :FIXME| Fixing this madness cannot wait, get to it!
(,(rx (group ":" (| "fixme" "FIXME") "|" (1+ any)))
. ((lambda (tag) (svg-tag-make tag :face 'org-upcoming-deadline :inverse t :crop-left t :beg 7))))
(,(rx (group ":" (| "fixme" "FIXME") "|"))
. ((lambda (tag)
(svg-tag-make tag :face 'org-upcoming-deadline :inverse nil :margin 0 :crop-right t :beg 1 :end -1))))
;; :HACK| Fix this regexp
;; :PERF| Fix this regexp
;; :MARK| Mark this regexp
(,(rx (group ":" (| "hack" "HACK" "PERF" "MARK") "|" (1+ any)))
. ((lambda (tag) (svg-tag-make tag :face 'org-priority :inverse t :crop-left t :beg 6))))
(,(rx (group ":" (| "hack" "HACK" "PERF" "MARK") "|"))
. ((lambda (tag)
(svg-tag-make tag :face 'org-priority :inverse nil :margin 0 :crop-right t :beg 1 :end -1))))
;; :NOTE| Reduce to a more general solution
(,(rx (group ":" (| "note" "NOTE") "|" (1+ any)))
. ((lambda (tag) (svg-tag-make tag :face 'org-quote :inverse t :crop-right t :beg 6))))
(,(rx (group ":" (| "note" "NOTE") "|"))
. ((lambda (tag) (svg-tag-make tag :face 'org-quote :inverse nil :margin 0 :crop-right t :beg 1 :end -1))))))
(defun svg-progress-percent (value)
(svg-image (svg-lib-concat
(svg-lib-progress-bar (/ (string-to-number value) 100.0)
nil :margin 0 :stroke 2 :radius 3 :padding 2 :width 11)
(svg-lib-tag (concat value "%")
nil :stroke 0 :margin 0)) :ascent 'center))
(defun svg-progress-count (value)
(let* ((seq (mapcar #'string-to-number (split-string value "/")))
(count (float (car seq)))
(total (float (cadr seq))))
(svg-image (svg-lib-concat
(svg-lib-progress-bar (/ count total) nil :margin 0 :stroke 2 :radius 3 :padding 2 :width 11)
(svg-lib-tag value nil :stroke 0 :margin 0)) :ascent 'center)))
;; Progress (fraction): [1/3]
(push `(,(rx (group "[" (1+ digit) "/" (1+ digit) "]"))
. ((lambda (tag) (svg-progress-count (substring tag 1 -1)))))
svg-tag-tags)
;; Progress (percentage): [45%]
(push `(,(rx (group "[" (** 1 3 digit) "%]"))
. ((lambda (tag) (svg-progress-percent (substring tag 1 -2)))))
svg-tag-tags)
(setq-local svg-tag-tags
;; Basic tags :THIS:
`((,(rx (group ":" (1+ alnum) ":"))
. ((lambda (tag) (svg-tag-make tag :face 'org-tag :beg 1 :end -1))))
;; Task priority [#a]
(,(rx (group "[#" (1+ word) "]"))
. ((lambda (tag) (svg-tag-make tag :face 'org-priority :beg 2 :end -1 :margin 0 :inverse t))))
;; Org TAGS
(,(rx (group ":" (| "todo" "TODO") ":")) . ((lambda (tag) (svg-tag-make "TODO" :face 'org-todo))))
(,(rx (group ":" (| "wip" "WIP") ":")) . ((lambda (tag) (svg-tag-make "WIP" :face 'org-cite))))
(,(rx (group ":" (| "done" "DONE") ":")) . ((lambda (tag) (svg-tag-make "DONE" :face 'org-done))))
(,(rx (group ":" (| "note" "NOTE") ":")) . ((lambda (tag) (svg-tag-make "NOTE" :face 'org-footnote))))
(,(rx (group ":" (| "scheduled" "SCHEDULED") ":")) . ((lambda (tag) (svg-tag-make "SCHEDULED" :face 'org-scheduled))))
(,(rx (group ":" (| "deadline" "DEADLINE") ":")) . ((lambda (tag) (svg-tag-make "DEADLINE" :face 'org-upcoming-deadline))))
;; Tagging some of Org's many blocks
(,(rx (group "#+" (| "name" "NAME") ":")) . ((lambda (tag) (svg-tag-make "NAME" :face 'org-meta-line))))
(,(rx (group "#+" (| "begin_src" "BEGIN_SRC"))) . ((lambda (tag) (svg-tag-make "BEGIN SRC" :face 'org-block-begin-line))))
(,(rx (group "#+" (| "end_src" "END_SRC"))) . ((lambda (tag) (svg-tag-make "END SRC" :face 'org-block-end-line))))
(,(rx (group "#+" (| "begin_export" "BEGIN_EXPORT"))) . ((lambda (tag) (svg-tag-make "BEGIN EXPORT" :face 'org-block-begin-line))))
(,(rx (group "#+" (| "end_export" "END_EXPORT"))) . ((lambda (tag) (svg-tag-make "END EXPORT" :face 'org-block-end-line))))
(,(rx (group "#+" (| "begin_example" "BEGIN_EXAMPLE"))) . ((lambda (tag) (svg-tag-make "BEGIN EXAMPLE" :face 'org-block-begin-line))))
(,(rx (group "#+" (| "end_example" "END_EXAMPLE"))) . ((lambda (tag) (svg-tag-make "END EXAMPLE" :face 'org-block-end-line))))
(,(rx (group "#+" (| "begin_quote" "BEGIN_QUOTE"))) . ((lambda (tag) (svg-tag-make "BEGIN QUOTE" :face 'org-quote :italic t))))
(,(rx (group "#+" (| "end_quote" "END_QUOTE"))) . ((lambda (tag) (svg-tag-make "END QUOTE" :face 'org-quote :italic t))))
(,(rx (group "#+" (| "begin_signature" "BEGIN_SIGNATURE"))) . ((lambda (tag) (svg-tag-make "BEGIN SIGNATURE" :face 'org-footnote :italic t))))
(,(rx (group "#+" (| "end_signature" "END_SIGNATURE"))) . ((lambda (tag) (svg-tag-make "END SIGNATURE" :face 'org-footnote :italic t))))
(,(rx (group "#+" (| "begin_sidenote" "BEGIN_SIDENOTE"))) . ((lambda (tag) (svg-tag-make "BEGIN SIDENOTE" :face 'org-quote :italic t))))
(,(rx (group "#+" (| "end_sidenote" "END_SIDENOTE"))) . ((lambda (tag) (svg-tag-make "END SIDENOTE" :face 'org-quote :italic t))))
(,(rx (group "#+" (| "results" "RESULTS") ":")) . ((lambda (tag) (svg-tag-make "RESULTS" :face 'org-done :underline nil))))
;; Citation of the form [cite:@Knuth:1984]
(,(rx (group "[" (| "cite" "CITE") ":@" (1+ word) ":"))
. ((lambda (tag) (svg-tag-make tag :inverse t :beg 7 :end -1 :crop-right t))))
(,(rx (seq "[" (| "cite" "CITE") ":@" (1+ word) ":" (group (1+ digit) "]")))
. ((lambda (tag) (svg-tag-make tag :end -1 :crop-left t))))
;; :XXX|YYY: -> "XXX" & "YYY"
(,(rx (seq (group ":" (1+ upper)) "|" (1+ alnum) ":"))
. ((lambda (tag) (svg-tag-make tag :beg 1 :inverse t :margin 0 :crop-right t))))
(,(rx (seq ":" (1+ upper) (group "|" (1+ alnum) ":")))
. ((lambda (tag) (svg-tag-make tag :beg 1 :end -1 :margin 0 :crop-left t))))
;; Active date <2023-04-03 Sun 17:45>
(,(format "\\(<%s>\\)" date-re) .
((lambda (tag) (svg-tag-make tag :beg 1 :end -1 :margin 0 :face 'org-date))))
(,(format "\\(<%s \\)%s>" date-re day-time-re) .
((lambda (tag) (svg-tag-make tag :beg 1 :inverse nil :crop-right t :margin 0 :face 'org-date))))
(,(format "<%s \\(%s>\\)" date-re day-time-re) .
((lambda (tag) (svg-tag-make tag :end -1 :inverse t :crop-left t :margin 0 :face 'org-date))))
;; Inactive date [2023-04-03 Sun 17:45]
(,(format "\\(\\[%s\\]\\)" date-re) .
((lambda (tag) (svg-tag-make tag :beg 1 :end -1 :margin 0 :face 'org-date))))
(,(format "\\(\\[%s \\)%s\\]" date-re day-time-re) .
((lambda (tag) (svg-tag-make tag :beg 1 :inverse nil :crop-right t :margin 0 :face 'org-date))))
(,(format "\\[%s \\(%s\\]\\)" date-re day-time-re) .
((lambda (tag) (svg-tag-make tag :end -1 :inverse t :crop-left t :margin 0 :face 'org-date))))))
(use-package doom-modeline
:hook (elpaca-after-init . doom-modeline-mode)
:custom
(doom-modeline-bar-width 4)
(doom-modeline-buffer-file-name 'relative-to-project)
(doom-modeline-github t)
(doom-modeline-github-interval (* 30 60))
(doom-modeline-height 35)
(when (display-graphic-p) (doom-modeline-hud t)))
(use-package centaur-tabs
:bind (:map centaur-tabs-mode-map
("C-<prior>" . #'centaur-tabs-backward-group)
("C-<next>" . #'centaur-tabs-forward-group)
("M-<prior>" . #'centaur-tabs-backward)
("M-<next>" . #'centaur-tabs-forward)
("M-S-<prior>" . #'centaur-tabs-move-current-tab-to-left)
("M-S-<next>" . #'centaur-tabs-move-current-tab-to-right))
:hook (elpaca-after-init . centaur-tabs-mode)
:config
(setq centaur-tabs-excluded-prefixes
`(,@centaur-tabs-excluded-prefixes
"*" " *" "consult-partial-preview" "Ement" "magit"))
:custom
(centaur-tabs-height 35)
(centaur-tabs-set-icons t)
;; (centaur-tabs-set-bar 'left)
(centaur-tabs-cycle-scope 'tabs)
(centaur-tabs-set-modified-marker t))
1.XX+s startup time is caused by Emacs dashboard. Emacs startup time without it is < 0.3Xs.
(use-package dashboard
:after (nerd-icons)
:hook (elpaca-after-init . dashboard-refresh-buffer)
:config
(setopt initial-buffer-choice (lambda () (get-buffer dashboard-buffer-name)))
:custom
(dashboard-display-icons-p t)
(dashboard-icon-type 'nerd-icons)
(dashboard-banner-logo-title "Welcome To The Underworld, Human. - Irkalla")
(dashboard-center-content t)
(dashboard-modify-heading-icons '((recents . "file-text") (bookmarks . "book")))
(dashboard-startup-banner (expand-file-name "logos/png/lotus.png" irkalla/underworld))
(dashboard-path-max-length 20)
(dashboard-set-heading-icons t)
(dashboard-set-file-icons t)
(dashboard-set-init-info t)
(dashboard-week-agenda t)
(dashboard-set-navigator t)
(dashboard-items '((recents . 5)
(bookmarks . 5)))
(dashboard-item-names
'(("Recent Files:" . " Recently opened files:")
("Bookmarks:" . " Pinned Items:"))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ launcher-hydra ()
("Application"
(("RET" dashboard-refresh-buffer "Dashboard")))))
(use-package emacs-everywhere
:commands (emacs-everywhere)
:custom (emacs-everywhere-copy-command '("sh" "-c" "cat %f | cb copy")))
(use-package esup
:commands (esup)
:custom (esup-depth 0))
(use-package helpful
:bind
([remap describe-callable] . helpful-callable)
([remap describe-function] . helpful-function)
([remap describe-variable] . helpful-variable)
([remap describe-key] . helpful-key)
([remap view-emacs-debugging] . helpful-at-point)
:pretty-hydra
((:title (pretty-hydra-title "──「 Utilities: Helpful 」──" 'mdicon "nf-md-help_network")
:color teal :quit-key "q")
("Describe"
(("k" helpful-key "Key(s)")
("f" helpful-function "Function(s)")
("F" helpful-callable "Interactive function(s)")
("v" helpful-variable "Variable(s)")
("c" helpful-command "Command(s)"))
"Action"
(("p" helpful-at-point "SYMB at point"))))
:init (setopt help-window-select t))
Since we created our Hydra with :pretty-hydra
keyword, we can just append our new hydra to our main Hydra.
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ main-hydra ()
("Main"
(("h" helpful-hydra/body "Helpful")))))
(use-feature recentf
:hook (elpaca-after-init . recentf-mode)
:custom
(recentf-case-fold-search t)
(recentf-max-saved-items 450)
(recentf-exclude
`(,(rx bos "/tmp/")
,(rx bos "/nix/store")
;; :NOTE| Compressed files & Archives
,(rx "."
(| "tar" "tbz2" "tbz" "tgz"
"bz2" "bz" "gz" "gzip" "xz" "zpaq"
"lz" "lrz" "lzo" "lzma" "shar" "kgb"
"zip" "Z" "7z" "rar")
eos)
;; :NOTE| TRAMP
,(rx bos "/sudo:")
,(rx bos "/ssh:"))))
(use-package pyim
:bind (:map text-mode-map ("M-j" pyim-convert-string-at-point))
:custom
(pyim-default-scheme 'quanpin)
(pyim-page-tooltip 'posframe)
(pyim-page-length 5)
(pyim-directory (no-littering-expand-var-file-name "pyim/"))
(pyim-dcache-directory (pyim-directory "dcache/")))
Also, I want several dictionaries to learn & recall the definitions of several words.
(use-package pyim-basedict
:after (pyim)
:hook (pyim-mode . pyim-basedict-enable))
(use-package youdao-dictionary
:commands (youdao-dictionary-search-at-point-posframe)
:bind (("C-c y" youdao-dictionary-search-at-point-posframe)))
(use-package shackle
:hook (elpaca-after-init . shackle-mode)
:custom
(shackle-default-size 0.33)
(shackle-rules
`((help-mode :align right :select t :size 0.45)
(helpful-mode :align right :select t :size 0.45)
(compilation-mode :align right)
(flymake-diagnostics-buffer-mode :align below)
(magit-process-mode :align below)
("*eldoc*" :align right)
("*Messages*" :align below)
("*Async-native-compile-log*" :align right)
("*devdocs*" :align right :select t :size 0.45)
("*mu4e-headers*" :align right :select t :size 0.75)
;; also launch without invoking J -> inbox manual select -> head into inbox by defeault
(,(rx "*" (* any) "REPL" (* any) "*") :align right :regexp t)
(,(rx bos "*" (* any)
(| "eat" "eshell" "shell" "term" "vterm")
(* any) "*" eos)
:align below :select t :regexp t :size 0.45))))
(use-package popper
:after (shackle)
:hook (shackle-mode . popper-mode)
:custom
(popper-echo-mode t)
(popper-display-control nil)
(popper-echo-dispatch-keys nil)
(popper-group-function #'popper-group-by-project)
(popper-reference-buffers
`(help-mode helpful-mode
,(rx "*Messages*")
,(rx "Output*" eos)
,(rx "*devdocs*")
,(rx "*" (* any) "REPL" (* any) "*")
compilation-mode magit-process-mode
eat-mode eshell-mode shell-mode term-mode vterm-mode)))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define popper-hydra
(:title (pretty-hydra-title "──「 Utilities: Popper 」──" 'mdicon "nf-md-lightbulb_on_outline")
:color teal :quit-key "q")
("Action(s)"
(("o" popper-toggle "Un/Toggle Popup")
("n" popper-cycle "Cycle Between Popup(s)")
("t" popper-toggle-type "Add Buf. To Popup"))))
(pretty-hydra-define+ main-hydra ()
("Action"
(("p" popper-hydra/body "Popper")))))
(use-feature dired
:custom
(dired-auto-revert-buffer t)
(dired-mouse-drag-files t)
(dired-kill-when-opening-new-dired-buffer t)
(mouse-drag-and-drop-region-cross-program t)
(mouse-1-click-follows-link nil)
(dired-movement-style 'cycle)
(dired-listing-switches "-alFh --group-directories-first"))
(use-feature dired-x
:after (dired)
:preface
(defun dired-external-launch (application extensions)
"External `APPLICATION' used for launching specific file-extensions."
(let ((pattern (rx "." extensions eos))
(entry (list pattern application)))
(add-to-list 'dired-guess-shell-alist-user entry)))
:custom
(dired-external-launch
(if (eq system-type 'gnu/linux) "mpv" "xdg-open")
'("avi" "flv" "mkv" "mov" "mp3" "mp4" "mpeg" "mpg" "ogg" "ogm" "wav" "wmv"))
(dired-external-launch
(if (eq system-type 'gnu/linux) "libreoffice" "xdg-open")
'("doc" "docx" "odt" "xls" "xlsx")))
Lastly, I want the different type of directories to have some form of syntax highlighting.
(use-package diredfl
:after (dired)
:hook (dired-mode . diredfl-mode)
:custom-face (diredfl-dir-name ((t :bold t))))
(use-feature which-key
:hook (elpaca-after-init . which-key-mode)
:config (which-key-setup-minibuffer)
:custom
(which-key-allow-evil-operators t)
(which-key-idle-delay 0.3)
(which-key-show-remaining-keys t)
(which-key-separator " → ")
(which-key-sort-order 'which-key-prefix-then-key-order))
(use-package consult
:hook (completion-list-mode . consult-preview-at-point-mode)
:config
(setq register-preview-function #'consult-register-format)
(advice-add #'register-preview :override #'consult-register-window)
(setopt register-preview-delay 0.5
;; Consult -> select xref locations with preview
xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref))
Allowing consult to interact with my ever-growing projects is something I consider to be useful.
(use-package consult-project-extra
:after (consult project))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define consult-hydra
(:title (pretty-hydra-title "──「 Utilities: Consult 」──" 'mdicon "nf-md-console")
:color teal :quit-key "q")
("Main"
(("f" consult-fd "Find files by NAME")
("r" consult-recent-file "Recent files")
("s" consult-project-extra-find "Switch project")
("/" consult-ripgrep "Grep <- REGEXP"))
"Action"
(("B" consult-bookmark "Open named bookmark")
("h" consult-history "Insert STR from hist.")
("p" consult-yank-pop "Paste yank <- reg.")
("t" consult-theme "Switch Theme"))))
(pretty-hydra-define+ main-hydra ()
("Action"
(("f" consult-hydra/body "Consult"))))
(pretty-hydra-define editor-consult-hydra
(:title (pretty-hydra-title "──「 Utilities: Consult 」──" 'mdicon "nf-md-console")
:color teal :quit-key "q")
("Jump To"
(("m" consult-mark "Marker")
("M" consult-global-mark "Glob. Marker")
("o" consult-outline "Buffer Outlines")
("f" consult-flymake "Flymake Diagnostics")
("e" consult-compile-error "Buffer Compile Errors"))))
(pretty-hydra-define+ editor-hydra ()
("Control"
(("f" editor-consult-hydra/body "Consult"))))
(pretty-hydra-define+ buffer-hydra ()
("Consult"
(("b" consult-buffer "Switch Buffer")
("B" consult-project-buffer "Project Buf. Switch")
("w" consult-buffer-other-window "Split Buf. Switch"))))
(pretty-hydra-define+ helpful-hydra ()
("Action"
(("?" consult-man "Consult MAN-page(s)")
("i" consult-info "Consult MANUAL")))))
(use-package embark
:config
(setq prefix-help-command #'embark-prefix-help-command)
;; :NOTE| Hide the mode line of the Embark live/completions buffers
(add-to-list 'display-buffer-alist
'(,(rx bos "*Embark Collect" (| "Live" "Completions") "*" eos)
nil
(window-parameters (mode-line-format . none))))
:custom
(embark-prompter #'embark-completing-read-prompter)
(embark-indicators
'(embark-highlight-indicator
embark-isearch-highlight-indicator)))
Well, since Embark
and Consult
can be linked… I do not see a reason for their seperation.
(use-package embark-consult
:after (embark consult)
:hook (embark-collect-mode . consult-preview-at-point-mode))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define embark-hydra
(:title (pretty-hydra-title "──「 Utilities: Embark 」──" 'mdicon "nf-md-lightbulb_on_outline")
:color teal :quit-key "q")
("Action(s)"
(("a" embark-act "Prompt -> perform")
("d" embark-dwim "Run default on buffer"))
"Documentation"
(("h" embark-bindings "Explore Emacs bindings"))))
(pretty-hydra-define+ main-hydra ()
("Action"
(("e" embark-hydra/body "Embark")))))
(use-package vertico
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word))
:hook ((elpaca-after-init . vertico-mode)
(rfn-eshadow-update-overlay . vertico-directory-tidy))
:custom
(vertico-cycle t)
(vertico-mouse-mode t))
Mini-buffers should be tweaked a little to accommodate our Vertico
buffers.
(use-feature emacs
:hook (minibuffer-setup . cursor-intangible-mode)
:init
(defun crm-indicator (args)
(cons (format "[CRM%s] %s"
(replace-regexp-in-string "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
:config
(setopt enable-recursive-minibuffers t
minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt)))
(use-package marginalia
:after (vertico)
:hook (vertico-mode . marginalia-mode)
:config
(with-eval-after-load 'nerd-icons-completion
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
:custom
(marginalia-max-relative-age 0)
(marginalia-align 'right))
(use-feature tramp
:defer t
:config
(setopt remote-file-name-inhibit-cache nil)
(add-to-list 'tramp-connection-properties
(list (regexp-quote "/ssh:YOUR_HOSTNAME:")
"direct-async-process" t))
:custom
(tramp-verbose 0)
(tramp-chunksize 2000)
(tramp-use-ssh-controlmaster-options nil))
Eshell
is a bit special, it acts as a seperate shell from your system shell and therefore you won’t access your system shell environment by default. And it also comes with unpolished aesthetics IMO, therefore I thought I should clean it up a bit.
(use-feature eshell
:commands (project-eshell)
:preface
<<eshell-shorten-directory-path>>
<<eshell-redesign-prompt>>
:custom
(eshell-error-if-no-glob t)
(eshell-hist-ignoredups t)
(eshell-save-history-on-exit t)
(eshell-scroll-to-bottom-on-input 'this)
(eshell-scroll-to-bottom-on-output nil)
(eshell-destroy-buffer-when-process-dies t)
;; :NOTE| Aesthetics of our semi-cursed prompt?
(eshell-prompt-function #'irkalla/eshell-prompt)
(eshell-prompt-regexp "^.*└─➤ 𝝺 "))
(defun shortened-path (path max-len)
(require 'cl-lib)
(let* ((components (split-string (abbreviate-file-name path) "/"))
(len (+ (1- (length components))
(cl-reduce '+ components :key 'length)))
(str ""))
(while (and (> len max-len) (cdr components))
(setq str (concat str (if (= 0 (length (car components)))
"/" (string (elt (car components) 0) ?/)))
len (- len (1- (length (car components))))
components (cdr components)))
(concat str (cl-reduce (lambda (a b) (concat a "/" b)) components))))
Because I cannot integrate starship-rs with Eshell
, I am forced to take things into hand.
(defun irkalla/eshell-prompt ()
(concat
(propertize (concat " " (shortened-path (eshell/pwd) 40)) 'face 'font-lock-constant-face)
(when (package-installed-p 'magit)
(propertize (if (magit-get-current-branch)
(concat " " (magit-get-current-branch)) "" 'face 'font-lock-variable-name-face)))
(when (package-installed-p 'envrc)
(propertize (if (string= envrc--status 'none)
"" " " 'face 'font-lock-string-face)))
(propertize (concat " " (format-time-string "%H:%M" (current-time))) 'face 'font-lock-variable-name-face)
(propertize "\n └─➤ 𝝺 " 'face 'font-lock-type-face)))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ launcher-hydra ()
("Terminal"
(("l" project-eshell "Eshell -> Project")
("n" nix-shell "Eshell -> Nix")))))
(use-package eat
:ensure (:host codeberg :repo "akib/emacs-eat"
:files ("*.el" ("term" "term/*.el") "*.texi"
"*.ti" ("terminfo/e" "terminfo/e/*")
("terminfo/65" "terminfo/65/*")
("integration" "integration/*")
(:exclude ".dir-locals.el" "*-tests.el")))
:hook ((eshell-mode . (lambda ()
(eat-eshell-mode)
(eat-eshell-visual-command-mode))))
:custom
(eat-kill-buffer-on-exit t)
(eat-enable-auto-line-mode t))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ launcher-hydra ()
("Terminal"
(("e" eat "EAT")
("p" eat-project "EAT -> Project")))))
(use-feature vterm
:commands (vterm)
:hook (vterm-mode . evil-emacs-state)
:bind (:map vterm-mode-map
("<S-prior>" . #'scroll-down-command)
("<S-next>" . #'scroll-up-command))
:custom
(vterm-timer-delay 0.01)
(vterm-max-scrollback 10000)
(vterm-clear-scrollback-when-clearing t))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ launcher-hydra ()
("Terminal"
(("e" vterm "VTerm")))))
(use-feature pdf-tools
:magic ("%PDF" . pdf-view-mode)
:mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
:hook (pdf-view-mode . pdf-view-midnight-minor-mode)
:bind (:map pdf-view-mode-map ([tab] . pdf-outline))
;; :HACK| Resolves elpaca's failure to detect ~epdfinfo~
:init (add-to-list 'elpaca-ignored-dependencies 'pdf-tools)
:config
;; :NOTE| Set the PDF free from the unnecessary borders.
(when (featurep 'evil)
(add-hook 'pdf-view-mode-hook
(lambda () (set (make-local-variable 'evil-normal-state-cursor)
(list nil)))))
;; :NOTE| Load PDF-Tools utilities when installed from Nix || Guix.
(if IS-REPRO (let ((inhibit-message t))
(load-library "pdf-tools-autoloads")))
;; :NOTE|Auto center PDF page on zoom-in/out.
(advice-add 'pdf-view-enlarge :after (lambda (&rest _args) (pdf-view-center-in-window)))
(advice-add 'pdf-view-shrink :after (lambda (&rest _args) (pdf-view-center-in-window)))
:custom
(pdf-view-use-scaling t)
(pdf-view-use-imagemagick nil)
(pdf-view-display-size 'fit-width)
(pdf-view-midnight-colors '("#DCD7BA" . "#16161D")))
Instead of maintaining a bookmark for each PDF file I read, I decided to add a package to help me defer that process.
(use-package pdf-view-restore
:hook (pdf-view-mode . pdf-view-restore-mode)
:custom (pdf-view-restore-filename (no-littering-expand-var-file-name "pdf-view-restore")))
(use-package nov
:mode ("\\.epub\\'" . nov-mode)
:config
(when (fboundp 'visual-fill-column-mode)
(add-hook 'nov-mode-hook #'visual-fill-column-mode))
:custom (nov-text-width t))
Being able to render the EPUB files in a cleaner fashion could sometimes be nice.
(use-package nov-xwidget
:ensure (:host github :repo "chenyanming/nov-xwidget")
:if (featurep 'xiwdget-internal)
:hook (nov-mode . nov-xwidget-inject-all-files)
:bind (:map nov-mode-map ("o" . #'nov-xwdiget-view)))
(use-feature newsticker
:preface
(defun irkalla/newsticker-start-newTab ()
"Launch NewsTicker (TreeView) in a new tab."
(interactive)
(let (success)
(unwind-protect (progn
(tab-bar-new-tab)
(call-interactively #'newsticker-treeview)
(tab-bar-rename-tab "newsticker")
(setq success t))
(unless success (tab-bar-close-tab)))))
(defun irkalla/newsticker-quit-newTab ()
"Quit NewsTicker (TreeView) -> stop NewsTicker -> close tab."
(interactive)
(newsticker-treeview-quit)
(newsticker-stop)
(tab-close))
:bind (:map newsticker-treeview-mode-map
("o" . newsticker-treeview-browse-url)
("q" . irkalla/newsticker-quit-newTab))
:config
(when (fboundp 'visual-fill-column-mode)
(add-hook 'newsticker-treeview-item-mode-hook #'visual-fill-column-mode))
:custom
(newsticker-automatically-mark-items-as-old nil)
(newsticker-automatically-mark-visited-items-as-old t)
(newsticker-obsolete-item-max-age 259200) ;; 3 days
(newsticker-retrieval-method 'extern)
(newsticker-treeview-automatically-mark-displayed-items-as-old nil)
(newsticker-url-list-defaults nil)
(newsticker-url-list
'(("Planet Emacslife" "https://planet.emacslife.com/atom.xml")
("Sacha Chua" "https://sachachua.com/blog/feed/")
("Mastering Emacs" "http://www.masteringemacs.org/feed/")
;; ---[ Science & Technology ]---
("Phys.org: Physics" "https://phys.org/rss-feed/")
("Quanta Magazine" "https://api.quantamagazine.org/feed/")
;; ---[ Mathematics ]---
("Arxiv: Mathematics" "http://arxiv.org/rss/math")
("Arxiv: Mathematical Physics" "http://arxiv.org/rss/math-ph")
("Terrence Tao (Blog)" "https://terrytao.wordpress.com/feed/")
("Stephen Wolfram (Blog)" "https://writings.stephenwolfram.com/feed/")
;; ---[ Computer Science ]---
("Arxiv: Computer Science" "http://arxiv.org/rss/cs")
;; ---[ Physics ]---
("Arxiv: Physics" "http://arxiv.org/rss/physics")))
(newsticker-wget-name "curl")
(newsticker-wget-arguments '("--silent" "--location" "--connect-timeout" "8")))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ launcher-hydra ()
("Application"
(("n" irkalla/newsticker-start-newTab "Newsticker (RSS)")))))
Syntax highlighting is a nice feature to have in our different chat clients.
(use-package htmlize
:defer t)
(use-feature mu4e
:if (executable-find "mu")
:commands (mu4e mu4e-update-mail-and-index)
:init (add-to-list 'elpaca-ignored-dependencies 'mu4e)
:custom
(mu4e-attachment-dir "~/Downloads")
(mu4e-get-mail-command "mbsync -a")
(mu4e-update-interval (* 10 60))
(mu4e-confirm-quit nil)
(mu4e-use-fancy-chars t)
(mu4e-notification-support t)
(mu4e-change-filenames-when-moving t)
(mu4e-completing-read-function 'completing-read)
(mu4e-context-policy 'pick-first)
(mu4e-headers-date-format "%d-%m")
(mu4e-headers-time-format "%H:%M")
;; :NOTE| control how e-mails should be composed
(sendmail-program "msmtp")
(send-mail-function #'sendmail-send-it)
(message-fill-column fill-column)
(message-kill-buffer-on-exit t))
To reduce the burden of setting up multiple accounts, let’s borrow the superpower of mu4easy
.
(use-package mu4easy
:after (mu4e)
:config (mu4easy-mode)
:custom
(mu4easy-contexts '((mu4easy-context
:c-name "Disroot"
:maildir "icy-thought"
:mail "[email protected]"
:smtp "disroot.org"
:sent-action delete))))
(use-package mu4e-thread-folding
:ensure (:host github :repo "rougier/mu4e-thread-folding")
:after (mu4e)
:hook (mu4e-headers-mode . mu4e-thread-folding-mode)
:bind (:map mu4e-headers-mode-map
("<tab>" . mu4e-headers-toggle-at-point)
("<left>" . mu4e-headers-fold-at-point)
("<S-left>" . mu4e-headers-fold-all)
("<right>" . mu4e-headers-unfold-at-point)
("<S-right>" . mu4e-headers-unfold-all))
:config
(add-to-list 'mu4e-header-info-custom
'(:empty . (:name "Empty"
:shortname ""
:function (lambda (msg) " "))))
:custom
(mu4e-headers-fields '((:empty . 2)
(:human-date . 12)
(:flags . 6)
(:mailing-list . 10)
(:from . 22)
(:subject . nil))))
(use-package org-msg
:after (mu4e org)
:config
(setopt mail-user-agent 'mu4e-user-agent)
(org-msg-mode-mu4e)
(org-msg-mode)
:custom
(org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil title:nil email:nil tex:imagemagick")
(org-msg-startup "hidestars indent inlineimages")
(org-msg-greeting-fmt "\nGreetings %s,\n\n")
(org-msg-greeting-name-limit 3)
(org-msg-default-alternatives
'((new . (utf-8 html))
(reply-to-text . (utf-8))
(reply-to-html . (utf-8 html))))
(org-msg-convert-citation t)
(org-msg-signature "
Kind Regards,
#+begin_signature
-- *Icy-Thought*
#+end_signature"))
(use-package ement
:commands (ement-connect)
:bind (:map ement-room-mode-map
([remap pixel-scroll-interpolate-up] . ement-room-scroll-down-command)
([remap pixel-scroll-interpolate-down] . ement-room-scroll-up-mark-read))
:config
(when (fboundp 'visual-fill-column-mode)
(add-hook 'ement-room-mode-hook #'visual-fill-column-mode))
(with-eval-after-load 'cape
(add-hook 'ement-room-read-string-setup-hook
(lambda ()
(setq-local completion-at-point-functions nil) ;; too much spam (members/rooms)
(add-hook 'completion-at-point-functions #'cape-emoji nil t))))
:custom
(ement-notify-notification-predicates
'(ement-notify--event-mentions-session-user-p
ement-notify--event-mentions-room-p))
(ement-room-images t)
(ement-room-message-format-spec "%S>%L %B%r%R[%t]")
(ement-room-send-message-filter #'ement-room-send-org-filter)
(ement-save-sessions t))
I also want to write a function that could later be used to spawn an Emacs (matrix) frame by XMonad or other window managers.
(defun irkalla/connect-to-matrix ()
"Form a connection between Emacs and the Matrix, using Ement."
(interactive)
(require 'ement)
(let* ((matrix-username "@icy-thought:matrix.org")
(session (map-elt ement-sessions matrix-username)))
(cond
(session
(message "Session already exists. Opening room list...")
(ement-room-list))
((ement--read-sessions)
(message "Connecting to a known session...")
(call-interactively #'ement-connect))
(t
(message "Starting a new Ement session...")
(ement-connect
:user-id matrix-username
:password (irkalla/read-secret-file "Ement")
:uri-prefix "http://localhost:8009")))))
(use-feature telega
:commands (telega)
:config
(advice-add 'telega-chatbuf-recenter-1
:around (lambda (orig-fun &rest args) (recenter -2)))
(when (fboundp 'visual-fill-column-mode)
(add-hook 'telega-chat-mode-hook #'visual-fill-column-mode))
;; :NOTE| Enable dictionary + emoji suggestions in compose area
(with-eval-after-load 'cape
(add-hook 'telega-chat-mode-hook
(lambda () (add-hook 'completion-at-point-functions #'cape-emoji nil t))))
:custom
(telega-directory (no-littering-expand-var-file-name "telega/"))
(telega-chat-bidi-display-reordering t)
(telega-notifications-mode t)
(telega-emoji-use-images nil)) ;; libsvg issue -> odd symbols
(use-package circe
:commands (circe circe-set-display-handler)
:config (enable-circe-color-nicks)
:custom (circe-reduce-lurker-spam t)
(circe-network-options
'(("Libera Chat"
:tls t
:nick "Icy-Thought"
:sasl-username "icy-thought"
;; :sasl-password (irkalla/read-secret-file "IRC")
:channels ("#emacs")))))
(use-feature eww
:preface
(defun auto-readable-wikipedia ()
"Run `eww-readable' if the current buffer is a Wikipedia article."
(when (and (eq major-mode 'eww-mode)
(string-match-p "\\bwikipedia\\.org\\b" (eww-current-url)))
(eww-readable)))
:hook (eww-after-render . auto-readable-wikipedia))
(use-package shrface
:hook ((shrface-mode . variable-pitch-mode)
(nov-mode . shrface-mode)
(eww-after-render . shrface-mode))
:custom (shrface-href-versatile t)
:config
(shrface-basic)
(shrface-trial)
(shrface-default-keybindings)
(with-eval-after-load 'org-modern
(setopt shrface-bullets-bullet-list
(string-glyph-split org-modern-replace-stars))))
I also would like for web-pages to properly render code blocks, and that is by displaying them with the appropriate syntax highlighting.
(use-package shr-tag-pre-highlight
:after (shrface)
:config
(add-to-list 'shr-external-rendering-functions '(pre . shr-tag-pre-highlight))
(advice-add 'eww-display-html :around
'eww-display-html--override-shr-external-rendering-functions))
(use-feature emacs
:config
(setopt backward-delete-char-untabify-method 'hungry
confirm-nonexistent-file-or-buffer nil
cursor-in-non-selected-windows nil
electric-indent-inhibit t
indent-tabs-mode nil
tab-width 4
remote-file-name-inhibit-locks t
shell-kill-buffer-on-exit t
text-mode-ispell-word-completion nil
x-stretch-cursor t
x-underline-at-descent-line t))
(use-package evil
:preface
(defun irkalla/extended-escape ()
(interactive)
(if (evil-ex-hl-active-p 'evil-ex-search)
(evil-ex-nohighlight)
(evil-force-normal-state)))
:bind (:map evil-normal-state-map ("<escape>" . irkalla/extended-escape))
:hook (elpaca-after-init . evil-mode)
:config (evil-select-search-module 'evil-search-module 'evil-search)
:custom
(evil-want-keybinding nil)
(evil-respect-visual-line-mode t)
(evil-undo-system 'undo-fu)
(evil-vsplit-window-right t))
(use-package evil-collection
:after (evil)
:hook (evil-mode . evil-collection-init)
:config
(with-eval-after-load 'corfu
(setopt evil-collection-corfu-key-themes '(tab-n-go))
(advice-add 'corfu--setup :after
(lambda (&rest _) (setopt corfu-preselect 'valid))))
:custom
(evil-collection-setup-minibuffer t)
(evil-collection-magit-use-y-for-yank t)
(evil-collection-magit-want-horizontal-movement t))
(use-package evil-snipe
:after (evil)
:hook (((prog-mode text-mode) . evil-snipe-local-mode)
(evil-snipe-local-mode . evil-snipe-override-local-mode))
:custom
(evil-snipe-scope 'visible)
(evil-snipe-repeat-scope 'whole-visible)
(evil-snipe-spillover-scope nil)
:config (push '(?\[ "[[{(]") evil-snipe-aliases))
(use-package evil-surround
:after (evil)
:hook (evil-mode . global-evil-surround-mode))
(use-package evil-goggles
:after (evil)
:hook (evil-mode . evil-goggles-mode)
:custom (evil-goggles-duration 0.1))
(use-package evil-nerd-commenter
:after (evil)
:pretty-hydra
((:title (pretty-hydra-title "──「 Editing: Evil Commenting 」──" 'faicon "nf-fa-code")
:color teal :quit-key "q")
("Actions"
(("c" evilnc-copy-and-comment-lines "Copy & Comment")
("l" evilnc-comment-or-uncomment-lines "Un/Comment Lines")
("p" evilnc-comment-or-uncomment-paragraphs "Un/Comment Paragraph"))
"Fancy"
(("b" evilnc-comment-box "Boxed Comment")
("l" evilnc-comment-or-uncomment-lines "Un/Comment Lines")
("p" evilnc-comment-or-uncomment-paragraphs "Un/Comment Paragraph"))
"Fancy"
(("b" evilnc-comment-box "Boxed Comment")
("n" evilnc-quick-comment-or-uncomment-to-the-line "Comment -> Nth Line")))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ editor-hydra ()
("Action"
((";" evil-nerd-commenter-hydra/body "Comment"))))
(pretty-hydra-define+ visual-editor-hydra ()
("Action"
((";" evil-nerd-commenter-hydra/body "Comment")))))
(use-package evil-multiedit
:after (evil)
:config (evil-multiedit-default-keybinds))
(use-package editorconfig
:hook (prog-mode . editorconfig-mode))
(use-package direnv
:hook ((prog-mode text-mode) . direnv-mode)
:config (add-to-list 'warning-suppress-types '(direnv))
:custom (direnv-always-show-summary nil))
(use-feature executable
:hook (after-save . executable-make-buffer-file-executable-if-script-p))
(use-feature display-line-numbers
:hook (prog-mode . display-line-numbers-mode)
:custom (display-line-numbers-type 'relative))
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
(use-package indent-bars
:ensure (:host github :repo "jdtsmith/indent-bars")
:hook (prog-mode . indent-bars-mode)
:custom
(indent-bars-zigzag nil)
(indent-bars-treesit-support t)
(indent-bars-treesit-ignore-blank-lines-types '("module")))
(use-package rainbow-mode
:hook (prog-mode . rainbow-mode))
(use-feature hl-line
:hook (elpaca-after-init . global-hl-line-mode)
:config
(setopt hl-line-sticky-flag nil
hl-line-range-function
(lambda () (cons (line-beginning-position) (line-end-position)))))
(use-feature frame
:hook (before-make-frame . window-divider-mode)
:custom
(window-divider-default-places t)
(window-divider-default-right-width 2)
(window-divider-default-bottom-width 2))
(use-feature whitespace
:hook (before-save . whitespace-cleanup)
:custom
(whitespace-style
'(face tab space newline
space-before-tab space-after-tab
indentation trailing))
(whitespace-display-mappings
'((tab-mark ?\t [?» ?\t])
(space-mark ?\ [?·] [?.])
(newline-mark ?\n [?¬ ?\n]))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ window-hydra ()
("Main"
(("w" whitespace-mode "Whitespace Mode" :toggle t)))))
(use-package ligature
:config
(global-ligature-mode t)
(ligature-set-ligatures 't '("www"))
(ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
(ligature-set-ligatures
'prog-mode
'("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
"\\\\" "://")))
(use-feature emacs
:config
(setopt fill-column 120
truncate-lines nil))
For buffers that are solely intended to read code (+ some minor modes), we want to only wrap the code if long and then visually split it into multiple lines.
(use-feature visual-wrap
:hook (((prog-mode conf-mode) . visual-wrap-prefix-mode)
(visual-wrap-prefix-mode . visual-line-mode)))
Whilst for other scenarios (example, modes derived from text-modes)
, we want to center the text and clean up the UI a bit.
(use-package visual-fill-column
:commands (visual-fill-column-mode)
:hook ((text-mode . visual-fill-column-mode)
(visual-fill-column-mode . visual-line-mode))
:custom (visual-fill-column-center-text t))
(use-feature elec-pair
:hook ((prog-mode text-mode) . electric-pair-local-mode)
:custom (electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit))
(use-feature autorevert
:hook ((prog-mode text-mode) . auto-revert-mode)
:custom
(auto-revert-interval 1)
(auto-revert-notify t)
(auto-revert-verbose t))
(use-feature emacs
:config
(setopt auto-save-interval 200
auto-save-timeout 30
history-length 1000
kept-new-versions 7
kept-old-versions 3
backup-by-copying t
delete-by-moving-to-trash t
delete-old-versions t
history-delete-duplicates t
make-backup-files t))
(use-feature savehist
:defer 1
:config (savehist-mode)
:custom
(savehist-autosave-interval 60)
(savehist-file (no-littering-expand-var-file-name "savehist"))
(savehist-additional-variables '(command-history evil-jumps-history))
(savehist-ignored-variables '(ement-room-message-history)))
(use-feature saveplace
:hook ((prog-mode text-mode) . save-place-mode)
:custom
(save-place-file (no-littering-expand-var-file-name "saveplace"))
(save-place-forget-unreadable-files t))
(use-package undo-fu
:if (>= emacs-major-version 29)
:config
(setopt undo-no-redo t
undo-limit (* 128 1024 1024)
undo-outer-limit (* 128 1024 1024)
undo-strong-limit (* 256 1024 1024)))
Also, Undo-Fu
activity ought to be tracked and saved for future sessions.
(use-package undo-fu-session
:after (undo-fu)
:hook ((prog-mode text-mode) . global-undo-fu-session-mode)
:custom
(undo-fu-session-directory (no-littering-expand-var-file-name "undo-fu-session/"))
(undo-fu-session-compression (if (executable-find "zstd") 'zst 'gz))
(undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'")))
(use-package vundo
:commands (vundo)
:bind (("C-c u" . vundo))
:custom
(vundo-compact-display t)
(vundo-glyph-alist vundo-unicode-symbols))
(defun irkalla/copy-to-sysclip ()
"Copy contents to the system clipboard."
(interactive)
(setopt select-enable-clipboard t)
(if (featurep 'evil)
(call-interactively #'evil-yank)
(kill-ring-save (region-beginning) (region-end)))
(setopt select-enable-clipboard nil))
(defun irkalla/paste-from-sysclip ()
"Paste contents to the system clipboard."
(interactive)
(setopt select-enable-clipboard t)
(if (featurep 'evil)
(call-interactively #'evil-paste-after)
(yank))
(setopt select-enable-clipboard nil))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ editor-hydra ()
("Action"
(("y" irkalla/copy-to-sysclip "Yank -> Sys-Clip")
("p" irkalla/paste-from-sysclip "Paste <- Sys-Clip"))))
(pretty-hydra-define+ visual-editor-hydra ()
("Action"
(("y" irkalla/copy-to-sysclip "Yank -> Sys-Clip")))))
(use-feature ediff
:hook((ediff-prepare-buffer . outline-show-all)
(ediff-quit . winner-undo))
:custom
(ediff-window-setup-function 'ediff-setup-windows-plain)
(ediff-split-window-function 'split-window-horizontally)
(ediff-merge-split-window-function 'split-window-horizontally))
(use-package ialign
:commands (ialign))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ visual-editor-hydra ()
("Action"
(("a" ialign "Align -> REGEXP")))))
(use-package expand-region
:commands (er/expand-region er/contract-region))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ visual-editor-hydra ()
("Navigation"
((")" er/expand-region "Increase -> semantic units")
("(" er/contract-region "Contract -> PREV size")))))
(use-feature paren
:hook (prog-mode . show-paren-mode)
:custom
(show-paren-delay 0)
(show-paren-style 'parenthesis)
(show-paren-when-point-in-periphery nil)
(show-paren-when-point-inside-paren nil))
(use-feature subword
:hook ((prog-mode text-mode) . subword-mode))
(use-package magit
:if (executable-find "git")
:commands (magit)
:custom
(magit-refs-show-commit-count 'all)
(magit-save-repository-buffers 'dontask)
(magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1))
(use-package magit-file-icons
:after (magit)
:hook (magit-mode . magit-file-icons-mode)
:custom
(magit-file-icons-enable-diff-file-section-icons t)
(magit-file-icons-enable-untracked-icons t)
(magit-file-icons-enable-diffstat-icons t))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define vc-hydra
(:title (pretty-hydra-title "──「 Editor: Version Control 」──" 'mdicon "nf-md-git")
:color teal :quit-key "q")
("Magit"
(("g" magit "Open Magit")
("s" magit-stage-buffer-file "Stage file")
("u" magit-unstage-buffer-file "Unstage file")
("b" magit-branch-checkout "Checkout Branch"))))
(pretty-hydra-define+ editor-hydra ()
("Control"
(("g" vc-hydra/body "Version Control")))))
(use-package hl-todo
:ensure (hl-todo :version (lambda (_) "3.6.0"))) ;; elpaca
(use-package magit-todos
:after (magit)
:hook (magit-mode . magit-todos-mode)
:custom
(magit-todos-recursive t)
(magit-todos-depth 10)
(magit-todos-exclude-globs '(".git/" "*.html"))
(magit-todos-nice (if (executable-find "nice") t nil))
(magit-todos-scanner #'magit-todos--scan-with-rg))
(use-package blamer
:if (executable-find "git")
:commands (blamer-show-posframe-commit-info)
:custom-face
(blamer-face ((t (:background nil :height 125 :italic t))))
:custom
(blamer-idle-time 0.5)
(blamer-min-offset 70)
(blamer-view 'overlay-right)
(blamer-type 'visual)
(blamer-max-commit-message-length 70)
(blamer-force-truncate-long-line nil)
(blamer-author-formatter " ✎ %s ")
(blamer-commit-formatter "● \'%s\' ● "))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ vc-hydra ()
("Blamer"
(("i" blamer-show-posframe-commit-info "Commit Info")))))
(use-package git-gutter
:if (executable-find "git")
:hook (prog-mode . git-gutter-mode)
:custom
(git-gutter:modified-sign "┃")
(git-gutter:added-sign "┃")
(git-gutter:deleted-sign "┃")
(git-gutter:unchanged-sign ""))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ vc-hydra ()
("Git-Gutter"
(("m" git-gutter:mark-hunk "Mark hunk")
("k" git-gutter:previous-hunk "Previous hunk")
("j" git-gutter:next-hunk "Next hunk")
("u" git-gutter:revert-hunk "Revert hunk")))))
(use-package beframe
:bind ([remap list-buffers] . beframe-switch-buffer)
:hook (elpaca-after-init . beframe-mode)
:config
(with-eval-after-load 'marginalia
(add-to-list 'marginalia-command-categories '(kill-buffer . buffer))
(add-to-list 'marginalia-command-categories '(beframe-switch-buffer . buffer)))
<<beframe-consult-buffers>>
:custom (beframe-rename-function nil))
(with-eval-after-load 'consult
(defface beframe-buffer '((t :inherit font-lock-string-face))
"Face for `consult' framed buffers.")
(defun my-beframe-buffer-names-sorted (&optional frame)
"Return the list of buffers from `beframe-buffer-names' sorted by visibility.
With optional argument FRAME, return the list of buffers of FRAME."
(beframe-buffer-names frame :sort #'beframe-buffer-sort-visibility))
(defvar beframe-consult-source
`( :name "Frame-specific buffers (current frame)"
:narrow ?F
:category buffer
:face beframe-buffer
:history beframe-history
:items ,#'my-beframe-buffer-names-sorted
:action ,#'switch-to-buffer
:state ,#'consult--buffer-state))
(setopt consult-buffer-sources '(beframe-consult-source)))
(use-feature desktop
:hook (elpaca-after-init . desktop-save-mode)
:custom
(desktop-base-file-name "last-session")
(desktop-base-lock-name (concat desktop-base-file-name ".lock"))
(desktop-restore-eager 25)
(desktop-file-checksum t)
(desktop-save-buffer t)
(desktop-save t))
(use-feature project
:custom
(project-vc-extra-root-markers
'(".projectile.el" ".project.el" ".project" ".dir-locals.el" ".repo"
"Makefile" "CMakeLists.txt"
"Cargo.toml"
"Dockerfile" "autogen.sh"
"package.json" "requirements.txt")))
(use-feature jinx
:commands (jinx-correct)
:hook (text-mode . jinx-mode)
:bind ([remap ispell-word] . jinx-correct))
(use-feature org
:preface
(defun irkalla/org-electric-dollar ()
"Inserts \\( \\) when $, and replaces it with \\[ \\] when $$."
(interactive)
(if (and (looking-at "\\\\)")
(looking-back "\\\\("))
(progn (delete-char 2)
(delete-char -2)
(insert "\\[\\]"))
(insert "\\(\\)")
(backward-char 2)))
:hook (org-mode . org-display-inline-images)
:config
(setopt org-directory "~/Workspace/memorandum/org-mode")
(with-eval-after-load 'evil
(evil-define-key 'insert org-mode-map (kbd "$") #'irkalla/org-electric-dollar))
;; :NOTE| Move our LaTeX previews to cache dir
(let ((latex-dir (no-littering-expand-var-file-name "latex-preview/")))
(unless (file-directory-p latex-dir)
(mkdir latex-dir t))
(setopt org-preview-latex-image-directory latex-dir))
;; :NOTE| Change the aesthetics of our LaTeX previews
(plist-put org-format-latex-options :background "Transparent")
(plist-put org-format-latex-options :scale 2.5)
(plist-put org-format-latex-options :zoom 1.15)
:custom
(org-agenda-files '("~/Workspace/memorandum/org-mode/agenda/init.org"))
(org-catch-invisible-edits 'show-and-error)
(org-cycle-include-plain-lists 'integrate)
(org-cycle-separator-lines 2)
(org-ellipsis "…")
(org-export-coding-system 'utf-8)
(org-export-preserve-breaks t)
(org-fontify-quote-and-verse-blocks t)
(org-hide-emphasis-markers t)
(org-highlight-latex-and-related '(native))
(org-insert-heading-respect-content t)
(org-latex-tables-centered t)
(org-special-ctrl-a/e t)
(org-startup-indented t)
(org-startup-with-inline-images t)
(org-support-shift-select t)
(org-tags-column 0)
(org-list-allow-alphabetical t)
(org-pretty-entities t)
(org-pretty-entities-include-sub-superscripts t)
;; Code blocks
(org-confirm-babel-evaluate nil)
(org-edit-src-content-indentation 0)
(org-edit-src-auto-save-idle-delay auto-save-timeout)
(org-edit-src-turn-on-auto-save t)
(org-src-fontify-natively t)
(org-src-preserve-indentation t)
(org-src-tab-acts-natively nil))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define org-hydra
(:title (pretty-hydra-title "──「 Langspec: Org-Mode 」──" 'sucicon "nf-custom-orgmode")
:color teal :quit-key "q")
("Buffer"
(("c" org-capture "Capture")
("e" org-export-dispatch "Export")
("t" org-babel-tangle "Tangle"))))
(pretty-hydra-define+ editor-hydra ()
("Markup"
(("o" (if (eq major-mode 'org-mode)
(org-hydra/body)
(message "You are not in an Org buffer.")) "Org-Mode")))))
(use-feature ob
:after (org)
:hook (org-babel-after-execute . org-display-inline-images)
:custom
(org-babel-default-header-args
'((:async . "yes")
(:cache . "no")
(:eval . "never-export")
(:exports . "both")
(:hlines . "no")
(:noweb . "yes")
(:results . "output replace")
(:session . "none")
(:tangle . "no")))
(org-export-use-babel nil)
(org-confirm-babel-evaluate nil)
:config
<<org-babel-language-on-demand>>
<<org-babel-execute-action>>
<<org-babel-tangle-config-on-save>>
(with-eval-after-load 'evil
(evil-define-key 'normal org-mode-map (kbd "<return>") #'irkalla/org-execute-action))
(advice-add 'org-babel-execute-src-block :around #'demand-babel-languages))
Source: https://emacs.stackexchange.com/a/20618
(defun demand-babel-languages (orig-fun &rest args)
"Load language if needed before executing a source block."
(let ((language (org-element-property :language (org-element-at-point))))
(unless (cdr (assoc (intern language) org-babel-load-languages))
(add-to-list 'org-babel-load-languages (cons (intern language) t))
(org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages))
(apply orig-fun args)))
(defun irkalla/org-execute-action ()
;; In a source block, call `org-babel-execute-src-block'.
(interactive)
(let ((context (org-element-context)))
(pcase (org-element-type context)
(`src-block
;; In a source block, call `org-babel-execute-src-block'.
(org-babel-eval-wipe-error-buffer)
(org-babel-execute-src-block current-prefix-arg))
(`babel-call
;; In a `:+CALL:' block, call `org-babel-execute-maybe'.
(call-interactively #'org-babel-execute-maybe))
(`table-row
;; In a table or table-cell, call `org-table-next-row'.
(call-interactively #'org-table-next-row))
((or `link `timestamp)
;; On a link or a timestamp, call `org-open-at-point'.
(call-interactively #'org-open-at-point))
(_
;; Fallback to evil standard command
(call-interactively #'forward-line)))))
(defun irkalla/tangle-config-file ()
"Tangle Irkalla Emacs configuration file on save."
(when (and (eq major-mode 'org-mode)
(string= (buffer-file-name)
(expand-file-name "config.org" irkalla/underworld)))
(org-babel-tangle)))
(add-hook 'after-save-hook #'irkalla/tangle-config-file)
(use-package toc-org
:after (org)
:hook (org-mode . toc-org-mode)
:custom (toc-org-max-depth 3))
(use-package org-fragtog
:after (org)
:hook (org-mode . org-fragtog-mode))
(use-package org-super-agenda
:after (org-agenda)
:hook (org-agenda-mode . org-super-agenda-mode)
:custom
(org-super-agenda-groups
'((:name "Today" :time-grid t :todo "TODAY")
(:name "Important" :tag "bills" :priority "A")
(:todo "WAITING" :order 8)
(:todo ("SOMEDAY" "TO-READ" "CHECK" "TO-WATCH" "WATCHING") :order 9)
(:priority<= "B" :order 1))))
(use-package org-modern
:after (org)
:hook ((org-mode . org-modern-mode)
(org-agenda-finalize . org-modern-agenda))
:custom-face (org-modern-symbol ((t (:family "DejaVu Sans"))))
:custom
;; :NOTE| Settings replaced by svg-tag-mode
(org-modern-tag nil)
(org-modern-todo nil)
(org-modern-block-name nil))
(use-package org-ql
:after (org)
:commands (org-ql-search))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ org-hydra ()
("Project"
(("/" org-ql-search "Search TAG Org Files")))))
(use-package org-timeblock
:after (org)
:commands (org-timeblock))
(use-package org-roam
:after (org)
:commands (org-roam-graph)
:custom
(org-roam-directory (file-truename "~/Workspace/memorandum/org-mode/org-roam"))
(org-roam-completion-everywhere t)
(org-roam-capture-templates
`(("d" "default" plain "%?"
:if-new (file+head
"%<%Y%m%d%H%M%S>-${slug}.org"
,(let ((options '("#+options: _:{}"
"#+options: ^:{}"
"#+startup: latexpreview"
"#+startup: entitiespretty"
"#+startup: inlineimages"
"#+title: ${title}")))
(mapconcat 'identity options "\n")))
:unnarrowed t)))
(org-roam-node-display-template "${title}"))
(use-package org-roam-ui
:after (org-roam)
:custom
(org-roam-ui-sync-theme t)
(org-roam-ui-follow t)
(org-roam-ui-update-on-save t)
(org-roam-ui-open-on-start nil))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ org-hydra ()
("Org-Roam"
(("l" org-roam-buffer-toggle "Toggle -> buffer" :toggle t)
("g" org-roam-graph "Node <- display graph")
("f" org-roam-node-find "Node <- find")
("i" org-roam-node-insert "Node <- insert ':id' link")
("C" org-roam-capture "Node <- Capture")))))
(use-package citar
:hook ((LaTeX-mode org-mode) . citar-capf-setup)
:custom (citar-bibliography '("~/Workspace/memorandum/references.bib")))
Also, allow Embark
to interact with those citations.
(use-package citar-embark
:after (citar embark)
:hook (org-mode . citar-embark-mode)
:config (setopt citar-at-point-function 'embark-act))
(use-package markdown-mode
:mode ("\\.md\\'" . gfm-mode)
:custom-face
(markdown-header-face-1 ((t (:inherit markdown-header-face :height 1.25 :weight extra-bold))))
(markdown-header-face-2 ((t (:inherit markdown-header-face :height 1.15 :weight bold))))
(markdown-header-face-3 ((t (:inherit markdown-header-face :height 1.08 :weight bold))))
(markdown-header-face-4 ((t (:inherit markdown-header-face :height 1.00 :weight bold))))
(markdown-header-face-5 ((t (:inherit markdown-header-face :height 0.90 :weight bold))))
(markdown-header-face-6 ((t (:inherit markdown-header-face :height 0.75 :weight extra-bold))))
:custom (markdown-command "multimarkdown"))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define markdown-hydra
(:title (pretty-hydra-title "──「 Langspec: Markdown 」──" 'devicon "nf-dev-markdown")
:color teal :quit-key "q")
("Interactive"
(("d" markdown-do "Perform -> action"))))
(pretty-hydra-define+ editor-hydra ()
("Markup"
(("m" (if (eq major-mode 'markdown-mode)
(markdown-hydra/body)
(message "You are not in a markdown buffer.")) "Markdown")))))
(use-feature emacs
:hook (compilation-filter . ansi-color-compilation-filter)
:config
(setopt tab-always-indent 'complete
compilation-scroll-output t
compilation-skip-visited t
compilation-always-kill t
read-buffer-completion-ignore-case t
read-file-name-completion-ignore-case t
read-extended-command-predicate #'command-completion-default-include-p))
(use-package orderless
:after (corfu)
:custom
(completions-detailed t)
(completion-ignore-case t)
(completion-styles '(orderless basic))
(completion-category-overrides '((file (styles basic partial-completion)))))
(use-package breadcrumb
:hook (prog-mode . breadcrumb-local-mode)
:config (fset 'breadcrumb--project-crumbs-1 #'ignore)
:custom (breadcrumb-project-max-length -1))
(use-package apheleia
:defer t
:commands (apheleia-format-buffer))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ editor-hydra ()
("Programming"
(("!" apheleia-mode "Fmt On Save" :toggle t))
"Action"
(("=" apheleia-format-buffer "Buf. Format")))))
(use-package corfu
:ensure (:files (:defaults "extensions/*.el"))
:hook (elpaca-after-init . global-corfu-mode)
:custom
(corfu-auto t)
(corfu-auto-delay 0.05)
(corfu-auto-prefix 1)
(corfu-on-exact-match nil))
(use-feature corfu-popupinfo
:after (corfu)
:bind (:map corfu-popupinfo-map
("M-TAB" . corfu-popupinfo-toggle)
("M-k" . corfu-popupinfo-scroll-down)
("M-j" . corfu-popupinfo-scroll-up))
:hook (corfu-mode . corfu-popupinfo-mode)
:custom (corfu-popupinfo-delay nil))
(use-package kind-icon
:after (corfu)
:config (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)
:custom (kind-icon-default-face 'corfu-default))
(use-package cape :defer 1
:init
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-keyword)
(add-hook 'completion-at-point-functions #'cape-elisp-symbol)
(add-hook 'completion-at-point-functions #'cape-tex))
(use-package yasnippet
:bind ("M-]" . yas-insert-snippet)
:hook (elpaca-after-init . yas-global-mode)
:init (add-to-list 'yas-snippet-dirs
(expand-file-name "snippets" irkalla/underworld)))
Instead of defining all of our snippets manually, we could use the help of an external package to help us add the basics and then expand on those bindings with our defined bindings.
(use-package yasnippet-snippets
:after (yasnippet))
Also, we want to be able to see the snippet completions inside Corfu.
(use-package yasnippet-capf
:after (cape)
:config (add-hook 'completion-at-point-functions #'yasnippet-capf)
:custom (yasnippet-capf-lookup-by 'name))
(use-feature eldoc
:custom
(eldoc-idle-delay 1.0)
(eldoc-echo-area-display-truncation-message nil)
(eldoc-echo-area-use-multiline-p nil)
(eldoc-echo-area-prefer-doc-buffer t)
(eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly))
Documentation of $SYMB
should be displayed in an isolated box, where viewing becomes pleasant.
(use-package eldoc-box
:commands (eldoc-box-help-at-point)
:bind ("M-TAB" . eldoc-box-help-at-point)
:config
(when (eglot-managed-p)
(local-set-key (kbd "M-j") (lambda () (interactive) (eldoc-box-scroll-up 3)))
(local-set-key (kbd "M-k") (lambda () (interactive) (eldoc-box-scroll-down 3)))))
(use-package treesit-auto
:custom (treesit-auto-install nil)
:config
(advice-add 'org-src-get-lang-mode
:filter-return (lambda (mode)
(pcase (assoc mode major-mode-remap-alist)
(`(,mode . ,ts-mode) ts-mode) (_ mode))))
(treesit-auto-add-to-auto-mode-alist 'all)
(global-treesit-auto-mode))
(use-package treesit-fold
:ensure (:host github :repo "emacs-tree-sitter/treesit-fold")
:hook (prog-mode . (lambda ()
(when (and (treesit-available-p)
(treesit-parser-list))
(treesit-fold-mode t)))))
(use-package combobulate
:ensure (:host github :repo "mickeynp/combobulate")
:commands (combobulate)
:hook (tree-sitter-after-on . combobulate-mode))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ editor-hydra ()
("Control"
(("c" combobulate "Combobulate")))))
(use-feature flymake
:commands (flymake-mode)
:custom
(flymake-indicator-type 'margins)
(flymake-margin-indicator-position 'right-margin)
(flymake-autoresize-margins t)
(flymake-margin-indicators-string
'((error " " compilation-error)
(warning " " compilation-warning)
(note " " compilation-info))))
(use-package dape
:commands (dape)
:custom
(dape-key-prefix "\C-x\C-a")
(dape-buffer-window-arrangement 'right)
(dape-cwd-fn 'project-vc-extra-root-markers))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define dape-hydra
(:title (pretty-hydra-title "──「 Coding: Debugger 」──" 'codicon "nf-cod-debug")
:color teal :quit-key "q")
("Main"
(("d" dape "Dape")
("k" dape-kill "Kill")
("D" dape-disconnect-quit "Disconnect")
("Q" dape-quit "Quit" :exit t))
"Stepping"
(("n" dape-next "Next")
("s" dape-step-in "Step In")
("o" dape-step-out "Step Out")
("c" dape-continue "Continue")
("p" dape-pause "Pause")
("r" dape-restart "Restart"))
"Breakpoint"
(("b" dape-breakpoint-toggle "Toggle" :toggle t)
("l" dape-breakpoint-log "Log")
("e" dape-breakpoint-expression "Expression")
("B" dape-breakpoint-remove-all "Clear"))
"Informative"
(("m" dape-read-memory "Read Memory")
("w" dape-watch-dwim "Watch DWIM")
("t" dape-select-thread "Select Thread")
("S" dape-select-stack "Select Stack")
("i" dape-info "Info")
("R" dape-repl "REPL"))))
(pretty-hydra-define+ editor-hydra ()
("Control"
(("d" dape-hydra/body "Dape")))))
(use-feature eglot
:pretty-hydra
((:title (pretty-hydra-title "──「 Coding: Eglot 」──" 'faicon "nf-fa-code")
:color teal :quit-key "q")
("Actions"
(("a" eglot-code-actions "Perform code-actions")
("r" eglot-rename "Rename $SYMB"))
"Look-up"
(("?" xref-find-references "Find -> references")
("f" xref-find-definitions "Find -> definition")
("/" xref-find-apropos "Find $SYMB <- pattern"))))
:config (advice-add 'jsonrpc--log-event :override #'ignore)
:custom
(eglot-menu-string "</>")
(eglot-autoshutdown t)
(eglot-sync-connect 0)
(eglot-extend-to-xref t)
(eglot-report-progress nil)
(eglot-confirm-server-initiated-edits nil)
(eglot-ignored-server-capabilities '(:documentHighlightProvider))
(eglot-events-buffer-config '(:size 0)))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ editor-hydra ()
("Control"
(("l" (if (eglot-managed-p)
(eglot-hydra/body)
(message "You are not in an Eglot buffer.")) "Eglot (LSP)")))))
There is another things that we could utilize to increase the responsiveness of our LSP completions, and that is to add eglot-booster to the stack!
(use-package eglot-booster
:ensure (:host github :repo "jdtsmith/eglot-booster")
:if (executable-find "emacs-lsp-booster")
:after (eglot)
:config (eglot-booster-mode)
:custom (eglot-booster-io-only t))
(use-feature c-ts-mode
:mode (("\\.c\\'" . c-ts-mode)
("\\.h\\'" . c-ts-mode))
:hook (c-ts-mode . eglot-ensure)
:custom
(c-ts-mode-indent-style 'k&r)
(c-ts-mode-indent-offset tab-width))
(when (executable-find "clang-format")
(with-eval-after-load 'apheleia
(let ((clang (assq 'clang-format apheleia-formatters)))
(setcdr clang
(append (cdr clang)
(unless (locate-dominating-file default-directory ".clang-format")
(when (and (boundp 'c-ts-mode-indent-offset) apheleia-formatters-respect-indent-level)
(list (format "--style={IndentWidth: %d}" c-ts-mode-indent-offset)))))))
(setf (alist-get 'c-ts-mode apheleia-mode-alist) '(clang-format))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define emacs-lisp-hydra
(:title (pretty-hydra-title "──「 Langspec: Emacs Lisp 」──" 'sucicon "nf-custom-emacs")
:color teal :quit-key "q")
("Actions"
(("a" apropos "Show $SYMB == pattern"))))
(pretty-hydra-define+ editor-hydra ()
("Programming"
(("x" emacs-lisp-hydra/body "Emacs Lisp")))))
(use-package haskell-ts-mode
:if (executable-find "ghc")
:mode ("\\.hs\\(c\\|-boot\\)?\\'" . haskell-ts-mode)
:hook (haskell-ts-mode . eglot-ensure)
:config (with-eval-after-load 'eglot (haskell-ts-setup-eglot))
:custom (haskell-ts-highlight-signature t))
(when (executable-find "fourmolu")
(with-eval-after-load 'apheleia
(setf (alist-get 'haskell-ts-mode apheleia-mode-alist) '(fourmolu))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define haskell-hydra
(:title (pretty-hydra-title "──「 Langspec: Haskell 」──" 'devicon "nf-dev-haskell")
:color teal :quit-key "q")
("General"
(("r" haskell-ts-run-haskell "Open REPL")
("c" haskell-ts-compile-region-and-go "Code -> REPL"))))
;; :TODO| add when -ts- supports features.
;; ("h" haskell-hoogle "Hoogle")
;; "Action"
;; (("C" haskell-check "Check")
;; ("c" haskell-compile "Compile"))
;; "Cabal"
;; (("b" haskell-process-cabal-build "Build")
;; ("B" haskell-process-cabal "Build +Flags"))
(pretty-hydra-define+ editor-hydra ()
("Programming"
(("h" (if (memq major-mode '(haskell-ts-mode))
(haskell-hydra/body)
(message "You are not in a Haskell buffer.")) "Haskell")))))
(use-package lua-mode
:if (executable-find "lua")
:mode ("\\.lua\\'" . lua-ts-mode)
:hook (lua-ts-mode . eglot-ensure))
(when (executable-find "stylua")
(with-eval-after-load 'apheleia
(setf (alist-get 'lua-ts-mode apheleia-mode-alist) '(stylua))))
(use-package nix-ts-mode
:if (executable-find "nix")
:mode ("\\.nix\\'" . nix-ts-mode)
:hook (nix-ts-mode . (lambda ()
(unless (string-match-p "nixpkgs" default-directory)
(eglot-ensure)))))
(when (executable-find "nixfmt")
(with-eval-after-load 'apheleia
(setf (alist-get 'nix-ts-mode apheleia-mode-alist) '(nixfmt))))
(use-package python-mode
:if (executable-find "python")
:hook (python-ts-mode . eglot-ensure)
:custom
(python-shell-interpreter "ipython")
(python-shell-interpreter-args "--simple-prompt"))
(when (executable-find "ruff")
(with-eval-after-load 'apheleia
(setf (alist-get 'python-ts-mode apheleia-mode-alist) '(ruff ruff-isort))))
(use-package rust-mode
:if (executable-find "rustc")
:commands (rust-run)
:hook (rust-ts-mode . eglot-ensure))
(use-package cargo
:if (executable-find "cargo")
:after (rust-mode)
:hook (rust-ts-mode . cargo-minor-mode))
Also, I would like to have the ability to interact with rust code-blocks defined within org-mode files.
(use-package ob-rust
:after (ob))
(when (executable-find "rustfmt")
(with-eval-after-load 'apheleia
(setf (alist-get 'rust-ts-mode apheleia-mode-alist) '(rustfmt))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define rust-hydra
(:title (pretty-hydra-title "──「 Langspec: Rust 」──" 'devicon "nf-dev-rust")
:color teal :quit-key "q")
("Actions"
(("d" rust-promote-module-into-dir "Module -> Dir")
("p" rust-playpen-region "Region -> Playground")
("P" rust-playpen-buffer "Buf -> Playground"))
"Build"
(("r" rust-run "Run Project")
("R" rust-run-release "Run Release")
("t" rust-test "Tests Project")
("l" rust-run-clippy "Run Clippy")
("c" rust-compile "Compile")
("C" rust-compile-release "Compile Release")
("e" rust-check "Check for Errors"))
"Cargo"
(("a" cargo-process-add "Add")
("x" cargo-process-rm "Delete")
("c" cargo-process-clean "Clean")
("h" cargo-process-doc "Docs")
("u" cargo-process-update "Update"))))
(pretty-hydra-define+ editor-hydra ()
("Programming"
(("r" (if (memq major-mode '(rust-mode rust-ts-mode))
(rust-hydra/body)
(message "You are not in a rust buffer.")) "Rust")))))
(use-package typst-ts-mode
:ensure (:host sourcehut :repo "meow_king/typst-ts-mode")
:if (executable-find "typst")
:mode ("\\.typ\\'" . typst-ts-mode)
:hook (typst-ts-mode . eglot-ensure)
:config
(when (executable-find "tinymist")
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '(typst-ts-mode "tinymist"))))
(with-eval-after-load 'consult-imenu
(setopt consult-imenu-config
(append consult-imenu-config
'((typst-ts-mode
:topLevel "Headings"
:types ((?h "Headings" typst-ts-markup-header-face)))))))
:custom (typst-ts-mode-enable-raw-blocks-highlight t))
(when (executable-find "typstyle")
(with-eval-after-load 'apheleia
(setf (alist-get 'typst-ts-mode apheleia-mode-alist) '(typstyle))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define typst-hydra
(:title (pretty-hydra-title "──「 Langspec: Typst 」──" 'mdicon "nf-md-math_compass")
:color teal :quit-key "q")
("Build"
(("c" typst-ts-mode-compile "Compile")
("p" typst-ts-mode-preview "Preview")
("u" typst-ts-mode-compile-and-preview "Run & Preview"))))
(pretty-hydra-define+ editor-hydra ()
("Markup"
(("t" (if (eq major-mode 'typst-ts-mode)
(typst-hydra/body)
(message "You are not in a typst buffer.")) "Typst")))))
(use-feature html-ts-mode
:mode ("\\.html\\'" . html-ts-mode)
:custom (html-ts-mode-indent-offset tab-width))
:TODO| add biome as a second LSP server when Eglot supports multiple LSPs.
(use-feature typescript-ts-mode
:if (executable-find "tsc")
:mode (("\\.ts\\'" . typescript-ts-mode)
("\\.tsx\\'" . tsx-ts-mode))
:hook (typescript-ts-mode . eglot-ensure)
:custom (typescript-ts-mode-indent-offset tab-width))
(when (executable-find "biome")
(with-eval-after-load 'apheleia
(setf (alist-get 'biome apheleia-formatters)
'("biome" "format" "--stdin-file-path" filepath))
(setf (alist-get typescript-ts-mode apheleia-mode-alist) '(biome))))
(use-package zig-mode
:if (executable-find "zig")
:mode ("\\.zig\\'" . zig-ts-mode)
:hook (zig-ts-mode . eglot-ensure)
:init (derived-mode-add-parents 'zig-ts-mode '(zig-mode)))
(when (executable-find "zig")
(with-eval-after-load 'apheleia
(setf (alist-get 'zig-ts-mode apheleia-mode-alist) '(zig-fmt))))
(use-package devdocs
:commands (devdocs-install devdocs-lookup)
:hook (devdocs-mode . visual-wrap-prefix-mode))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ helpful-hydra ()
("Action"
(("d" devdocs-lookup "Lookup DevDocs")))))
(use-package leetcode
:commands (leetcode)
:custom
(leetcode-save-solutions t)
(leetcode-prefer-language "python3")
(leetcode-directory (no-littering-expand-var-file-name "leetcode/")))
;;; init.el ends here