Skip to content

A stripped-down version of my primary Emacs configuration, it is designed to leverage only the default built-in features of Emacs

License

Notifications You must be signed in to change notification settings

captainflasmr/Emacs-vanilla

Repository files navigation

Emacs vanilla

Introduction

This is a stripped-down version of my primary Emacs configuration. It is designed to leverage only the default built-in features of the more recent versions of Emacs (v27.2 and later). This makes it ideal for virtual machines or scenarios where you need a fast, package-free Emacs setup that works out of the box on most recent systems.

Supported Emacs versions:

  • 27.2 2021-03-25
  • 28.2 2022-09-12
  • 29.4 2024-06-22

Supported Platforms:

  • SUSE Linux Enterprise (SLES15)
  • SwayWM - Wayland
  • Windows 10

The configuration focuses on ease of use by providing custom keybindings, basic completion support, and visual enhancements without relying on any external packages.

Note that this package works in combination with https://github.com/captainflasmr/Emacs-enhanced which is a conversion of major Emacs packages / functions, especially ones that I rely on to single defuns.

Whats New

<2025-03-13>

Added flyspell error collection

  • Introduce `my/collect-flyspell-errors` to list and navigate flyspell errors
  • Enhance `imenu-generic-expression` to index function definitions
  • Set `ispell-personal-dictionary` path

<2025-02-04>

Refactor global keybindings and add selective text folding function

  • Updated `C-x k` to `kill-buffer`.
  • Moved tab-bar history bindings to `C-c j` and `C-c k`.
  • Added `C-c f` to bind `my/selective-display-fold`.
  • Defined `my/selective-display-fold` function for selective text folding.

<2025-02-02>

Imported shell environment variables and enabled org-babel for C

  • Added `import-shell-environment` function to import shell variables like PATH.
  • Configured `org-babel` to support C language.

<2025-01-28>

Refactor color adjustment logic and improve hl-line background handling

  • Replace `darken-color` with a more versatile `adjust-color` function that supports both darkening and lightening with minimum visible increments for very dark colors.
  • Update `set-hl-line-darker-background` to `set-simple-hl-line`:
    • Adjust background dynamically based on theme brightness.
    • Lightens backgrounds of dark themes by 20% and darkens light themes by 5%.

Issues

TODOTitleParent Title

Benefits

This Emacs init configuration provides the following advantages:

Portability and Compatibility

  • Universal usability: Works on any system with Emacs (v27.1+) installed, even without internet or additional dependencies.
  • Repository-free operation: Ideal for air-gapped or restricted environments without access to MELPA or ELPA.
  • Cross-platform adaptability:
    • Linux: Avoids fragile GUI dependencies.
    • Windows: Resolves common issues like slow startups.
    • macOS: Compatible with Emacs’s pre-installed versions.
  • Version-agnostic: Leverages stable vanilla features, ensuring compatibility across Emacs 27.1+ without relying on bleeding-edge updates.

Performance

  • Optimized startup: No external package initialization or network delays.
  • Lightweight: No dependency bloat—ideal for resource-limited systems like VMs.

Simplicity and Stability

  • Eliminates external package managers (MELPA, ELPA, etc.), reducing update-related breakages.
  • Leverages core, well-tested Emacs functionality, ensuring stability.

Usability

  • Beginner-friendly: Introduces users to Emacs’s native capabilities without third-party distractions.
  • Low maintenance: Perfect for temporary setups, Docker containers, or ephemeral environments.

Practicality

  • Offline-ready: Works in isolated, air-gapped networks.
  • Easy distribution: A single self-contained file requiring no external dependencies.

Security and Privacy

  • Self-contained: Avoids risks related to external downloads or unverified code.
  • Privacy-conscious: No external queries, ensuring safe usage in sensitive environments.

Learning and Mastery

  • Promotes deeper understanding of Emacs’s native functionality and workflows.
  • Provides a foundation for further customization without dependency on third-party tools.

Flexibility for Advanced Users

  • Functions as a reliable core config for incremental builds or as an emergency fallback.
  • Endures across versions, prioritizing timeless Emacs workflows.

Use Cases

  • Debug issues by isolating third-party conflicts.
  • Portable, distraction-free editor for temporary systems.
  • Reliable setup for VMs or testing environments.

Key Features

Built-in Completion

  • Default abbreviation expansion and predictive file/directory completion are implemented via hippie-expand.
  • Minibuffer Configuration: Vanilla configuration achieved through enabling fido-mode provides an intuitive, fast, and clean completion interface in the minibuffer using the built-in icomplete framework without requiring third-party tools.

Leveraging External Tools Where Possible

It is often advantageous to use external tools that are optimized for specific tasks. Emacs offers many built-in tools, but these may not always be the fastest or most feature-rich option available. By integrating with system-based utilities when they exist, we can achieve both performance improvements and enhanced capabilities. However, in cases where external tools are unavailable, Emacs’ built-in options serve as a reliable fallback.

ripgrep - grepping files

Functionality

  • Searching across project files for a specific term or pattern.
  • Faster than Emacs’ built-in grep or rgrep
  • Better default handling of ignored files (e.g., .gitignore rules).
  • Offers rich pattern matching (regex or literal strings).

Fallback

  • Emacs’ grep or rgrep commands provide file searching through built-in utilities like grep.
  • While slower and less feature-rich, they can still handle basic directory searches.

ripgrep (alternative fd) - finding files

Functionality

  • Searching for files in a directory or project.
  • Supports advanced filtering options (e.g., searching by filename extension, ignoring gitignored files).

Fallback

  • find-name-dired or project-find-file for locating files within Emacs projects.
  • Relatively slower on larger directories, but sufficient for small-scale tasks.

Keybindings for Navigation and Files

  • Global Keybindings: Intuitive shortcut keys grant fast access to commonly used directories, files, and dired buffers. These can be configured to cater to personal file organization preferences.
  • Tab and Buffer Management: Includes keybindings for:
    • Quickly creating, killing, and cycling through buffers.
    • Simplified navigation through tabs in Emacs (using its native tab-bar-mode or tab-line-mode).
    • Use mnemonic key combinations to split, balance, and move between windows, akin to tiling window managers.

Window and Visual Controls

  • Flexibly toggle visual Emacs elements such as:
    • Fonts (adjust point size with keyboard shortcuts).
    • Line numbers (switch between absolute and relative numbering).
    • Themes (light/dark mode switching via a single key).
    • Minor display elements like fringe, scroll bars, and menu bars depending on needs.
  • Window Management*
    • Handy shortcuts for splitting windows, resizing panes, and reshuffling the layout in a minimal keystroke setup.
    • A handcrafted toggle-centered-buffer function focuses content by placing the active buffer in the centre, hiding distractions in other windows.

Custom Functions

  • Handcrafted Lisp Utilities: A small collection of reusable functions that enhance workflow directly without accessing external configuration files or plugins:
    • toggle-centered-buffer: Dynamically rebalanced the window layout for distraction-free working.
    • my/dired-duplicate-file: Quickly duplicates the currently selected file in dired mode, increasing workflow efficiency for file templating.
    • my/copy-buffer-to-kill-ring: Copies the entirety of the current buffer content directly to the kill ring for seamless external clipboard usage.

No External Packages Required

  • No Dependency on ELPA/MELPA: This configuration deliberately avoids using 3rd-party packages, ensuring it remains lightweight and portable across systems. All enhancements and ergonomic tweaks leverage built-in Emacs capabilities and Emacs Lisp.
  • Offline-First Design: With no dependency on online repositories or external tools, this setup works out of the box even in restricted or air-gapped environments.

Additional Ergonomic Setup

  • Cross-Platform Key Remapping: Accompanying the Emacs configuration directory are external scripts to enforce ergonomic system-wide keybindings for more efficient Emacs usage:

Windows / wowee:

Note that this is present as a submodule leveraging https://github.com/captainflasmr/wowee which are AutoHotKey scripts that allow easy key remapping and also Emacs type key commands across Windows.

The idea here is that the keys are mapped through AutoHotKey and then Sticky Keys are natively enable to give a nice ergonomic experience through Emacs.

Remappings

  • CapsLock → Ctrl
  • Right Alt → Ctrl
  • Sticky Keys natively enabled

