1
0
Files
nix-shared/home/develop/emacs/custom.el
Timo Bingmann a0b0357466 home/develop/emacs: add yasnippet snippets and make snippet dirs configurable
This commit makes two significant changes to the Emacs configuration:

1. Refactors emacs.nix to use proper NixOS module structure:
   - Converts from simple configuration to a module with options/config
   - Adds `tb.emacs.extraSnippetDirs` option to allow per-machine snippet
     directories (defaults to ~/.emacs.d/snippets/)
   - Moves yasnippet configuration from custom.el to nix-generated elisp,
     enabling dynamic snippet directory configuration via Nix

2. Adds a comprehensive collection of yasnippet snippets for multiple modes:
   - C++ mode: 50 snippets for common patterns (class, namespace, operators,
     copy/move semantics, debug helpers, documentation, threading primitives)
   - CC mode (shared C/C++): for, switch, main, include guards, etc.
   - CMake mode: if/else, foreach, function, macro, project boilerplate
   - LaTeX mode: document structure, beamer frames, figures, lists
   - Perl mode: package boilerplate, shebang
   - Python/Shell/Nix/Protobuf/Java/CSS/Dockerfile modes: horizontal rules
     and common patterns
   - Text mode: modeline snippets for locale settings

The snippets use yasnippet's inheritance system via .yas-parents files,
so c++-mode inherits from cc-mode and text-mode, reducing duplication.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 22:42:14 -08:00

1208 lines
45 KiB
EmacsLisp

