From 11332b71e203e41c8eb9d19a71059ff14906eaa9 Mon Sep 17 00:00:00 2001 From: Timo Bingmann Date: Tue, 8 Jul 2025 14:12:51 -0700 Subject: [PATCH] add emacs config --- home/develop/emacs.nix | 18 +- home/develop/emacs/custom.el | 1186 ++++++++++++++++++++++++++++++++++ 2 files changed, 1193 insertions(+), 11 deletions(-) create mode 100644 home/develop/emacs/custom.el diff --git a/home/develop/emacs.nix b/home/develop/emacs.nix index 7086de5..3d6a14a 100644 --- a/home/develop/emacs.nix +++ b/home/develop/emacs.nix @@ -1,4 +1,4 @@ -{ ... }: +{ pkgs, ... }: { programs.emacs = { enable = true; @@ -6,9 +6,6 @@ # Optionally provide extra packages not in the configuration file. extraPackages = epkgs: with epkgs; [ - # === SEARCH AND FILE UTILITIES === - ag # Silver searcher integration for fast text search - # === WEB AND SERVER CONFIGURATION === apache-mode # Syntax highlighting for Apache config files nginx-mode # Syntax highlighting for Nginx config files @@ -45,6 +42,11 @@ yaml-mode # YAML configuration files gnuplot-mode # Gnuplot script editing and plotting integration + # === SEARCH AND FILE UTILITIES === + rg # Ripgrep searcher integration for fast text search + diredfl + dired-ranger + # === TEXT COMPLETION AND PRODUCTIVITY === company # Text completion framework smex # Enhanced M-x command with history @@ -111,18 +113,12 @@ scala-ts-mode # Scala with tree-sitter (alternative to scala-mode) swift-ts-mode # Swift programming with tree-sitter - #"dired+" - #dired-copy-paste #frame-cmds #frame-fns #sourcepair #zoom-frm ]; - extraConfig = '' - (setq standard-indent 2) - - (load-theme 'grandshell t) - ''; + extraConfig = builtins.readFile ./emacs/custom.el; }; } diff --git a/home/develop/emacs/custom.el b/home/develop/emacs/custom.el new file mode 100644 index 0000000..f88d614 --- /dev/null +++ b/home/develop/emacs/custom.el @@ -0,0 +1,1186 @@ +;; ============================================================================ +;; 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) + +;; ----------------------------------------------------------------------------- +;; --- 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))) + +;; Yet another snippet extension for Emacs. +(use-package yasnippet + :init + (setq yas-snippet-dirs '("~/.emacs.d/snippets/")) + :config + (yas-reload-all) + (yas-global-mode 1)) + +;; Clean up whitespace +(use-package whitespace-cleanup-mode + :diminish + :config + (global-whitespace-cleanup-mode)) + +;; Rainbow delimiters +(use-package rainbow-delimiters) + +;; Smooth scrolling +(use-package smooth-scrolling) + +;; ----------------------------------------------------------------------------- +;; --- Navigation & Bookmarks +;; ----------------------------------------------------------------------------- + +;; Visible bookmarks +(use-package bm + :bind (("" . bm-next) + ("" . bm-previous) + ("" . 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 + :confirm never + :dir current) + + :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 "") '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 (("" . magit-status) + ("" . magit-blame-addition) + ("" . 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-" . company-complete)) + :hook ((python-mode . eglot-ensure) + (scala-mode . eglot-ensure) + (scala-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" . my-ag-grep))) + +;; ----------------------------------------------------------------------------- +;; --- Python Configuration +;; ----------------------------------------------------------------------------- + +(use-package python-mode + :defer t + :bind (:map python-mode-map + ("C-c C-s" . ripgrep))) + +;; ----------------------------------------------------------------------------- +;; --- 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))))) + +;; ----------------------------------------------------------------------------- +;; --- 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-" . 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) + (start-process "terminal" nil "urxvt"))) + +;; ----------------------------------------------------------------------------- +;; --- 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-") 'shrink-window-horizontally) +(global-set-key (kbd "S-C-") 'enlarge-window-horizontally) +(global-set-key (kbd "S-C-") 'shrink-window) +(global-set-key (kbd "S-C-") '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] 'ff-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 "") (lambda() (interactive) (insert ?\ä))) +(global-set-key (kbd "") (lambda() (interactive) (insert ?\Ä))) +(global-set-key (kbd "") (lambda() (interactive) (insert ?\ö))) +(global-set-key (kbd "") (lambda() (interactive) (insert ?\Ö))) +(global-set-key (kbd "") (lambda() (interactive) (insert ?\ü))) +(global-set-key (kbd "") (lambda() (interactive) (insert ?\Ü))) +(global-set-key (kbd "") (lambda() (interactive) (insert ?\ß))) + +;; ----------------------------------------------------------------------------- +;; --- Mouse & Display +;; ----------------------------------------------------------------------------- + +;; Mouse wheel +(global-set-key (kbd "") (lambda (event) (interactive "e"))) +(global-set-key (kbd "") (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 "") 'acg/zoom-frame) +(global-set-key (kbd "") 'acg/zoom-frame-out) + +;; ----------------------------------------------------------------------------- +;; --- Special Commands +;; ----------------------------------------------------------------------------- + +(global-set-key (kbd "C-x K") 'nuke-all-buffers) + +;; Shell/Comint +(define-key comint-mode-map (kbd "") 'comint-previous-input) +(define-key comint-mode-map (kbd "") 'comint-next-input) + +;; ============================================================================ +;; THE END. +;; ============================================================================