- Early Initialization of Irkalla
- Core Configuration(s)
- Part 1: Beginning of A Journey
- Part 2: Decorating Irkalla
- Autothemer: Macros To Ease The Theming Process
- Prettified Symbols
- Typeface’s & Saner Fonts
- Nerd-Icons: Adding A Sprinkle of Beautiful Icons
- SVG-Tag: Decorating Buffers With SVG
- Modeline(s) -> Status-Bar(s) For Emacs
- Containers For Displaying Tab(s) & Buffer(s)
- Dashboard: A Welcoming Buffer For New Frames
- Part 3: Expanding The Utility Box
- Part 4: Editing Environment
- 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
- Leetcode: Dashboard For Code Challenges
- ChatGPT: Code Prediction At The Cost of Freedom
- 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: 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 '((alpha-background . 85)
(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 (* 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))))
(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-splash-screen t
inhibit-startup-buffer-menu t
inhibit-startup-echo-area-message user-login-name
inhibit-startup-message t
inhibit-startup-screen t
mode-line-format nil
use-dialog-box nil
use-file-dialog nil)
(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
select-enable-clipboard nil
command-line-x-option-alist nil
default-input-method nil
ring-bell-function 'ignore
select-active-regions 'only
load-prefer-newer noninteractive)
We might as well set our default Emacs environment to UTF-8
.
(set-default-coding-systems '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)
(setq 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...
(setq 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
(setq 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 emacs
:config
(defun irkalla/opacify-frame ()
(let ((alpha-value
(if (equal (frame-parameter nil 'alpha-background) 100)
85 100)))
(set-frame-parameter nil 'alpha-background alpha-value)
(add-to-list 'default-frame-alist `(alpha-background . ,alpha-value))))
(define-minor-mode irkalla/opacify-frame-mode
"Toggle (on/off) Emacs frame transparency on demand!"
:group 'irkalla
:global nil
(irkalla/opacify-frame))
(setopt window-combination-resize t))
(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")
("Main"
(("o" irkalla/opacify-frame-mode "Opacify Frame" :toggle t))
"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-feature emacs
:hook (emacs-lisp-mode . prettify-symbols-mode)
:config (setopt prettify-symbols-unprettify-at-point 'right-edge))
(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-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)
(with-eval-after-load 'marginalia
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup)))
(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)))
(setq 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 minions
:hook (elpaca-after-init . minions-mode))
(use-package moody
:after (minions)
:config
(moody-replace-mode-line-front-space)
(moody-replace-mode-line-buffer-identification)
(moody-replace-vc-mode))
(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))
(use-feature tab-bar
:bind (("C-<next>" . tab-next)
("C-<prior>" . tab-previous))
:custom
(tab-bar-show 1)
(tab-bar-tab-hints t)
(tab-bar-close-button " ")
(tab-bar-new-tab-choice "*dashboard*"))
(use-feature tab-line
:config (setq tab-line-close-button " "))
(use-package tabspaces
:hook (elpaca-after-init . tabspaces-mode)
:config
<<tabspaces-consult-buffers>>
:custom
(tabspaces-session t)
(tabspaces-keymap-prefix "C-c p")
(tabspaces-default-tab "Default")
(tabspaces-remove-to-default t)
(tabspaces-include-buffers '("*scratch*"))
(tabspaces-initialize-project-with-todo t)
(tabspaces-todo-file-name "project-todo.org")
(tabspaces-use-filtered-buffers-as-default t))
(with-eval-after-load '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))))
(add-to-list 'consult-buffer-sources 'consult--source-workspace))
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
(setq 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 (setq 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
:config (recentf-mode)
:custom
(recentf-save-file-modes #o600)
(recentf-max-saved-items 1024)
(recentf-auto-cleanup 600)
(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)
("*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)
:bind (:map popper-mode-map)
: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 "*" (* 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-listing-switches "-al --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-delay 0.5
register-preview-function #'consult-register-format)
(advice-add #'register-preview :override #'consult-register-window)
;; Consult -> select xref locations with preview
(setq 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)
(vertico-multiform-categories
'((file grid reverse)
(consult-location buffer)
(consult-grep buffer)
(minor-mode reverse)
(imenu buffer)
(t unobtrusive)))
(vertico-multiform-commands
'((consult-dir reverse)
(execute-extended-command flat)
(embark-prefix-help-command reverse)
(completion-at-point reverse))))
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)
:custom
(marginalia-max-relative-age 0)
(marginalia-align 'right))
(use-feature tramp
:defer t
:config
(setq 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
(with-eval-after-load 'visual-fill-column
(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
(with-eval-after-load 'visual-fill-column
(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
(setq 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
(with-eval-after-load 'visual-fill-column
(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)))
(with-eval-after-load 'visual-fill-column
(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
(setq 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-package evil
:bind (:map evil-normal-state-map ("-" . evil-ex-nohighlight))
: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
(setq evil-collection-corfu-key-themes '(tab-n-go))
(advice-add 'corfu--setup :after
(lambda (&rest _) (setq 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-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
fill-column 120
remote-file-name-inhibit-locks t
text-mode-ispell-word-completion nil
truncate-lines t
truncate-string-ellipsis "↴"
x-stretch-cursor t
x-underline-at-descent-line t))
(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 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
:config (global-hl-line-mode))
(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-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 project
:custom
(project-vc-extra-root-markers
'(".dir-locals.el" "Makefile" "CMakeLists.txt" "Cargo.toml" "Dockerfile"
"package.json" "requirements.txt")))
(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
(setq 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)
(setq select-enable-clipboard t)
(if (featurep 'evil)
(call-interactively #'evil-yank)
(kill-ring-save (region-beginning) (region-end)))
(setq select-enable-clipboard nil))
(defun irkalla/paste-from-sysclip ()
"Paste contents to the system clipboard."
(interactive)
(setq select-enable-clipboard t)
(if (featurep 'evil)
(call-interactively #'evil-paste-after)
(yank))
(setq 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))
(add-to-list 'consult-buffer-sources 'beframe-consult-source))
(use-package activities
:hook (elpaca-after-init . activities-mode))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define activities-hydra
(:title (pretty-hydra-title "──「 Utilities: Activities 」──" 'mdicon "nf-md-file_tree")
:color teal :quit-key "q")
("Workflow"
(("l" activities-list "List")
("RET" activities-switch "Switch")
("e" activities-rename "Rename")
("w" activities-save-all "Save All"))
"Task Operation"
(("n" activities-new "Create")
("k" activities-kill "Kill")
("u" activities-revert "Revert"))
"Pause/Resume"
(("s" activities-suspend "Suspend")
("r" activities-resume "Resume")
("d" activities-discard "Discard"))))
(pretty-hydra-define+ editor-hydra ()
("Control"
(("a" activities-hydra/body "Activities")))))
(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
(setq 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))
(setq org-preview-latex-image-directory latex-dir))
;; :NOTE| Change the aesthetics of our LaTeX previews
(setq org-latex-preview-options
(progn (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-edit-src-auto-save-idle-delay 5)
(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)
;; Code blocks
(org-confirm-babel-evaluate nil)
(org-edit-src-content-indentation 0)
(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 (setq 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
:config
(setopt tab-always-indent 'complete
compilation-always-kill t
compilation-ask-about-save nil
compilation-scroll-output t
compilation-scroll-output 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
(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-package leetcode
:commands (leetcode)
:custom
(leetcode-save-solutions t)
(leetcode-prefer-language "python3")
(leetcode-directory (no-littering-expand-var-file-name "leetcode/")))
(use-package chatgpt-shell
:commands (chatgpt-shell dall-e-shell)
:custom (chatgpt-shell-openai-key (lambda () (irkalla/read-secret-file "ClosedAI"))))
(with-eval-after-load 'pretty-hydra
(pretty-hydra-define+ editor-hydra ()
("ChatGPT"
(("c" chatgpt-shell "Shell")
("d" dall-e-shell "Dall-E")))))
(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
;; :PERF| Noticeable LSP Buff. diff!
(with-eval-after-load 'jsonrpc
(setq jsonrpc-inhibit-debug-on-error t)
(setq jsonrpc-event-hook nil)
(fset #'jsonrpc--log-event #'ignore))
(with-eval-after-load 'nerd-icons (setq eglot-menu-string " "))
:custom
(eglot-events-buffer-config '(:size 0))
(eglot-autoshutdown t)
(eglot-extend-to-xref t)
(eglot-confirm-server-initiated-edits nil)
(eglot-ignored-server-capabilities '(:documentHighlightProvider)))
(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-offset tab-width))
(when (executable-find "clang-format")
(with-eval-after-load 'apheleia
(setf (alist-get 'clang-format apheleia-formatters)
`("clang-format"
"-assume-filename"
(or (apheleia-formatters-local-buffer-file-name)
(apheleia-formatters-mode-extension)
".c")
(when apheleia-formatters-respect-indent-level
(unless (locate-dominating-file default-directory ".clang-format")
(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
(setq 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))))
;;; init.el ends here