;; ============================================================================
;; SECTION 1: CORE EMACS CONFIGURATION
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Startup & Initial Setup
;; -----------------------------------------------------------------------------
;; Disable welcome startup screen
(setq-default inhibit-startup-screen t)
;; Make all "yes or no" prompts show "y or n" instead
(fset 'yes-or-no-p 'y-or-n-p)
;; Disable annoying bell
(setq ring-bell-function 'ignore)
;; Skip package.el entirely when using Nix
(setq package-enable-at-startup nil)
(setq package-archives nil)
(when (and (boundp 'use-package-ensure-function)
(getenv "NIX_PROFILES"))
(setq use-package-ensure-function 'ignore)
(setq use-package-always-ensure nil))
;; -----------------------------------------------------------------------------
;; --- Editor Defaults
;; -----------------------------------------------------------------------------
;; Text editing fundamentals
(setq-default fill-column 90)
(setq-default indent-tabs-mode nil) ; Use spaces instead of tabs
(setq sentence-end-double-space nil) ; Single space after sentence
(setq truncate-lines t) ; Don't wrap lines
;; Buffer and editing behavior
(setq column-number-mode t) ; Show column numbers
(setq size-indication-mode t) ; Show file size
(setq blink-cursor-mode nil) ; No blinking cursor
(setq undo-limit 8000000) ; Increase undo limits
(setq undo-outer-limit 120000000)
(setq undo-strong-limit 120000000)
;; Change whitespace which makes searching much better in texts with newlines.
(setq search-whitespace-regexp "[ \t\r\n]+"
replace-lax-whitespace t
replace-regexp-lax-whitespace t)
;; Parentheses matching
(show-paren-mode t)
(setq show-paren-style 'mixed)
;; Tree-sitter support
(setq treesit-max-buffer-size 4194304000)
;; -----------------------------------------------------------------------------
;; --- File & Backup Management
;; -----------------------------------------------------------------------------
;; Centralize all temporary files
(defconst emacs-tmp-dir (format "%s/%s%s/" temporary-file-directory "emacs" (user-uid)))
(setq backup-directory-alist `((".*" . ,emacs-tmp-dir)))
(setq auto-save-file-name-transforms `((".*" ,emacs-tmp-dir t)))
(setq auto-save-list-file-prefix emacs-tmp-dir)
(setq image-dired-dir (format "%s/image-dired" emacs-tmp-dir))
;; Backup and autosave settings
(setq make-backup-files nil) ; Disable backup files
(setq auto-save-default nil ; Disable auto-save
auto-save-interval 3000
auto-save-timeout 60)
;; Lock file management
(setq lock-file-name-transforms
'(("\\`/.*/\\([^/]+\\)\\'" "/tmp/\\1" t)))
;; -----------------------------------------------------------------------------
;; --- Remote File Access (Tramp)
;; -----------------------------------------------------------------------------
(setq remote-file-name-inhibit-auto-save-visited t)
(setq remote-file-name-inhibit-cache nil)
(setq remote-file-name-inhibit-locks t)
(setq tramp-auto-save-directory emacs-tmp-dir)
(setq tramp-copy-size-limit (* 1024 1024))
(setq tramp-persistency-file-name (format "%s/tramp" emacs-tmp-dir))
(setq tramp-use-scp-direct-remote-copying t)
(setq tramp-use-ssh-controlmaster-options nil)
(setq tramp-verbose 2)
(connection-local-set-profile-variables
'remote-direct-async-process
'((tramp-direct-async-process . t)))
(connection-local-set-profiles
'(:application tramp :protocol "scp")
'remote-direct-async-process)
(setq magit-tramp-pipe-stty-settings 'pty)
(with-eval-after-load 'tramp
(with-eval-after-load 'compile
(remove-hook 'compilation-mode-hook #'tramp-compile-disable-ssh-controlmaster-options)))
;; Disable version control for remote files
(setq vc-ignore-dir-regexp
(format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp))
;; ============================================================================
;; SECTION 2: USER INTERFACE
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Frame and Window Appearance
;; -----------------------------------------------------------------------------
;; Disable GUI elements
(tool-bar-mode -1)
(scroll-bar-mode -1)
;(menu-bar-mode -1)
;; Frame title format: "filename [mode]"
(setq frame-title-format '("%f [mode: %m]"))
;; Load theme
(load-theme 'grandshell t)
;; -----------------------------------------------------------------------------
;; --- Scrolling Configuration
;; -----------------------------------------------------------------------------
(setq redisplay-dont-pause t) ; Scroll line by line
(setq scroll-margin 2) ; Lines at top/bottom
(setq scroll-preserve-screen-position nil)
(setq scroll-step 4) ; Keyboard scroll lines
(setq smooth-scroll-margin 3)
(setq smooth-scroll-strict-margins 't)
;; Mouse wheel settings
(setq mouse-wheel-scroll-amount '(4 ((shift) . 4))) ; four line at a time
(setq mouse-wheel-progressive-speed 't) ; accelerate scrolling
(setq mouse-wheel-follow-mouse 't) ; scroll window under mouse
;; -----------------------------------------------------------------------------
;; --- Color Theme and Faces
;; -----------------------------------------------------------------------------
(defun set-faces (&rest face-specs)
"Set faces using custom-set-faces format but without customize."
(dolist (spec face-specs)
(face-spec-set (car spec) (cadr spec))))
(set-faces
;; Core faces
'(default ((t (:foreground "white" :background "black"))))
'(cursor ((t (:background "palegoldenrod"))))
'(region ((t (:background "blue3"))))
'(fringe ((t (:background "gray10"))))
'(border ((t (:background "gray20"))))
'(minibuffer-prompt ((t (:foreground "cyan"))))
'(secondary-selection ((t (:background "SkyBlue4"))))
;; Font styles
'(bold ((t (:weight bold))))
'(italic ((t (:slant italic))))
'(bold-italic ((t (:slant italic :weight bold))))
'(underline ((t (:underline "dark gray"))))
'(shadow ((t (:foreground "gray60"))))
'(link ((t (:foreground "turquoise" :underline t))))
;; Status indicators
'(success ((t (:foreground "green"))))
'(warning ((t (:foreground "orange"))))
'(error ((t (:foreground "red"))))
;; Mode line
'(mode-line ((t (:foreground "white" :background "gray10" :height 135))))
'(mode-line-buffer-id ((t (:foreground "white" :background "gray10"))))
'(mode-line-inactive ((t (:foreground "white" :background "gray10"))))
'(modeline ((t (:foreground "white" :background "gray10"))))
;; Search and highlighting
'(isearch ((t (:foreground "brown4" :background "palevioletred2"))))
'(isearch-fail ((t (:background "black" :inherit font-lock-warning-face :inverse-video t))))
'(lazy-highlight ((t (:foreground "cyan" :background "black" :inverse-video t))))
'(match ((t (:foreground "dodgerblue" :background "black" :inverse-video t))))
'(highlight ((t (:inverse-video nil :background "#222299"))))
;; Syntax highlighting
'(font-lock-builtin-face ((t (:foreground "LightSteelBlue"))))
'(font-lock-comment-face ((t (:foreground "chocolate1"))))
'(font-lock-comment-delimiter-face ((t (:inherit font-lock-comment-face :foreground "chocolate1"))))
'(font-lock-constant-face ((t (:foreground "Aquamarine"))))
'(font-lock-doc-face ((t (:inherit font-lock-string-face))))
'(font-lock-function-name-face ((t (:foreground "turquoise"))))
'(font-lock-keyword-face ((t (:foreground "SteelBlue1"))))
'(font-lock-preprocessor-face ((t (:foreground "Orchid"))))
'(font-lock-string-face ((t (:foreground "salmon"))))
'(font-lock-type-face ((t (:foreground "SeaGreen1"))))
'(font-lock-variable-name-face ((t (:foreground "LightGoldenrod"))))
'(font-lock-warning-face ((t (:foreground "Red" :weight bold))))
;; Parentheses
'(show-paren-match ((t (:foreground "#FFE200" :background nil :slant italic :weight bold))))
'(show-paren-mismatch ((t (:background "black" :inherit font-lock-warning-face :inverse-video t))))
;; Whitespace
'(trailing-whitespace ((t (:background "red" :underline nil))))
'(column-marker-1 ((t (:background "dark red" :weight bold))))
;; Widgets and UI elements
'(header-line ((t (:inherit mode-line :foreground "magenta" :background nil))))
'(widget-button ((t (:underline t))))
'(widget-field ((t (:background "gray20" :box (:line-width 1 :color "gray60")))))
'(which-func ((t (:foreground "dodgerblue" :background nil :weight bold))))
;; Custom faces
'(custom-variable-tag ((t (:foreground "dodgerblue"))))
'(custom-group-tag ((t (:foreground "dodgerblue"))))
'(custom-state-tag ((t (:foreground "green"))))
;; Mode-specific faces will be defined with their modes below...
)
;; -----------------------------------------------------------------------------
;; --- Presentation Mode Functions
;; -----------------------------------------------------------------------------
(defun my-presentation-font ()
"Set the default font to be BIG (for presentations)."
(interactive)
(set-face-font 'default "-*-terminus-medium-r-*-*-28-*-*-*-*-*-*-*"))
(defun my-presentation-light-font ()
"Set the default font to be BIG (for presentations) and switch to light theme."
(interactive)
(set-face-font 'default "-*-terminus-medium-r-*-*-28-*-*-*-*-*-*-*")
(disable-theme 'mytheme)
(disable-theme 'grandshell)
(load-theme 'leuven t))
;; Resize the whole frame, and not only a window
;; Adapted from https://stackoverflow.com/a/24714383/5103881
(defun acg/zoom-frame (&optional amt frame)
"Increaze FRAME font size by amount AMT. Defaults to selected
frame if FRAME is nil, and to 1 if AMT is nil."
(interactive "p")
(let* ((frame (or frame (selected-frame)))
(font (face-attribute 'default :font frame))
(size (font-get font :size))
(amt (or amt 1))
(new-size (+ size amt)))
(set-frame-font (font-spec :size new-size) t `(,frame))
(message "Frame's font new size: %d" new-size)))
(defun acg/zoom-frame-out (&optional amt frame)
"Call `acg/zoom-frame' with negative argument."
(interactive "p")
(acg/zoom-frame (- (or amt 1)) frame))
;; ============================================================================
;; SECTION 3: EXTERNAL TOOLS & INTEGRATIONS
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Spell Checking (Hunspell)
;; -----------------------------------------------------------------------------
(setq ispell-program-name "hunspell")
(setq flyspell-issue-welcome-flag nil)
(setq ispell-local-dictionary "en_US")
(setq ispell-local-dictionary-alist
'((nil ; default
"[A-Za-z]" "[^A-Za-z]" "[']" t ("-d" "en_US") nil utf-8)
("en_US" ; Yankee English
"[A-Za-z]" "[^A-Za-z]" "[']" t ("-d" "en_US") nil utf-8)
("en_GB" ; British English
"[A-Za-z]" "[^A-Za-z]" "[']" t ("-d" "en_GB") nil utf-8)
("de_DE"
"[a-zäöüßA-ZÄÖÜ]" "[^a-zäöüßA-ZÄÖÜ]" "['-]" t ("-d" "de_DE") nil utf-8)
))
;; -----------------------------------------------------------------------------
;; --- Compilation and Build Tools
;; -----------------------------------------------------------------------------
(require 'compile)
(setq compilation-disable-input nil
compilation-last-buffer nil
compilation-scroll-output t
compilation-always-kill t
mode-compile-always-save-buffer-p t)
;; Grep configuration
(setq grep-command "grep -nH "
grep-find-ignored-directories '(".svn" ".git" ".hg" ".bzr" "extlib" "b" "build"))
;; -----------------------------------------------------------------------------
;; --- Diff and Merge Tools
;; -----------------------------------------------------------------------------
(setq ediff-autostore-merges t)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
;; Additional diff faces
(set-faces
'(diff-added ((t (:foreground "green"))))
'(diff-changed ((t (:foreground "violet"))))
'(diff-removed ((t (:foreground "orange"))))
'(diff-header ((t (:foreground "cyan" :background nil))))
'(diff-file-header ((t (:foreground "dodgerblue" :background nil))))
'(diff-hunk-header ((t (:foreground "magenta"))))
'(diff-refine-removed ((t (:inherit magit-diff-removed-highlight :foreground "red1"))))
'(diff-refine-added ((t (:inherit magit-diff-added-highlight :foreground "deepskyblue"))))
'(diff-hl-change ((t (:foreground "dodgerblue" :background "midnightblue"))))
'(diff-hl-delete ((t (:foreground "pink" :background "maroon"))))
'(diff-hl-insert ((t (:foreground "green" :background "darkgreen"))))
'(diff-refine-added ((t (:foreground "medium spring green" :background "#003300" :inherit magit-diff-added-highlight))))
'(diff-refine-removed ((t (:foreground "#ffbbbb" :background "#330000" :inherit magit-diff-removed-highlight))))
)
;; ============================================================================
;; SECTION 4: PACKAGE MANAGEMENT & CORE EXTENSIONS
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Navigation & Completion
;; -----------------------------------------------------------------------------
;; Ido - Interactive completion
(use-package ido
:init
(setq ido-everywhere t
ido-enable-flex-matching t
ido-virtual-buffers t
ido-use-faces t
ido-show-dot-for-dired t
ido-default-file-method 'selected-window
ido-default-buffer-method 'selected-window
ido-auto-merge-delay-time 0.5
ido-auto-merge-work-directories-length -1)
:config
(ido-mode)
;; Ido faces
(set-faces
'(ido-first-match ((t (:foreground "gold" :weight bold))))
'(ido-only-match ((t (:foreground "green"))))
'(ido-subdir ((t (:foreground "yellow")))))
)
;; Smex - M-x enhancement
(use-package smex
:bind (("M-x" . smex))
:init
(setq smex-save-file "~/.emacs.d/smex-items")
:config
(smex-initialize))
;; Company - completion framework
(use-package company
:config
(global-company-mode))
;; -----------------------------------------------------------------------------
;; --- Text Editing Enhancements
;; -----------------------------------------------------------------------------
;; Multiple cursors
(use-package iedit
:bind (("C-\\" . iedit-mode)))
;; Expand region semantically
(use-package hippie-exp
:bind (("M-/" . hippie-expand))
:config
(setq hippie-expand-try-functions-list
'(try-expand-dabbrev
try-expand-dabbrev-all-buffers
try-expand-dabbrev-from-kill
try-complete-file-name-partially
try-complete-file-name
try-expand-all-abbrevs
try-expand-list
try-expand-line)))
;; Clean up whitespace
(use-package ethan-wspace
:ensure t
:diminish ethan-wspace-mode
:config
;; Enable globally
(global-ethan-wspace-mode 1)
;; Customize what to clean
(setq mode-require-final-newline nil)) ; ethan-wspace handles this
;; Rainbow delimiters
(use-package rainbow-delimiters)
;; Smooth scrolling
(use-package smooth-scrolling)
;; -----------------------------------------------------------------------------
;; --- Navigation & Bookmarks
;; -----------------------------------------------------------------------------
;; Visible bookmarks
(use-package bm
:bind (("<f2>" . bm-next)
("<S-f2>" . bm-previous)
("<M-f2>" . bm-toggle))
:config
(set-faces
'(bm-face ((t (:background "#604000"))))
'(bm-fringe-face ((t (:background "DarkOrange1")))))
)
;; Go to last change
(use-package goto-last-change
:bind (("C-M-l" . goto-last-change)))
;; -----------------------------------------------------------------------------
;; --- Search Tools
;; -----------------------------------------------------------------------------
;; Ripgrep integration
(use-package rg
:ensure t
:config
;; Set custom command line flags to ignore specific directories
(setq rg-command-line-flags
'("--multiline"
"--glob=!build/"
"--glob=!extlib/"
"--glob=!doxygen-html/"))
(rg-enable-default-bindings)
(rg-define-search rg-dwim-current-dir-no-ask
"Search for literal in files matching the current file under the current directory."
:query ask
:format literal
:files current
;:dir current
:confirm never)
:bind (("C-c C-s" . rg-dwim-current-dir-no-ask)))
;; -----------------------------------------------------------------------------
;; --- File Management
;; -----------------------------------------------------------------------------
;; Enhanced dired
(defun my-dired-mouse-find-file (event)
"In dired, visit the file or directory name you click on."
(interactive "e")
(let (window pos file)
(save-excursion
(setq window (posn-window (event-end event))
pos (posn-point (event-end event)))
(if (not (windowp window))
(error "No file chosen"))
(set-buffer (window-buffer window))
(goto-char pos)
(setq file (dired-get-file-for-visit)))
(if (file-directory-p file)
(or (and (cdr dired-subdir-alist)
(dired-goto-subdir file))
(progn
(select-window window)
(dired file)))
(select-window window)
(find-file (file-name-sans-versions file t)))))
(use-package dired
:ensure nil
:init
(setq dired-dwim-target t)
:bind (:map dired-mode-map
;; backspace
([backspace] . dired-up-directory)
;; for some reason mouse-2 = left click (mouse-1)
([mouse-2] . 'my-dired-mouse-find-file)
([M-mouse-2] . 'diredp-mouse-find-file-other-frame)
;; copy and paste of files.
("M-w" . dired-ranger-copy)
("C-w" . dired-ranger-move)
("C-y" . dired-ranger-paste)))
;; Colorful dired
(use-package diredfl
:ensure t
:hook (dired-mode . diredfl-mode)
:config
(diredfl-global-mode 1)
;; Dired faces
(set-faces
;; ;; dired+ mode TODO
;; '(diredp-dir-priv ((t (:foreground "#74FFFF"))))
;; '(diredp-file-name ((t (:foreground "white"))))
;; '(diredp-dir-name ((t (:foreground "#74FFFF"))))
;; '(diredp-omit-file-name ((t (:inherit diredp-ignored-file-name :strike-through nil))))
'(diredfl-dir-name ((t (:foreground "#74FFFF" :background "black"))))
'(diredfl-dir-priv ((t (:foreground "#74FFFF" :background "black"))))
'(diredfl-exec-priv ((t (:foreground "#8F7B61" :background "black"))))
'(diredfl-file-name ((t nil)))
'(diredfl-no-priv ((t (:background "black"))))
'(diredfl-read-priv ((t (:foreground "#b96285" :background "black"))))
'(diredfl-write-priv ((t (:foreground "#45bF49" :background "black")))))
)
;; Enhanced buffer menu
(defun my-ibuffer-keys ()
"Modify keymaps used by `ibuffer'."
(local-set-key (kbd "<down-mouse-1>") 'ibuffer-visit-buffer))
(add-hook 'ibuffer-hook 'my-ibuffer-keys)
;; -----------------------------------------------------------------------------
;; --- Version Control (Git)
;; -----------------------------------------------------------------------------
;; Magit - Git interface
(use-package magit
:init
(setq magit-diff-arguments '("--ignore-all-space")
magit-log-arguments '("--graph" "--color" "--decorate" "-n100")
magit-no-confirm '(stage-all-changes unstage-all-changes amend-published)
magit-pull-arguments nil
magit-refs-show-commit-count 'all
magit-status-buffer-switch-function 'switch-to-buffer
magit-diff-refine-hunk 'all
git-link-use-commit t)
:bind (("<C-f12>" . magit-status)
("<S-f12>" . magit-blame-addition)
("<M-f12>" . magit-log-buffer-file))
:config
;; Magit faces
(set-faces
'(magit-diff-added ((t (:foreground "#33ff33"))))
'(magit-diff-added-highlight ((t (:inherit magit-diff-added :weight normal))))
'(magit-diff-removed ((t (:inherit diff-changed :foreground "red"))))
'(magit-diff-removed-highlight ((t (:inherit magit-diff-removed :weight normal))))
'(magit-section-highlight ((t (:foreground "goldenrod")))))
)
(add-hook 'magit-status-mode-hook
(lambda () (define-key magit-status-mode-map "\M-2"
(lambda() (interactive) (make-frame)))))
;; Enable spell checking in commit messages
(add-hook 'git-commit-mode-hook 'turn-on-flyspell)
;; -----------------------------------------------------------------------------
;; --- Project Management
;; -----------------------------------------------------------------------------
;; Projectile
(use-package projectile
:ensure t
:init
(projectile-mode +1)
:bind (:map projectile-mode-map
("C-c p" . projectile-command-map))
:config
(setq projectile-project-search-path '("~/ebay/"))
(setq projectile-switch-project-action 'projectile-dired))
;; Directory environment variables
(use-package direnv
:config
(direnv-mode))
;; -----------------------------------------------------------------------------
;; --- Utility Packages
;; -----------------------------------------------------------------------------
;; Diminish - hide minor modes
(use-package diminish)
;; AI coding assistant
(use-package aidermacs
:bind (("C-c a" . aidermacs-transient-menu)))
;; ============================================================================
;; SECTION 5: PROGRAMMING SUPPORT
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Language Server Protocol (LSP)
;; -----------------------------------------------------------------------------
(use-package eglot
:config
(setq eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))
(add-to-list 'eglot-server-programs
`((scala-mode scala-ts-mode)
. ,(alist-get 'scala-mode eglot-server-programs)))
:bind (:map eglot-mode-map
("C-c e r" . eglot-rename)
("S-<tab>" . company-complete))
:hook ((python-mode . eglot-ensure)
(scala-mode . eglot-ensure)
(scala-ts-mode . eglot-ensure)
(typescript-ts-mode . eglot-ensure))
:config
;; Eglot faces
(set-faces
'(eglot-highlight-symbol-face ((t (:background "#502500" :inherit bold))))
'(eglot-inlay-hint-face ((t (:height 0.8 :foreground "IndianRed4" :inherit shadow)))))
)
(add-hook 'eglot-managed-mode-hook
(lambda ()
(eglot-inlay-hints-mode -1)))
;; -----------------------------------------------------------------------------
;; --- C/C++ Configuration
;; -----------------------------------------------------------------------------
;; C/C++ indentation settings
(setq c-basic-offset 4)
(setq c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "k&r")))
(setq c-offsets-alist '((inline-open . +) (innamespace . +)))
(setq c-tab-always-indent nil)
;; Hook for all c-like programming modes.
(defun tb-c-common-hook ()
"Common setup for C-like modes."
;; Enable `direnv` environment switching.
;(direnv-update-environment)
;; flyspell mode for comments
(flyspell-prog-mode)
;; Enable eglot.
(eglot-ensure)
(setq paragraph-start "^[ ]*\\(//+\\|\\**\\)[ ]*\\([ ]*$\\|@\\(param\\|return\\|throw\\)\\)\\|^\f"))
(add-hook 'c-mode-common-hook 'tb-c-common-hook)
(use-package cc-mode
:bind (:map c++-mode-map
("C-c C-s" . rg-dwim-current-dir-no-ask)))
;; -----------------------------------------------------------------------------
;; --- Python Configuration
;; -----------------------------------------------------------------------------
(use-package python-mode
:defer t
:bind (:map python-mode-map
("C-c C-s" . rg-dwim-current-dir-no-ask)))
;; -----------------------------------------------------------------------------
;; --- Scala Configuration
;; -----------------------------------------------------------------------------
(add-to-list 'major-mode-remap-alist '(scala-mode . scala-ts-mode))
;; Hook for Scala programming modes.
(defun tb-scala-common-hook ()
"Common setup for Scala modes."
;; Enable `direnv` environment switching.
;(direnv-update-environment)
;; flyspell mode for comments
(flyspell-prog-mode)
;; Enable eglot.
(eglot-ensure)
(setq paragraph-start "^[ ]*\\(//+\\|\\**\\)[ ]*\\([ ]*$\\|@\\(param\\|return\\|throw\\)\\)\\|^\f"))
(add-hook 'scala-mode-hook 'tb-scala-common-hook)
(add-hook 'scala-ts-mode-hook 'tb-scala-common-hook)
;; Scala3 compilation patterns
(add-to-list 'compilation-error-regexp-alist 'scala3-error)
(add-to-list 'compilation-error-regexp-alist-alist
'(scala3-error
"^\\[error\\].*?: \\(.*\\.scala\\):\\([0-9]+\\):\\([0-9]+\\)"
1 2 3))
(add-to-list 'compilation-error-regexp-alist 'scala3-warn)
(add-to-list 'compilation-error-regexp-alist-alist
'(scala3-warn
"^\\[warn\\] -- Warning: \\(.*\\.scala\\):\\([0-9]+\\):\\([0-9]+\\)"
1 2 3))
;; Compilation warning face
(set-faces
'(compilation-warning ((t (:foreground "magenta" :inherit warning)))))
;; -----------------------------------------------------------------------------
;; --- Perl Configuration
;; -----------------------------------------------------------------------------
;; Perl configuration
(defalias 'perl-mode 'cperl-mode)
(setq cperl-continued-brace-offset -4)
(setq cperl-continued-statement-offset 4)
(setq cperl-extra-newline-before-brace nil)
(setq cperl-extra-newline-before-brace-multiline nil)
(setq cperl-indent-level 4)
(setq cperl-indent-parens-as-block t)
(setq cperl-label-offset -4)
(setq cperl-merge-trailing-else nil)
;; Perl faces
(set-faces
'(cperl-array-face ((t (:foreground "#5555ff" :weight bold))))
'(cperl-hash-face ((t (:foreground "orange" :slant italic :weight bold)))))
;; -----------------------------------------------------------------------------
;; --- Typescript Languages
;; -----------------------------------------------------------------------------
(use-package typescript-ts-mode
:ensure t
:after eglot
:config
(setq typescript-ts-mode-indent-offset 4))
;; -----------------------------------------------------------------------------
;; --- Other Programming Languages
;; -----------------------------------------------------------------------------
;; Quick loading of various modes
(use-package arduino-mode :defer t)
(use-package basic-mode :defer t)
(use-package bison-mode :defer t)
(use-package cmake-mode :defer t)
(use-package coffee-mode :defer t)
(use-package csharp-mode :defer t)
(use-package groovy-mode
:defer t
:bind (:map groovy-mode-map
("C-c C-s" . ripgrep)))
(use-package haskell-mode :defer t)
(use-package jinja2-mode :defer t)
(use-package lua-mode :defer t)
(use-package nix-mode :defer t)
(use-package php-mode :defer t)
(use-package protobuf-mode :defer t)
(use-package qml-mode :defer t)
;; Shell script configuration
(setq sh-indent-after-continuation 'always)
;; Custom Cassini mode
(define-generic-mode
'cassini-text-mode
'("//" "#")
'("false" "true" "container" "query")
'(("+" . 'font-lock-operator)
("-" . 'font-lock-operator)
("*" . 'font-lock-operator)
("/" . 'font-lock-operator)
("==" . 'font-lock-operator)
("!=" . 'font-lock-operator)
("<" . 'font-lock-operator)
(">" . 'font-lock-operator)
("<=" . 'font-lock-operator)
(">=" . 'font-lock-operator)
(";" . 'font-lock-builtin)
("[+-]?\\([0-9]*\\.[0-9]+\\|[0-9]+\\.[0-9]*\\)\\([eE][+-]?[0-9]+\\)?" . 'font-lock-constant-face)
("\\b0x[0-9a-fA-F]+\\b" . 'font-lock-constant-face)
("\\b[0-9]+\\b" . 'font-lock-constant-face)
("\\b[A-Za-z_][A-Za-z0-9_]*:" . 'font-lock-variable-name-face)
("\\b\\([A-Za-z_][A-Za-z0-9_]*\\)[:space:]*[({]" 1 'font-lock-function-name-face))
'("\\.cql\\.txt$"
"\\.cql$")
nil
"A mode for Cassini Text files")
;; ============================================================================
;; SECTION 6: DOCUMENT & TEXT MODES
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Markup Languages
;; -----------------------------------------------------------------------------
;; Markdown
(use-package markdown-mode
:mode ("\\.md\\'" . markdown-mode)
:hook (markdown-mode . (lambda ()
(visual-line-mode 1)
(flyspell-mode)
(set-variable 'fill-column 100000)))
:config
(set-faces
'(markdown-header-face-1 ((t (:inherit org-level-1))))
'(markdown-header-face-2 ((t (:inherit org-level-2))))
'(markdown-header-face-3 ((t (:inherit org-level-3))))
'(markdown-header-face-4 ((t (:inherit org-level-4))))))
;; Web modes
(use-package web-beautify)
(use-package yaml-mode)
(use-package apache-mode :defer t)
(use-package nginx-mode :defer t)
(use-package dockerfile-mode :defer t)
(use-package csv-mode :defer t)
(use-package pandoc-mode :defer t)
;; Web-related settings
(setq css-indent-offset 2)
(setq js-indent-level 2)
;; File associations
(add-to-list 'auto-mode-alist '("\\.htt\\'" . html-mode))
(add-to-list 'auto-mode-alist '("\\.blog\\'" . html-mode))
;; -----------------------------------------------------------------------------
;; --- LaTeX Configuration
;; -----------------------------------------------------------------------------
(use-package latex
:defer t
:ensure auctex
:config
;; BibTeX settings
(setq bibtex-comma-after-last-field t)
(setq bibtex-maintain-sorted-entries 'crossref)
;; Compilation settings
(setq TeX-command-force "LaTeX")
(setq TeX-command-list
'(("LaTeX" "%`~/.emacs.d/flymake-pdflatex -shell-escape --synctex=1 %(mode)%' %t" TeX-run-TeX nil
(latex-mode doctex-mode) :help "Run LaTeX")
("Makeinfo" "makeinfo %(extraopts) %t" TeX-run-compile nil
(texinfo-mode) :help "Run Makeinfo with Info output")
("BibTeX" "bibtex %s" TeX-run-BibTeX nil t :help "Run BibTeX")
("Biber" "biber %s" TeX-run-Biber nil t :help "Run Biber")
("View" "%V" TeX-run-discard-or-function t t :help "Run Viewer")
("Clean" "TeX-clean" TeX-run-function nil t :help "Delete generated intermediate files")
("Clean All" "(TeX-clean t)" TeX-run-function nil t :help "Delete generated intermediate and output files")
("Other" "" TeX-run-command t t :help "Run an arbitrary command")))
:bind (:map LaTeX-mode-map
("C-c C-a" . (lambda (arg) (interactive "P")
(let ((TeX-command-force nil)) (TeX-command-master arg))))
("C-M-r" . reftex-reference)
("C-M-c" . reftex-citation)
("M-S-<down>" . reftex-toc))
:config
;; TeX faces
(set-faces
'(TeX-fold-unfolded-face ((t (:background "#151823"))))))
(defun tb-latex-common-hook ()
"Add some latex macro keys"
(interactive)
(turn-on-reftex)
(visual-line-mode 1)
;; show frames in section list -> very useful for beamer presentations
(setq reftex-section-levels
(cons '("begin{frame}" . 3) reftex-section-levels))
;; auto folding of tikzpicture and algorithm environments in tex files
(TeX-fold-mode 0)
(add-hook 'find-file-hook 'TeX-fold-buffer t))
(add-hook 'TeX-mode-hook 'tb-latex-common-hook)
(add-hook 'LaTeX-mode-hook 'tb-latex-common-hook)
;; RefTeX settings
(setq reftex-ref-macro-prompt nil)
(setq reftex-label-alist
'(("theorem" ?t "thm:" "~\\ref{%s}" t ("theorem" "th.") -2)
("lemma" ?t "lem:" "~\\ref{%s}" t ("lemma" "lem") -2)
("algorithm" ?a "alg:" "~\\ref{%s}" t ("algorithm" "alg") -2)))
;; -----------------------------------------------------------------------------
;; --- Org-Mode Configuration
;; -----------------------------------------------------------------------------
(use-package org
:config
;; Agenda settings
(setq org-agenda-columns-add-appointments-to-effort-sum t
org-agenda-files '("/home/tb/0/org-tassen/")
org-agenda-skip-deadline-if-done t
org-agenda-skip-scheduled-if-done t
org-agenda-skip-timestamp-if-done t)
;; Capture templates
(setq org-capture-templates
'(("t" "TODO" entry (file+headline "~/0/org-tassen/gtd.org" "Inbox") "* TODO %?\12%T\12%i"
:prepend t :jump-to-captured t :empty-lines-before 1 :empty-lines-after 1)))
;; Link and export settings
(setq org-confirm-shell-link-function nil
org-link-shell-confirm-function nil
org-link-frame-setup
'((vm . vm-visit-folder-other-frame) (vm-imap . vm-visit-imap-folder-other-frame)
(gnus . org-gnus-no-new-news) (file . find-file) (wl . wl-other-frame))
org-export-allow-bind-keywords t
org-export-backends '(ascii html latex md)
org-html-validation-link "")
;; Display settings
(setq org-reverse-note-order t
org-tab-follows-link t
org-tags-column -80
org-time-clocksum-format '(:hours "%d" :require-hours t :minutes ":%02d" :require-minutes t)
org-priority-faces '((65 . "red") (66 . "yellow") (67 . "chartreuse"))
org-table-number-regexp
"^\\([<>]?[-+^.,0-9]*[0-9][-+^.,0-9eEdDx()%:]*\\|[<>]?[-+]?0[xX][[:xdigit:].]+\\|[<>]?[-+]?[0-9]+#[0-9a-zA-Z.,]+\\|nan\\|[-+u]?inf\\)$")
;; Org faces
(set-faces
'(org-checkbox ((t (:inherit bold :background "DarkGoldenrod4"))))
'(org-date ((t (:foreground "dark magenta" :underline nil))))
'(org-drawer ((t (:foreground "dark slate blue"))))
'(org-headline-done ((t (:foreground "DarkOrange4"))))
'(org-level-1 ((t (:foreground "navajo white"))))
'(org-level-2 ((t (:extend nil :foreground "deep sky blue"))))
'(org-level-3 ((t (:extend nil :foreground "DarkOrange1"))))
'(org-link ((t (:inherit link :foreground "cyan1" :underline t))))
'(org-priority ((t nil)))
'(org-table ((t (:foreground "light sky blue"))))
'(org-tag ((t (:foreground "orchid"))))
'(org-todo ((t (:inherit nil :foreground "chartreuse")))))
)
(add-hook 'org-mode-hook
(lambda ()
(local-unset-key [(meta shift up)])
(local-unset-key [(meta shift down)])
(local-set-key [(control shift up)] 'org-move-subtree-up)
(local-set-key [(control shift down)] 'org-move-subtree-down)
(local-set-key [(control shift left)] 'org-promote-subtree)
(local-set-key [(control shift right)] 'org-demote-subtree)
(local-set-key [(control return)] 'org-insert-heading)
(local-set-key [(meta control return)] 'org-time-stamp)
(local-set-key [f5] 'org-html-export-to-html)
(local-set-key [f6] 'org-latex-export-to-pdf)))
;; -----------------------------------------------------------------------------
;; --- Accounting
;; -----------------------------------------------------------------------------
(set-faces
'(beancount-account ((t (:foreground "DeepSkyBlue1" :inherit font-lock-builtin-face))))
'(beancount-directive ((t (:foreground "violet red" :inherit font-lock-keyword-face)))))
;; ============================================================================
;; SECTION 7: UTILITY FUNCTIONS
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Buffer Management
;; -----------------------------------------------------------------------------
(defun nuke-all-buffers ()
"Kill all buffers, leaving *scratch* only."
(interactive)
(mapcar (lambda (x) (kill-buffer x)) (buffer-list))
(delete-other-windows))
;; -----------------------------------------------------------------------------
;; --- System Integration
;; -----------------------------------------------------------------------------
(defun my-terminal (&optional arg)
"Launch terminal in current directory."
(interactive)
(if (file-remote-p default-directory)
(shell)
(let ((terminal-cmd (getenv "TERMINAL")))
(when terminal-cmd
;; Split the command properly, handling spaces in paths
(let ((args (split-string-and-unquote terminal-cmd)))
(apply 'start-process "terminal" nil args))))))
;; -----------------------------------------------------------------------------
;; --- Compilation
;; -----------------------------------------------------------------------------
(defun my-compile (pfx)
"Saves all unsaved buffers, and runs 'compile' with optional ede project customization."
(interactive "p")
(save-some-buffers t)
(if (buffer-live-p compilation-last-buffer)
(recompile)
(let* ((fname (or (buffer-file-name (current-buffer)) default-directory))
(current-dir (file-name-directory fname))
(proj (ede-current-project current-dir)))
(if proj
(project-compile-project proj)
(call-interactively 'compile)))))
(defun tb-save-stage-and-format ()
"Save all files, stage all changes with Magit, and run git-clang-format."
(interactive)
(save-some-buffers t)
(magit-stage-modified)
(let ((default-directory (magit-toplevel)))
(if default-directory
(progn
(message "Running git-clang-format...")
(shell-command "git-clang-format"))
(message "Not inside a git repository.")))
(magit-refresh))
;; -----------------------------------------------------------------------------
;; --- Text Manipulation
;; -----------------------------------------------------------------------------
;; Kill without adding to kill-ring
(defun dove-backward-kill-word (&optional arg)
"Backward kill word, but do not insert it into kill-ring"
(interactive "P")
(let ((end (point))
(beg (progn (backward-word arg) (point))))
(delete-region beg end)))
(defun dove-forward-kill-word (&optional arg)
"Forward kill word, but do not insert it into kill-ring"
(interactive "P")
(let ((beg (point))
(end (progn (forward-word arg) (point))))
(delete-region beg end)))
;; Case conversion
(defun split-name (s)
(split-string
(let ((case-fold-search nil))
(downcase
(replace-regexp-in-string "\\([a-z]\\)\\([A-Z]\\)" "\\1 \\2" s)))
"[^A-Za-z0-9]+"))
(defun camelcase (s) (mapconcat 'capitalize (split-name s) ""))
(defun underscore (s) (mapconcat 'downcase (split-name s) "_"))
(defun camelscore (s)
(cond ((string-match-p "-" s) (colonize s))
((string-match-p "_" s) (dasherize s))
(t (underscore s))))
(defun camelscore-word-at-point ()
(interactive)
(let* ((case-fold-search nil)
(beg (and (skip-chars-backward "[:alnum:]_") (point)))
(end (and (skip-chars-forward "[:alnum:]_") (point)))
(txt (buffer-substring beg end))
(cml (camelscore txt)))
(if cml (progn (delete-region beg end) (insert cml)))))
;; JSON formatting
(defun beautify-json ()
(interactive)
(let ((b (if mark-active (min (point) (mark)) (point-min)))
(e (if mark-active (max (point) (mark)) (point-max))))
(shell-command-on-region b e
"python -mjson.tool" (current-buffer) t)))
;; Org-mode utilities
(defun org-insert-now-timestamp()
"Insert org mode timestamp at point with current date and time."
(interactive)
(org-insert-time-stamp (current-time) t))
;; -----------------------------------------------------------------------------
;; --- Number Manipulation
;; -----------------------------------------------------------------------------
(defun increment-number-decimal (&optional arg)
"Increment the number forward from point by 'arg'."
(interactive "p*")
(save-excursion
(save-match-data
(let (inc-by field-width answer)
(setq inc-by (if arg arg 1))
(skip-chars-backward "0123456789")
(when (re-search-forward "[0-9]+" nil t)
(setq field-width (- (match-end 0) (match-beginning 0)))
(setq answer (+ (string-to-number (match-string 0) 10) inc-by))
(when (< answer 0)
(setq answer (+ (expt 10 field-width) answer)))
(replace-match (format (concat "%0" (int-to-string field-width) "d")
answer)))))))
(defun decrement-number-decimal (&optional arg)
(interactive "p*")
(increment-number-decimal (if arg (- arg) -1)))
(defun increment-number-hexadecimal (&optional arg)
"Increment the hexadecimal number forward from point by 'arg'."
(interactive "p*")
(save-excursion
(save-match-data
(let (inc-by field-width answer hex-format)
(setq inc-by (if arg arg 1))
(skip-chars-backward "0123456789abcdefABCDEF")
(when (re-search-forward "[0-9a-fA-F]+" nil t)
(setq field-width (- (match-end 0) (match-beginning 0)))
(setq answer (+ (string-to-number (match-string 0) 16) inc-by))
(when (< answer 0)
(setq answer (+ (expt 16 field-width) answer)))
(if (equal (match-string 0) (upcase (match-string 0)))
(setq hex-format "X")
(setq hex-format "x"))
(replace-match (format (concat "%0" (int-to-string field-width)
hex-format)
answer)))))))
(defun decrement-number-hexadecimal (&optional arg)
(interactive "p*")
(increment-number-hexadecimal (if arg (- arg) -1)))
;; ============================================================================
;; SECTION 8: KEY BINDINGS
;; ============================================================================
;; -----------------------------------------------------------------------------
;; --- Window Management
;; -----------------------------------------------------------------------------
(require 'windmove)
(windmove-default-keybindings)
;; Window navigation
(global-set-key [M-left] 'windmove-left)
(global-set-key [M-right] 'windmove-right)
(global-set-key [M-up] 'windmove-up)
(global-set-key [M-down] 'windmove-down)
;; Window resizing
(global-set-key (kbd "S-C-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "S-C-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "S-C-<down>") 'shrink-window)
(global-set-key (kbd "S-C-<up>") 'enlarge-window)
;; Window operations
(global-set-key "\M-`" 'delete-other-windows)
(global-set-key "\M-2" 'make-frame)
(global-set-key "\M-3" 'delete-frame)
;; -----------------------------------------------------------------------------
;; --- Navigation & Editing
;; -----------------------------------------------------------------------------
;; Basic navigation
(global-set-key "\M-g" 'goto-line)
(global-set-key (kbd "C-x f") 'find-file-at-point)
;; Buffer navigation
(global-set-key [C-x C-b] 'buffer-menu)
(global-set-key [M-S-up] 'ibuffer)
(global-set-key [M-S-left] 'previous-buffer)
(global-set-key [M-S-right] 'next-buffer)
;; Text manipulation
(global-set-key (kbd "C-c SPC") 'comment-or-uncomment-region)
(global-set-key [(meta backspace)] 'backward-kill-word)
(global-set-key [(control backspace)] 'dove-backward-kill-word)
(global-set-key [(meta delete)] 'kill-word)
(global-set-key [(control delete)] 'dove-forward-kill-word)
;; -----------------------------------------------------------------------------
;; --- Function Keys
;; -----------------------------------------------------------------------------
;; F2 reserved for bookmarks (bm package)
(global-set-key [f3] 'projectile-find-other-file) ; Switch between .cpp/.hpp
(global-set-key [f4] 'my-terminal) ; Launch terminal
(global-set-key [f5] 'my-compile) ; Compile
(global-set-key [f6] 'tb-save-stage-and-format) ; Save, stage, format
;; German umlauts
(global-set-key (kbd "<f9>") (lambda() (interactive) (insert ?\ä)))
(global-set-key (kbd "<S-f9>") (lambda() (interactive) (insert ?\Ä)))
(global-set-key (kbd "<f10>") (lambda() (interactive) (insert ?\ö)))
(global-set-key (kbd "<S-f10>") (lambda() (interactive) (insert ?\Ö)))
(global-set-key (kbd "<f11>") (lambda() (interactive) (insert ?\ü)))
(global-set-key (kbd "<S-f11>") (lambda() (interactive) (insert ?\Ü)))
(global-set-key (kbd "<f12>") (lambda() (interactive) (insert ?\ß)))
;; -----------------------------------------------------------------------------
;; --- Mouse & Display
;; -----------------------------------------------------------------------------
;; Mouse wheel
(global-set-key (kbd "<mouse-6>") (lambda (event) (interactive "e")))
(global-set-key (kbd "<mouse-7>") (lambda (event) (interactive "e")))
;; Frame zoom
(global-set-key (kbd "C-x C-=") 'acg/zoom-frame)
(global-set-key (kbd "C-x C--") 'acg/zoom-frame-out)
(global-set-key (kbd "<C-down-mouse-4>") 'acg/zoom-frame)
(global-set-key (kbd "<C-down-mouse-5>") 'acg/zoom-frame-out)
;; -----------------------------------------------------------------------------
;; --- Special Commands
;; -----------------------------------------------------------------------------
(global-set-key (kbd "C-x K") 'nuke-all-buffers)
;; Shell/Comint
(define-key comint-mode-map (kbd "<up>") 'comint-previous-input)
(define-key comint-mode-map (kbd "<down>") 'comint-next-input)
;; ============================================================================
;; THE END.
;; ============================================================================