Instructions for use

  1. **Install AutoHotKey**: Download and install AutoHotKey from [AutoHotKey’s official website](https://www.autohotkey.com/).
  2. **Run WOWEE**: Double-click on the `wowee.ahk` script to start WOWEE. Once running, Emacs commands will be available in your Windows environment.
  3. **Quit WOWEE**: To quit WOWEE, right-click the AutoHotKey icon in the task tray and select “Exit.”

Linux (X11/Wayland):

Includes custom xkb configuration files to enable similar ergonomic key remappings:

Remappings

  • CapsLock → Ctrl
  • Right Alt → Ctrl
  • Sticky Keys

Instructions for use

xkbcomp keymap_with_sticky_modifiers.xkb $DISPLAY

Portable Directory Structure

  • A self-contained folder structure that encapsulates all necessary files:
    emacs-vanilla/
    ├── init.el                          # Main Emacs configuration file
    ├── keymap_with_sticky_modifiers.xkb # linux key configuration for ergonomic key remapping
    ├── wowee/                           # Windows autohotkey scripts for ergonomic key remapping
    └── README.org                       # Literate setup guide and readme
        

This directory can be zipped, copied, and unpacked on any machine to instantly set up a usable, ergonomic Emacs environment.

Setup

  • Clone the repository:
    git clone https://github.com/captainflasmr/Emacs-vanilla ~/.emacs.d.vanilla
        
  • Symlink the init file:
    ln -s ~/.emacs.d.core/init.el ~/.emacs.d/init.el
        

OR

  • Define startup directory
    emacs --init-directory=~/.emacs.d.core
        

requires-core

Ensures essential packages are loaded at startup.

;;
;; -> requires-core
;;
(require 'org)
(require 'grep)
(require 'bookmark)
(require 'dired)
(require 'ox-md)

completion-core

Simple abbrev completion

;;
;; -> completion-core
;;
(setq-default abbrev-mode t)
(setq hippie-expand-try-functions-list
      '(try-complete-file-name-partially
        try-complete-file-name
        try-expand-all-abbrevs try-expand-dabbrev
        try-expand-dabbrev-all-buffers try-expand-dabbrev-from-kill
        try-complete-lisp-symbol-partially try-complete-lisp-symbol))

(setq completion-category-overrides
      '((project-file (styles substring basic partial-completion))
        (file (styles substring basic partial-completion))
        (buffer (styles substring basic partial-completion))
        (command (styles substring basic partial-completion))))

modeline-completion-core

Now fido-mode, note the having to add advice to overried/set up the completion as by default fido-mode comes with flex.

;;
;; -> modeline-completion-core
;;
(defun my-fido-completion-styles-advice (&rest _args)
  "Override completion styles after fido setup."
  (when (and fido-mode (icomplete-simple-completing-p))
    (setq-local completion-styles '(substring basic partial-completion))))

(advice-add 'icomplete--fido-mode-setup :after #'my-fido-completion-styles-advice)

(if (fboundp 'fido-vertical-mode)
    (fido-vertical-mode 1)
  (fido-mode 1))

Now for the old icomplete, lets see if I can get used to this!

;;
;; -> modeline-completion-core
;;
(if (fboundp 'icomplete-vertical-mode)
    (icomplete-vertical-mode 1)
  (fido-mode 1))

(setq icomplete-scroll t)
(setq completion-ignore-case t)
(setq read-file-name-completion-ignore-case t)
(setq read-buffer-completion-ignore-case t)

(with-eval-after-load 'icomplete
  (setq completion-styles '(substring basic partial-completion emacs22)))

Now the generic completing-read stuff

;;
;; -> modeline-completion-core
;;
(custom-set-faces
  '(icomplete-first-match
    ((t (:foreground "#7c7c75" :background "#3a3a3a" :weight bold))))
  '(icomplete-selected-match
    ((t (:foreground "#ffffff" :background "#5f87af" :weight bold))))
  '(completions-common-part
    ((t (:foreground "#87ceeb"))))
  '(completions-first-difference
    ((t (:foreground "#ffb6c1")))))
(setq icomplete-compute-delay 0)
(setq icomplete-show-matches-on-no-input t)

keys-navigation-core

Defines custom keybindings for navigating through files and Emacs features like tabs, dired, and scratch buffers.

;;
;; -> keys-navigation-core
;;
(defvar my-jump-keymap (make-sparse-keymap))
(global-set-key (kbd "M-l") my-jump-keymap)
(define-key my-jump-keymap (kbd "=") #'tab-bar-new-tab)
(define-key my-jump-keymap (kbd "b") (lambda () (interactive) (find-file "~/bin")))
(define-key my-jump-keymap (kbd "c") (lambda () (interactive) (find-file "~/DCIM/content/aaa--calendar.org")))
(define-key my-jump-keymap (kbd "d") (lambda () (interactive) (find-file (expand-file-name diary-file))))
(define-key my-jump-keymap (kbd "e")
            (lambda ()
              (interactive)
              (find-file (expand-file-name "init.el" user-emacs-directory))))
(define-key my-jump-keymap (kbd "g") (lambda () (interactive) (find-file "~/.config")))
(define-key my-jump-keymap (kbd "h") (lambda () (interactive) (find-file "~")))
(define-key my-jump-keymap (kbd "j") (lambda () (interactive) (find-file "~/DCIM/content/aaa--todo.org")))
(define-key my-jump-keymap (kbd "k")
            (lambda () (interactive)
              (find-file (concat user-emacs-directory "emacs--core.org"))))
(define-key my-jump-keymap (kbd "l") #'my/load-theme)
(define-key my-jump-keymap (kbd "r") (lambda () (interactive) (switch-to-buffer "*scratch*")))
(define-key my-jump-keymap (kbd "s") (lambda () (interactive) (find-file "~/source")))
(define-key my-jump-keymap (kbd "w") (lambda () (interactive) (find-file "~/DCIM/content/")))
(define-key my-jump-keymap (kbd "-") #'tab-close)
(global-set-key (kbd "M-;") #'my/quick-window-jump)

hooks-core

Any functions that are run being associated with a mode.

;;
;; -> keys-visual-core
;;
(add-hook 'text-mode-hook 'visual-line-mode)

keys-visual-core

Sets up keybindings for quickly toggling visual features like font, theme, line numbers, and other window displays.

;;
;; -> keys-visual-core
;;
(defvar my-win-keymap (make-sparse-keymap))
(global-set-key (kbd "C-q") my-win-keymap)
(define-key my-win-keymap (kbd "b") #'(lambda () (interactive)(tab-bar-mode 'toggle)))
(define-key my-win-keymap (kbd "c") #'display-fill-column-indicator-mode)
(define-key my-win-keymap (kbd "d") #'window-divider-mode)
(define-key my-win-keymap (kbd "e") #'whitespace-mode)
(define-key my-win-keymap (kbd "f") #'font-lock-mode)
(define-key my-win-keymap (kbd "g") #'global-hl-line-mode)
(define-key my-win-keymap (kbd "h") #'font-lock-update)
(define-key my-win-keymap (kbd "l") #'my/sync-ui-accent-color)
(define-key my-win-keymap (kbd "n") #'display-line-numbers-mode)
(define-key my-win-keymap (kbd "o") #'toggle-centered-buffer)
(define-key my-win-keymap (kbd "p") #'variable-pitch-mode)
(define-key my-win-keymap (kbd "q") #'toggle-menu-bar-mode-from-frame)
(define-key my-win-keymap (kbd "r") #'my/rainbow-mode)
(define-key my-win-keymap (kbd "u") #'set-cursor-color)
(define-key my-win-keymap (kbd "U") #'set-foreground-color)
(define-key my-win-keymap (kbd "B") #'set-background-color)

keys-other-core

Configures a sparse keymap for miscellaneous actions like evaluating expressions and capturing content with Org mode.

;;
;; -> keys-other-core
;;
(global-set-key (kbd "M-s =") #'ediff-buffers)
(global-set-key (kbd "M-s +") #'ediff-regions-linewise)
(global-set-key (kbd "M-h") #'my/mark-block)
(global-set-key (kbd "M-s x") #'diff-buffer-with-file)
(global-set-key (kbd "M-s ;") #'my/copy-buffer-to-kill-ring)
(global-set-key (kbd "M-s /") #'my/find-file)
(global-set-key (kbd "M-s '") #'my/grep)

keybinding-core

Demonstrates a broad set of global keybindings for common actions like saving buffers, controlling text scale, and navigating large documents.

;;
;; -> keybinding-core
;;
(global-set-key (kbd "C-M-l") (lambda () (interactive)
                                (my/adaptive-resize t -2)))
(global-set-key (kbd "C-M-h") (lambda () (interactive)
                                (my/adaptive-resize t 2)))
(global-set-key (kbd "C-M-j") (lambda () (interactive)
                                (my/adaptive-resize nil -1)))
(global-set-key (kbd "C-M-k") (lambda () (interactive)
                                (my/adaptive-resize nil 1)))
(global-set-key (kbd "C--") (lambda ()(interactive)(text-scale-adjust -1)))
(global-set-key (kbd "C-=") (lambda ()(interactive)(text-scale-adjust 1)))
(global-set-key (kbd "C-c a") #'org-agenda)
(global-set-key (kbd "<f12>") #'(lambda ()(interactive)(async-shell-command "do_backup home" "*backup*")))
(global-set-key (kbd "C-c c") #'org-capture)
(global-set-key (kbd "M-[") #'my/shell-menu)
(global-set-key (kbd "C-x C-b") 'ibuffer)
(global-set-key (kbd "C-x [") #'beginning-of-buffer)
(global-set-key (kbd "C-x ]") #'end-of-buffer)
(global-set-key (kbd "C-x k") #'kill-buffer)
(global-set-key (kbd "C-c j") #'(lambda() (interactive)(tab-bar-history-back)(my/repeat-history)))
(global-set-key (kbd "C-c k") #'(lambda() (interactive)(tab-bar-history-forward)(my/repeat-history)))
(global-set-key (kbd "C-x l") #'scroll-lock-mode)
(global-set-key (kbd "C-x v e") 'vc-ediff)
(global-set-key (kbd "C-x x g") #'revert-buffer)
(global-set-key (kbd "C-x x t") #'toggle-truncate-lines)
(global-set-key (kbd "C-z") #'save-buffer)
(global-set-key (kbd "C-;") #'my/comment-or-uncomment)
(global-set-key (kbd "C-1") 'delete-other-windows)
(global-set-key (kbd "M-0") 'delete-window)
(global-set-key (kbd "M-1") #'delete-other-windows)
(global-set-key (kbd "M-2") #'split-window-vertically)
(global-set-key (kbd "M-3") #'split-window-horizontally)
(global-set-key (kbd "C-0") 'delete-window)
(global-set-key (kbd "C-1") #'delete-other-windows)
(global-set-key (kbd "C-2") #'split-window-vertically)
(global-set-key (kbd "C-3") #'split-window-horizontally)
(global-set-key (kbd "M-e") #'dired-jump)
(global-set-key (kbd "M-g i") #'imenu)
(global-set-key (kbd "M-g o") #'org-goto)
(global-set-key (kbd "M-j") #'(lambda ()(interactive)(scroll-up (/ (window-height) 4))))
(global-set-key (kbd "M-k") #'(lambda ()(interactive)(scroll-down (/ (window-height) 4))))
(global-set-key (kbd "M-o") #'bookmark-jump)
(global-set-key (kbd "M-m") #'my/fido-recentf)
(global-set-key (kbd "M-u") #'tab-bar-switch-to-prev-tab)
(global-set-key (kbd "M-i") #'tab-bar-switch-to-next-tab)
(global-set-key (kbd "C-c U") #'my/disk-space-query)
(global-set-key (kbd "M-z") #'visual-line-mode)
(global-set-key (kbd "M-s i") #'my/convert-markdown-clipboard-to-org)
(global-set-key (kbd "M-s u") #'my/org-promote-all-headings)
(global-unset-key (kbd "C-h h"))
(global-unset-key (kbd "C-t"))
(with-eval-after-load 'vc-dir
  (define-key vc-dir-mode-map (kbd "e") #'vc-ediff))
(with-eval-after-load 'diff-mode
  (define-key diff-mode-map (kbd "M-j") #'nil)
  (define-key diff-mode-map (kbd "M-k") #'nil))

modes-core

Turns on various modes like `global-font-lock-mode` for syntax highlighting and `show-paren-mode` for matching parenthesis visualization, and configures preferences for a wide array of basic behaviours and visual indicators.

;;
;; -> modes-core
;;
(tooltip-mode -1)
(column-number-mode 1)
(desktop-save-mode -1)
(display-time-mode -1)
(global-auto-revert-mode t)
(savehist-mode 1)
(show-paren-mode t)
(tab-bar-history-mode 1)
(global-font-lock-mode t)

bell-core

Suppresses the auditory bell function in Emacs and opts for a visible bell or completely ignores bell triggers, improving the user interface experience during invalid operations.

;;
;; -> bell-core
;;
(setq visible-bell t)
(setq ring-bell-function 'ignore)

setqs-core

This broad category includes a wide range of `setq` configurations that modify the behaviour of Emacs’s core features — from file handling to search behaviours, reinforcing the customization of Emacs.

;;
;; -> setqs-core
;;
(setq mouse-highlight nil)
(setq show-help-function nil)
(setq custom-safe-themes t)
(setq enable-local-variables :all)
(setq frame-title-format "%f")
(setq kill-whole-line t)
(setq-default truncate-lines t)
(setq frame-inhibit-implied-resize t)
(setq native-comp-async-report-warnings-errors nil)

confirm-core

Configures aliases and settings for reducing the need for confirmations in repetitive tasks, streamlining user workflows.

;;
;; -> confirm-core
;;
(defalias 'yes-or-no-p 'y-or-n-p)
(setq confirm-kill-emacs 'y-or-n-p)
(setq confirm-kill-processes nil)
(setq confirm-nonexistent-file-or-buffer nil)
(set-buffer-modified-p nil)

backups-core

Adjusts Emacs’s file backup settings for a better experience, specifying backup file locations and policies to prevent data loss while keeping the working directory clean.

;;
;; -> backups-core
;;
(setq make-backup-files 1)
(setq backup-directory-alist '(("." . "~/backup"))
      backup-by-copying t    ; Don't delink hardlinks
      version-control t      ; Use version numbers on backups
      delete-old-versions t  ; Automatically delete excess backups
      kept-new-versions 10   ; how many of the newest versions to keep
      kept-old-versions 5)   ; and how many of the old

custom-settings-core

Places for `custom-set-variables` and `custom-set-faces` used by Emacs’s customization system to record user preferences set through the graphical customize interface.

;;
;; -> custom-settings-core
;;
(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(mode-line ((t (:height 140 :underline nil :overline nil :box nil))))
 '(mode-line-inactive ((t (:height 140 :underline nil :overline nil :box nil))))
 '(org-level-1 ((t (:inherit default :weight regular :height 1.0))))
 '(org-level-2 ((t (:inherit default :weight light :height 1.0))))
 '(org-level-3 ((t (:inherit default :weight light :height 1.0))))
 '(org-level-4 ((t (:inherit default :weight light :height 1.0))))
 '(org-level-5 ((t (:inherit default :weight light :height 1.0))))
 '(org-level-6 ((t (:inherit default :weight light :height 1.0))))
 '(ediff-current-diff-A ((t (:extend t :background "#b5daeb" :foreground "#000000"))))
 '(ediff-even-diff-A ((t (:background "#bafbba" :foreground "#000000" :extend t))))
 '(ediff-fine-diff-A ((t (:background "#f4bd92" :foreground "#000000" :extend t))))
 '(ediff-odd-diff-A ((t (:background "#b8fbb8" :foreground "#000000" :extend t))))
 '(font-lock-warning-face ((t (:foreground "#930000" :inverse-video nil))))
 '(org-link ((t (:underline nil))))
 '(indent-guide-face ((t (:background "#282828" :foreground "#666666"))))
 '(widget-button ((t (:inherit fixed-pitch :weight regular))))
 '(window-divider ((t (:foreground "black"))))
 '(org-tag ((t (:height 0.9))))
 '(vertical-border ((t (:foreground "#000000")))))

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(custom-enabled-themes '(misterioso))
 '(warning-suppress-log-types '((frameset)))
 '(warning-suppress-types '((frameset))))

(set-cursor-color "white")

defun-core

Defines a suite of custom functions to extend Emacs’s functionality tailored to specific tasks or personal preferences, showcasing the extensibility of Emacs with Lisp programming.

;;
;; -> defun-core
;;
(defun save-macro (name)
  "Save a macro by NAME."
  (interactive "SName of the macro: ")
  (kmacro-name-last-macro name)
  (find-file user-init-file)
  (goto-char (point-max))
  (newline)
  (insert-kbd-macro name)
  (newline))

(defun my/comment-or-uncomment ()
  "Comments or uncomments the current line or region."
  (interactive)
  (if (region-active-p)
      (comment-or-uncomment-region
       (region-beginning)(region-end))
    (comment-or-uncomment-region
     (line-beginning-position)(line-end-position))))

(defun my/dired-duplicate-file (arg)
  "Duplicate a file from DIRED with an incremented number.
                                If ARG is provided, it sets the counter."
  (interactive "p")
  (let* ((file (dired-get-file-for-visit))
         (dir (file-name-directory file))
         (name (file-name-nondirectory file))
         (base-name (file-name-sans-extension name))
         (extension (file-name-extension name t))
         (counter (if arg (prefix-numeric-value arg) 1))
         (new-file))
    (while (and (setq new-file
                      (format "%s%s_%03d%s" dir base-name counter extension))
                (file-exists-p new-file))
      (setq counter (1+ counter)))
    (if (file-directory-p file)
        (copy-directory file new-file)
      (copy-file file new-file))
    (dired-revert)))

(defun my/mark-block ()
  "Marking a block of text surrounded by a newline."
  (interactive)
  (when (not (region-active-p))
    (backward-char))
  (skip-chars-forward " \n\t")
  (re-search-backward "^[ \t]*\n" nil 1)
  (skip-chars-forward " \n\t")
  (when (not (region-active-p))
    (push-mark))
  (re-search-forward "^[ \t]*\n" nil 1)
  (skip-chars-backward " \n\t")
  (setq mark-active t))
;;
(defun my/repeat-history ()
  "Set up a transient keymap for navigating tab bar history."
  (interactive)
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "j") (lambda () (interactive)
                                (tab-bar-history-back)))
    (define-key map (kbd "k") (lambda () (interactive)
                                (tab-bar-history-forward)))
    (set-transient-map map t)))

(defun my/get-window-position ()
  "Return the position of the current window as 'left', 'right', 'top', or 'bottom'."
  (let* ((edges (window-edges))
         (min-x (nth 0 edges))
         (min-y (nth 1 edges))
         (max-x (nth 2 edges))
         (max-y (nth 3 edges))
         (frame-width (frame-width))
         (frame-height (frame-height)))
    (cond
     ((<= min-x 0) 'left)
     ((>= max-x frame-width) 'right)
     ((= min-y 0) 'top)
     ((= max-y frame-height) 'bottom)
     (t 'center))))

(defun my/adaptive-resize (horizontal delta)
  "Resize the current window adaptively based on its position.
HORIZONTAL is non-nil for horizontal resizing (left/right).
DELTA is the amount to resize (positive to grow, negative to shrink)."
  (let ((pos (my/get-window-position)))
    (cond
     ((and horizontal (eq pos 'left)) (enlarge-window (- delta) t))
     ((and horizontal (eq pos 'right)) (enlarge-window delta t))
     ((and (not horizontal) (eq pos 'top)) (enlarge-window delta nil))
     ((and (not horizontal) (eq pos 'bottom)) (enlarge-window (- delta) nil))
     (t (enlarge-window delta horizontal)))))

(defun my/dired-du ()
  "Run 'du -hc' and count the total number of files in the directory under
  the cursor in Dired, then display the output in a buffer named *dired-du*."
  (interactive)
  (let ((current-dir (dired-get-file-for-visit)))
    (if (file-directory-p current-dir)
        (let ((output-buffer-name "*dired-du*"))
          (with-current-buffer (get-buffer-create output-buffer-name)
            (erase-buffer)
            (let* ((command (format "du -hc --max-depth=1 %s && echo && echo 'File counts per subdirectory:' && find %s -maxdepth 2 -type d -exec sh -c 'echo -n \"{}: \"; find \"{}\" -type f | wc -l' \\;"
                                    (shell-quote-argument current-dir)
                                    (shell-quote-argument current-dir)))
                   (process (start-process-shell-command "dired-du" output-buffer-name command)))
              (set-process-sentinel process 
                                    (lambda (proc event)
                                      (when (string-match "finished" event)
                                        (with-current-buffer output-buffer-name
                                          (goto-char (point-min))))))
              (pop-to-buffer output-buffer-name))))
      (message "The current point is not a directory."))))

(defun adjust-color (color percent)
  "Adjust COLOR by PERCENT (positive to lighten, negative to darken).
For very dark backgrounds, ensures a minimum visible difference."
  (let* ((rgb (color-values color))
         (factor (/ (+ 100 percent) 100.0))
         (min-increment 4096)  ; minimum increment for very dark colors
         (new-rgb (mapcar (lambda (x)
                            (if (> percent 0)
                                ;; When lightening, ensure minimum increment
                                (max (+ x min-increment)
                                     (round (* x factor)))
                              ;; When darkening, just use factor
                              (max 0 (round (* x factor)))))
                          rgb)))
    (apply 'format "#%02x%02x%02x" (mapcar (lambda (x) (/ x 256)) new-rgb))))

(defun set-simple-hl-line ()
  "Set the hl-line background based on current theme.
Lightens dark themes by 20%, darkens light themes by 5%."
  (interactive)
  (require 'hl-line)
  (unless global-hl-line-mode
    (global-hl-line-mode 1))
  (when (facep 'hl-line)
    (let* ((bg (face-background 'default))
           (is-dark (not (string-greaterp bg "#888888")))
           (adjusted-bg (if is-dark
                            (adjust-color bg 20)
                          (adjust-color bg -5))))
      (custom-set-faces
       `(hl-line ((t (:background ,adjusted-bg))))))))

(defun my/load-theme ()
  "Prompt to select a theme from available themes and load the selected theme."
  (interactive)
  (let ((theme (completing-read "Choose theme: " (mapcar 'symbol-name (custom-available-themes)))))
    (dolist (item custom-enabled-themes)
      (disable-theme item))
    (load-theme (intern theme) t)))

(defun my/html-flush-divs ()
  "Flush the divs in export to improve on Confluence import"
  (interactive)
  (let* ((org-file (buffer-file-name))
         (html-file (concat (file-name-sans-extension org-file) ".html")))
    (with-temp-buffer
      (insert-file-contents html-file)
      (goto-char (point-min))
      (flush-lines "</?div.*>?")
      (write-region (point-min) (point-max) html-file))))

(defun my/html-promote-headers ()
  "Promote all headers in the HTML file by one level (e.g., h2 -> h1, h3 -> h2, etc.), accounting for attributes."
  (interactive)
  (let* ((org-file (buffer-file-name))
         (html-file (concat (file-name-sans-extension org-file) ".html")))
    (with-temp-buffer
      (insert-file-contents html-file)
      (goto-char (point-min))
      (let ((header-levels '("h1" "h2" "h3" "h4" "h5" "h6")))
        (dolist (level header-levels)
          (let* ((current-level (string-to-number (substring level 1)))
                 (new-level (max 1 (1- current-level)))  ;; Promote but don't go below h1
                 (open-tag-regex (format "<%s\\([^>]*\\)>" level))  ;; Regex for opening tag with attributes
                 (close-tag-regex (format "</%s>" level))  ;; Regex for closing tag
                 (new-open-tag (format "<h%d\\1>" new-level))  ;; Replacement for opening tag, preserving attributes
                 (new-close-tag (format "</h%d>" new-level)))  ;; Replacement for closing tag
            ;; Replace opening tags
            (goto-char (point-min))
            (while (re-search-forward open-tag-regex nil t)
              (replace-match new-open-tag))
            ;; Replace closing tags
            (goto-char (point-min))
            (while (re-search-forward close-tag-regex nil t)
              (replace-match new-close-tag)))))
      (write-region (point-min) (point-max) html-file))))

(defun my/copy-buffer-to-kill-ring ()
  "Copy the entire buffer to the kill ring without changing the point."
  (interactive)
  (kill-ring-save (point-min) (point-max))
  (message (concat (buffer-file-name) " Copied")))

(defun my/dired-file-to-org-link ()
  "Transform the file path under the cursor in Dired to an Org mode
  link and copy to kill ring.
  This function transforms the current file path in Dired mode into
  an Org link with attributes for both org-mode and HTML width
  settings. The generated link is then copied to the kill ring for
  easy pasting."
  (interactive)
  (let ((file-path (dired-get-file-for-visit)))
    (if file-path
        (let* ((relative-path (file-relative-name file-path
                                                  (project-root-safe)))
               (org-link (concat "#+attr_org: :width 300px\n"
                                 "#+attr_html: :width 100%\n"
                                 "[[file:" relative-path "]]\n")))
          (kill-new org-link)
          (message "Copied to kill ring: %s" org-link))
      (message "No file under the cursor"))))

(defun my/collate-issues-into-table ()
  "Insert all Org headings in the current buffer into the Org file."
  (interactive)
  (let ((rows '())
        (header '("TODO" "Title" "Parent Title")) ;; Table header
        (issue-tag "issues")) ;; The tag to filter for
    (save-excursion
      (goto-char (point-max)) ;; Ensure we append the results at the end
      (org-map-entries
       (lambda ()
         (let* ((todo (org-get-todo-state))
                (title (org-get-heading t t t t))
                (parent))
           (save-excursion
             (when (org-up-heading-safe) ;; Move to parent heading if it exists
               (setq parent (org-get-heading t t t t))))
           (when (member issue-tag (org-get-tags))
             (setq rows (append rows (list (list (or todo "") title (or parent ""))))))))
       nil 'file))
    (setq rows (reverse rows))
    (push 'hline rows)
    (cons header rows)))

(defun my/kill-ring-save (beg end flash)
  (interactive (if (use-region-p)
                   (list (region-beginning) (region-end) nil)
                 (list (line-beginning-position)
                       (line-beginning-position 2) 'flash)))
  (kill-ring-save beg end))
(global-set-key [remap kill-ring-save] 'my/kill-ring-save)

(defun my/disk-space-query ()
  "Run 'df -h' and display the output in a new buffer."
  (interactive)
  (let ((output-buffer-name "*Disk Space*"))
    (with-current-buffer (get-buffer-create output-buffer-name)
      (erase-buffer)
      (let* ((command "df -h")
             (process (start-process-shell-command "disk-space" output-buffer-name command)))
        (set-process-sentinel process 
                              (lambda (proc event)
                                (when (string-match "finished" event)
                                  (with-current-buffer output-buffer-name
                                    (goto-char (point-min))))))
        (pop-to-buffer output-buffer-name)))))

(defvar highlight-rules
  '((th . (("TODO" . "#999")))
    (td . (("\\&gt" . "#bbb")
           ("-\\&gt" . "#ccc")
           ("- " . "#ddd")
           ("- - - - " . "#eee")
           ("- - - - - - - - " . "#fff")
           ("HDR" . "#ffd")
           ("TODO" . "#fdd")
           ("DOING" . "#ddf")
           ("DONE" . "#dfd"))))
  "Alist of elements ('th or 'td) and associated keywords/colors for row highlighting.")

(defun apply-row-style (row-start row-attributes color)
  "Apply a background COLOR to the row starting at ROW-START with ROW-ATTRIBUTES."
  (goto-char row-start)
  (kill-line)
  (insert (format "<tr%s style=\"background: %s\">\n" row-attributes color)))

(defun highlight-row-by-rules (row-start row-end row-attributes element)
  "Highlight a row based on ELEMENT ('th or 'td) keyword rules within ROW-START to ROW-END."
  (let ((rules (cdr (assoc element highlight-rules))))
    (dolist (rule rules)
      (let ((keyword (car rule))
            (color (cdr rule)))
        (when (save-excursion
                (and (re-search-forward (format "<%s.*>%s.*</%s>" element keyword element) row-end t)
                     (goto-char row-start)))
          (apply-row-style row-start row-attributes color))))))

(defun my/html-org-table-highlight ()
  "Open the exported HTML file, find tables with specific classes,
                                                        and add background styles to rows containing keywords in <td> or <th> elements."
  (interactive)
  (let* ((org-file (buffer-file-name))
         (html-file (concat (file-name-sans-extension org-file) ".html")))
    (with-temp-buffer
      (insert-file-contents html-file)
      (goto-char (point-min))
      (while (re-search-forward "<table.*>" nil t)
        (let ((table-start (point))
              (table-end (save-excursion
                           (when (re-search-forward "</table>" nil t)
                             (point)))))
          (when table-end
            (save-restriction
              (narrow-to-region table-start table-end)
              (goto-char (point-min))
              (while (re-search-forward "<tr\\(.*\\)>" nil t)
                (let ((row-start (match-beginning 0))
                      (row-attributes (match-string 1))
                      (row-end (save-excursion (search-forward "</tr>"))))
                  (highlight-row-by-rules row-start row-end row-attributes 'th)
                  (highlight-row-by-rules row-start row-end row-attributes 'td)))))))
      (write-region (point-min) (point-max) html-file))))

(defun my/html-flush-divs ()
  "Flush the divs in export to improve on Confluence import"
  (interactive)
  (let* ((org-file (buffer-file-name))
         (html-file (concat (file-name-sans-extension org-file) ".html")))
    (with-temp-buffer
      (insert-file-contents html-file)
      (goto-char (point-min))
      (flush-lines "</?div.*>?")
      (write-region (point-min) (point-max) html-file))))

(defun my/format-to-table (&optional match properties-to-display)
  "Format Org headings into a structured alist, optionally filtered by MATCH
   and displaying only specified PROPERTIES-TO-DISPLAY (e.g., '(\"ID\" \"PRIORITY\"))."
  (interactive)
  (let ((rows '())
        (header (list "TODO" "Tags" "Title" "Contents" "Properties")))
    
      (org-map-entries
       (lambda ()
         (let* ((heading (org-get-heading t t t t))  ; Clean heading without TODO/tags
                (level (org-outline-level))
                (tags (org-get-tags))
                (todo (org-get-todo-state))
                (properties (org-entry-properties))
                (contents ""))
           
           ;; Extract first paragraph content
           (save-excursion
             (org-end-of-meta-data t)
             (when (looking-at-p "[ \t]*[^*\n]") ; Not another heading
               (let ((start (point)))
                 (forward-paragraph)
                 (setq contents (string-trim 
                               (replace-regexp-in-string 
                                "[ \t\n]+" " " 
                                (buffer-substring-no-properties start (point))))))))
           
           ;; Format title with indentation
           (let ((formatted-title 
                  (concat (mapconcat (lambda (_) " - ") (make-list (- level 2) nil) "")
                         (cond ((= level 1) "â–¶ ")
                               ((= level 2) "â–· ")
                               (t "• "))
                         heading))
                 ;; Filter properties
                 (filtered-props
                  (if properties-to-display
                      (string-join
                       (delq nil
                             (mapcar (lambda (prop)
                                       (let ((val (cdr (assoc prop properties))))
                                         (when val (format "%s:%s" prop val))))
                                     properties-to-display))
                       " ")
                    "")))
             
             (push (list (or todo "")
                         (if tags (string-join tags ":") "")
                         formatted-title
                         contents
                         filtered-props)
                   rows))))
       match)
    
    (when rows
      (setq rows (reverse rows))
      (push 'hline rows))
    (cons header rows)))

window-positioning-core

Configures rules and behaviours for display-buffer functions to control how new buffers are shown, whether in existing windows or new splits, enhancing window management in Emacs.

;;
;; -> window-positioning-core
;;
(add-to-list 'display-buffer-alist
             '("\\*\\(.*shell\\|.*term.*\\|eldoc.*\\*\\|Flymake.*\\)"
               (display-buffer-reuse-window display-buffer-at-bottom)
               (inhibit-same-window . t)
               (window-height . 0.3)))
(add-to-list 'display-buffer-alist
             '("\\*\\(Completions.*\\)"
               (display-buffer-reuse-window display-buffer-at-bottom)
               (inhibit-same-window . t)
               (window-height . 0.2)))
(add-to-list 'display-buffer-alist
             '("\\*grep"
               (display-buffer-reuse-window display-buffer-in-direction)
               (direction . leftmost)
               (dedicated . t)
               (window-width . 0.4)
               (inhibit-same-window . t)))
(add-to-list 'display-buffer-alist
             '("\\*compilation"
               (display-buffer-reuse-window display-buffer-in-direction)
               (direction . leftmost)
               (dedicated . t)
               (window-width . 0.3)
               (inhibit-same-window . t)))
(add-to-list 'display-buffer-alist
             '("\\*Help\\*"
               (display-buffer-reuse-window display-buffer-same-window)))
(add-to-list 'display-buffer-alist
             '("\\*Async" display-buffer-no-window
               (allow-no-window . t)))
(add-to-list 'display-buffer-alist
             '("\\*Messages" display-buffer-same-window))
(add-to-list 'display-buffer-alist
             '("\\*Process" display-buffer-same-window))
(add-to-list 'display-buffer-alist
             '("\\*vc-dir\\*"
               (display-buffer-same-window)))
(setq kill-buffer-query-functions nil)

org-core

Extends and customizes Org mode for document structuring, note-taking, and project management, highlighting customization options for exporting, appearance, and functionality enhancements.

;;
;; -> org-core
;;
(setq org-table-convert-region-max-lines 9999)
(setq org-src-tab-acts-natively t)
(setq org-log-done t)
(setq org-export-with-sub-superscripts nil)
(setq org-deadline-warning-days 365)
(setq org-image-actual-width (list 50))
(setq org-return-follows-link t)
(setq org-use-fast-todo-selection 'expert)
(setq org-reverse-note-order t)
(setq org-src-preserve-indentation t)
(setq org-cycle-separator-lines 0)
(setq org-edit-src-content-indentation 0)
(setq org-tags-sort-function 'org-string-collate-greaterp)
(setq org-startup-indented t)
(setq org-use-speed-commands t)
(setq org-hide-leading-stars t)
(setq org-todo-keywords
      '((sequence "TODO(t)" "DOING(g)" "ORDR(o)" "SENT(s)" "|" "DONE(n)" "CANCELLED(c)")))
(setq org-todo-keyword-faces
      '(("TODO" . "#ee6273")
        ("DOING" . "#6e8baa")
        ("ORDR" . "#c96eee")
        ("SENT" . "#c86bee")
        ("DONE" . "#77aa66")
        ("CANCELLED" . "#426b3e")))
(setq org-goto-interface 'outline-path-completionp)
(setq org-outline-path-complete-in-steps nil)
(setq org-imenu-depth 1)
(with-eval-after-load 'org
  (define-key org-mode-map (kbd "C-'") #'nil)
  (define-key org-mode-map (kbd "C-,") #'nil))
(setq imenu-flatten t)

org-agenda-core

Customizes the Org Agenda for a personalized task management and calendar view, adjusting settings for diary integration and custom agenda views to fit specific planning needs.

;;
;; -> org-agenda-core
;;
(with-eval-after-load 'org-agenda
  (setq org-agenda-include-diary t)
  (setq org-agenda-show-all-dates t)
  (setq org-refile-targets '((org-agenda-files :maxlevel . 1)))
  (setq org-agenda-custom-commands
        '(("j" "Month View" agenda ""
           ((org-agenda-start-day "today")
            (org-agenda-span 30)
            (org-agenda-time-grid nil)))))
  (defun display-year-agenda (&optional year)
    "Display an agenda entry for a whole year."
    (interactive (list (read-string "Enter the year: "
                                    (format-time-string "%Y" (current-time)))))
    (setq year (string-to-number year))
    (org-agenda-list)
    (org-agenda-year-view year)
    (setq this-year (string-to-number (format-time-string "%Y" (current-time))))
    (when (= year this-year)
      (org-agenda-goto-today)
      (recenter-top-bottom 10))))

(global-set-key (kbd "C-c y") 'display-year-agenda)

scroll-core

Adjusts scrolling behaviours and settings for a smoother navigation experience within buffer contents.

;;
;; -> scroll-core
;;
(setq scroll-margin 10)
(setq scroll-conservatively 10)
(setq scroll-preserve-screen-position t)

dired-core

Enhances Dired, the directory editor, with additional functionalities like async deletion, improving file management workflows within Emacs.

;;
;; -> dired-core
;;
(setq dired-dwim-target t)
(setq dired-listing-switches "-alGgh")
(setq dired-auto-revert-buffer t)
(setq dired-confirm-shell-command nil)
(setq dired-no-confirm t)
(setq dired-deletion-confirmer '(lambda (x) t))
(setq dired-recursive-deletes 'always)

(with-eval-after-load 'dired
  (define-key dired-mode-map (kbd "C") 'dired-copy-file)
  (define-key dired-mode-map (kbd "C-c d") 'my/dired-duplicate-file)
  (define-key dired-mode-map (kbd "C-c u") 'my/dired-du)
  (define-key dired-mode-map (kbd "C-c U") 'my/disk-space-query)
  (define-key dired-mode-map (kbd "C-t d") 'my/image-dired-sort)
  (define-key dired-mode-map (kbd "b") 'my/dired-file-to-org-link)
  (define-key dired-mode-map (kbd "_") #'dired-create-empty-file))

(defun my-dired-switch-to-destination ()
  "Switch to the destination window after copying in Dired."
  (when-let ((dest-window
              (get-window-with-predicate
               (lambda (win)
                 (with-current-buffer (window-buffer win)
                   (and (derived-mode-p 'dired-mode)
                        (not (eq win (selected-window)))))))))
    (select-window dest-window)))

(defadvice dired-sort-toggle-or-edit (after dired-sort-move-to-first-file activate)
  "Move point to the first file or directory after sorting, skipping . and .."
  (goto-char (point-min))
  (dired-next-line 2)  ;; Skip past header and move to first entry
  (while (and (not (eobp))
              (looking-at-p ".*\\.\\.?$"))  ;; Check if line is . or ..
    (dired-next-line 1)))

(advice-add 'dired-do-copy :after (lambda (&rest _) (my-dired-switch-to-destination)))
(advice-add 'dired-do-rename :after (lambda (&rest _) (my-dired-switch-to-destination)))

visuals-core

Configures various visual aspects of Emacs, including menu bar, toolbar, and scroll bar visibility, as well as window transparency and edge padding for a cleaner and more focused editing environment.

;;
;; -> visuals-core
;;
(menu-bar-mode -1)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(setq inhibit-startup-screen t)
(setq use-dialog-box nil)
(setq window-divider-default-bottom-width 2)
(setq window-divider-default-right-width 2)
(setq window-divider-default-places t)
(window-divider-mode -1)
(defvar my/internal-border-width 0 "Default internal border width for toggling.")
(modify-all-frames-parameters `((internal-border-width . ,my/internal-border-width)))
(set-fringe-mode '(20 . 20))
(setq bookmark-set-fringe-mark nil)
(setq bookmark-fringe-mark nil)

(add-hook 'prog-mode-hook #'my/rainbow-mode)
(add-hook 'org-mode-hook #'my/rainbow-mode)
(add-hook 'conf-space-mode-hook #'my/rainbow-mode)

imenu-core

Customizes the Imenu index-building functionality for improved navigation within structured documents or source code, demonstrating regex-based configurations for specific file types.

;;
;; -> imenu-core
;;
(defun my-imenu-create-index ()
  "Create an index using definitions starting with ';; ->'."
  (let ((index-alist '())
        (regex "^;;[[:space:]]->\\(.+\\)$"))
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward regex nil t)
        (let ((name (s-trim (match-string 1)))
              (pos (match-beginning 0)))
          (push (cons name (set-marker (make-marker) pos)) index-alist))))
    (setq imenu--index-alist (sort
                              index-alist
                              (lambda (a b)
                                (string< (car a) (car b)))))))
;;
;; (setq imenu-create-index-function #'my-imenu-create-index)
;;
(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (setq truncate-lines t)
            (setq imenu-sort-function 'imenu--sort-by-name)
            (setq imenu-generic-expression
                  '(
                    ;; Match comment-based section markers: ;; -> Section Name
                    (nil "^;;[[:space:]]+-> \\(.*\\)$" 1)
                    ;; Match function definitions
                    (nil "^\\s-*(defun\\s-+\\([^( \t\n]+\\)" 1)
                    ))
            (imenu-add-menubar-index)))

(add-hook 'conf-space-mode-hook
          (lambda ()
            (setq imenu-sort-function 'imenu--sort-by-name)
            (setq imenu-generic-expression
                  '((nil "^#[[:space:]]+-> \\(.*\\)$" 1)))
            (imenu-add-menubar-index)))

recentf-core

Optimizes the handling of recently opened files list, tweaking preferences for the number of items shown and integration points for quick access to recent files.

;;
;; -> recentf-core
;;
(save-place-mode 1)
(recentf-mode 1)
(setq recentf-max-menu-items 10)
(setq recentf-max-saved-items 10)

(defun my/fido-recentf (arg)
  "Use fido to select from recently opened files.
With universal argument, use the traditional recentf-open-files interface."
  (interactive "P")
  (if arg
      (recentf-open-files)
    (find-file (completing-read "Recent file: " recentf-list nil t nil 'recentf-list))))

grep-core

My aim here is to make rgrep as similar to deadgrep as possible for easier switching back and forth between a more vanilla like emacs experience.

;;
;; -> grep-core
;;
(eval-after-load 'grep
  '(progn
     (dolist (dir '("nas" ".cache" "cache" "elpa" "chromium" ".local/share" "syncthing" ".mozilla" ".local/lib" "Games"))
       (push dir grep-find-ignored-directories))
     (dolist (file '(".cache" "*cache*" "*.iso" "*.xmp" "*.jpg" "*.mp4"))
       (push file grep-find-ignored-files))
     ))

gdb-core

Sets up GDB, the GNU Debugger, integration for debugging within Emacs, tweaking interface elements and keybindings for a more convenient debugging workflow.

;;
;; -> gdb-core
;;
(setq gdb-display-io-nopopup 1)
(setq gdb-many-windows t)
(global-set-key (kbd "<f9>") 'gud-break)
(global-set-key (kbd "<f10>") 'gud-next)
(global-set-key (kbd "<f11>") 'gud-step)

compilation-core

Customizes the Compilation mode for handling output from external commands, adjusting styles, behaviours, and filtering for an improved feedback loop during code build or script execution.

;;
;; -> compilation-core
;;
(setq compilation-always-kill t)
(setq compilation-context-lines 3)
(setq compilation-scroll-output t)
;; ignore warnings
(setq compilation-skip-threshold 2)
(global-set-key (kbd "<f5>") 'my/project-compile)

diff-core

Customizes the appearance and behaviour of diff and merge tools within Emacs, adjusting styles for better readability and control over version control diffs and conflict resolution.

;;
;; -> diff-core
;;
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-highlight-all-diffs t)
(setq ediff-split-window-function 'split-window-horizontally)
(add-hook 'ediff-prepare-buffer-hook #'outline-show-all)
(add-hook 'ediff-prepare-buffer-hook (lambda () (visual-line-mode -1)))

project-core

Customizes Emacs’s project management features for handling multiple projects, demonstrating configurations for project discovery, switching, and build command integration.

;;
;; -> project-core
;;
(require 'project)
(defun project-root-safe ()
  "Return the project root or nil if unavailable."
  (if (fboundp 'project-root)
      ;; Use project-root if available (Emacs 29+)
      (when-let ((project (project-current)))
        (project-root project))
    ;; Compatibility for Emacs < 29
    (when-let ((project (project-current)))
      (cdr (project-roots project)))))

(defun my/project-create-compilation-search-path ()
  "Populate the 'compilation-search-path' variable.
With directories under project root using find."
  (interactive)
  (let ((find-command
         (concat "find " (project-root-safe)
                 " \\( -path \\*/.local -o -path \\*/.config -o
 -path \\*/.svn -o -path \\*/.git -o -path \\*/nas \\) -prune -o
 -type d -print")))
    (setq compilation-search-path
          (split-string
           (shell-command-to-string find-command)
           "\n" t))))

(setq project-vc-extra-root-markers '(".project"))

indentation-core

Defining very specific indentation and highlight guides

;;
;; -> indentation-core
;;
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

(defun indent-whole-buffer ()
  "Indent the entire buffer without affecting point or mark."
  (interactive)
  (save-excursion
    (save-restriction
      (indent-region (point-min) (point-max)))))

(global-set-key (kbd "C-c i") 'indent-whole-buffer)

shell-core

Demonstrates customizations for shell integration within Emacs, optimizing settings for shell modes, command history, and shorthand functions for frequent shell-related tasks.

;;
;; -> shell-core
;;
(defun my/shell-create (name)
  "Create a custom-named eshell buffer with NAME."
  (interactive "sName: ")
  (eshell 'new)
  (let ((new-buffer-name (concat "*eshell-" name "*")))
    (rename-buffer new-buffer-name t)))

(setq eshell-scroll-to-bottom-on-input t)
(setq-local tab-always-indent 'complete)

(org-babel-do-load-languages
 'org-babel-load-languages
 '((shell . t)))

(defun ansi-term-update-mode-line ()
  "Update the mode-line to show whether `ansi-term` is in character or line mode."
  (setq mode-name
        (if (term-in-char-mode)
            "Ansi-Term [Char]"
          "Ansi-Term [Line]"))
  (force-mode-line-update))

(defun enable-ansi-term-mode-line-indicator ()
  "Enable dynamic mode-line indicator for `ansi-term` modes."
  (add-hook 'post-command-hook #'ansi-term-update-mode-line nil t))

(defun disable-ansi-term-mode-line-indicator ()
  "Disable dynamic mode-line indicator for `ansi-term` modes."
  (remove-hook 'post-command-hook #'ansi-term-update-mode-line t))

(add-hook 'term-mode-hook #'enable-ansi-term-mode-line-indicator)
(add-hook 'term-mode-hook
          (lambda ()
            (add-hook 'kill-buffer-hook #'disable-ansi-term-mode-line-indicator nil t)))

(defun my/shell-menu ()
  "Menu for Shell commands."
  (interactive)
  (let ((key (read-key
              (propertize
               "--- Shell Commands [q] Quit: ---
[e] eshell
[s] shell
[a] ansi-term"

               'face 'minibuffer-prompt))))
    (pcase key
      (?e (call-interactively 'my/shell-create))
      (?s (call-interactively 'shell))
      (?a (call-interactively 'ansi-term))
      ;; Quit
      (?q (message "Quit Shell menu."))
      (?\C-g (message "Quit Shell menu."))
      ;; Default Invalid Key
      (_ (message "Invalid key: %c" key)))))

tab-bar-core

Details configurations for Emacs’s tab bar, showcasing customizations for tab behaviour, appearance, and integration points for keyboard navigation and tab management.

;;
;; -> tab-bar-core
;;
(setq tab-bar-close-button-show nil)
(setq tab-bar-new-button-show nil)
(setq tab-bar-new-tab-to 'rightmost)
(setq tab-bar-close-button-show nil)

windows-specific-core

Curates configurations specific to the Windows operating system, adjusting paths, fonts, and environment variables for optimal use of Emacs on Windows.

;;
;; -> windows-specific-core
;;
(when (eq system-type 'windows-nt)
  (setq home-dir "c:/users/jimbo")
  (let ((xPaths
         `(,(expand-file-name "~/bin")
           ,(expand-file-name "~/bin/PortableGit/bin")
           ,(expand-file-name "~/bin/PortableGit/usr/bin")
           ,(expand-file-name "~/bin/Apache-Subversion/bin/")
           ,(expand-file-name "~/bin/svn2git-2.4.0/bin")
           ,(expand-file-name "~/bin/clang/bin")
           ,(expand-file-name "~/bin/find")
           ,(expand-file-name "~/bin/omnisharp-win-x64")
           "c:/GnuWin32/bin"
           "c:/GNAT/2021/bin")))
    (setenv "PATH" (mapconcat 'identity xPaths ";"))
    (setq exec-path (append xPaths (list "." exec-directory))))

  (custom-theme-set-faces
   'user
   '(variable-pitch ((t (:family "Consolas" :height 110 :weight normal))))
   '(fixed-pitch ((t ( :family "Consolas" :height 110)))))

  (setq font-general "Consolas 11")
  (set-frame-font font-general nil t)
  (add-to-list 'default-frame-alist `(font . ,font-general)))

(setq tab-bar-show 1)

linux-specific-core

Curates configurations specific to Linux, making adjustments for paths, fonts, and system integrations ensuring Emacs is well integrated with the Linux desktop environment.

;;
;; -> linux-specific-core
;;
(when (eq system-type 'gnu/linux)
  (custom-theme-set-faces
   'user
   '(variable-pitch ((t (:family "DejaVu Sans" :height 120 :weight normal))))
   '(fixed-pitch ((t ( :family "Source Code Pro" :height 110)))))
  (setq font-general "MonoSpace 12")
  (set-frame-font font-general nil t)
  (add-to-list 'default-frame-alist `(font . ,font-general))
  )

programming-core

;;
;; -> programming-core
;;
;;

;; import shell variables
(defun import-shell-environment (env-var)
  "Import a specific ENV-VAR from the shell and set it inside Emacs."
  (let ((value (shell-command-to-string
                (concat "bash --login -c 'echo $" env-var "'"))))
    (when value
      (setenv env-var value)
      (when (string= env-var "PATH") ;; Special handling for PATH
        (setq exec-path (split-string value path-separator))))))

(import-shell-environment "PATH")

;; org babel
(require 'org)
(org-babel-do-load-languages
 'org-babel-load-languages '((C . t)))

(defun my/eglot-dir-locals ()
  "Create .dir-locals.el file for eglot ada-mode using the selected DIRED path."
  (interactive)
  (add-dir-local-variable
   'ada-mode
   'eglot-workspace-configuration
   `((ada . (:projectFile ,(dired-get-filename))))))

(setq vc-handled-backends '(SVN Git))

(global-set-key (kbd "C-c f") 'my/selective-display-fold)

(defun my/selective-display-fold (&optional level)
  "Fold text indented same of more than the cursor.
If level is set, set the indent level to LEVEL.
If 'selective-display' is already set to LEVEL, clicking
F5 again will unset 'selective-display' by setting it to 0."
  (interactive "P")
  (if (eq selective-display (1+ (current-column)))
      (set-selective-display 0)
    (set-selective-display (or level (1+ (current-column))))))

(defun concatenate-project-files (root-dir output-file &optional exclude-patterns)
  "Traverse ROOT-DIR recursively and concatenate all text files into OUTPUT-FILE.
Each file is prefixed with a header showing its relative path.
EXCLUDE-PATTERNS is an optional list of regex patterns to exclude files/directories."
  (interactive "DRoot directory: \nFOutput file: ")
  (let ((default-exclude '("\\.git/" "node_modules/" "\\.DS_Store" "\\.pyc$" "\\.o$" "\\.so$" "\\.gitconfig"
                           "\\.jpg$" "\\.jpeg$" "\\.png$" "\\.gif$" "\\.pdf$" "\\.zip$" "\\.csproj" "\\.gitignore"
                           "\\.tar$" "\\.gz$" "\\.exe$" "\\.dll$" "\\.class$")))
    (setq exclude-patterns (append default-exclude exclude-patterns))
    
    (with-temp-buffer
      (insert (format "=== PROJECT CONCATENATION ===\n"))
      (insert (format "Root Directory: %s\n" root-dir))
      (insert (format "Generated: %s\n\n" (current-time-string)))
      
      (let ((file-count 0))
        (dolist (file (concatenate-project-files--get-text-files root-dir exclude-patterns))
          (let ((relative-path (file-relative-name file root-dir)))
            (insert (format "\n" ))
            (insert (format "=== FILE: %s ===\n" relative-path))
            (insert (format "\n"))
            
            (condition-case err
                (progn
                  (insert-file-contents file)
                  (goto-char (point-max))
                  (insert "\n")
                  (setq file-count (1+ file-count)))
              (error 
               (insert (format "ERROR: Could not read file - %s\n" (error-message-string err)))))))
        
        (insert (format "\n=== END OF CONCATENATION ===\n"))
        (insert (format "Total files processed: %d\n" file-count)))
      
      (write-region (point-min) (point-max) output-file)
      (message "Concatenated %d files from %s to %s" 
               (length (concatenate-project-files--get-text-files root-dir exclude-patterns))
               root-dir output-file))))

(defun concatenate-project-files--get-text-files (dir exclude-patterns)
  "Get all text files in DIR recursively, excluding files matching EXCLUDE-PATTERNS."
  (let ((files '()))
    (dolist (file (directory-files-recursively dir ".*" nil))
      (unless (concatenate-project-files--should-exclude-p file exclude-patterns)
        (when (concatenate-project-files--is-text-file-p file)
          (push file files))))
    (sort files 'string<)))

(defun concatenate-project-files--should-exclude-p (file exclude-patterns)
  "Check if FILE should be excluded based on EXCLUDE-PATTERNS."
  (catch 'excluded
    (dolist (pattern exclude-patterns)
      (when (string-match pattern file)
        (throw 'excluded t)))
    nil))

(defun concatenate-project-files--is-text-file-p (file)
  "Check if FILE is likely a text file by examining its content."
  (and (file-regular-p file)
       (file-readable-p file)
       (> (nth 7 (file-attributes file)) 0) ; file size > 0
       (condition-case nil
           (with-temp-buffer
             (insert-file-contents file nil 0 1024) ; read first 1KB
             (not (string-match-p "[\000-\010\016-\037]" (buffer-string))))
         (error nil))))

;; Convenience function for git projects
(defun concatenate-git-project (git-root output-file)
  "Concatenate all text files in a git project, respecting .gitignore patterns."
  (interactive "DGit project root: \nFOutput file: ")
  (let ((gitignore-patterns (concatenate-project-files--parse-gitignore 
                             (expand-file-name ".gitignore" git-root))))
    (concatenate-project-files git-root output-file gitignore-patterns)))

(defun concatenate-project-files--parse-gitignore (gitignore-file)
  "Parse .gitignore file and return list of regex patterns."
  (when (file-exists-p gitignore-file)
    (with-temp-buffer
      (insert-file-contents gitignore-file)
      (let ((patterns '()))
        (goto-char (point-min))
        (while (not (eobp))
          (let ((line (string-trim (thing-at-point 'line t))))
            (when (and (> (length line) 0)
                       (not (string-prefix-p "#" line)))
              ;; Convert gitignore pattern to regex
              (let ((pattern (replace-regexp-in-string "\\*" ".*" line)))
                (push pattern patterns))))
          (forward-line 1))
        patterns))))

ada-core

Taken originally from :

https://github.com/sebastianpoeplau/ada-light-mode

Unfortunately this may be the only elisp package that I may need to bake into this whole concept. Current ada-mode isn’t supported out of the box in Emacs, it is suggested to use the AdaCore ada-mode but that is a pain to compile (mainly due to trying to install gnatcoll), I have found the old ada mode used by Emacs for that adequate, so this may need to be separately downloaded and baked in.

A lightweight Ada mode for Emacs

ada-light-mode is a very light alternative to =ada-mode=. It depends only on the compat library for compatibility with older Emacs versions, and it aims to be easy to set up, fast, and reliable.

Features

  • Highlight reserved words of the language
  • Identify comments and strings, so that you can use the usual commands for (un)commenting, as well as Emacs features for spell-checking, URL detection, etc.
  • Imenu support for subprograms, packages, and types

You can combine ada-light-mode with the [Ada language server](https://github.com/AdaCore/ada/language/server) for more advanced features (see below).

Language server integration

The Ada language server can enhance ada-light-mode with LSP features like documentation lookup, jump-to-definition, refactoring, and on-the-fly error checking. This section documents how to set it up with =eglot=; the alternative =lsp-mode= should work too but will require additional work.

First, install eglot (e.g., via M-x package-install RET eglot RET) and make sure that the ada/language/server binary is on your PATH. Then, create a .dir-locals.el file in your project to tell the language server where to find the Ada project definition:

;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((ada-light-mode . ((eglot-workspace-configuration . (:ada
                                                      (:projectFile "/path/to/project.gpr"))))))

Finally, open a source file and run M-x eglot to start the language server.

When eglot is active, indentation uses the language server’s formatting capabilities to indent code (i.e., =textDocument.rangeFormatting=). It actually does a bit more than that, possibly breaking the to-be-indented line up into multiple lines if that’s how the language server suggests to format it. Note that the Ada language server sometimes modifies code beyond the current line in response to such a request. This can be especially confusing when you simply inserted a newline - automatic indentation of the just-finished line triggers the undesired behavior. In such cases, you can insert the newline with C-j to circumvent automatic indentation.

The Ada language server uses on-type formatting to insert space characters whenever you type a newline. This behavior doesn’t work well with Emacs’ own indentation logic; the language server’s space characters end up after point. You may want to disable on-type formatting to work around this issue:

(push :documentOnTypeFormattingProvider eglot-ignored-server-capabilities)

The Ada language server exposes a custom command als-other-file that lets you jump between specification and body files; use it with `M-x ada-light-other-file= (after starting =eglot`) or bind the command to a key for easy access.

;;
;; -> ada-core
;;
(defvar ada-light-mode-keywords
  ;; https://www.adaic.org/resources/add_content/standards/05rm/html/RM-2-9.html
  '("abort" "else" "new" "return" "abs" "elsif" "not" "reverse" "abstract" "end"
    "null" "accept" "entry" "select" "access" "exception" "of" "separate"
    "aliased" "exit" "or" "subtype" "all" "others" "synchronized" "and" "for"
    "out" "array" "function" "overriding" "tagged" "at" "task" "generic"
    "package" "terminate" "begin" "goto" "pragma" "then" "body" "private" "type"
    "if" "procedure" "case" "in" "protected" "until" "constant" "interface"
    "use" "is" "raise" "declare" "range" "when" "delay" "limited" "record"
    "while" "delta" "loop" "rem" "with" "digits" "renames" "do" "mod" "requeue"
    "xor")
  "Keywords of the Ada 2012 language.")
;;
(defvar ada-light-mode--font-lock-rules
  (list (regexp-opt ada-light-mode-keywords 'symbols))
  "Rules for search-based fontification in `ada-light-mode'.
The format is appropriate for `font-lock-keywords'.")
;;
(defvar ada-light-mode-syntax-table     ; used automatically by define-derived-mode
  (let ((table (make-syntax-table)))
    ;; Comments start with "--".
    (modify-syntax-entry ?- ". 12" table)
    ;; Newlines end comments.
    (modify-syntax-entry ?\n ">" table)
    (modify-syntax-entry ?\r ">" table)
    ;; Backslash is a regular symbol, not an escape character.
    (modify-syntax-entry ?\\ "_" table)
    table)
  "Syntax table used in `ada-light-mode'.")
;;
(defvar ada-light-mode-other-file-alist
  '(("\\.ads\\'" (".adb"))
    ("\\.adb\\'" (".ads")))
  "Value for `ff-other-file-alist' in `ada-light-mode'.")
;;
(defun ada-light-mode--syntax-propertize (start end)
  "Apply syntax properties to the region from START to END."
  ;; Ada delimits character literals with single quotes, but also uses the
  ;; single quote for other purposes. Since character literals are always
  ;; exactly one character long (i.e., there are no escape sequences), we can
  ;; easily find them with a regular expression and change the syntax class of
  ;; the enclosing single quotes to "generic string". This also nicely handles
  ;; the case of '"': generic string delimiters only match other generic string
  ;; delimiters, but not ordinary quote characters (i.e., the double quote).
  (goto-char start)
  (while-let ((pos (re-search-forward "'.'" end t)))
    (put-text-property (- pos 3) (- pos 2) 'syntax-table '(15))
    (put-text-property (- pos 1) pos 'syntax-table '(15))))
;;
(defvar ada-light-mode--imenu-rules
  `(("Functions"
     ,(rx bol
          (* space)
          (? (? "not" (* space)) "overriding" (* space))
          "function"
          (+ space)
          (group (+ (or word (syntax symbol)))))
     1)
    ("Procedures"
     ,(rx bol
          (* space)
          (? (? "not" (* space)) "overriding" (* space))
          "procedure"
          (+ space)
          (group (+ (or word (syntax symbol)))))
     1)
    ("Types"
     ,(rx bol
          (* space)
          (? "sub")
          "type"
          (+ space)
          (group (+ (or word (syntax symbol)))))
     1)
    ("Packages"
     ,(rx bol
          (* space)
          "package"
          (+ space)
          (group (+ (or word (syntax symbol))))
          (+ space)
          "is")
     1))
  "Imenu configuration for `ada-light-mode'.
The format is appropriate for `imenu-generic-expression'.")
;;
(defun ada-light-mode--indent-line ()
  "Indent a single line of Ada code."
  ;; This is a really dumb implementation which just indents to the most recent
  ;; non-empty line's indentation. It's better than the default though because
  ;; it stops there, so that users who want completion on TAB can get it after
  ;; indenting. (The default behavior is to insert TAB characters indefinitely.)
  (let ((indent (save-excursion
                  (beginning-of-line)
                  (if (re-search-backward "^[^\n]" nil t) ; non-empty line
                      (current-indentation)
                    0))))
    (if (<= (current-column) (current-indentation))
        (indent-line-to indent)
      (when (< (current-indentation) indent)
        (save-excursion (indent-line-to indent))))))
;;
;;;###autoload
(define-derived-mode ada-light-base-mode prog-mode "AdaLBase"
  "Base mode for `ada-light-mode' and `gpr-light-mode'."
  ;; Set up commenting; Ada uses "--" followed by two spaces.
  (setq-local comment-use-syntax t
              comment-start "--"
              comment-padding 2)
  ;; Set up fontification.
  (setq-local font-lock-defaults '(ada-light-mode--font-lock-rules nil t)
              syntax-propertize-function #'ada-light-mode--syntax-propertize)
  ;; And finally, configure indentation. Since our indentation function isn't
  ;; particularly good, don't force it upon the user.
  (setq-local standard-indent 3
              tab-width 3               ; used by eglot for range formatting
              indent-line-function 'ada-light-mode--indent-line
              electric-indent-inhibit t))
;;
;;;###autoload
(define-derived-mode ada-light-mode ada-light-base-mode "AdaL"
  "Major mode for the Ada programming language.
It doesn't define any keybindings. In comparison with `ada-mode',
`ada-light-mode' is faster but less accurate."
  (setq-local ff-other-file-alist ada-light-mode-other-file-alist
              imenu-generic-expression ada-light-mode--imenu-rules))
;;
;;;###autoload
(define-derived-mode gpr-light-mode ada-light-base-mode "GPRL"
  "Major mode for GPR project files."
  :syntax-table ada-light-mode-syntax-table)
;;
;; Register the mode for Ada code following GNAT naming conventions.
;;;###autoload
(progn (add-to-list 'auto-mode-alist '("\\.ad[abcs]\\'" . ada-light-mode))
       (add-to-list 'auto-mode-alist '("\\.gpr\\'" . gpr-light-mode)))
;;
;; Configure eglot if available.
(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs '((ada-light-mode :language-id "ada")
                                        "ada_language_server"))
  ;; The Ada Language Server doesn't support formatting .gpr files, but it
  ;; provides completion and detects syntax errors.
  (add-to-list 'eglot-server-programs '((gpr-light-mode :language-id "ada")
                                        "ada_language_server" "--language-gpr"))
  (defun ada-light-other-file ()
    "Jump from spec to body or vice versa using the Ada Language Server."
    (interactive)
    (if-let ((server (eglot-current-server)))
        (eglot-execute-command server
                               "als-other-file"
                               (vector (eglot--TextDocumentIdentifier)))
      (message "%s" "Not connected to the Ada Language Server")))
  ;; The "als-other-file" command used by `ada-light-other-file' requires
  ;; support for the "window/showDocument" server request in eglot; add it if
  ;; necessary.
  (unless (cl-find-method 'eglot-handle-request nil '(t (eql window/showDocument)))
    (cl-defmethod eglot-handle-request
      (_server (_method (eql window/showDocument)) &key uri &allow-other-keys)
      (find-file (eglot--uri-to-path uri))
      (list :success t)))
  ;;
  (defun ada-light-mode--current-line-empty-p ()
    (save-excursion
      (beginning-of-line)
      (looking-at-p (rx (* space) eol))))
  ;;
  (defun ada-light-mode--indent-line-eglot ()
    "Indent the current line using the Ada Language Server."
    (interactive)
    (if (ada-light-mode--current-line-empty-p)
        ;; Let's not "indent" empty lines with the language server, it would
        ;; just delete them. Instead, take a guess at the required indentation
        ;; based on the most recent non-empty line.
        (indent-relative t t)
      (condition-case err
          (eglot-format (line-beginning-position) (line-end-position))
        ;; When `eglot-format' fails due to a server issue it signals the
        ;; underlying `jsonrpc-error'. In this case, let's return normally to
        ;; give completion a chance.
        (jsonrpc-error
         (when-let ((msg (alist-get 'jsonrpc-error-message (cdr err))))
           (message "Language server error: %s" msg))
         nil))))
  ;;
  (defun ada-light-mode--eglot-setup ()
    "Set up `eglot' integration for `ada-light-mode'."
    (when (eq major-mode 'ada-light-mode)
      (if (eglot-managed-p)
          (setq-local indent-line-function 'ada-light-mode--indent-line-eglot
                      electric-indent-inhibit nil)
        (setq-local indent-line-function 'ada-light-mode--indent-line
                    electric-indent-inhibit t))))
  ;;
  (add-hook 'eglot-managed-mode-hook #'ada-light-mode--eglot-setup))
;;
(provide 'ada-light-mode)

development-core

;;
;; -> development-core
;;
(defun my-icomplete-copy-candidate ()
  "Copy the current Icomplete candidate to the kill ring."
  (interactive)
  (let ((candidate (car completion-all-sorted-completions)))
    (when candidate
      (kill-new (substring-no-properties candidate))
      (abort-recursive-edit))))

(define-key minibuffer-local-completion-map (kbd "C-c ,") 'my-icomplete-copy-candidate)

(add-to-list 'display-buffer-alist
             '("\\*my-rg-results"
               (display-buffer-reuse-window display-buffer-in-direction)
               (direction . leftmost)
               (dedicated . t)
               (window-width . 0.33)
               (inhibit-same-window . t)))

(defun without-gc (&rest args)
  (let ((gc-cons-threshold most-positive-fixnum))
    (apply args)))

(setq ispell-personal-dictionary (concat user-emacs-directory "Emacs-vanilla/my-dictionary"))

(define-key help-map (kbd "=") #'describe-char)
(define-key help-map (kbd "j") #'describe-face)
(define-key help-map (kbd "-") #'describe-keymap)

(defun show-file-path ()
  "Show the full path of the current buffer's file in the minibuffer."
  (interactive)
  (message (buffer-file-name)))

(global-set-key (kbd "C-c p") 'show-file-path)

image-dired

Customizes the behaviour and appearance of Image-Dired, the image management extension of Dired, streamlining the browsing and manipulation of image files.

;;
;; -> image-dired
;;
(require 'image-mode)
(require 'image-dired)

(add-to-list 'display-buffer-alist
             '("\\*image-dired\\*"
               display-buffer-in-direction
               (direction . left)
               (window . root)
               (window-width . 0.5)))

(add-to-list 'display-buffer-alist
             '("\\*image-dired-display-image\\*"
               display-buffer-in-direction
               (direction . right)
               (window . root)
               (window-width . 0.5)))

(defun my/image-dired-sort (arg)
  "Sort images in various ways given ARG."
  (interactive "P")
  ;; Use `let` to temporarily set `dired-actual-switches`
  (let ((dired-actual-switches
         (cond
          ((equal arg nil)            ; no C-u
           "-lGghat --ignore=*.xmp")
          ((equal arg '(4))           ; C-u
           "-lGgha --ignore=*.xmp")
          ((equal arg 1)              ; C-u 1
           "-lGgha --ignore=*.xmp"))))
    (let ((w (selected-window)))
      (delete-other-windows)
      (revert-buffer)
      (image-dired default-directory)
      (let ((idw (selected-window)))
        (select-window w)
        (dired-unmark-all-marks)
        (select-window idw)
        (image-dired-display-this)
        (image-dired-line-up-dynamic)))))

(setq image-use-external-converter t)
(setq image-dired-external-viewer "/usr/bin/gthumb")
(setq image-dired-show-all-from-dir-max-files 999)
(setq image-dired-thumbs-per-row 999)
(setq image-dired-thumb-relief 0)
(setq image-dired-thumb-margin 5)
(setq image-dired-thumb-size 150)

(defun my/image-save-as ()
  "Save the current image buffer as a new file."
  (interactive)
  (let* ((file (buffer-file-name))
         (dir (file-name-directory file))
         (name (file-name-nondirectory file))
         (base-name (file-name-sans-extension name))
         (extension (file-name-extension name t))
         (initial_mode major-mode)
         (counter 1)
         (new-file))
    (while (and (setq new-file
                      (format "%s%s_%03d%s" dir base-name counter extension))
                (file-exists-p new-file))
      (setq counter (1+ counter)))
    (write-region (point-min) (point-max) new-file nil 'no-message)
    (revert-buffer nil t nil)
    ;; (delete-file file t)
    (if (equal initial_mode 'image-dired-image-mode)
        (progn
          (image-dired ".")
          (image-dired-display-this))
      (find-file new-file t))))

(defun my/delete-current-image-and-move-to-next ()
  "Delete the current image file and move to the next image in the directory."
  (interactive)
  (let ((current-file (buffer-file-name)))
    (when current-file
      (image-next-file 1)
      (delete-file current-file)
      (message "Deleted %s" current-file))))

(defun my/delete-current-image-thumbnails ()
  "Delete the current image file and move to the next image in the directory."
  (interactive)
  (let ((file-name (image-dired-original-file-name)))
    (delete-file file-name)
    (image-dired-delete-char)
    (image-dired-display-this)))

(eval-after-load 'image-mode
  '(progn
     (define-key image-mode-map (kbd "C-d") 'my/delete-current-image-and-move-to-next)
     (define-key image-mode-map (kbd "C-x C-s") 'my/image-save-as)))

(eval-after-load 'image-dired
  '(progn
     (define-key image-dired-thumbnail-mode-map (kbd "C-d") 'my/delete-current-image-thumbnails)
     (define-key image-dired-thumbnail-mode-map (kbd "n")
                 (lambda ()(interactive)(image-dired-forward-image)(image-dired-display-this)))
     (define-key image-dired-thumbnail-mode-map (kbd "p")
                 (lambda ()(interactive)(image-dired-backward-image)(image-dired-display-this)))
     ))

dwim

Demonstrates “Do What I Mean” functionalities custom to Emacs, streamlining operations like conversion, searching, and executing context-aware actions.

;;
;; -> dwim
;;
(when (file-exists-p "/home/jdyer/bin/category-list-uniq.txt")
  (progn
    (defvar my/dwim-convert-commands
      '("ConvertNoSpace" "AudioConvert" "AudioInfo" "AudioNormalise"
        "AudioTrimSilence" "PictureAutoColour" "PictureConvert"
        "PictureCrush" "PictureFrompdf" "PictureInfo" "PictureMontage"
        "PictureOrganise" "PictureCrop" "PictureRotateFlip" "PictureEmail"
        "PictureUpdateFromCreateDate" "VideoTag2XMP"
        "PictureRotateLeft" "PictureRotateRight" "PictureScale"
        "PictureUpscale" "PictureGetText" "PictureOrientation"
        "PictureUpdateToCreateDate" "VideoConcat" "VideoConvert" "VideoConvertToGif"
        "VideoCut" "VideoDouble" "VideoExtractAudio" "VideoExtractFrames"
        "VideoFilter" "VideoFromFrames" "VideoInfo" "VideoRemoveAudio"
        "VideoReplaceVideoAudio" "VideoRescale" "VideoReverse"
        "VideoRotate" "VideoRotateLeft" "VideoRotateRight" "VideoShrink"
        "VideoSlowDown" "VideoSpeedUp" "VideoZoom" "WhatsAppConvert"
        "PictureCorrect" "Picture2pdf" "PictureTag" "PictureTagRename" "PictureTagAndRenameEmacs"
        "OtherTagDate" "VideoRemoveFlips" "PictureFixWhatsApp")
      "List of commands for dwim-convert.")

    (defvar my/org-dired-marked-files nil
      "Stores the current dired marked files.")

    (defun my/read-lines (file-path)
      "Return a list of lines of a file at FILE-PATH."
      (with-temp-buffer
        (insert-file-contents file-path)
        (split-string (buffer-string) "\n" t)))

    (defun my/dwim-convert-generic-menu (command)
      "Execute a dwim-shell-command-on-marked-files with the given COMMAND."
      (let* ((unique-text-file "/home/jdyer/bin/category-list-uniq.txt")
             (user-selection nil)
             (files my/org-dired-marked-files)
             (command-and-files (concat command " " (mapconcat 'identity files " "))))
        (prin1 files)
        (when (string= command "PictureTag")
          (setq user-selection (completing-read "Choose an option: "
                                                (my/read-lines unique-text-file)
                                                nil t)))
        (async-shell-command (if user-selection
                                 (concat command " " user-selection " " (mapconcat 'identity files " "))
                               (concat command " " (mapconcat 'identity files " ")))
                             "*convert*")))
    ;; (save-buffers-kill-terminal))

    (defun my/dwim-convert-with-selection-files-command (files-string chosen-command)
      "Prompt user to choose command and execute dwim-shell-command-on-marked-files."
      (interactive)
      (setq my/org-dired-marked-files (split-string files-string ";" t))
      (my/dwim-convert-generic-menu chosen-command))

    (defun my/dwim-convert-with-selection-files (files-string)
      "Prompt user to choose command and execute dwim-shell-command-on-marked-files."
      (interactive)
      (setq my/org-dired-marked-files (split-string files-string ";" t))
      (let ((chosen-command (completing-read "Choose command: "
                                             my/dwim-convert-commands)))
        (my/dwim-convert-generic-menu chosen-command)))
    
    (global-set-key (kbd "C-c v")
                    (lambda ()
                      (interactive)
                      (let ((files (my/get-files-from-context)))
                        (when files
                          (let ((files-string (mapconcat 'identity files ";")))
                            (my/dwim-convert-with-selection-files files-string))))))))

(defun my/image-dired-get-original-files ()
  "Get original file paths from image-dired marked thumbnails or current thumbnail."
  (let ((files '())
        (continue t))
    (save-excursion
      (goto-char (point-min))
      ;; Move to first image if not already on one
      (condition-case nil
          (unless (image-dired-image-at-point-p)
            (image-dired-forward-image))
        (error nil))
      
      ;; Collect all marked files
      (while (and continue
                  (condition-case nil
                      (image-dired-image-at-point-p)
                    (error nil)))
        (when (condition-case nil
                  (image-dired-thumb-file-marked-p)
                (error nil))
          (let ((orig-file (condition-case nil
                               (image-dired-original-file-name)
                             (error nil))))
            (when orig-file
              (push orig-file files))))
        ;; Move to next image, stop if we can't move forward
        (let ((current-pos (point)))
          (condition-case nil
              (image-dired-forward-image)
            (error nil))
          ;; If we didn't move, we're at the end
          (when (= current-pos (point))
            (setq continue nil)))))
    
    ;; If no marked files found, get current file
    (when (and (null files) 
               (condition-case nil
                   (image-dired-image-at-point-p)
                 (error nil)))
      (let ((orig-file (condition-case nil
                           (image-dired-original-file-name)
                         (error nil))))
        (when orig-file
          (push orig-file files))))
    
    (nreverse files)))

(defun my/get-files-from-context ()
  "Get files based on current context (dired, image-dired, etc.)"
  (cond
   ;; In image-dired thumbnail mode
   ((eq major-mode 'image-dired-thumbnail-mode)
    (my/image-dired-get-original-files))
   
   ;; In dired mode
   ((eq major-mode 'dired-mode)
    (dired-get-marked-files))
   
   ;; In image-dired display mode
   ((eq major-mode 'image-dired-display-image-mode)
    (list (image-dired-original-file-name)))
   
   ;; Default: try to get current file
   (t
    (when buffer-file-name
      (list buffer-file-name)))))

(defun my/universal-picture-tag ()
  "Tag pictures from any context (dired or image-dired)."
  (interactive)
  (let ((files (my/get-files-from-context)))
    (if files
        (let ((files-string (mapconcat 'identity files ";")))
          (my/dwim-convert-with-selection-files-command files-string "PictureTag"))
      (message "No files found to tag"))))

(defun my/universal-picture-tag-rename ()
  "Tag and rename pictures from any context (dired or image-dired)."
  (interactive)
  (let ((files (my/get-files-from-context)))
    (if files
        (let ((files-string (mapconcat 'identity files ";")))
          (my/dwim-convert-with-selection-files-command files-string "PictureTag")
          (my/dwim-convert-with-selection-files-command files-string "PictureTagRename"))
      (message "No files found to tag and rename"))))

(defun my/picture-tag-rename-and-update ()
  "Run PictureTag, PictureTagRename, and PictureUpdateFromCreateDate in sequence."
  (interactive)
  (let* ((files (my/get-files-from-context))
         (files-string (when files (mapconcat 'identity files ";"))))
    (if files-string
        (progn
          ;; First, tag the pictures
          (message "Step 1/3: Tagging pictures...")
          (my/dwim-convert-with-selection-files-command files-string "PictureTag")
          (sit-for 1)  ; Brief pause between operations
          
          ;; Then rename based on tags
          (message "Step 2/3: Renaming pictures...")
          (my/dwim-convert-with-selection-files-command files-string "PictureUpdateFromCreateDate")

          (sit-for 1)
          
          ;; Finally update dates
          (message "Step 3/3: Updating dates from CreateDate...")
          (my/dwim-convert-with-selection-files-command files-string "PictureTagRename")
          
          (message "All operations completed!"))
      (message "No files found to process"))))

(global-set-key (kbd "C-t t") 'my/picture-tag-rename-and-update)

;; Set up keybindings for both dired and image-dired
(defun my/setup-picture-keybindings ()
  "Set up consistent keybindings for picture operations."
  (local-set-key (kbd "C-t t") 'my/picture-tag-rename-and-update))

;; Apply to both modes
(add-hook 'dired-mode-hook 'my/setup-picture-keybindings)
(add-hook 'image-dired-thumbnail-mode-hook 'my/setup-picture-keybindings)

;; Optional: Add menu items
(defun my/add-picture-menu-items ()
  "Add picture tagging items to the menu."
  (when (featurep 'easymenu)
    (easy-menu-add-item
     nil '("Tools")
     ["Tag Pictures" my/universal-picture-tag
      :help "Tag selected pictures"]
     "Games")
    (easy-menu-add-item
     nil '("Tools")
     ["Tag and Rename Pictures" my/universal-picture-tag-rename
      :help "Tag and rename selected pictures"]
     "Games")))

(add-hook 'dired-mode-hook 'my/add-picture-menu-items)
(add-hook 'image-dired-thumbnail-mode-hook 'my/add-picture-menu-items)

publishing-core

A soft wrapper around the Emacs basic publishing mechanism for my own exporting pleasure!

;;
;; -> publishing-core
;;
(defun my/export-menu ()
  "Menu for Export/Publishing commands."
  (interactive)
  (let ((key (read-key
              (propertize
               "--- Export Commands [q] Quit: ---
[w] Export to HTML (with table highlighting)
[d] Export to DOCX (via ODT)"
               'face 'minibuffer-prompt))))
    (pcase key
      (?w (progn
            (org-html-export-to-html)
            (my/html-promote-headers)
            (my/html-org-table-highlight)
            (my/html-flush-divs)))
      (?d (progn
            (org-odt-export-to-odt)
            (async-shell-command
             (concat "libreoffice --headless --convert-to docx "
                     (file-name-with-extension
                      (file-name-nondirectory (buffer-file-name))
                      "odt")) "*create-docs*")))
      ;; Quit
      (?q (message "Quit Export menu."))
      (?\C-g (message "Quit Export menu."))
      ;; Default Invalid Key
      (_ (message "Invalid key: %c" key)))))

(global-set-key (kbd "C-c e") 'my/export-menu)

emacs-enhanced

The defun replacements which are loaded from the generated .el file which will be tangled separately.

;;
;; -> core-configuration
;;
(load-file "~/.emacs.d/Emacs-DIYer/init.el")

startup

Functions to run

;;
;; -> dwim
;;
;;
(my/sync-ui-accent-color "coral")

Testing

Created an emacs-vanilla.desktop file containing the following:

[Desktop Entry]
Name=Emacs Core
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=emacs --init-directory=~/.emacs.d.core
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupNotify=true
StartupWMClass=Emacs
Keywords=emacs;
Actions=new-window;new-instance;

[Desktop Action new-window]
Name=New Window
Exec=emacs --init-directory=~/.emacs.d.core

[Desktop Action new-instance]
Name=New Instance
Exec=emacs --init-directory=~/.emacs.d.core %F

and run the basic core Emacs config and test the new features with the Exec changing to different emacs versions as built in the script in this repository : build-emacs-versions.sh

About

A stripped-down version of my primary Emacs configuration, it is designed to leverage only the default built-in features of Emacs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published