diff --git a/early-init.el b/early-init.el index b39f4e2..598588d 100644 --- a/early-init.el +++ b/early-init.el @@ -1,6 +1,11 @@ -;;; No GUI -(dolist (mode '(menu-bar-mode tool-bar-mode scroll-bar-mode)) - (when (fboundp mode) (funcall mode -1))) +;;;; No GUI +;; I do not use those graphical elements by default, but I do enable +;; them from time-to-time for testing purposes or to demonstrate +;; something. NEVER tell a beginner to disable any of these. They +;; are helpful. +(menu-bar-mode -1) +(scroll-bar-mode -1) +(tool-bar-mode -1) ;; A big contributor to startup times is garbage collection. diff --git a/init.el b/init.el index 3abc2ad..7419dbf 100644 --- a/init.el +++ b/init.el @@ -42,12 +42,52 @@ ("melpa" . 2) ("nongnu" . 1))) +;; Let `package-install' suggest upgrades for built-in packages too. (setq package-install-upgrade-built-in t) +(defmacro prot-emacs-comment (&rest body) + "Determine what to do with BODY. + +If BODY contains an unquoted plist of the form (:eval t) then +return BODY inside a `progn'. + +Otherwise, do nothing with BODY and return nil, with no side +effects." + (declare (indent defun)) + (let ((eval)) + (dolist (element body) + (when-let* (((plistp element)) + (key (car element)) + ((eq key :eval)) + (val (cadr element))) + (setq eval val + body (delq element body)))) + (when eval `(progn ,@body)))) + +(defmacro prot-emacs-abbrev (table &rest definitions) + "Expand abbrev DEFINITIONS for the given TABLE. +DEFINITIONS is a sequence of (i) string pairs mapping the +abbreviation to its expansion or (ii) a string and symbol pair +making an abbreviation to a function." + (declare (indent 1)) + (unless (zerop (% (length definitions) 2)) + (error "Uneven number of key+command pairs")) + `(if (abbrev-table-p ,table) + (progn + ,@(mapcar + (lambda (pair) + (let ((abbrev (nth 0 pair)) + (expansion (nth 1 pair))) + (if (stringp expansion) + `(define-abbrev ,table ,abbrev ,expansion) + `(define-abbrev ,table ,abbrev "" ,expansion)))) + (seq-split definitions 2))) + (error "%s is not an abbrev table" ,table))) + (require 'unravel-theme) (require 'unravel-essentials) ;; (require 'unravel-modeline) -;; (require 'unravel-completion) +(require 'unravel-completion) ;; (require 'unravel-search) ;; (require 'unravel-dired) ;; (require 'unravel-window) diff --git a/unravel-modules/unravel-completion.el b/unravel-modules/unravel-completion.el index 48a2e23..5b7a175 100644 --- a/unravel-modules/unravel-completion.el +++ b/unravel-modules/unravel-completion.el @@ -78,7 +78,7 @@ ;; SPC should never complete: use it for `orderless' groups. ;; The `?' is a regexp construct. - :bind (:map minibuffer-local-completion-map + :bind ( :map minibuffer-local-completion-map ("SPC" . nil) ("?" . nil))) @@ -188,6 +188,44 @@ (corfu-history-mode 1) (add-to-list 'savehist-additional-variables 'corfu-history))) +;;; Enhanced minibuffer commands (consult.el) +(use-package consult + :ensure t + :hook (completion-list-mode . consult-preview-at-point-mode) + :bind + (:map global-map + ("M-g M-g" . consult-goto-line) + ("M-K" . consult-keep-lines) ; M-S-k is similar to M-S-5 (M-%) + ("M-F" . consult-focus-lines) ; same principle + ("M-s M-b" . consult-buffer) + ("M-s M-f" . consult-find) + ("M-s M-g" . consult-grep) + ("M-s M-h" . consult-history) + ("M-s M-i" . consult-imenu) + ("M-s M-l" . consult-line) + ("M-s M-m" . consult-mark) + ("M-s M-y" . consult-yank-pop) + ("M-s M-s" . consult-outline) + :map consult-narrow-map + ("?" . consult-narrow-help)) + :config + (setq consult-line-numbers-widen t) + ;; (setq completion-in-region-function #'consult-completion-in-region) + (setq consult-async-min-input 3) + (setq consult-async-input-debounce 0.5) + (setq consult-async-input-throttle 0.8) + (setq consult-narrow-key nil) + (setq consult-find-args + (concat "find . -not ( " + "-path */.git* -prune " + "-or -path */.cache* -prune )")) + (setq consult-preview-key 'any) + (setq consult-project-function nil) ; always work from the current directory (use `cd' to switch directory) + + (add-to-list 'consult-mode-histories '(vc-git-log-edit-mode . log-edit-comment-ring)) + ;; the `imenu' extension is in its own file + (require 'consult-imenu)) + ;;; Extended minibuffer actions and more (embark.el and prot-embark.el) (use-package embark :ensure t @@ -232,33 +270,33 @@ ;; I define my own keymaps because I only use a few functions in a ;; limited number of contexts. -;; (use-package prot-embark -;; :ensure nil -;; :after embark -;; :bind -;; ( :map global-map -;; ("C-," . prot-embark-act-no-quit) -;; ("C-." . prot-embark-act-quit) -;; :map embark-collect-mode-map -;; ("C-," . prot-embark-act-no-quit) -;; ("C-." . prot-embark-act-quit) -;; :map minibuffer-local-filename-completion-map -;; ("C-," . prot-embark-act-no-quit) -;; ("C-." . prot-embark-act-quit)) -;; :config -;; (setq embark-keymap-alist -;; '((buffer prot-embark-buffer-map) -;; (command prot-embark-command-map) -;; (expression prot-embark-expression-map) -;; (file prot-embark-file-map) -;; (function prot-embark-function-map) -;; (identifier prot-embark-identifier-map) -;; (package prot-embark-package-map) -;; (region prot-embark-region-map) -;; (symbol prot-embark-symbol-map) -;; (url prot-embark-url-map) -;; (variable prot-embark-variable-map) -;; (t embark-general-map)))) +(use-package prot-embark + :ensure nil + :after embark + :bind + ( :map global-map + ("C-," . prot-embark-act-no-quit) + ("C-." . prot-embark-act-quit) + :map embark-collect-mode-map + ("C-," . prot-embark-act-no-quit) + ("C-." . prot-embark-act-quit) + :map minibuffer-local-filename-completion-map + ("C-," . prot-embark-act-no-quit) + ("C-." . prot-embark-act-quit)) + :config + (setq embark-keymap-alist + '((buffer prot-embark-buffer-map) + (command prot-embark-command-map) + (expression prot-embark-expression-map) + (file prot-embark-file-map) + (function prot-embark-function-map) + (identifier prot-embark-identifier-map) + (package prot-embark-package-map) + (region prot-embark-region-map) + (symbol prot-embark-symbol-map) + (url prot-embark-url-map) + (variable prot-embark-variable-map) + (t embark-general-map)))) ;; Needed for correct exporting while using Embark with Consult ;; commands. diff --git a/unravel-modules/unravel-essentials.el b/unravel-modules/unravel-essentials.el index 57fcbcc..42020eb 100644 --- a/unravel-modules/unravel-essentials.el +++ b/unravel-modules/unravel-essentials.el @@ -75,47 +75,6 @@ (border-width . 0) (no-special-glyphs . t)))) -;;;; World clock (M-x world-clock) -(use-package time - :ensure nil - :commands (world-clock) - :config - (setq display-time-world-list t) - (setq zoneinfo-style-world-list ; M-x shell RET timedatectl list-timezones - '(("America/Los_Angeles" "Los Angeles") - ("America/Vancouver" "Vancouver") - ("Canada/Pacific" "Canada/Pacific") - ("America/Chicago" "Chicago") - ("Brazil/Acre" "Rio Branco") - ("America/Toronto" "Toronto") - ("America/New_York" "New York") - ("Canada/Atlantic" "Canada/Atlantic") - ("Brazil/East" "Brasília") - ("UTC" "UTC") - ("Europe/Lisbon" "Lisbon") - ("Europe/Brussels" "Brussels") - ("Europe/Athens" "Athens") - ("Asia/Riyadh" "Riyadh") - ("Asia/Tehran" "Tehran") - ("Asia/Tbilisi" "Tbilisi") - ("Asia/Yekaterinburg" "Yekaterinburg") - ("Asia/Kolkata" "Kolkata") - ("Asia/Singapore" "Singapore") - ("Asia/Shanghai" "Shanghai") - ("Asia/Seoul" "Seoul") - ("Asia/Tokyo" "Tokyo") - ("Asia/Vladivostok" "Vladivostok") - ("Australia/Brisbane" "Brisbane") - ("Australia/Sydney" "Sydney") - ("Pacific/Auckland" "Auckland"))) - - ;; All of the following variables are for Emacs 28 - (setq world-clock-list t) - (setq world-clock-time-format "%R %z (%Z) %A %d %B") - (setq world-clock-buffer-name "*world-clock*") ; Placement handled by `display-buffer-alist' - (setq world-clock-timer-enable t) - (setq world-clock-timer-second 60)) - ;;;; Emacs server (allow emacsclient to connect to running session) (use-package server :ensure nil @@ -158,8 +117,8 @@ word. Fall back to regular `expreg-expand'." (setq battery-mode-line-format (cond ((eq battery-status-function #'battery-linux-proc-acpi) - "⏻%b%p%%,%d°C ") + "⏻ %b%p%%,%d°C ") (battery-status-function - "⏻%b%p%% ")))) + "⏻ %b%p%% ")))) (provide 'unravel-essentials) diff --git a/unravel-modules/unravel-langs.el b/unravel-modules/unravel-langs.el index 56b6622..262c47b 100644 --- a/unravel-modules/unravel-langs.el +++ b/unravel-modules/unravel-langs.el @@ -117,4 +117,113 @@ dictionary-create-buttons nil dictionary-use-single-buffer t)) +;;; Denote (simple note-taking and file-naming) + +;; Read the manual: . This does +;; not include all the useful features of Denote. I have a separate +;; private setup for those, as I need to test everything is in order. +(use-package denote + :ensure t + :hook + ;; If you use Markdown or plain text files you want to fontify links + ;; upon visiting the file (Org renders links as buttons right away). + ((text-mode . denote-fontify-links-mode-maybe) + + ;; Highlight Denote file names in Dired buffers. Below is the + ;; generic approach, which is great if you rename files Denote-style + ;; in lots of places as I do. + ;; + ;; If you only want the `denote-dired-mode' in select directories, + ;; then modify the variable `denote-dired-directories' and use the + ;; following instead: + ;; + ;; (dired-mode . denote-dired-mode-in-directories) + (dired-mode . denote-dired-mode)) + :bind + ;; Denote DOES NOT define any key bindings. This is for the user to + ;; decide. Here I only have a subset of what Denote offers. + ( :map global-map + ("C-c n n" . denote) + ("C-c n N" . denote-type) + ("C-c n o" . denote-sort-dired) ; "order" mnemonic + ;; Note that `denote-rename-file' can work from any context, not + ;; just Dired buffers. That is why we bind it here to the + ;; `global-map'. + ;; + ;; Also see `denote-rename-file-using-front-matter' further below. + ("C-c n r" . denote-rename-file) + ;; If you intend to use Denote with a variety of file types, it is + ;; easier to bind the link-related commands to the `global-map', as + ;; shown here. Otherwise follow the same pattern for + ;; `org-mode-map', `markdown-mode-map', and/or `text-mode-map'. + :map text-mode-map + ("C-c n i" . denote-link) ; "insert" mnemonic + ("C-c n I" . denote-add-links) + ("C-c n b" . denote-backlinks) + ;; Also see `denote-rename-file' further above. + ("C-c n R" . denote-rename-file-using-front-matter) + :map org-mode-map + ("C-c n d l" . denote-org-extras-dblock-insert-links) + ("C-c n d b" . denote-org-extras-dblock-insert-backlinks) + ;; Key bindings specifically for Dired. + :map dired-mode-map + ("C-c C-d C-i" . denote-dired-link-marked-notes) + ("C-c C-d C-r" . denote-dired-rename-marked-files) + ("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords) + ("C-c C-d C-f" . denote-dired-rename-marked-files-using-front-matter)) + :config + ;; Remember to check the doc strings of those variables. + (setq denote-directory (expand-file-name "~/Documents/notes/")) + (setq denote-file-type 'text) ; Org is the default file type + + ;; If you want to have a "controlled vocabulary" of keywords, + ;; meaning that you only use a predefined set of them, then you want + ;; `denote-infer-keywords' to be nil and `denote-known-keywords' to + ;; have the keywords you need. + (setq denote-known-keywords '("emacs" "philosophy" "politics" "economics")) + (setq denote-infer-keywords t) + (setq denote-sort-keywords t) + + (setq denote-excluded-directories-regexp nil) + (setq denote-date-format nil) ; read its doc string + (setq denote-rename-confirmations nil) ; CAREFUL with this if you are not familiar with Denote! + + (setq denote-backlinks-show-context nil) + + (setq denote-rename-buffer-format "[D] %t%b") + (setq denote-buffer-has-backlinks-string " (<--->)") + + ;; Automatically rename Denote buffers when opening them so that + ;; instead of their long file name they have a literal "[D]" + ;; followed by the file's title. Read the doc string of + ;; `denote-rename-buffer-format' for how to modify this. + (denote-rename-buffer-mode 1) + + ;; ----- PERSONAL TWEAKS FOR EXPERIMENTS ----- + (setq denote-text-front-matter "title: %s\n\n") + + (defun prot/denote-add-text-front-matter-separator () + "Add separator equal to the length of the title. +Do this when the `denote-file-type' is `text'." + (when (and (eq denote-file-type 'text) + ;; Not `string=' because there may be a .gpg extension as well. + (string-match-p (file-name-extension buffer-file-name) "txt")) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "title:" nil t) + (let ((text (buffer-substring-no-properties (line-beginning-position) (line-end-position)))) + (if (re-search-forward "^$" nil t) + (insert (make-string (length text) ?-)) + (error "Could not find an empty line after the front matter"))))))) + + (add-hook 'denote-after-new-note-hook #'prot/denote-add-text-front-matter-separator)) + +(use-package consult-denote + :ensure t + :bind + (("C-c n f" . consult-denote-find) + ("C-c n g" . consult-denote-grep)) + :config + (consult-denote-mode 1)) + (provide 'unravel-langs) diff --git a/unravel-modules/unravel-theme.el b/unravel-modules/unravel-theme.el index 36f7572..ebf65f9 100644 --- a/unravel-modules/unravel-theme.el +++ b/unravel-modules/unravel-theme.el @@ -59,6 +59,22 @@ ;; it is very flexible. (setq spacious-padding-subtle-mode-line t)) +;;;; Rainbow mode for colour previewing (rainbow-mode.el) +(use-package rainbow-mode + :ensure t + :init + (setq rainbow-ansi-colors nil) + (setq rainbow-x-colors nil) + + (defun prot/rainbow-mode-in-themes () + (when-let* ((file (buffer-file-name)) + ((derived-mode-p 'emacs-lisp-mode)) + ((string-match-p "-theme" file))) + (rainbow-mode 1))) + :bind ( :map ctl-x-x-map + ("c" . rainbow-mode)) ; C-x x c + :hook (emacs-lisp-mode . prot/rainbow-mode-in-themes)) + ;;; Cursor appearance (cursory) ;; Read the manual: . (use-package cursory @@ -121,7 +137,7 @@ (let ((modus-themes-p (featurep 'modus-themes)) (ef-themes-p (featurep 'ef-themes))) (setq theme-buffet-menu 'end-user) - (setq theme-buffet-time-offset 5) + (setq theme-buffet-time-offset 0) (setq theme-buffet-end-user '(:night (ef-dark ef-winter ef-autumn ef-night ef-duo-dark ef-symbiosis ef-owl) :morning (ef-light ef-cyprus ef-spring ef-frost ef-duo-light ef-eagle) @@ -166,11 +182,11 @@ :inherit medium :default-height 150) (live-stream - :default-family "Iosevka Comfy Wide Motion" + :default-family "Iosevka" :default-height 150 :default-weight medium - :fixed-pitch-family "Iosevka Comfy Wide Motion" - :variable-pitch-family "Iosevka Comfy Wide Duo" + :fixed-pitch-family "Iosevka" + :variable-pitch-family "Iosevka" :bold-weight extrabold) (presentation :default-height 180) @@ -180,13 +196,13 @@ ;; I keep all properties for didactic purposes, but most can be ;; omitted. See the fontaine manual for the technicalities: ;; . - :default-family "Iosevka Comfy" + :default-family "Iosevka" :default-weight regular :default-slant normal :default-width normal :default-height 100 - :fixed-pitch-family "Iosevka Comfy" + :fixed-pitch-family "Iosevka Fixed" :fixed-pitch-weight nil :fixed-pitch-slant nil :fixed-pitch-width nil @@ -198,55 +214,55 @@ :fixed-pitch-serif-width nil :fixed-pitch-serif-height 1.0 - :variable-pitch-family "Iosevka Comfy Motion Duo" + :variable-pitch-family "Iosevka" :variable-pitch-weight nil :variable-pitch-slant nil :variable-pitch-width nil :variable-pitch-height 1.0 - :mode-line-active-family nil + :mode-line-active-family "Iosevka Term" :mode-line-active-weight nil :mode-line-active-slant nil :mode-line-active-width nil :mode-line-active-height 1.0 - :mode-line-inactive-family nil + :mode-line-inactive-family "Iosevka Term" :mode-line-inactive-weight nil :mode-line-inactive-slant nil :mode-line-inactive-width nil :mode-line-inactive-height 1.0 - :header-line-family nil + :header-line-family "Iosevka Term" :header-line-weight nil :header-line-slant nil :header-line-width nil :header-line-height 1.0 - :line-number-family nil + :line-number-family "Iosevka Term" :line-number-weight nil :line-number-slant nil :line-number-width nil :line-number-height 1.0 - :tab-bar-family nil + :tab-bar-family "Iosevka Term" :tab-bar-weight nil :tab-bar-slant nil :tab-bar-width nil :tab-bar-height 1.0 - :tab-line-family nil + :tab-line-family "Iosevka Term" :tab-line-weight nil :tab-line-slant nil :tab-line-width nil :tab-line-height 1.0 - :bold-family nil + :bold-family "Iosevka" :bold-slant nil :bold-weight bold :bold-width nil :bold-height 1.0 - :italic-family nil + :italic-family "Iosevka" :italic-weight nil :italic-slant italic :italic-width nil @@ -254,4 +270,27 @@ :line-spacing nil)))) +;;;;; `variable-pitch-mode' setup +(use-package face-remap + :ensure nil + :functions prot/enable-variable-pitch + :bind ( :map ctl-x-x-map + ("v" . variable-pitch-mode)) + :hook ((text-mode notmuch-show-mode elfeed-show-mode) . prot/enable-variable-pitch) + :config + ;; NOTE 2022-11-20: This may not cover every case, though it works + ;; fine in my workflow. I am still undecided by EWW. + (defun prot/enable-variable-pitch () + (unless (derived-mode-p 'mhtml-mode 'nxml-mode 'yaml-mode) + (variable-pitch-mode 1))) +;;;;; Resize keys with global effect + :bind + ;; Emacs 29 introduces commands that resize the font across all + ;; buffers (including the minibuffer), which is what I want, as + ;; opposed to doing it only in the current buffer. The keys are the + ;; same as the defaults. + (("C-x C-=" . global-text-scale-adjust) + ("C-x C-+" . global-text-scale-adjust) + ("C-x C-0" . global-text-scale-adjust))) + (provide 'unravel-theme)