From e3a587221876defd9833b6e2d3b2ad26e932bfaa Mon Sep 17 00:00:00 2001 From: Jens Luedicke Date: Mon, 8 Sep 2025 12:14:12 +0200 Subject: [PATCH] Split init.el into seperate modules --- early-init.el | 49 ++ emacs-dev-config-modern.el | 231 ++++++++++ emacs-dev-config.el | 9 + god-mode-config.el | 12 +- init.el | 894 +++++-------------------------------- lisp/init-compile.el | 66 +++ lisp/init-completion.el | 205 +++++++++ lisp/init-core.el | 153 +++++++ lisp/init-dired.el | 91 ++++ lisp/init-editor.el | 53 +++ lisp/init-eglot.el | 140 ++++++ lisp/init-emergency-fix.el | 72 +++ lisp/init-eslint-fix.el | 102 +++++ lisp/init-keybindings.el | 56 +++ lisp/init-markdown.el | 51 +++ lisp/init-project.el | 21 + lisp/init-qol.el | 190 ++++++++ lisp/init-search.el | 30 ++ lisp/init-terminal.el | 122 +++++ lisp/init-treemacs.el | 125 ++++++ lisp/init-treesitter.el | 109 +++++ lisp/init-ui.el | 100 +++++ lisp/init-utils.el | 117 +++++ lisp/init-vcs.el | 78 ++++ shr-config.el | 13 + 25 files changed, 2306 insertions(+), 783 deletions(-) create mode 100644 early-init.el create mode 100644 emacs-dev-config-modern.el create mode 100644 lisp/init-compile.el create mode 100644 lisp/init-completion.el create mode 100644 lisp/init-core.el create mode 100644 lisp/init-dired.el create mode 100644 lisp/init-editor.el create mode 100644 lisp/init-eglot.el create mode 100644 lisp/init-emergency-fix.el create mode 100644 lisp/init-eslint-fix.el create mode 100644 lisp/init-keybindings.el create mode 100644 lisp/init-markdown.el create mode 100644 lisp/init-project.el create mode 100644 lisp/init-qol.el create mode 100644 lisp/init-search.el create mode 100644 lisp/init-terminal.el create mode 100644 lisp/init-treemacs.el create mode 100644 lisp/init-treesitter.el create mode 100644 lisp/init-ui.el create mode 100644 lisp/init-utils.el create mode 100644 lisp/init-vcs.el diff --git a/early-init.el b/early-init.el new file mode 100644 index 0000000..6af1461 --- /dev/null +++ b/early-init.el @@ -0,0 +1,49 @@ +;;; early-init.el --- Early initialization settings -*- lexical-binding: t -*- +;;; Commentary: +;;; This file is loaded before the package system and GUI is initialized. +;;; Use it for settings that need to be set very early in the startup process. + +;;; Code: + +;; Defer garbage collection further back in the startup process +;; This makes startup faster by preventing frequent GC +(setq gc-cons-threshold most-positive-fixnum + gc-cons-percentage 0.6) + +;; Store default values to restore after init +(defvar default-gc-cons-threshold 16777216) ; 16MB is a good default +(defvar default-gc-cons-percentage 0.1) + +;; In Emacs 27+, package initialization occurs before `user-init-file' is loaded, +;; but after `early-init-file'. We handle package initialization ourselves. +(setq package-enable-at-startup nil) + +;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early. +(push '(menu-bar-lines . 0) default-frame-alist) +(push '(tool-bar-lines . 0) default-frame-alist) +(push '(vertical-scroll-bars) default-frame-alist) + +;; Resizing the Emacs frame can be a terribly expensive part of changing the +;; font. By inhibiting this, we easily halve startup times with fonts that are +;; larger than the system default. +(setq frame-inhibit-implied-resize t) + +;; Native compilation settings for Emacs 28+ +(when (and (fboundp 'native-comp-available-p) + (native-comp-available-p)) + ;; Silence compiler warnings as they can be pretty disruptive + (setq native-comp-async-report-warnings-errors nil) + ;; Make native compilation happen asynchronously + (setq native-comp-deferred-compilation t) + ;; Set the right directory for native compilation cache + (add-to-list 'native-comp-eln-load-path (expand-file-name "eln-cache/" user-emacs-directory)) + ;; Compile AOT all the .el files in the configuration + (setq native-comp-speed 2) ; Max optimization + (setq native-comp-jit-compilation t)) ; JIT compile loaded files + +;; Disable some warnings during initialization +(setq byte-compile-warnings '(not obsolete)) +(setq warning-suppress-log-types '((comp) (bytecomp))) + +(provide 'early-init) +;;; early-init.el ends here \ No newline at end of file diff --git a/emacs-dev-config-modern.el b/emacs-dev-config-modern.el new file mode 100644 index 0000000..05e8846 --- /dev/null +++ b/emacs-dev-config-modern.el @@ -0,0 +1,231 @@ +;;; emacs-dev-config-modern.el --- Modern development configuration using Eglot -*- lexical-binding: t -*- +;;; Commentary: +;;; Development configuration using built-in Eglot instead of lsp-mode +;;; This is a lighter, faster alternative to the original dev config + +;;; Code: + +(defvar dev-mode-modern-enabled nil + "Flag indicating whether modern development mode is enabled.") + +(defvar dev-mode-modern-packages + '(;; Core development tools + eglot ; Only needed for Emacs < 29 + corfu corfu-terminal cape ; Modern completion + consult-eglot ; Consult integration with Eglot + flycheck ; Can still use alongside Flymake + yasnippet + projectile + ggtags + multiple-cursors expand-region + hl-todo rainbow-delimiters + origami ; Code folding + + ;; Version control + magit + forge ; GitHub/GitLab integration + magit-delta ; Better diffs if delta is installed + treemacs-magit + + ;; Languages + clang-format + qml-mode + + ;; Debugging + dap-mode) + "List of packages for modern development mode.") + +(defun dev-mode-modern-ensure-packages () + "Ensure all modern development packages are installed." + (dolist (package dev-mode-modern-packages) + (unless (or (package-installed-p package) + (and (eq package 'eglot) (fboundp 'eglot))) ; Eglot is built-in for Emacs 29+ + (package-refresh-contents) + (package-install package)))) + +(defun dev-mode-modern-setup-eglot () + "Setup Eglot for modern development." + ;; Load eglot configuration + (require 'init-eglot) + + ;; Additional Eglot configuration for development + (with-eval-after-load 'eglot + ;; Enable format on save for specific modes + (dolist (mode '(c-mode-hook c++-mode-hook python-mode-hook)) + (add-hook mode #'eglot-format-buffer-on-save)) + + ;; Configure Consult-Eglot if available + (when (fboundp 'consult-eglot-symbols) + (define-key eglot-mode-map (kbd "C-c l s") 'consult-eglot-symbols)))) + +(defun dev-mode-modern-setup-completion () + "Setup modern completion with Corfu." + ;; Corfu is already configured in init-completion.el + ;; Add development-specific configurations here + (with-eval-after-load 'corfu + ;; More aggressive completion in programming modes + (add-hook 'prog-mode-hook + (lambda () + (setq-local corfu-auto-delay 0.1) + (setq-local corfu-auto-prefix 1))))) + +(defun dev-mode-modern-setup-yasnippet () + "Configure yasnippet for code snippets." + (use-package yasnippet + :ensure t + :config + (yas-global-mode 1) + ;; Load snippets from the snippets directory if it exists + (let ((snippets-dir (expand-file-name "snippets" user-emacs-directory))) + (when (file-directory-p snippets-dir) + (add-to-list 'yas-snippet-dirs snippets-dir))))) + +(defun dev-mode-modern-setup-flycheck () + "Configure Flycheck alongside Flymake." + (use-package flycheck + :ensure t + :init + ;; Use Flycheck for modes not well-supported by Flymake + (add-hook 'sh-mode-hook 'flycheck-mode) + (add-hook 'json-mode-hook 'flycheck-mode) + (add-hook 'yaml-mode-hook 'flycheck-mode) + :config + (setq flycheck-display-errors-delay 0.3))) + +(defun dev-mode-modern-setup-projectile () + "Configure projectile for project management." + ;; Already configured in init-project.el + ;; Add development-specific configurations here + (with-eval-after-load 'projectile + (define-key projectile-command-map (kbd "t") 'projectile-test-project) + (define-key projectile-command-map (kbd "c") 'projectile-compile-project))) + +(defun dev-mode-modern-setup-magit () + "Configure Magit for version control." + (use-package magit + :ensure t + :bind (("C-x g" . magit-status) + ("C-x M-g" . magit-dispatch) + ("C-c g" . magit-file-dispatch)) + :config + (setq magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) + + ;; Forge for GitHub/GitLab integration + (use-package forge + :ensure t + :after magit) + + ;; Magit-delta for better diffs (if delta is installed) + (when (executable-find "delta") + (use-package magit-delta + :ensure t + :hook (magit-mode . magit-delta-mode)))) + +(defun dev-mode-modern-setup-debugging () + "Configure debugging with dap-mode." + (use-package dap-mode + :ensure t + :commands dap-debug + :config + ;; Python debugging + (require 'dap-python) + ;; C/C++ debugging + (require 'dap-gdb-lldb) + ;; Automatic configuration + (dap-auto-configure-mode 1))) + +(defun dev-mode-modern-setup-languages () + "Configure language-specific settings." + ;; C/C++ formatting + (use-package clang-format + :ensure t + :bind (:map c-mode-map + ("C-c C-f" . clang-format-buffer) + :map c++-mode-map + ("C-c C-f" . clang-format-buffer))) + + ;; QML support + (use-package qml-mode + :ensure t + :mode "\\.qml\\'" + :hook (qml-mode . eglot-ensure))) + +(defun dev-mode-modern-setup-editing-tools () + "Setup advanced editing tools for development." + ;; These are now in init-qol.el, but we can add dev-specific configs + (with-eval-after-load 'hl-todo + (add-hook 'prog-mode-hook #'hl-todo-mode)) + + (with-eval-after-load 'rainbow-delimiters + (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)) + + ;; Origami for code folding + (use-package origami + :ensure t + :hook (prog-mode . origami-mode) + :bind (:map origami-mode-map + ("C-c f f" . origami-toggle-node) + ("C-c f o" . origami-open-node) + ("C-c f c" . origami-close-node) + ("C-c f a" . origami-close-all-nodes) + ("C-c f A" . origami-open-all-nodes)))) + +(defun dev-mode-modern-setup-keybindings () + "Setup development-specific keybindings." + ;; Compile commands + (global-set-key (kbd "") 'compile) + (global-set-key (kbd "") 'recompile) + + ;; Testing + (global-set-key (kbd "C-c t p") 'projectile-test-project) + (global-set-key (kbd "C-c t f") 'projectile-test-file) + + ;; Navigation + (global-set-key (kbd "M-.") 'xref-find-definitions) + (global-set-key (kbd "M-,") 'xref-pop-marker-stack) + (global-set-key (kbd "M-?") 'xref-find-references)) + +;;;###autoload +(defun enable-dev-mode-modern () + "Enable modern development mode with Eglot and other tools." + (interactive) + (if dev-mode-modern-enabled + (message "Modern development mode is already enabled") + (message "Enabling modern development mode...") + ;; Ensure packages are installed + (dev-mode-modern-ensure-packages) + ;; Set up all development features + (dev-mode-modern-setup-eglot) + (dev-mode-modern-setup-completion) + (dev-mode-modern-setup-yasnippet) + (dev-mode-modern-setup-flycheck) + (dev-mode-modern-setup-projectile) + (dev-mode-modern-setup-magit) + (dev-mode-modern-setup-debugging) + (dev-mode-modern-setup-languages) + (dev-mode-modern-setup-editing-tools) + (dev-mode-modern-setup-keybindings) + ;; Load tree-sitter if available + (when (file-exists-p (expand-file-name "lisp/init-treesitter.el" user-emacs-directory)) + (require 'init-treesitter)) + (setq dev-mode-modern-enabled t) + (message "Modern development mode enabled! Eglot will auto-start for supported files."))) + +;;;###autoload +(defun disable-dev-mode-modern () + "Disable modern development mode." + (interactive) + (if (not dev-mode-modern-enabled) + (message "Modern development mode is not enabled") + (message "Disabling modern development mode...") + ;; Shutdown all Eglot servers + (when (fboundp 'eglot-shutdown-all) + (eglot-shutdown-all)) + ;; Disable some modes + (yas-global-mode -1) + (global-flycheck-mode -1) + (setq dev-mode-modern-enabled nil) + (message "Modern development mode disabled."))) + +(provide 'emacs-dev-config-modern) +;;; emacs-dev-config-modern.el ends here \ No newline at end of file diff --git a/emacs-dev-config.el b/emacs-dev-config.el index 88c17ad..fb17261 100644 --- a/emacs-dev-config.el +++ b/emacs-dev-config.el @@ -359,11 +359,20 @@ (message "Patch saved to %s" file) (error "Failed to save patch")))) + ;; Set up keybindings for saving patches (with-eval-after-load 'magit (define-key magit-revision-mode-map (kbd "C-c C-p") 'magit-save-commit-as-patch) (define-key magit-log-mode-map (kbd "C-c C-p") 'magit-save-commit-as-patch) (define-key magit-log-select-mode-map (kbd "C-c C-p") 'magit-save-commit-as-patch)) + ;; Also set up hooks to ensure keybindings are available + (add-hook 'magit-revision-mode-hook + (lambda () (local-set-key (kbd "C-c C-p") 'magit-save-commit-as-patch))) + (add-hook 'magit-log-mode-hook + (lambda () (local-set-key (kbd "C-c C-p") 'magit-save-commit-as-patch))) + (add-hook 'magit-log-select-mode-hook + (lambda () (local-set-key (kbd "C-c C-p") 'magit-save-commit-as-patch))) + ;; Optional: Integrate diff-hl with Magit when both are available ;; Only enable if you want Magit to control diff-hl updates ;; Comment out these lines if diff-hl has issues with Magit diff --git a/god-mode-config.el b/god-mode-config.el index 9bb7e23..b9d3250 100644 --- a/god-mode-config.el +++ b/god-mode-config.el @@ -15,11 +15,13 @@ ("C-x C-3" . split-window-right) ("C-x C-0" . delete-window)) :config - ;; Define keybindings after god-mode is loaded - (define-key god-local-mode-map (kbd ".") 'repeat) - (define-key god-local-mode-map (kbd "[") 'backward-paragraph) - (define-key god-local-mode-map (kbd "]") 'forward-paragraph) - (define-key god-local-mode-map (kbd "i") 'god-mode-all) + ;; Define keybindings after god-mode is loaded and map exists + (with-eval-after-load 'god-mode + (when (boundp 'god-local-mode-map) + (define-key god-local-mode-map (kbd ".") 'repeat) + (define-key god-local-mode-map (kbd "[") 'backward-paragraph) + (define-key god-local-mode-map (kbd "]") 'forward-paragraph) + (define-key god-local-mode-map (kbd "i") 'god-mode-all))) ;; Update cursor to indicate god-mode state (defun god-mode-update-cursor () "Update cursor style based on god-mode state." diff --git a/init.el b/init.el index 5730b3d..a1848bf 100644 --- a/init.el +++ b/init.el @@ -1,767 +1,67 @@ -;;; -*- lexical-binding: t -*- +;;; init.el --- Main Emacs configuration file -*- lexical-binding: t -*- ;;; Commentary: +;;; This is the main configuration file that loads all modular configuration files. +;;; The configuration has been split into logical modules for better organization +;;; and maintainability. -;;; Improved Emacs Configuration -;;; Keeping: treemacs, helm, diff-hl, magit +;;; Code: -;;; Package Management - Consolidated -(require 'package) -(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) -(package-initialize) +;; Add lisp subdirectory to load path (avoids load-path warning) +(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory)) -;; Core package list - consolidated and cleaned -(setq package-selected-packages - '(;; Core utilities - use-package which-key +;; EMERGENCY FIX - Load this first to ensure editing works +(require 'init-emergency-fix) - ;; Themes - modus-themes +;;; Load core modules in order +(require 'init-core) ; Core settings and package management +(require 'init-completion); Modern completion with Vertico, Consult, etc. +(require 'init-ui) ; UI and theme configurations +(require 'init-editor) ; Basic editor enhancements +(require 'init-project) ; Project management with projectile +(require 'init-vcs) ; Version control (diff-hl) +(require 'init-search) ; Search tools +(require 'init-dired) ; Dired configuration +(require 'init-treemacs) ; Treemacs file browser +(require 'init-markdown) ; Markdown support +(require 'init-utils) ; Utility functions +(require 'init-keybindings); Global keybindings +(require 'init-compile) ; Byte compilation utilities +(require 'init-qol) ; Quality of life improvements +(require 'init-treesitter); Tree-sitter support for Emacs 29+ +(require 'init-eglot) ; Built-in LSP client +(require 'init-eslint-fix); Fix ESLint configuration issues +(require 'init-terminal) ; Terminal emulator configuration - ;; Version control - diff-hl +;;; Load optional configurations - ;; File management - treemacs treemacs-projectile treemacs-all-the-icons - neotree all-the-icons all-the-icons-dired diredfl +;; Development configuration - Modern version with Eglot +(let ((dev-config-modern (expand-file-name "emacs-dev-config-modern.el" user-emacs-directory))) + (when (file-exists-p dev-config-modern) + (load-file dev-config-modern) + (add-hook 'emacs-startup-hook + (lambda () + (run-with-timer 1 nil + (lambda () + (message "Modern development mode available. Use M-x enable-dev-mode-modern to activate."))))))) - ;; Helm ecosystem - helm helm-xref helm-projectile - - ;; Core project management - projectile - - ;; Markdown & Notes - markdown-mode markdown-toc grip-mode - obsidian olivetti - - ;; Search and navigation - deadgrep ripgrep wgrep anzu - ibuffer-sidebar ibuffer-projectile - - ;; Required for some functionality - org dash s f ht spinner lv hydra avy - - ;; RSS/News reader - elfeed elfeed-org - - ;; Email (mu4e installed separately) - - ;; Finance tracking - beancount - )) - -;; Auto-install missing packages -(when (cl-find-if-not #'package-installed-p package-selected-packages) - (package-refresh-contents) - (mapc #'package-install package-selected-packages)) - -;; Ensure use-package is loaded -(eval-when-compile - (require 'use-package)) -(setq use-package-always-ensure t) - -;;; Custom Settings (preserved from original) -(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. - '(before-save-hook '(delete-trailing-whitespace)) - '(column-number-mode t) - '(cua-mode t) - '(custom-enabled-themes '(developer-dark)) - '(custom-safe-themes - '("2974c2d5ffede4f111b02701ccdfadfbcde3d158ad9b09dade627b3ce3049ea1" - "f8859f15bd0089a85d8d14e21dd774a9a8b391dac076e3ff17a13529b3d16576" - "de2f009a49f8eaf2f323519d86016849cd1716d979bc3f7e4afb58899e52ddb7" - "9fb69436c074b82a62b78b8d733e6274d0bd16d156f7b094e2afe4345c040c49" - "004f174754c688f358fa2afc4f8699b5db647fbfaa0d6b55ff39f63e05bfbbf5" - "ca1b398ceb1b61709197478dc7f705b8337a0a9631e399948e643520c5557382" - "75eef60308d7328ed14fa27002e85de255c2342e73275173a14ed3aa1643d545" - "77f281064ea1c8b14938866e21c4e51e4168e05db98863bd7430f1352cab294a" - "242e6f00c98aa43539b41c505ef094d21cbc981203809a82949efaa2bc6cb194" - "c9e63647d90e7dc59d52c176fbfd46fd2cfed275cd2bad9b29b6cf620d3389b6" - "ad7d874d137291e09fe2963babc33d381d087fa14928cb9d34350b67b6556b6d" - default)) - '(diff-hl-global-modes t) - '(display-line-numbers t) - '(display-line-numbers-type 'relative) - '(global-display-line-numbers-mode t) - '(ls-lisp-dirs-first t) - '(neo-show-hidden-files t) - '(neo-window-width 40) - '(package-selected-packages - '(all-the-icons all-the-icons-dired anzu avy clang-format - clang-format+ commenter company company-box - company-qml cyberpunk-theme dap-mode dash deadgrep - diff-hl diredfl expand-region f flycheck ggtags - god-mode grip-mode helm helm-lsp helm-projectile - helm-xref hl-todo ht hydra ibuffer-projectile - ibuffer-sidebar lsp-mode lsp-treemacs lsp-ui lv - magit magit-delta markdown-mode markdown-toc - modus-themes multiple-cursors neotree obsidian - olivetti org origami projectile qml-mode - rainbow-delimiters ripgrep s spinner treemacs - treemacs-all-the-icons treemacs-magit - treemacs-projectile use-package wgrep which-key - yasnippet)) - '(ring-bell-function 'ignore) - '(safe-local-variable-values - '((company-backends - (company-qml company-capf company-files company-yasnippet)) - (lsp-clients-qml-server-executable . "/usr/lib/qt6/bin/qmlls") - (company-minimum-prefix-length . 1) (company-idle-delay . 0.2) - (lsp-clangd-binary-path . "clangd") - (lsp-clients-clangd-args - "--compile-commands-dir=/home/jens/sources/thulio" - "--background-index" "--clang-tidy" - "--completion-style=detailed" "--header-insertion=iwyu" - "--pch-storage=memory") - (projectile-project-root . "/home/jens/sources/thulio"))) - '(save-place-mode t) - '(show-trailing-whitespace t) - '(tool-bar-mode nil) - '(url-proxy-services - '(("https" . "eudewerepo001:3128") ("http" . "eudewerepo001:3128")))) - -(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. - '(default ((t (:family "0xProto Nerd Font Mono" :foundry "nil" :slant normal :weight regular :height 140 :width normal)))) - '(diff-hl-change ((t (:background "blue3" :foreground "blue3")))) - '(diff-hl-delete ((t (:background "red3" :foreground "red3")))) - '(diff-hl-insert ((t (:background "green3" :foreground "green3"))))) - -;;; Helm Configuration (Primary interface) -(use-package helm - :ensure t - :demand t - :bind (("M-x" . helm-M-x) - ("C-x C-f" . helm-find-files) - ("C-x b" . helm-buffers-list) - ("C-x r b" . helm-bookmarks) - ("M-y" . helm-show-kill-ring) - ("C-h SPC" . helm-all-mark-rings)) - :config - (helm-mode 1) - (setq helm-split-window-in-side-p t - helm-move-to-line-cycle-in-source t - helm-ff-search-library-in-sexp t - helm-scroll-amount 8 - helm-ff-file-name-history-use-recentf t - helm-echo-input-in-header-line t) - ;; Make helm more responsive - (setq helm-input-idle-delay 0.01 - helm-cycle-resume-delay 2 - helm-follow-input-idle-delay 0.01) - ;; Prevent helm from opening new frames - (setq helm-always-two-windows nil) - (setq helm-display-buffer-default-height 15) - (setq helm-default-display-buffer-functions '(display-buffer-in-side-window))) - -(use-package helm-xref - :ensure t - :after helm) - -(use-package helm-projectile - :ensure t - :after (helm projectile) - :config - (helm-projectile-on) - ;; Prevent helm-projectile from opening new frames - (setq helm-projectile-sources-list - '(helm-source-projectile-buffers-list - helm-source-projectile-recentf-list - helm-source-projectile-files-list - helm-source-projectile-projects)) - ;; Use current window for file actions - (setq helm-display-function 'helm-default-display-buffer)) - -;;; Load development configuration (optional - use M-x enable-dev-mode) +;; Legacy development configuration (lsp-mode) - kept for compatibility (let ((dev-config (expand-file-name "emacs-dev-config.el" user-emacs-directory))) (when (file-exists-p dev-config) (load-file dev-config))) -;;; Version Control (basic) - Magit moved to development mode - -;;; Diff-hl Configuration -(use-package diff-hl - :ensure t - :hook ((prog-mode . diff-hl-mode) - (dired-mode . diff-hl-dired-mode) - (text-mode . diff-hl-mode) - (conf-mode . diff-hl-mode)) - :bind (("M-n" . diff-hl-next-hunk) - ("M-p" . diff-hl-previous-hunk) - ("C-c v r" . diff-hl-revert-hunk) - ("C-c v s" . diff-hl-diff-goto-hunk) - ("C-c v u" . diff-hl-refresh)) - :init - ;; Set fringe width before diff-hl loads - (setq-default left-fringe-width 8) - (setq-default right-fringe-width 8) - ;; Ensure fringes are visible - (fringe-mode 8) - :config - ;; Configure VC backend for Git - (setq vc-handled-backends '(Git)) - (setq vc-git-diff-switches '("--histogram")) - - ;; IMPORTANT: Tell diff-hl to use VC backend, not Magit - (setq diff-hl-reference-revision nil) ; Use working tree, not index - (setq diff-hl-disable-on-remote nil) ; Work even on remote files - - ;; Make diff-hl use the left fringe - (setq diff-hl-side 'left) - - ;; Ensure diff-hl draws in fringe, not margin - (setq diff-hl-draw-borders nil) - (setq diff-hl-margin-mode nil) - - ;; Set diff-hl fringe bitmaps (ensure they're visible) - (setq diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-type) - - ;; Enable flydiff for real-time updates using VC - (diff-hl-flydiff-mode 1) - - ;; Update immediately when visiting a file - (setq diff-hl-flydiff-delay 0.3) - - ;; Make sure diff-hl updates on various events - (add-hook 'after-save-hook 'diff-hl-update) - (add-hook 'after-revert-hook 'diff-hl-update) - (add-hook 'find-file-hook 'diff-hl-update) - (add-hook 'vc-checkin-hook 'diff-hl-update) - - ;; Enable globally - (global-diff-hl-mode 1) - - ;; Explicitly disable Magit integration in base config - ;; (Magit hooks will only be added when dev-mode is enabled) - (setq diff-hl-disable-on-remote nil) - - ;; Manual refresh command - (defun diff-hl-refresh () - "Manually refresh diff-hl indicators in all buffers." - (interactive) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when diff-hl-mode - (diff-hl-update))))) - - ;; Troubleshooting function - (defun diff-hl-diagnose () - "Diagnose diff-hl issues." - (interactive) - (let ((diagnosis '())) - (push (format "diff-hl-mode: %s" (if diff-hl-mode "enabled" "disabled")) diagnosis) - (push (format "Buffer file: %s" (or buffer-file-name "none")) diagnosis) - (push (format "VC backend: %s" (or (vc-backend buffer-file-name) "none")) diagnosis) - (push (format "VC responsible backend: %s" (or (vc-responsible-backend default-directory) "none")) diagnosis) - (push (format "Modified: %s" (if (and buffer-file-name (buffer-modified-p)) "yes" "no")) diagnosis) - (push (format "VC state: %s" (when buffer-file-name (vc-state buffer-file-name))) diagnosis) - (push (format "Left fringe width: %s" left-fringe-width) diagnosis) - (push (format "Right fringe width: %s" right-fringe-width) diagnosis) - (push (format "diff-hl-side: %s" diff-hl-side) diagnosis) - (push (format "diff-hl-margin-mode: %s" diff-hl-margin-mode) diagnosis) - (push (format "diff-hl-reference-revision: %s" diff-hl-reference-revision) diagnosis) - (push (format "Magit loaded: %s" (if (fboundp 'magit-status) "yes" "no")) diagnosis) - (message (mapconcat 'identity (nreverse diagnosis) "\n")))) - - ;; Force VC to refresh its state - (defun diff-hl-force-vc-refresh () - "Force VC to refresh state and then update diff-hl." - (interactive) - (when buffer-file-name - (vc-refresh-state) - (diff-hl-update) - (message "VC state refreshed and diff-hl updated"))) - - ;; Disable Magit integration if causing issues - (defun diff-hl-disable-magit () - "Disable Magit integration with diff-hl." - (interactive) - (setq diff-hl-disable-magit-integration t) - (remove-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh) - (remove-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh) - (message "Magit integration with diff-hl disabled. Using pure VC backend.")) - - ;; Ensure we're using VC backend - (defun diff-hl-ensure-vc-backend () - "Ensure diff-hl is using VC backend." - (interactive) - (setq diff-hl-reference-revision nil) - (diff-hl-disable-magit) - (diff-hl-refresh) - (message "diff-hl configured to use VC backend only"))) - -;;; Treemacs Configuration -(use-package treemacs - :ensure t - :defer t - :bind (("M-0" . treemacs-select-window) - ("C-x t t" . treemacs) - ("C-x t 1" . treemacs-delete-other-windows) - ("C-x t d" . treemacs-select-directory) - ("C-x t B" . treemacs-bookmark) - ("" . treemacs) - :map treemacs-mode-map - ("/" . treemacs-search-file) - ("C-s" . helm-projectile-find-file) - ("s" . helm-projectile-grep)) - :config - (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0) - treemacs-deferred-git-apply-delay 0.5 - treemacs-directory-name-transformer #'identity - treemacs-display-in-side-window t - treemacs-eldoc-display 'simple - treemacs-file-event-delay 2000 - treemacs-file-follow-delay 0.2 - treemacs-follow-after-init t - treemacs-expand-after-init t - treemacs-find-workspace-method 'find-for-file-or-pick-first - treemacs-hide-dot-git-directory t - treemacs-indentation 2 - treemacs-indentation-string " " - treemacs-is-never-other-window nil - treemacs-max-git-entries 5000 - treemacs-missing-project-action 'ask - treemacs-move-forward-on-expand nil - treemacs-no-delete-other-windows t - treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) - treemacs-position 'left - treemacs-recenter-distance 0.1 - treemacs-recenter-after-file-follow nil - treemacs-recenter-after-tag-follow nil - treemacs-recenter-after-project-jump 'always - treemacs-recenter-after-project-expand 'on-distance - treemacs-show-cursor nil - treemacs-show-hidden-files t - treemacs-silent-filewatch nil - treemacs-silent-refresh nil - treemacs-sorting 'alphabetic-asc - treemacs-space-between-root-nodes t - treemacs-tag-follow-cleanup t - treemacs-tag-follow-delay 1.5 - treemacs-width 35 - treemacs-width-is-initially-locked t - treemacs-workspace-switch-cleanup nil) - - (treemacs-follow-mode t) - (treemacs-filewatch-mode t) - (treemacs-fringe-indicator-mode 'always) - (when treemacs-python-executable - (treemacs-git-commit-diff-mode t)) - - (pcase (cons (not (null (executable-find "git"))) - (not (null treemacs-python-executable))) - (`(t . t) (treemacs-git-mode 'deferred)) - (`(t . _) (treemacs-git-mode 'simple))) - - (treemacs-hide-gitignored-files-mode nil)) - -(use-package treemacs-projectile - :ensure t - :after (treemacs projectile)) - -(use-package treemacs-all-the-icons - :ensure t - :after (treemacs all-the-icons) - :config - (treemacs-load-theme "all-the-icons")) - -;; Treemacs helper functions -(defun treemacs-toggle-and-focus () - "Toggle treemacs and focus on it if it's visible." - (interactive) - (if (treemacs-get-local-window) - (treemacs-toggle) - (progn - (treemacs) - (treemacs-select-window)))) - -(defun treemacs-search-file () - "Search for a file in the current project using helm." - (interactive) - (if (fboundp 'helm-projectile-find-file) - (helm-projectile-find-file) - (helm-find-files))) - -(defun treemacs-open-marked-files () - "Open all marked files in treemacs." - (interactive) - (when (eq major-mode 'treemacs-mode) - (treemacs-bulk-file-actions - :actions '(treemacs-visit-node-no-split)))) - -(defun treemacs-mark-visible-files () - "Mark all visible files in the current directory." - (interactive) - (when (eq major-mode 'treemacs-mode) - (save-excursion - (treemacs-goto-parent-node) - (treemacs-TAB-action) - (forward-line 1) - (while (and (not (eobp)) - (> (treemacs--get-depth-of-item) 0)) - (when (treemacs-is-node-file?) - (treemacs-do-mark)) - (forward-line 1))))) - -(global-set-key (kbd "C-c t f") 'treemacs-toggle-and-focus) -(global-set-key (kbd "C-c t s") 'treemacs-search-file) - -;;; Search Configuration (with Helm) -(use-package deadgrep - :ensure t - :bind (("C-c r" . deadgrep))) - -(use-package wgrep - :ensure t - :config - (setq wgrep-auto-save-buffer t) - (setq wgrep-enable-key "r")) - -(use-package anzu - :ensure t - :config - (global-anzu-mode 1) - :bind (([remap query-replace] . anzu-query-replace) - ([remap query-replace-regexp] . anzu-query-replace-regexp))) - -;;; Project Management -(use-package projectile - :ensure t - :init - (projectile-mode +1) - :bind-keymap ("C-c p" . projectile-command-map) - :config - (setq projectile-completion-system 'helm) - (setq projectile-switch-project-action #'projectile-dired)) - -;;; Development Tools - Moved to emacs-dev-config.el -;;; Use M-x enable-dev-mode to activate development features - -;;; Editor Enhancements -(use-package which-key - :ensure t - :init (which-key-mode) - :config - (setq which-key-idle-delay 0.3) - (setq which-key-popup-type 'side-window)) - -;;; Editor Enhancements - Development-specific features moved to emacs-dev-config.el - -(use-package all-the-icons-dired - :ensure t - :hook (dired-mode . all-the-icons-dired-mode)) - -(use-package diredfl - :ensure t - :config (diredfl-global-mode 1)) - -;; Enhanced Dired configuration for multi-file operations -(use-package dired - :ensure nil - :bind (("C-c d" . dired-jump) - ("C-c D" . projectile-dired)) - :config - (setq dired-dwim-target t) ; Guess target directory - (setq dired-recursive-copies 'always) - (setq dired-recursive-deletes 'always) - ;; Use macOS-compatible ls options (no --group-directories-first) - (setq dired-listing-switches "-alh") - - ;; Enable multiple file marking with mouse - (define-key dired-mode-map (kbd "") 'dired-find-file) - (define-key dired-mode-map (kbd "C-") 'dired-mouse-mark) - - ;; Quick marking shortcuts - (define-key dired-mode-map (kbd "* .") 'dired-mark-extension) - (define-key dired-mode-map (kbd "* /") 'dired-mark-directories)) - - ;; Custom sorting: directories first (dotted first), then files (dotted first) - (defun dired-sort-dotfiles-first () - "Sort dired: dirs first (dots first within), then files (dots first within)." - (save-excursion - (let (buffer-read-only) - (goto-char (point-min)) - ;; Skip past the directory header - (while (and (not (eobp)) - (not (looking-at "^ \\|^\\s-*$"))) - (forward-line 1)) - (let ((start (point)) - dirs dotdirs files dotfiles special-entries) - ;; Collect all entries - (while (not (eobp)) - (when (looking-at "^ \\(.*\\)$") - (let* ((line (match-string 0)) - (filename (ignore-errors (dired-get-filename 'no-dir t)))) - (cond - ;; Keep . and .. entries separate to put at top - ((member filename '("." "..")) - (push line special-entries)) - ;; Process other entries - (filename - (let ((fullpath (ignore-errors (dired-get-filename t)))) - (when fullpath - (cond - ;; Dot directory - ((and (file-directory-p fullpath) - (string-prefix-p "." filename)) - (push line dotdirs)) - ;; Regular directory - ((file-directory-p fullpath) - (push line dirs)) - ;; Dotfile - ((string-prefix-p "." filename) - (push line dotfiles)) - ;; Regular file - (t - (push line files))))))))) - (forward-line 1)) - ;; Delete old content and insert sorted - (when (or special-entries dirs dotdirs files dotfiles) - (delete-region start (point-max)) - (goto-char start) - ;; Insert in order: . and .., dot dirs, regular dirs, dotfiles, regular files - (dolist (line (nreverse special-entries)) - (insert line "\n")) - (dolist (line (sort (nreverse dotdirs) 'string<)) - (insert line "\n")) - (dolist (line (sort (nreverse dirs) 'string<)) - (insert line "\n")) - (dolist (line (sort (nreverse dotfiles) 'string<)) - (insert line "\n")) - (dolist (line (sort (nreverse files) 'string<)) - (insert line "\n"))))) - (set-buffer-modified-p nil))) - - ;; Apply custom sorting after dired reads directory - (add-hook 'dired-after-readin-hook 'dired-sort-dotfiles-first) - -;;; Markdown Support -(use-package markdown-mode - :ensure t - :mode (("\\.md\\'" . markdown-mode) - ("\\.markdown\\'" . markdown-mode)) - :hook (markdown-mode . (lambda () - (visual-line-mode 1) - (flyspell-mode 1) - (auto-fill-mode -1))) - :bind (:map markdown-mode-map - ("C-c C-l" . markdown-insert-link) - ("C-c C-i" . markdown-insert-image) - ("C-c C-c p" . markdown-preview) - ("C-c C-c l" . markdown-live-preview-mode)) - :config - (setq markdown-command "markdown") - (setq markdown-enable-wiki-links t) - (setq markdown-italic-underscore t) - (setq markdown-asymmetric-header t) - (setq markdown-make-gfm-checkboxes-buttons t) - (setq markdown-gfm-uppercase-checkbox t) - (setq markdown-fontify-code-blocks-natively t)) - -(use-package markdown-toc - :ensure t - :after markdown-mode - :bind (:map markdown-mode-map - ("C-c C-t" . markdown-toc-generate-or-refresh-toc))) - -(use-package grip-mode - :ensure t - :after markdown-mode - :bind (:map markdown-mode-map - ("C-c C-c g" . grip-mode))) - - -;;; UI Configuration -(setq-default display-fill-column-indicator-column 80) -(setq-default display-fill-column-indicator-character ?\u2502) -(global-display-fill-column-indicator-mode 1) -(set-face-attribute 'fill-column-indicator nil :foreground "red") - -;; Enable mouse window resizing -(setq mouse-autoselect-window nil) ; Don't auto-select windows on mouse hover -(setq window-divider-default-places t) ; Show dividers everywhere -(setq window-divider-default-bottom-width 1) ; Bottom divider width -(setq window-divider-default-right-width 1) ; Right divider width -(window-divider-mode 1) ; Enable window dividers for easier mouse dragging - -;; CUA mode for rectangles -(setq cua-auto-tabify-rectangles nil) -(setq cua-keep-region-after-copy t) -(global-set-key (kbd "C-") 'cua-set-rectangle-mark) - -;;; Session Management -(desktop-save-mode 1) -(setq desktop-save t) -(setq desktop-auto-save-timeout 300) -(setq desktop-path '("~/.emacs.d/")) -(setq desktop-dirname "~/.emacs.d/") -(setq desktop-base-file-name "emacs-desktop") -(setq desktop-restore-eager 10) -(setq desktop-restore-frames t) - -(savehist-mode 1) -(setq savehist-file "~/.emacs.d/savehist") -(setq history-length 1000) - -(save-place-mode 1) -(setq save-place-file "~/.emacs.d/saveplace") - -(recentf-mode 1) -(setq recentf-max-menu-items 50) -(setq recentf-max-saved-items 200) - -;;; Performance Optimizations -(setq gc-cons-threshold 100000000) -(setq read-process-output-max (* 1024 1024)) - -;;; General Settings -(setq-default indent-tabs-mode nil) -(setq tab-width 4) -(setq inhibit-startup-screen t) -(global-auto-revert-mode t) -(electric-pair-mode 1) -(show-paren-mode 1) -(setq show-paren-delay 0) -(global-hl-line-mode 1) - -;; Use system ls for better performance and features -;; Use ls-lisp (Emacs's built-in ls emulation) for better cross-platform compatibility -;; This avoids issues with macOS ls not supporting GNU ls options -(setq ls-lisp-use-insert-directory-program nil) ; Use Emacs's ls-lisp instead of system ls -(setq ls-lisp-dirs-first t) ; Group directories first (already set in custom-variables) - -;; Auto-save and backup settings -(setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) -(setq delete-old-versions t - kept-new-versions 6 - kept-old-versions 2 - version-control t) -(setq auto-save-file-name-transforms - '((".*" "~/.emacs.d/auto-save-list/" t))) - -;;; Custom Functions -(defun kill-current-buffer-no-confirm () - "Kill the current buffer without confirmation, unless it has unsaved changes." - (interactive) - (kill-buffer (current-buffer))) - -(defun package-refresh-without-proxy () - "Temporarily disable proxy and refresh packages." - (interactive) - (let ((url-proxy-services nil)) - (package-refresh-contents) - (message "Package list refreshed without proxy"))) - -(defun package-install-without-proxy (package) - "Install PACKAGE without using proxy." - (interactive - (list (intern (completing-read "Install package: " - (mapcar #'car package-archive-contents))))) - (let ((url-proxy-services nil)) - (package-install package) - (message "Package %s installed without proxy" package))) - -(defun install-dev-packages () - "Install development packages without proxy." - (interactive) - (let ((url-proxy-services nil) - (dev-packages '(lsp-mode lsp-ui lsp-treemacs - company company-box yasnippet - flycheck magit forge))) - (package-refresh-contents) - (dolist (pkg dev-packages) - (unless (package-installed-p pkg) - (condition-case err - (progn - (package-install pkg) - (message "Installed %s" pkg)) - (error - (message "Failed to install %s: %s" pkg err))))) - (message "Development packages installation complete"))) - -(defvar url-proxy-services-backup nil - "Backup of proxy settings.") - -(defun toggle-proxy () - "Toggle proxy settings on/off." - (interactive) - (if url-proxy-services - (progn - (setq url-proxy-services-backup url-proxy-services) - (setq url-proxy-services nil) - (message "Proxy disabled")) - (progn - (setq url-proxy-services (or url-proxy-services-backup - '(("https" . "eudewerepo001:3128") - ("http" . "eudewerepo001:3128")))) - (message "Proxy enabled: %s" (cdr (assoc "http" url-proxy-services)))))) - -(defun reload-emacs-config () - "Reload the Emacs configuration file and all dependent configs." - (interactive) - ;; First reload the main init.el - (load-file (expand-file-name "init.el" user-emacs-directory)) - - ;; Reload development config if it exists - (let ((dev-config (expand-file-name "emacs-dev-config.el" user-emacs-directory))) - (when (file-exists-p dev-config) - (load-file dev-config))) - - ;; Reload SHR config if it exists - (let ((shr-config (expand-file-name "shr-config.el" user-emacs-directory))) - (when (file-exists-p shr-config) - (load-file shr-config))) - - ;; Reload elfeed config if it exists - (let ((elfeed-config (expand-file-name "elfeed-config.el" user-emacs-directory))) - (when (file-exists-p elfeed-config) - (load-file elfeed-config))) - - ;; Reload mu4e config if it exists - (let ((mu4e-config (expand-file-name "mu4e-config.el" user-emacs-directory))) - (when (file-exists-p mu4e-config) - (condition-case err - (load-file mu4e-config) - (error - (message "mu4e config available but mu4e not installed"))))) - - (message "Emacs configuration fully reloaded!")) - -;;; Final Keybindings -(global-set-key (kbd "C-x k") 'kill-current-buffer-no-confirm) -(global-set-key (kbd "C-c C-r") 'reload-emacs-config) -(global-set-key (kbd "C-x C-b") 'helm-buffers-list) - -;;; Fix for keybinding conflicts with CUA mode -(let ((keybinding-fix (expand-file-name "keybinding-fix.el" user-emacs-directory))) - (when (file-exists-p keybinding-fix) - (load-file keybinding-fix) - ;; Automatically apply fixes for special modes - (fix-elfeed-keybindings) - (fix-portfolio-tracker-keybindings) - (disable-cua-in-special-modes) - (message "Keybinding fixes loaded and applied."))) - -;;; SHR Configuration (for HTML rendering in mu4e, elfeed, eww) +;; SHR Configuration (for HTML rendering in mu4e, elfeed, eww) (let ((shr-config (expand-file-name "shr-config.el" user-emacs-directory))) (when (file-exists-p shr-config) (load-file shr-config) (message "SHR configuration loaded."))) -;;; RSS Reader Configuration (Elfeed) +;; RSS Reader Configuration (Elfeed) (let ((elfeed-config (expand-file-name "elfeed-config.el" user-emacs-directory))) (when (file-exists-p elfeed-config) (load-file elfeed-config) (message "Elfeed RSS reader configuration loaded."))) -;;; Email Configuration (mu4e) +;; Email Configuration (mu4e) (let ((mu4e-config (expand-file-name "mu4e-config.el" user-emacs-directory))) (when (file-exists-p mu4e-config) (condition-case err @@ -771,56 +71,94 @@ (error (message "mu4e configuration available but mu4e not installed. Install mu4e package to enable email."))))) -;;; Beancount Configuration +;; Beancount Configuration (let ((beancount-config (expand-file-name "beancount-config.el" user-emacs-directory))) (when (file-exists-p beancount-config) (load-file beancount-config) (message "Beancount portfolio tracking configuration loaded."))) -;;; Portfolio Tracker Configuration +;; Portfolio Tracker Configuration (with-eval-after-load 'tabulated-list (let ((portfolio-tracker (expand-file-name "portfolio-tracker-v2.el" user-emacs-directory))) (when (file-exists-p portfolio-tracker) (load-file portfolio-tracker) (message "Portfolio tracker with live prices loaded.")))) +;; Keybinding fixes for special modes +(let ((keybinding-fix (expand-file-name "keybinding-fix.el" user-emacs-directory))) + (when (file-exists-p keybinding-fix) + (load-file keybinding-fix) + ;; Automatically apply fixes for special modes + (fix-elfeed-keybindings) + (fix-portfolio-tracker-keybindings) + (disable-cua-in-special-modes) + (message "Keybinding fixes loaded and applied."))) + +;;; Custom Settings (preserved from original) +;;; These are managed by Emacs Custom system - do not edit manually +(setq custom-file (expand-file-name "custom.el" user-emacs-directory)) + +;; Create custom.el with the existing customizations if it doesn't exist +(unless (file-exists-p custom-file) + (with-temp-file custom-file + (insert ";;; custom.el --- Custom variables set by Emacs -*- lexical-binding: t -*-\n") + (insert ";;; Commentary:\n") + (insert ";;; This file contains customizations set through the Custom interface.\n") + (insert ";;; It is automatically loaded by init.el.\n\n") + (insert ";;; Code:\n\n") + (insert "(custom-set-variables\n") + (insert " ;; custom-set-variables was added by Custom.\n") + (insert " ;; If you edit it by hand, you could mess it up, so be careful.\n") + (insert " ;; Your init file should contain only one such instance.\n") + (insert " ;; If there is more than one, they won't work right.\n") + (insert " '(custom-enabled-themes '(developer-dark))\n") + (insert " '(custom-safe-themes\n") + (insert " '(\"2974c2d5ffede4f111b02701ccdfadfbcde3d158ad9b09dade627b3ce3049ea1\"\n") + (insert " \"f8859f15bd0089a85d8d14e21dd774a9a8b391dac076e3ff17a13529b3d16576\"\n") + (insert " \"de2f009a49f8eaf2f323519d86016849cd1716d979bc3f7e4afb58899e52ddb7\"\n") + (insert " \"9fb69436c074b82a62b78b8d733e6274d0bd16d156f7b094e2afe4345c040c49\"\n") + (insert " \"004f174754c688f358fa2afc4f8699b5db647fbfaa0d6b55ff39f63e05bfbbf5\"\n") + (insert " \"ca1b398ceb1b61709197478dc7f705b8337a0a9631e399948e643520c5557382\"\n") + (insert " \"75eef60308d7328ed14fa27002e85de255c2342e73275173a14ed3aa1643d545\"\n") + (insert " \"77f281064ea1c8b14938866e21c4e51e4168e05db98863bd7430f1352cab294a\"\n") + (insert " \"242e6f00c98aa43539b41c505ef094d21cbc981203809a82949efaa2bc6cb194\"\n") + (insert " \"c9e63647d90e7dc59d52c176fbfd46fd2cfed275cd2bad9b29b6cf620d3389b6\"\n") + (insert " \"ad7d874d137291e09fe2963babc33d381d087fa14928cb9d34350b67b6556b6d\"\n") + (insert " default))\n") + (insert " '(diff-hl-global-modes t)\n") + (insert " '(neo-show-hidden-files t)\n") + (insert " '(neo-window-width 40)\n") + (insert " '(url-proxy-services\n") + (insert " '((\"https\" . \"eudewerepo001:3128\") (\"http\" . \"eudewerepo001:3128\")))\n") + (insert " '(safe-local-variable-values\n") + (insert " '((company-backends\n") + (insert " (company-qml company-capf company-files company-yasnippet))\n") + (insert " (lsp-clients-qml-server-executable . \"/usr/lib/qt6/bin/qmlls\")\n") + (insert " (company-minimum-prefix-length . 1) (company-idle-delay . 0.2)\n") + (insert " (lsp-clangd-binary-path . \"clangd\")\n") + (insert " (lsp-clients-clangd-args\n") + (insert " \"--compile-commands-dir=/home/jens/sources/thulio\"\n") + (insert " \"--background-index\" \"--clang-tidy\"\n") + (insert " \"--completion-style=detailed\" \"--header-insertion=iwyu\"\n") + (insert " \"--pch-storage=memory\")\n") + (insert " (projectile-project-root . \"/home/jens/sources/thulio\"))))\n\n") + (insert "(custom-set-faces\n") + (insert " ;; custom-set-faces was added by Custom.\n") + (insert " ;; If you edit it by hand, you could mess it up, so be careful.\n") + (insert " ;; Your init file should contain only one such instance.\n") + (insert " ;; If there is more than one, they won't work right.\n") + (insert " )\n\n") + (insert "(provide 'custom)\n") + (insert ";;; custom.el ends here\n"))) + +;; Load custom file +(when (file-exists-p custom-file) + (load custom-file)) + ;; Optional: Set default portfolio file to load on startup (defvar portfolio-tracker-default-file (expand-file-name "sample-portfolio-v2.el" user-emacs-directory) "Default portfolio file to load.") -;; Keybinding for quick access to portfolio tracker -(global-set-key (kbd "C-c $") 'portfolio-tracker) - -;;; Theme Management -(defun load-jens-dark-theme () - "Load the custom jens-dark theme." - (interactive) - (load-theme 'jens-dark t) - (message "Jens Dark theme loaded")) - -(defun switch-theme (theme) - "Switch to a different theme interactively." - (interactive - (list (intern (completing-read "Load theme: " - (mapcar #'symbol-name (custom-available-themes)))))) - (mapc #'disable-theme custom-enabled-themes) - (load-theme theme t) - (message "Switched to %s theme" theme)) - -;;; Development Mode Information -(defun show-dev-mode-info () - "Show information about development mode." - (interactive) - (message "Development mode is available. Use M-x enable-dev-mode to activate LSP, company-mode, flycheck, and other development tools.")) - -;; Show info on startup if dev config is loaded -(when (featurep 'emacs-dev-config) - (add-hook 'emacs-startup-hook - (lambda () - (run-with-timer 1 nil - (lambda () - (message "Development mode available. Use M-x enable-dev-mode to activate.")))))) - (provide 'init) -;;; init.el ends here +;;; init.el ends here \ No newline at end of file diff --git a/lisp/init-compile.el b/lisp/init-compile.el new file mode 100644 index 0000000..52ef46d --- /dev/null +++ b/lisp/init-compile.el @@ -0,0 +1,66 @@ +;;; init-compile.el --- Byte compilation utilities -*- lexical-binding: t -*- +;;; Commentary: +;;; Functions to byte-compile configuration files for faster loading + +;;; Code: + +(defun byte-compile-init-files () + "Byte compile all init files in the lisp directory." + (interactive) + (let ((lisp-dir (expand-file-name "lisp" user-emacs-directory))) + (byte-recompile-directory lisp-dir 0 t) + (message "All init files byte-compiled"))) + +(defun byte-compile-config () + "Byte compile the entire Emacs configuration." + (interactive) + (byte-compile-file (expand-file-name "init.el" user-emacs-directory)) + (byte-compile-file (expand-file-name "early-init.el" user-emacs-directory)) + (byte-compile-init-files) + (message "Configuration byte-compiled successfully")) + +(defun clean-byte-compiled-files () + "Remove all byte-compiled files from configuration." + (interactive) + (let ((lisp-dir (expand-file-name "lisp" user-emacs-directory))) + ;; Remove .elc files from lisp directory + (dolist (file (directory-files lisp-dir t "\\.elc$")) + (delete-file file)) + ;; Remove main init.elc and early-init.elc + (let ((init-elc (expand-file-name "init.elc" user-emacs-directory)) + (early-elc (expand-file-name "early-init.elc" user-emacs-directory))) + (when (file-exists-p init-elc) (delete-file init-elc)) + (when (file-exists-p early-elc) (delete-file early-elc))) + (message "All byte-compiled files removed"))) + +;; Auto-compile on save if enabled +(defvar auto-compile-on-save nil + "If non-nil, automatically byte-compile init files when saved.") + +(defun maybe-byte-compile-file () + "Byte compile the current file if it's an init file and auto-compile is enabled." + (when (and auto-compile-on-save + (string-match-p "\\(init\\|early-init\\).*\\.el$" buffer-file-name) + (not (string-match-p "\\.elc$" buffer-file-name))) + (byte-compile-file buffer-file-name))) + +(add-hook 'after-save-hook #'maybe-byte-compile-file) + +;; Byte compile configuration after packages are installed +(defun byte-compile-after-package-install () + "Byte compile configuration after new packages are installed." + (when (and (boundp 'package-alist) + (> (length package-alist) 0)) + (byte-compile-config))) + +;; Add startup message about byte compilation +(add-hook 'emacs-startup-hook + (lambda () + (let ((init-elc (expand-file-name "init.elc" user-emacs-directory))) + (if (file-exists-p init-elc) + (message "Running byte-compiled configuration") + (message "Running interpreted configuration (use M-x byte-compile-config for faster startup)")))) + 90) + +(provide 'init-compile) +;;; init-compile.el ends here \ No newline at end of file diff --git a/lisp/init-completion.el b/lisp/init-completion.el new file mode 100644 index 0000000..c72eec2 --- /dev/null +++ b/lisp/init-completion.el @@ -0,0 +1,205 @@ +;;; init-completion.el --- Modern completion configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Configuration for Vertico, Consult, Orderless, Marginalia, and Embark +;;; This replaces Helm with a lighter, faster, more native completion system + +;;; Code: + +;;; Vertico - Vertical completion UI +(use-package vertico + :ensure t + :init + (vertico-mode) + :config + ;; Different scroll margin + (setq vertico-scroll-margin 0) + ;; Show more candidates + (setq vertico-count 15) + ;; Grow and shrink the Vertico minibuffer + (setq vertico-resize t) + ;; Enable cycling + (setq vertico-cycle t)) + +;;; Savehist - Persist history over Emacs restarts +(use-package savehist + :ensure nil + :init + (savehist-mode)) + +;;; Orderless - Powerful completion style +(use-package orderless + :ensure t + :custom + (completion-styles '(orderless basic)) + (completion-category-overrides '((file (styles basic partial-completion))))) + +;;; Marginalia - Rich annotations in minibuffer +(use-package marginalia + :ensure t + :init + (marginalia-mode) + :bind (:map minibuffer-local-map + ("M-A" . marginalia-cycle))) + +;;; Consult - Practical commands based on completing-read +(use-package consult + :ensure t + :bind (;; C-c bindings (mode-specific-map) + ("C-c h" . consult-history) + ("C-c m" . consult-mode-command) + ("C-c k" . consult-kmacro) + ;; C-x bindings (ctl-x-map) + ("C-x M-:" . consult-complex-command) + ("C-x b" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ("C-x r b" . consult-bookmark) + ("C-x p b" . consult-project-buffer) + ;; Custom M-# bindings for fast register access + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) + ("C-M-#" . consult-register) + ;; Other custom bindings + ("M-y" . consult-yank-pop) + ;; M-g bindings (goto-map) + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) + ("M-g g" . consult-goto-line) + ("M-g M-g" . consult-goto-line) + ("M-g o" . consult-outline) + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ("M-g i" . consult-imenu) + ("M-g I" . consult-imenu-multi) + ;; M-s bindings (search-map) + ("M-s d" . consult-find) + ("M-s D" . consult-locate) + ("M-s g" . consult-grep) + ("M-s G" . consult-git-grep) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s k" . consult-keep-lines) + ("M-s u" . consult-focus-lines) + ;; Isearch integration + ("M-s e" . consult-isearch-history) + :map isearch-mode-map + ("M-e" . consult-isearch-history) + ("M-s e" . consult-isearch-history) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ;; Minibuffer history + :map minibuffer-local-map + ("M-s" . consult-history) + ("M-r" . consult-history)) + + ;; Enable automatic preview at point in the *Completions* buffer. + :hook (completion-list-mode . consult-preview-at-point-mode) + + :init + ;; Optionally configure the register formatting. This improves the register + ;; preview for `consult-register', `consult-register-load', + ;; `consult-register-store' and the Emacs built-ins. + (setq register-preview-delay 0.5 + register-preview-function #'consult-register-format) + + ;; Optionally tweak the register preview window. + (advice-add #'register-preview :override #'consult-register-window) + + ;; Use Consult to select xref locations with preview + (setq xref-show-xrefs-function #'consult-xref + xref-show-definitions-function #'consult-xref) + + :config + ;; Optionally configure preview. The default value + ;; is 'any, such that any key triggers the preview. + (setq consult-preview-key 'any) + + ;; Configure other variables and modes + (setq consult-narrow-key "<") ;; "C-+" + + ;; Make narrowing help available in the minibuffer. + (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help) + + ;; By default `consult-project-function' uses `project-root' from project.el. + ;; Configure a different project root function. + (autoload 'projectile-project-root "projectile") + (setq consult-project-function (lambda (_) (projectile-project-root)))) + +;;; Embark - Contextual actions +(use-package embark + :ensure t + :bind + (("C-." . embark-act) ;; pick some comfortable binding + ("C-;" . embark-dwim) ;; good alternative: M-. + ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings' + + :init + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command) + + :config + ;; Hide the mode line of the Embark live/completions buffers + (add-to-list 'display-buffer-alist + '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" + nil + (window-parameters (mode-line-format . none))))) + +;;; Embark-Consult - Integration between Embark and Consult +(use-package embark-consult + :ensure t + :hook + (embark-collect-mode . consult-preview-at-point-mode)) + +;;; Corfu - In-buffer completion popup +(use-package corfu + :ensure t + :custom + (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' + (corfu-auto t) ;; Enable auto completion + (corfu-auto-delay 0.2) + (corfu-auto-prefix 2) + (corfu-separator ?\s) ;; Orderless field separator + (corfu-quit-at-boundary nil) ;; Never quit at completion boundary + (corfu-quit-no-match nil) ;; Never quit, even if there is no match + (corfu-preview-current nil) ;; Disable current candidate preview + (corfu-preselect 'prompt) ;; Preselect the prompt + (corfu-on-exact-match nil) ;; Configure handling of exact matches + (corfu-scroll-margin 5) ;; Use scroll margin + + :init + (global-corfu-mode)) + +;;; Cape - Completion extensions for Corfu +(use-package cape + :ensure t + :init + ;; Add `completion-at-point-functions', used by `completion-at-point'. + (add-to-list 'completion-at-point-functions #'cape-dabbrev) + (add-to-list 'completion-at-point-functions #'cape-file) + (add-to-list 'completion-at-point-functions #'cape-keyword) + ;;(add-to-list 'completion-at-point-functions #'cape-tex) + ;;(add-to-list 'completion-at-point-functions #'cape-sgml) + ;;(add-to-list 'completion-at-point-functions #'cape-rfc1345) + ;;(add-to-list 'completion-at-point-functions #'cape-abbrev) + ;;(add-to-list 'completion-at-point-functions #'cape-dict) + ;;(add-to-list 'completion-at-point-functions #'cape-symbol) + ;;(add-to-list 'completion-at-point-functions #'cape-line) + ) + +;;; Additional Consult commands for enhanced functionality +(defun consult-ripgrep-project-root () + "Search project root with ripgrep." + (interactive) + (let ((root (or (projectile-project-root) default-directory))) + (consult-ripgrep root))) + +(global-set-key (kbd "C-c r") 'consult-ripgrep-project-root) + +;;; Make completion work nicely with Projectile +(with-eval-after-load 'projectile + (define-key projectile-command-map (kbd "b") #'consult-project-buffer) + (define-key projectile-command-map (kbd "r") #'consult-ripgrep)) + +(provide 'init-completion) +;;; init-completion.el ends here \ No newline at end of file diff --git a/lisp/init-core.el b/lisp/init-core.el new file mode 100644 index 0000000..10f5cd0 --- /dev/null +++ b/lisp/init-core.el @@ -0,0 +1,153 @@ +;;; init-core.el --- Core settings and package management -*- lexical-binding: t -*- +;;; Commentary: +;;; Core Emacs settings, package management, and fundamental configurations + +;;; Code: + +;;; Package Management +(require 'package) +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) +(package-initialize) + +;; Core package list - consolidated and cleaned +(setq package-selected-packages + '(;; Core utilities + use-package which-key + + ;; Themes + modus-themes + + ;; Version control + diff-hl + + ;; File management + treemacs treemacs-projectile treemacs-all-the-icons + neotree all-the-icons all-the-icons-dired diredfl + + ;; Modern completion ecosystem (replaces Helm) + vertico ; Vertical completion UI + consult ; Practical commands based on completing-read + orderless ; Powerful completion style + marginalia ; Rich annotations in minibuffer + embark ; Contextual actions + embark-consult ; Embark integration with Consult + corfu ; In-buffer completion popup + cape ; Completion extensions for Corfu + + ;; Core project management + projectile + + ;; Markdown & Notes + markdown-mode markdown-toc grip-mode + obsidian olivetti + + ;; Search and navigation + deadgrep ripgrep wgrep anzu + ibuffer-sidebar ibuffer-projectile + + ;; Required for some functionality + org dash s f ht spinner lv hydra avy + + ;; RSS/News reader + elfeed elfeed-org + + ;; Email (mu4e installed separately) + + ;; Finance tracking + beancount + + ;; God mode for modal editing + god-mode + + ;; Quality of life improvements + helpful ; Better help buffers + undo-tree ; Visual undo history + smartparens ; Better than electric-pair-mode + crux ; Collection of useful functions + ace-window ; Quick window switching + + ;; Terminal emulator + eat ; Emacs terminal emulator + )) + +;; Auto-install missing packages +(when (cl-find-if-not #'package-installed-p package-selected-packages) + (package-refresh-contents) + (mapc #'package-install package-selected-packages)) + +;; Ensure use-package is loaded +(eval-when-compile + (require 'use-package)) +(setq use-package-always-ensure t) + +;;; Performance Optimizations +;; Reset GC threshold after init for better runtime performance +(add-hook 'emacs-startup-hook + (lambda () + ;; Use the defaults we stored in early-init.el + (setq gc-cons-threshold (if (boundp 'default-gc-cons-threshold) + default-gc-cons-threshold + 16777216) ; 16MB + gc-cons-percentage (if (boundp 'default-gc-cons-percentage) + default-gc-cons-percentage + 0.1)) + (message "GC threshold reset to %s" gc-cons-threshold)) + 50) ; Run after most other startup hooks + +(setq read-process-output-max (* 1024 1024)) + +;;; General Settings +(setq-default indent-tabs-mode nil) +(setq tab-width 4) +(setq inhibit-startup-screen t) +(setq ring-bell-function 'ignore) + +;; Auto-save and backup settings +(setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) +(setq delete-old-versions t + kept-new-versions 6 + kept-old-versions 2 + version-control t) +(setq auto-save-file-name-transforms + '((".*" "~/.emacs.d/auto-save-list/" t))) + +;; Use ls-lisp (Emacs's built-in ls emulation) for better cross-platform compatibility +(setq ls-lisp-use-insert-directory-program nil) +(setq ls-lisp-dirs-first t) + +;;; Session Management +(save-place-mode 1) +(setq save-place-file "~/.emacs.d/saveplace") + +(savehist-mode 1) +(setq savehist-file "~/.emacs.d/savehist") +(setq history-length 1000) + +(recentf-mode 1) +(setq recentf-max-menu-items 50) +(setq recentf-max-saved-items 200) + +;; Desktop save mode - with lazy loading for better performance +;; Defer desktop restoration to speed up initial startup +(setq desktop-restore-eager 5) ; Restore only 5 buffers initially +(setq desktop-lazy-verbose nil) +(setq desktop-lazy-idle-delay 1) ; Restore rest after 1 second idle + +;; Enable desktop-save-mode after startup +(add-hook 'after-init-hook + (lambda () + (desktop-save-mode 1) + (setq desktop-save t) + (setq desktop-auto-save-timeout 300) + (setq desktop-path '("~/.emacs.d/")) + (setq desktop-dirname "~/.emacs.d/") + (setq desktop-base-file-name "emacs-desktop") + (setq desktop-restore-frames t) + ;; Load desktop after a delay + (run-with-idle-timer 2 nil + (lambda () + (desktop-read) + (message "Desktop restored"))))) + +(provide 'init-core) +;;; init-core.el ends here \ No newline at end of file diff --git a/lisp/init-dired.el b/lisp/init-dired.el new file mode 100644 index 0000000..68c2ab3 --- /dev/null +++ b/lisp/init-dired.el @@ -0,0 +1,91 @@ +;;; init-dired.el --- Dired configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Enhanced Dired configuration for file management + +;;; Code: + +(use-package diredfl + :ensure t + :defer t + :hook (dired-mode . diredfl-mode)) + +;; Enhanced Dired configuration for multi-file operations +(use-package dired + :ensure nil + :config + (setq dired-dwim-target t) ; Guess target directory + (setq dired-recursive-copies 'always) + (setq dired-recursive-deletes 'always) + ;; Use macOS-compatible ls options (no --group-directories-first) + (setq dired-listing-switches "-alh") + + ;; Enable multiple file marking with mouse + (define-key dired-mode-map (kbd "") 'dired-find-file) + (define-key dired-mode-map (kbd "C-") 'dired-mouse-mark) + + ;; Quick marking shortcuts + (define-key dired-mode-map (kbd "* .") 'dired-mark-extension) + (define-key dired-mode-map (kbd "* /") 'dired-mark-directories)) + +;; Custom sorting: directories first (dotted first), then files (dotted first) +(defun dired-sort-dotfiles-first () + "Sort dired: dirs first (dots first within), then files (dots first within)." + (save-excursion + (let (buffer-read-only) + (goto-char (point-min)) + ;; Skip past the directory header + (while (and (not (eobp)) + (not (looking-at "^ \\|^\\s-*$"))) + (forward-line 1)) + (let ((start (point)) + dirs dotdirs files dotfiles special-entries) + ;; Collect all entries + (while (not (eobp)) + (when (looking-at "^ \\(.*\\)$") + (let* ((line (match-string 0)) + (filename (ignore-errors (dired-get-filename 'no-dir t)))) + (cond + ;; Keep . and .. entries separate to put at top + ((member filename '("." "..")) + (push line special-entries)) + ;; Process other entries + (filename + (let ((fullpath (ignore-errors (dired-get-filename t)))) + (when fullpath + (cond + ;; Dot directory + ((and (file-directory-p fullpath) + (string-prefix-p "." filename)) + (push line dotdirs)) + ;; Regular directory + ((file-directory-p fullpath) + (push line dirs)) + ;; Dotfile + ((string-prefix-p "." filename) + (push line dotfiles)) + ;; Regular file + (t + (push line files))))))))) + (forward-line 1)) + ;; Delete old content and insert sorted + (when (or special-entries dirs dotdirs files dotfiles) + (delete-region start (point-max)) + (goto-char start) + ;; Insert in order: . and .., dot dirs, regular dirs, dotfiles, regular files + (dolist (line (nreverse special-entries)) + (insert line "\n")) + (dolist (line (sort (nreverse dotdirs) 'string<)) + (insert line "\n")) + (dolist (line (sort (nreverse dirs) 'string<)) + (insert line "\n")) + (dolist (line (sort (nreverse dotfiles) 'string<)) + (insert line "\n")) + (dolist (line (sort (nreverse files) 'string<)) + (insert line "\n"))))) + (set-buffer-modified-p nil))) + +;; Apply custom sorting after dired reads directory +(add-hook 'dired-after-readin-hook 'dired-sort-dotfiles-first) + +(provide 'init-dired) +;;; init-dired.el ends here \ No newline at end of file diff --git a/lisp/init-editor.el b/lisp/init-editor.el new file mode 100644 index 0000000..ac23d0e --- /dev/null +++ b/lisp/init-editor.el @@ -0,0 +1,53 @@ +;;; init-editor.el --- Editor enhancements and editing settings -*- lexical-binding: t -*- +;;; Commentary: +;;; Basic editor settings and enhancements for a better editing experience + +;;; Code: + +;;; Basic editing settings +(global-auto-revert-mode t) +;; Electric-pair-mode is replaced by smartparens in init-qol.el + +;; Enable shift-select mode for selecting text with Shift+Arrow keys +(setq shift-select-mode t) +(transient-mark-mode t) + +;;; Text manipulation +(global-set-key (kbd "C-") 'cua-set-rectangle-mark) + +;;; Anzu - show match information in mode line +(use-package anzu + :ensure t + :defer t + :config + (global-anzu-mode 1) + :bind (([remap query-replace] . anzu-query-replace) + ([remap query-replace-regexp] . anzu-query-replace-regexp))) + +;;; Avy - jump to visible text +(use-package avy + :ensure t + :defer t + :bind (("C-:" . avy-goto-char) + ("C-'" . avy-goto-char-2) + ("M-g f" . avy-goto-line) + ("M-g w" . avy-goto-word-1))) + +;;; Olivetti - distraction-free writing +(use-package olivetti + :ensure t + :defer t + :commands olivetti-mode + :config + (setq olivetti-body-width 100)) + +;;; God-mode configuration (disabled by default) +;; Uncomment the following lines to enable god-mode +;; (let ((god-config (expand-file-name "god-mode-config.el" user-emacs-directory))) +;; (when (file-exists-p god-config) +;; (condition-case err +;; (load-file god-config) +;; (error (message "Failed to load god-mode config: %s" err))))) + +(provide 'init-editor) +;;; init-editor.el ends here \ No newline at end of file diff --git a/lisp/init-eglot.el b/lisp/init-eglot.el new file mode 100644 index 0000000..81bc482 --- /dev/null +++ b/lisp/init-eglot.el @@ -0,0 +1,140 @@ +;;; init-eglot.el --- Eglot (built-in LSP) configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Lightweight LSP client using Emacs built-in Eglot (Emacs 29+) + +;;; Code: + +;; Eglot is built-in for Emacs 29+, otherwise install it +(unless (fboundp 'eglot) + (use-package eglot + :ensure t)) + +;; Configure Eglot +(with-eval-after-load 'eglot + ;; Performance tuning + (setq eglot-autoshutdown t) ; Shutdown language server after closing last file + (setq eglot-sync-connect 0) ; Don't block on language server startup + (setq eglot-connect-timeout 10) ; Connection timeout in seconds + (setq eglot-events-buffer-size 0) ; Disable events buffer for performance + (setq eglot-ignored-server-capabilities '(:documentHighlightProvider)) ; Disable some features for speed + + ;; Don't let Eglot manage Flymake, we'll configure it separately + (setq eglot-stay-out-of '(flymake)) + + ;; Workspace configuration for different servers + (setq-default eglot-workspace-configuration + '(:pylsp (:plugins (:pycodestyle (:enabled t) + :pyflakes (:enabled t) + :pylint (:enabled nil))) + :gopls (:usePlaceholders t) + ;; Disable ESLint for TypeScript/JavaScript by default + :typescript (:validate (:enable :json-false)) + :javascript (:validate (:enable :json-false)) + :eslint (:enable :json-false))) + + ;; Configure language servers + (add-to-list 'eglot-server-programs + '((c-mode c++-mode c-ts-mode c++-ts-mode) . ("clangd" + "--background-index" + "--clang-tidy" + "--completion-style=detailed" + "--header-insertion=iwyu" + "--pch-storage=memory"))) + + (add-to-list 'eglot-server-programs + '((python-mode python-ts-mode) . ("pylsp"))) + + (add-to-list 'eglot-server-programs + '((rust-mode rust-ts-mode) . ("rust-analyzer"))) + + (add-to-list 'eglot-server-programs + '((go-mode go-ts-mode) . ("gopls"))) + + (add-to-list 'eglot-server-programs + '((typescript-mode typescript-ts-mode tsx-ts-mode) . ("typescript-language-server" "--stdio"))) + + (add-to-list 'eglot-server-programs + '((js-mode js-ts-mode) . ("typescript-language-server" "--stdio"))) + + ;; QML support (if qmlls is available) + (when (executable-find "qmlls") + (add-to-list 'eglot-server-programs + '(qml-mode . ("qmlls")))) + + ;; Format on save + (defun eglot-format-buffer-on-save () + "Format buffer with eglot before saving." + (add-hook 'before-save-hook #'eglot-format-buffer -10 t)) + + ;; Keybindings + (define-key eglot-mode-map (kbd "C-c l r") 'eglot-rename) + (define-key eglot-mode-map (kbd "C-c l a") 'eglot-code-actions) + (define-key eglot-mode-map (kbd "C-c l f") 'eglot-format) + (define-key eglot-mode-map (kbd "C-c l F") 'eglot-format-buffer) + (define-key eglot-mode-map (kbd "C-c l h") 'eldoc) + (define-key eglot-mode-map (kbd "C-c l d") 'xref-find-definitions) + (define-key eglot-mode-map (kbd "C-c l R") 'xref-find-references) + (define-key eglot-mode-map (kbd "C-c l i") 'eglot-find-implementation) + (define-key eglot-mode-map (kbd "C-c l t") 'eglot-find-typeDefinition)) + +;; Hook eglot to programming modes +(defun maybe-enable-eglot () + "Enable eglot if a language server is available." + (when (and (not (bound-and-true-p lsp-mode)) ; Don't conflict with lsp-mode + (eglot-current-server)) ; Only if server is available + (eglot-ensure))) + +;; Enable eglot for specific modes +(dolist (mode '(c-mode-hook + c++-mode-hook + c-ts-mode-hook + c++-ts-mode-hook + python-mode-hook + python-ts-mode-hook + rust-mode-hook + rust-ts-mode-hook + go-mode-hook + go-ts-mode-hook + js-mode-hook + js-ts-mode-hook + typescript-mode-hook + typescript-ts-mode-hook + tsx-ts-mode-hook)) + (add-hook mode #'eglot-ensure)) + +;; Integration with Corfu for completion +(with-eval-after-load 'corfu + (setq completion-category-overrides '((eglot (styles orderless)))) + (add-hook 'eglot-managed-mode-hook + (lambda () + (setq-local completion-at-point-functions + (list (cape-super-capf + #'eglot-completion-at-point + #'cape-file)))))) + +;; Eldoc configuration for better documentation display +(with-eval-after-load 'eldoc + (setq eldoc-echo-area-use-multiline-p nil) ; Don't use multi-line eldoc + (setq eldoc-idle-delay 0.5)) + +;; Diagnostic display with Flymake (built-in) +(with-eval-after-load 'flymake + (define-key flymake-mode-map (kbd "C-c ! n") 'flymake-goto-next-error) + (define-key flymake-mode-map (kbd "C-c ! p") 'flymake-goto-prev-error) + (define-key flymake-mode-map (kbd "C-c ! l") 'flymake-show-buffer-diagnostics) + (define-key flymake-mode-map (kbd "C-c ! L") 'flymake-show-project-diagnostics)) + +;; Helper functions +(defun eglot-organize-imports () + "Organize imports using Eglot." + (interactive) + (eglot-code-actions nil nil "source.organizeImports" t)) + +(defun eglot-restart-server () + "Restart the Eglot server." + (interactive) + (call-interactively #'eglot-shutdown) + (call-interactively #'eglot)) + +(provide 'init-eglot) +;;; init-eglot.el ends here \ No newline at end of file diff --git a/lisp/init-emergency-fix.el b/lisp/init-emergency-fix.el new file mode 100644 index 0000000..34d5e47 --- /dev/null +++ b/lisp/init-emergency-fix.el @@ -0,0 +1,72 @@ +;;; init-emergency-fix.el --- Emergency fixes for editing issues -*- lexical-binding: t -*- +;;; Commentary: +;;; Quick fixes for when editing is broken + +;;; Code: + +;; Emergency function to restore editing capability +(defun fix-editing-now () + "Emergency fix to restore editing capability." + (interactive) + ;; Disable read-only mode + (read-only-mode -1) + (setq buffer-read-only nil) + + ;; Disable god-mode if it's enabled + (when (boundp 'god-local-mode) + (god-local-mode -1)) + (when (boundp 'god-global-mode) + (god-global-mode -1)) + + ;; Disable view-mode if active + (when (bound-and-true-p view-mode) + (view-mode -1)) + + ;; Ensure we're not in special modes + (when (bound-and-true-p special-mode) + (fundamental-mode)) + + ;; Disable any potential read-only causing modes + (when (bound-and-true-p buffer-read-only) + (toggle-read-only -1)) + + ;; Reset cursor type + (setq cursor-type 'bar) + + ;; Ensure CUA mode isn't causing issues + (when cua-mode + (cua-mode -1) + (cua-mode 1)) + + (message "Editing should be restored. If not, try M-x fundamental-mode")) + +;; Diagnostic function +(defun diagnose-editing-issue () + "Diagnose why editing might be disabled." + (interactive) + (let ((issues '())) + (when buffer-read-only + (push "Buffer is read-only" issues)) + (when (bound-and-true-p god-local-mode) + (push "God-mode is enabled" issues)) + (when (bound-and-true-p view-mode) + (push "View-mode is enabled" issues)) + (when (bound-and-true-p special-mode) + (push "Special-mode is active" issues)) + (when (eq major-mode 'fundamental-mode) + (push "In fundamental-mode" issues)) + (if issues + (message "Issues found: %s" (string-join issues ", ")) + (message "No obvious issues found. Mode: %s, Read-only: %s" + major-mode buffer-read-only)))) + +;; Global keybinding for emergency fix +(global-set-key (kbd "C-c C-!") 'fix-editing-now) +(global-set-key (kbd "C-c C-?") 'diagnose-editing-issue) + +;; Ensure fundamental settings are correct +(setq-default buffer-read-only nil) +(setq inhibit-read-only nil) + +(provide 'init-emergency-fix) +;;; init-emergency-fix.el ends here \ No newline at end of file diff --git a/lisp/init-eslint-fix.el b/lisp/init-eslint-fix.el new file mode 100644 index 0000000..18480bb --- /dev/null +++ b/lisp/init-eslint-fix.el @@ -0,0 +1,102 @@ +;;; init-eslint-fix.el --- Fix ESLint configuration issues -*- lexical-binding: t -*- +;;; Commentary: +;;; Handles ESLint configuration issues when no .eslintrc is present + +;;; Code: + +;; Function to check if ESLint is configured in the current project +(defun project-has-eslint-config-p () + "Check if the current project has ESLint configuration." + (let ((project-root (or (projectile-project-root) + (locate-dominating-file default-directory ".git") + default-directory))) + (or (file-exists-p (expand-file-name ".eslintrc" project-root)) + (file-exists-p (expand-file-name ".eslintrc.js" project-root)) + (file-exists-p (expand-file-name ".eslintrc.json" project-root)) + (file-exists-p (expand-file-name ".eslintrc.yml" project-root)) + (file-exists-p (expand-file-name ".eslintrc.yaml" project-root)) + (file-exists-p (expand-file-name "eslint.config.js" project-root)) + (file-exists-p (expand-file-name "package.json" project-root))))) + +;; Disable ESLint for TypeScript/JavaScript if no config exists +(defun maybe-disable-eslint () + "Disable ESLint if no configuration is found." + (when (and (derived-mode-p 'js-mode 'js-ts-mode 'typescript-mode 'typescript-ts-mode 'tsx-ts-mode) + (not (project-has-eslint-config-p))) + ;; Tell Eglot to ignore ESLint diagnostics + (setq-local eglot-ignored-server-capabilities + (append eglot-ignored-server-capabilities + '(:documentFormattingProvider :documentRangeFormattingProvider))) + (message "ESLint disabled - no configuration found in project"))) + +;; Hook to check ESLint configuration +(add-hook 'eglot-managed-mode-hook #'maybe-disable-eslint) + +;; Alternative: Completely disable ESLint in typescript-language-server +(with-eval-after-load 'eglot + (defun eglot-disable-eslint-for-typescript () + "Disable ESLint for TypeScript language server." + (when (cl-find-if (lambda (server) + (and (eglot--server-capable server :textDocument/publishDiagnostics) + (string-match-p "typescript" (eglot--project-nickname server)))) + (eglot-current-server)) + (setq-local eglot-workspace-configuration + '(:typescript (:validate (:enable :json-false)) + :javascript (:validate (:enable :json-false)) + :eslint (:enable :json-false)))))) + +;; Function to manually disable ESLint in current buffer +(defun disable-eslint-in-buffer () + "Disable ESLint validation in the current buffer." + (interactive) + (when (eglot-current-server) + (eglot--server-capable-or-lose (eglot-current-server) :workspace/didChangeConfiguration) + (jsonrpc-notify + (eglot-current-server) + :workspace/didChangeConfiguration + '(:settings (:eslint (:enable :json-false) + :validate (:enable :json-false)))) + (message "ESLint disabled in current buffer"))) + +;; Function to create a basic .eslintrc if needed +(defun create-basic-eslintrc () + "Create a basic .eslintrc.json file in the project root." + (interactive) + (let* ((project-root (or (projectile-project-root) + (locate-dominating-file default-directory ".git") + default-directory)) + (eslintrc-path (expand-file-name ".eslintrc.json" project-root))) + (if (file-exists-p eslintrc-path) + (message ".eslintrc.json already exists") + (with-temp-file eslintrc-path + (insert "{\n") + (insert " \"env\": {\n") + (insert " \"browser\": true,\n") + (insert " \"es2021\": true,\n") + (insert " \"node\": true\n") + (insert " },\n") + (insert " \"extends\": \"eslint:recommended\",\n") + (insert " \"parserOptions\": {\n") + (insert " \"ecmaVersion\": \"latest\",\n") + (insert " \"sourceType\": \"module\"\n") + (insert " },\n") + (insert " \"rules\": {}\n") + (insert "}\n")) + (message "Created basic .eslintrc.json in %s" project-root)))) + +;; Global keybinding to disable ESLint - using C-c L e for LSP ESLint commands +(global-set-key (kbd "C-c L e d") 'disable-eslint-in-buffer) +(global-set-key (kbd "C-c L e c") 'create-basic-eslintrc) + +;; Advice to suppress ESLint errors in the echo area +(defun suppress-eslint-errors (orig-fun &rest args) + "Suppress ESLint configuration errors." + (let ((msg (apply orig-fun args))) + (if (and msg (string-match-p "ESLint.*not configured" msg)) + nil ; Don't show the message + msg))) + +(advice-add 'eglot--message :around #'suppress-eslint-errors) + +(provide 'init-eslint-fix) +;;; init-eslint-fix.el ends here \ No newline at end of file diff --git a/lisp/init-keybindings.el b/lisp/init-keybindings.el new file mode 100644 index 0000000..de581e1 --- /dev/null +++ b/lisp/init-keybindings.el @@ -0,0 +1,56 @@ +;;; init-keybindings.el --- Global keybindings -*- lexical-binding: t -*- +;;; Commentary: +;;; Global keybinding configurations + +;;; Code: + +;;; Buffer management +(global-set-key (kbd "C-x k") 'kill-current-buffer-no-confirm) + +;;; Configuration reload +(global-set-key (kbd "C-c C-r") 'reload-emacs-config) + +;;; Portfolio tracker +(global-set-key (kbd "C-c $") 'portfolio-tracker) + +;;; Window management +;; Consider adding ace-window or windmove keybindings here +;; (global-set-key (kbd "M-o") 'ace-window) +;; (windmove-default-keybindings) + +;;; Better defaults +(global-set-key (kbd "C-x C-r") 'recentf-open-files) +(global-set-key (kbd "M-/") 'hippie-expand) + +;;; Text manipulation helpers +(defun duplicate-current-line-or-region (arg) + "Duplicate current line, or region if active. +With argument ARG, make ARG copies." + (interactive "p") + (let (beg end (origin (point))) + (if (and mark-active (> (point) (mark))) + (exchange-point-and-mark)) + (setq beg (line-beginning-position)) + (if mark-active + (exchange-point-and-mark)) + (setq end (line-end-position)) + (let ((region (buffer-substring-no-properties beg end))) + (dotimes (i arg) + (goto-char end) + (newline) + (insert region) + (setq end (point))) + (goto-char (+ origin (* (length region) arg) arg))))) + +(global-set-key (kbd "C-c C-d") 'duplicate-current-line-or-region) + +;;; Quick buffer switching +(defun switch-to-previous-buffer () + "Switch to the most recently used buffer." + (interactive) + (switch-to-buffer (other-buffer (current-buffer) 1))) + +(global-set-key (kbd "C-c b") 'switch-to-previous-buffer) + +(provide 'init-keybindings) +;;; init-keybindings.el ends here \ No newline at end of file diff --git a/lisp/init-markdown.el b/lisp/init-markdown.el new file mode 100644 index 0000000..f7a9fe0 --- /dev/null +++ b/lisp/init-markdown.el @@ -0,0 +1,51 @@ +;;; init-markdown.el --- Markdown support configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Markdown editing and preview configuration + +;;; Code: + +(use-package markdown-mode + :ensure t + :defer t + :commands (markdown-mode gfm-mode) + :mode (("\\.md\\'" . markdown-mode) + ("\\.markdown\\'" . markdown-mode) + ("README\\.md\\'" . gfm-mode)) + :hook (markdown-mode . (lambda () + (visual-line-mode 1) + (flyspell-mode 1) + (auto-fill-mode -1))) + :bind (:map markdown-mode-map + ("C-c C-l" . markdown-insert-link) + ("C-c C-i" . markdown-insert-image) + ("C-c C-c p" . markdown-preview) + ("C-c C-c l" . markdown-live-preview-mode)) + :config + (setq markdown-command "markdown") + (setq markdown-enable-wiki-links t) + (setq markdown-italic-underscore t) + (setq markdown-asymmetric-header t) + (setq markdown-make-gfm-checkboxes-buttons t) + (setq markdown-gfm-uppercase-checkbox t) + (setq markdown-fontify-code-blocks-natively t)) + +(use-package markdown-toc + :ensure t + :after markdown-mode + :defer t + :bind (:map markdown-mode-map + ("C-c C-t" . markdown-toc-generate-or-refresh-toc))) + +(use-package grip-mode + :ensure t + :after markdown-mode + :defer t + :bind (:map markdown-mode-map + ("C-c C-c g" . grip-mode))) + +(use-package obsidian + :ensure t + :defer t) + +(provide 'init-markdown) +;;; init-markdown.el ends here \ No newline at end of file diff --git a/lisp/init-project.el b/lisp/init-project.el new file mode 100644 index 0000000..00c98e3 --- /dev/null +++ b/lisp/init-project.el @@ -0,0 +1,21 @@ +;;; init-project.el --- Project management configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Projectile and project management settings + +;;; Code: + +;;; Projectile - Project Management +(use-package projectile + :ensure t + :init + (projectile-mode +1) + :bind-keymap ("C-c p" . projectile-command-map) + :bind (("C-c d" . dired-jump) + ("C-c D" . projectile-dired)) + :config + (setq projectile-completion-system 'default) ; Use default completion (works with Vertico) + (setq projectile-switch-project-action #'projectile-dired) + (setq projectile-enable-caching t)) + +(provide 'init-project) +;;; init-project.el ends here \ No newline at end of file diff --git a/lisp/init-qol.el b/lisp/init-qol.el new file mode 100644 index 0000000..af287f5 --- /dev/null +++ b/lisp/init-qol.el @@ -0,0 +1,190 @@ +;;; init-qol.el --- Quality of Life improvements -*- lexical-binding: t -*- +;;; Commentary: +;;; Better help, undo, parentheses, and useful functions + +;;; Code: + +;;; Helpful - Better help buffers +(use-package helpful + :ensure t + :defer t + :bind + (("C-h f" . helpful-callable) + ("C-h v" . helpful-variable) + ("C-h k" . helpful-key) + ("C-h F" . helpful-function) + ("C-h C" . helpful-command) + ("C-c C-d" . helpful-at-point)) + :config + ;; Make helpful buffers more readable + (setq helpful-max-buffers 3)) + +;;; Undo-tree - Visual undo history +(use-package undo-tree + :ensure t + :diminish undo-tree-mode + :init + (global-undo-tree-mode) + :config + (setq undo-tree-visualizer-timestamps t) + (setq undo-tree-visualizer-diff t) + (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo-tree-history"))) + (setq undo-tree-auto-save-history t) + :bind (("C-x u" . undo-tree-visualize) + ("C-/" . undo-tree-undo) + ("C-?" . undo-tree-redo))) + +;;; Smartparens - Better parentheses handling +(use-package smartparens + :ensure t + :diminish smartparens-mode + :hook (prog-mode . smartparens-mode) + :init + ;; Start disabled to avoid conflicts + (setq smartparens-global-mode nil) + :config + ;; Load default config + (require 'smartparens-config) + + ;; Keybindings + (define-key smartparens-mode-map (kbd "C-M-f") 'sp-forward-sexp) + (define-key smartparens-mode-map (kbd "C-M-b") 'sp-backward-sexp) + (define-key smartparens-mode-map (kbd "C-M-n") 'sp-next-sexp) + (define-key smartparens-mode-map (kbd "C-M-p") 'sp-previous-sexp) + (define-key smartparens-mode-map (kbd "C-M-k") 'sp-kill-sexp) + (define-key smartparens-mode-map (kbd "C-M-w") 'sp-copy-sexp) + (define-key smartparens-mode-map (kbd "M-") 'sp-unwrap-sexp) + (define-key smartparens-mode-map (kbd "M-") 'sp-backward-unwrap-sexp) + (define-key smartparens-mode-map (kbd "C-") 'sp-forward-slurp-sexp) + (define-key smartparens-mode-map (kbd "C-") 'sp-forward-barf-sexp) + (define-key smartparens-mode-map (kbd "C-M-") 'sp-backward-barf-sexp) + (define-key smartparens-mode-map (kbd "C-M-") 'sp-backward-slurp-sexp) + (define-key smartparens-mode-map (kbd "M-D") 'sp-splice-sexp) + (define-key smartparens-mode-map (kbd "C-]") 'sp-select-next-thing-exchange) + (define-key smartparens-mode-map (kbd "C-<") 'sp-select-previous-thing) + (define-key smartparens-mode-map (kbd "C->") 'sp-select-next-thing) + + ;; Disable smartparens in minibuffer + (setq sp-ignore-modes-list '(minibuffer-mode minibuffer-inactive-mode)) + + ;; Don't use strict mode by default as it can prevent editing + ;; (dolist (mode '(emacs-lisp-mode-hook + ;; lisp-mode-hook + ;; lisp-interaction-mode-hook + ;; scheme-mode-hook + ;; clojure-mode-hook)) + ;; (add-hook mode #'smartparens-strict-mode)) + ) + +;;; Crux - Collection of useful functions +(use-package crux + :ensure t + :bind (("C-c o" . crux-open-with) + ("C-c n" . crux-cleanup-buffer-or-region) + ("C-c f" . crux-recentf-find-file) + ("C-M-z" . crux-indent-defun) + ("C-c u" . crux-view-url) + ("C-c e" . crux-eval-and-replace) + ("C-c w" . crux-swap-windows) + ("C-c D" . crux-delete-file-and-buffer) + ("C-c r" . crux-rename-buffer-and-file) + ("C-c t" . crux-visit-term-buffer) + ("C-c k" . crux-kill-other-buffers) + ("C-c TAB" . crux-indent-rigidly-and-copy-to-clipboard) + ("C-c I" . crux-find-user-init-file) + ("C-c S" . crux-find-shell-init-file) + ("s-r" . crux-recentf-find-file) + ("s-j" . crux-top-join-line) + ("C-^" . crux-top-join-line) + ("s-k" . crux-kill-whole-line) + ("C-" . crux-kill-line-backwards) + ("s-o" . crux-smart-open-line-above) + ("C-S-RET" . crux-smart-open-line-above) + ("C-RET" . crux-smart-open-line) + ("C-c c" . crux-create-scratch-buffer) + ("C-c d" . crux-duplicate-current-line-or-region) + ("C-c M-d" . crux-duplicate-and-comment-current-line-or-region) + ("C-c F" . crux-recentf-find-directory) + ([remap move-beginning-of-line] . crux-move-beginning-of-line) + ([remap kill-whole-line] . crux-kill-whole-line) + ([remap kill-line] . crux-smart-kill-line)) + :config + ;; Use crux cleanup on save for programming modes + (add-hook 'before-save-hook 'crux-cleanup-buffer-or-region)) + +;;; Ace-window - Quick window switching +(use-package ace-window + :ensure t + :bind (("M-o" . ace-window) + ("C-x o" . ace-window)) + :config + (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) + (setq aw-scope 'frame) + (setq aw-background t) + ;; Make ace-window numbers larger and more visible + (custom-set-faces + '(aw-leading-char-face + ((t (:inherit ace-jump-face-foreground :height 3.0)))))) + +;;; Additional useful functions +(defun qol-toggle-window-split () + "Toggle between horizontal and vertical split." + (interactive) + (if (= (count-windows) 2) + (let* ((this-win-buffer (window-buffer)) + (next-win-buffer (window-buffer (next-window))) + (this-win-edges (window-edges (selected-window))) + (next-win-edges (window-edges (next-window))) + (this-win-2nd (not (and (<= (car this-win-edges) + (car next-win-edges)) + (<= (cadr this-win-edges) + (cadr next-win-edges))))) + (splitter + (if (= (car this-win-edges) + (car (window-edges (next-window)))) + 'split-window-horizontally + 'split-window-vertically))) + (delete-other-windows) + (let ((first-win (selected-window))) + (funcall splitter) + (if this-win-2nd (other-window 1)) + (set-window-buffer (selected-window) this-win-buffer) + (set-window-buffer (next-window) next-win-buffer) + (select-window first-win) + (if this-win-2nd (other-window 1)))))) + +(global-set-key (kbd "C-x |") 'qol-toggle-window-split) + +;;; Window movement with windmove +(windmove-default-keybindings) ; Use Shift+arrows to move between windows +(setq windmove-wrap-around t) + +;;; Better buffer names for duplicates +(require 'uniquify) +(setq uniquify-buffer-name-style 'forward) +(setq uniquify-separator "/") +(setq uniquify-after-kill-buffer-p t) +(setq uniquify-ignore-buffers-re "^\\*") + +;;; Remember recently opened files +(setq recentf-max-saved-items 500) +(setq recentf-max-menu-items 15) +(setq recentf-auto-cleanup 'never) +(setq recentf-exclude '("^/var/folders\\.*" + "COMMIT_EDITMSG\\'" + ".*-autoloads\\.el\\'" + "[/\\]\\.elpa/")) + +;;; Better scrolling +(setq scroll-conservatively 101) +(setq scroll-margin 3) +(setq scroll-preserve-screen-position t) + +;;; Enable some disabled commands +(put 'narrow-to-region 'disabled nil) +(put 'narrow-to-page 'disabled nil) +(put 'upcase-region 'disabled nil) +(put 'downcase-region 'disabled nil) + +(provide 'init-qol) +;;; init-qol.el ends here \ No newline at end of file diff --git a/lisp/init-search.el b/lisp/init-search.el new file mode 100644 index 0000000..2230d81 --- /dev/null +++ b/lisp/init-search.el @@ -0,0 +1,30 @@ +;;; init-search.el --- Search and navigation tools -*- lexical-binding: t -*- +;;; Commentary: +;;; Configuration for search tools like deadgrep, ripgrep, and wgrep + +;;; Code: + +;;; Deadgrep - fast search with ripgrep +(use-package deadgrep + :ensure t + :defer t + :commands deadgrep + :bind (("C-c r" . deadgrep))) + +;;; Wgrep - editable grep buffers +(use-package wgrep + :ensure t + :defer t + :after grep + :config + (setq wgrep-auto-save-buffer t) + (setq wgrep-enable-key "r")) + +;;; Ripgrep +(use-package ripgrep + :ensure t + :defer t + :commands (ripgrep-regexp)) + +(provide 'init-search) +;;; init-search.el ends here \ No newline at end of file diff --git a/lisp/init-terminal.el b/lisp/init-terminal.el new file mode 100644 index 0000000..af7868e --- /dev/null +++ b/lisp/init-terminal.el @@ -0,0 +1,122 @@ +;;; init-terminal.el --- Terminal emulator configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Configuration for eat (Emacs Terminal Emulator) and other terminal settings + +;;; Code: + +;;; Eat - Emacs Terminal Emulator +(use-package eat + :ensure t + :defer t + :hook ((eshell-mode . eat-eshell-mode) + (eshell-mode . eat-eshell-visual-command-mode)) + :config + ;; Close eat buffer when process exits + (setq eat-kill-buffer-on-exit t) + + ;; Enable eat for eshell + (eat-eshell-mode 1) + + ;; Better color support + (setq eat-term-name "xterm-256color") + + ;; Set default shell + (setq eat-shell (getenv "SHELL")) + + ;; Increase scrollback + (setq eat-term-scrollback-size 10000) + + ;; Enable mouse support + (setq eat-enable-mouse t) + + ;; Custom keybindings for eat + (add-hook 'eat-mode-hook + (lambda () + (local-set-key (kbd "C-c C-d") 'eat-self-input) ; Send C-d to terminal + (local-set-key (kbd "C-c C-c") 'eat-self-input) ; Send C-c to terminal + (local-set-key (kbd "C-c C-z") 'eat-self-input) ; Send C-z to terminal + (local-set-key (kbd "C-c C-k") 'eat-kill-process) ; Kill terminal process + (local-set-key (kbd "C-c C-p") 'eat-previous-shell-prompt) + (local-set-key (kbd "C-c C-n") 'eat-next-shell-prompt))) + + ;; Auto-close on exit codes + (defun eat-close-on-exit () + "Close eat buffer automatically when shell exits." + (when (and (eq major-mode 'eat-mode) + (not (process-live-p (get-buffer-process (current-buffer))))) + (kill-buffer))) + + ;; Add hook to close buffer when process exits + (add-hook 'eat-exit-hook 'eat-close-on-exit) + + ;; Alternative: Close with a delay to see exit message + (defun eat-close-on-exit-with-delay () + "Close eat buffer with a small delay after exit." + (run-with-timer 0.5 nil + (lambda (buf) + (when (buffer-live-p buf) + (kill-buffer buf))) + (current-buffer))) + + ;; Uncomment this and comment the previous hook if you want a delay + ;; (add-hook 'eat-exit-hook 'eat-close-on-exit-with-delay) + ) + +;;; Quick terminal access +(defun open-eat-terminal () + "Open a new eat terminal in the current window." + (interactive) + (eat)) + +(defun open-eat-terminal-here () + "Open a new eat terminal in the current directory." + (interactive) + (let ((default-directory (if (buffer-file-name) + (file-name-directory (buffer-file-name)) + default-directory))) + (eat))) + +(defun open-eat-terminal-split () + "Open a new eat terminal in a horizontal split." + (interactive) + (split-window-below) + (other-window 1) + (eat)) + +(defun open-eat-terminal-vsplit () + "Open a new eat terminal in a vertical split." + (interactive) + (split-window-right) + (other-window 1) + (eat)) + +;;; Global keybindings for terminal access - using C-c E for Eat terminal +(global-set-key (kbd "C-c E t") 'open-eat-terminal) +(global-set-key (kbd "C-c E h") 'open-eat-terminal-here) +(global-set-key (kbd "C-c E s") 'open-eat-terminal-split) +(global-set-key (kbd "C-c E v") 'open-eat-terminal-vsplit) + +;;; Eshell configuration (as alternative to eat) +(use-package eshell + :ensure nil + :config + ;; Close eshell on exit + (defun eshell-close-on-exit () + "Close eshell window on exit." + (when (not (one-window-p)) + (delete-window))) + + (add-hook 'eshell-exit-hook 'eshell-close-on-exit) + + ;; Better prompt + (setq eshell-prompt-function + (lambda () + (concat + (propertize (abbreviate-file-name (eshell/pwd)) 'face '(:foreground "blue")) + (if (= (user-uid) 0) " # " " $ ")))) + + (setq eshell-highlight-prompt t) + (setq eshell-prompt-regexp "^[^#$\n]* [#$] ")) + +(provide 'init-terminal) +;;; init-terminal.el ends here \ No newline at end of file diff --git a/lisp/init-treemacs.el b/lisp/init-treemacs.el new file mode 100644 index 0000000..7942850 --- /dev/null +++ b/lisp/init-treemacs.el @@ -0,0 +1,125 @@ +;;; init-treemacs.el --- Treemacs configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; File tree configuration with Treemacs + +;;; Code: + +(use-package treemacs + :ensure t + :defer t + :commands (treemacs treemacs-select-window) + :bind (("M-0" . treemacs-select-window) + ("C-c T t" . treemacs) + ("C-c T 1" . treemacs-delete-other-windows) + ("C-c T d" . treemacs-select-directory) + ("C-c T B" . treemacs-bookmark) + ("" . treemacs) + ("C-c T f" . treemacs-toggle-and-focus) + ("C-c T s" . treemacs-search-file) + :map treemacs-mode-map + ("/" . treemacs-search-file) + ("C-s" . projectile-find-file) + ("s" . consult-ripgrep)) + :config + (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0) + treemacs-deferred-git-apply-delay 0.5 + treemacs-directory-name-transformer #'identity + treemacs-display-in-side-window t + treemacs-eldoc-display 'simple + treemacs-file-event-delay 2000 + treemacs-file-follow-delay 0.2 + treemacs-follow-after-init t + treemacs-expand-after-init t + treemacs-find-workspace-method 'find-for-file-or-pick-first + treemacs-hide-dot-git-directory t + treemacs-indentation 2 + treemacs-indentation-string " " + treemacs-is-never-other-window nil + treemacs-max-git-entries 5000 + treemacs-missing-project-action 'ask + treemacs-move-forward-on-expand nil + treemacs-no-delete-other-windows t + treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) + treemacs-position 'left + treemacs-recenter-distance 0.1 + treemacs-recenter-after-file-follow nil + treemacs-recenter-after-tag-follow nil + treemacs-recenter-after-project-jump 'always + treemacs-recenter-after-project-expand 'on-distance + treemacs-show-cursor nil + treemacs-show-hidden-files t + treemacs-silent-filewatch nil + treemacs-silent-refresh nil + treemacs-sorting 'alphabetic-asc + treemacs-space-between-root-nodes t + treemacs-tag-follow-cleanup t + treemacs-tag-follow-delay 1.5 + treemacs-width 35 + treemacs-width-is-initially-locked t + treemacs-workspace-switch-cleanup nil) + + (treemacs-follow-mode t) + (treemacs-filewatch-mode t) + (treemacs-fringe-indicator-mode 'always) + (when treemacs-python-executable + (treemacs-git-commit-diff-mode t)) + + (pcase (cons (not (null (executable-find "git"))) + (not (null treemacs-python-executable))) + (`(t . t) (treemacs-git-mode 'deferred)) + (`(t . _) (treemacs-git-mode 'simple))) + + (treemacs-hide-gitignored-files-mode nil)) + +(use-package treemacs-projectile + :ensure t + :after (treemacs projectile) + :defer t) + +(use-package treemacs-all-the-icons + :ensure t + :after (treemacs all-the-icons) + :defer t + :config + (treemacs-load-theme "all-the-icons")) + +;; Treemacs helper functions +(defun treemacs-toggle-and-focus () + "Toggle treemacs and focus on it if it's visible." + (interactive) + (if (treemacs-get-local-window) + (treemacs-toggle) + (progn + (treemacs) + (treemacs-select-window)))) + +(defun treemacs-search-file () + "Search for a file in the current project using consult." + (interactive) + (if (fboundp 'projectile-find-file) + (projectile-find-file) + (consult-find))) + +(defun treemacs-open-marked-files () + "Open all marked files in treemacs." + (interactive) + (when (eq major-mode 'treemacs-mode) + (treemacs-bulk-file-actions + :actions '(treemacs-visit-node-no-split)))) + +(defun treemacs-mark-visible-files () + "Mark all visible files in the current directory." + (interactive) + (when (eq major-mode 'treemacs-mode) + (save-excursion + (treemacs-goto-parent-node) + (treemacs-TAB-action) + (forward-line 1) + (while (and (not (eobp)) + (> (treemacs--get-depth-of-item) 0)) + (when (treemacs-is-node-file?) + (treemacs-do-mark)) + (forward-line 1))))) + +(provide 'init-treemacs) +;;; init-treemacs.el ends here \ No newline at end of file diff --git a/lisp/init-treesitter.el b/lisp/init-treesitter.el new file mode 100644 index 0000000..df8cc51 --- /dev/null +++ b/lisp/init-treesitter.el @@ -0,0 +1,109 @@ +;;; init-treesitter.el --- Tree-sitter configuration for Emacs 29+ -*- lexical-binding: t -*- +;;; Commentary: +;;; Modern syntax highlighting and code analysis with tree-sitter + +;;; Code: + +;; Only load tree-sitter configuration if Emacs 29+ with tree-sitter support +(when (and (fboundp 'treesit-available-p) + (treesit-available-p)) + + ;; Configure tree-sitter languages + (setq treesit-language-source-alist + '((bash "https://github.com/tree-sitter/tree-sitter-bash") + (c "https://github.com/tree-sitter/tree-sitter-c") + (cmake "https://github.com/uyha/tree-sitter-cmake") + (cpp "https://github.com/tree-sitter/tree-sitter-cpp") + (css "https://github.com/tree-sitter/tree-sitter-css") + (elisp "https://github.com/Wilfred/tree-sitter-elisp") + (go "https://github.com/tree-sitter/tree-sitter-go") + (html "https://github.com/tree-sitter/tree-sitter-html") + (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src") + (json "https://github.com/tree-sitter/tree-sitter-json") + (make "https://github.com/alemuller/tree-sitter-make") + (markdown "https://github.com/ikatyang/tree-sitter-markdown") + (python "https://github.com/tree-sitter/tree-sitter-python") + (rust "https://github.com/tree-sitter/tree-sitter-rust") + (toml "https://github.com/tree-sitter/tree-sitter-toml") + (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") + (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") + (yaml "https://github.com/ikatyang/tree-sitter-yaml"))) + + ;; Function to install a tree-sitter grammar + (defun treesit-install-language-grammar (lang) + "Install tree-sitter grammar for LANG." + (interactive (list (intern (completing-read "Language: " + (mapcar #'car treesit-language-source-alist))))) + (unless (treesit-language-available-p lang) + (let ((lang-source (alist-get lang treesit-language-source-alist))) + (if lang-source + (treesit-install-language-grammar lang) + (message "Language source not configured for %s" lang))))) + + ;; Install all configured grammars + (defun treesit-install-all-grammars () + "Install all configured tree-sitter grammars." + (interactive) + (dolist (lang-source treesit-language-source-alist) + (let ((lang (car lang-source))) + (unless (treesit-language-available-p lang) + (message "Installing tree-sitter grammar for %s..." lang) + (condition-case err + (treesit-install-language-grammar lang) + (error (message "Failed to install %s: %s" lang err))))))) + + ;; Remap major modes to use tree-sitter variants + (setq major-mode-remap-alist + '((yaml-mode . yaml-ts-mode) + (bash-mode . bash-ts-mode) + (js2-mode . js-ts-mode) + (javascript-mode . js-ts-mode) + (js-mode . js-ts-mode) + (typescript-mode . typescript-ts-mode) + (json-mode . json-ts-mode) + (css-mode . css-ts-mode) + (python-mode . python-ts-mode) + (c-mode . c-ts-mode) + (c++-mode . c++-ts-mode) + (cmake-mode . cmake-ts-mode) + (toml-mode . toml-ts-mode) + (rust-mode . rust-ts-mode) + (go-mode . go-ts-mode))) + + ;; Auto-mode-alist for tree-sitter modes + (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.js\\'" . js-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.mjs\\'" . js-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.toml\\'" . toml-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode)) + + ;; Enhanced font-lock for tree-sitter modes + (setq treesit-font-lock-level 4) ; Maximum highlighting + + ;; Tree-sitter debugging helpers + (defun treesit-inspect-node-at-point () + "Show tree-sitter node information at point." + (interactive) + (when (treesit-parser-list) + (let ((node (treesit-node-at (point)))) + (message "Node: %s, Type: %s, Text: %s" + node + (treesit-node-type node) + (treesit-node-text node))))) + + ;; Add to startup hook to check for tree-sitter grammars + (add-hook 'emacs-startup-hook + (lambda () + (when (and (treesit-available-p) + (not (treesit-language-available-p 'python))) + (message "Tree-sitter grammars not installed. Run M-x treesit-install-all-grammars")))) + + (message "Tree-sitter support enabled")) + +(provide 'init-treesitter) +;;; init-treesitter.el ends here \ No newline at end of file diff --git a/lisp/init-ui.el b/lisp/init-ui.el new file mode 100644 index 0000000..fb11427 --- /dev/null +++ b/lisp/init-ui.el @@ -0,0 +1,100 @@ +;;; init-ui.el --- UI and theme configurations -*- lexical-binding: t -*- +;;; Commentary: +;;; User interface settings, themes, and visual configurations + +;;; Code: + +;;; Display Settings +(column-number-mode t) +(global-display-line-numbers-mode t) +(setq display-line-numbers-type 'relative) +(tool-bar-mode -1) +(when (fboundp 'scroll-bar-mode) + (scroll-bar-mode -1)) +(global-hl-line-mode 1) +(show-paren-mode 1) +(setq show-paren-delay 0) + +;; CUA mode for rectangles - use selection mode only to avoid conflicts +(cua-selection-mode t) ; Only rectangle selection, not full CUA bindings +(setq cua-auto-tabify-rectangles nil) +(setq cua-keep-region-after-copy t) + +;; Trailing whitespace +(setq show-trailing-whitespace t) +(add-hook 'before-save-hook 'delete-trailing-whitespace) + +;; Fill column indicator +(setq-default display-fill-column-indicator-column 80) +(setq-default display-fill-column-indicator-character ?\u2502) +(global-display-fill-column-indicator-mode 1) +(set-face-attribute 'fill-column-indicator nil :foreground "red") + +;; Window dividers for mouse resizing +(setq mouse-autoselect-window nil) +(setq window-divider-default-places t) +(setq window-divider-default-bottom-width 1) +(setq window-divider-default-right-width 1) +(window-divider-mode 1) + +;;; Font Settings (preserved from custom-set-faces) +(set-face-attribute 'default nil + :family "0xProto Nerd Font Mono" + :foundry "nil" + :slant 'normal + :weight 'regular + :height 140 + :width 'normal) + +;;; Diff-hl face customizations +(with-eval-after-load 'diff-hl + (set-face-attribute 'diff-hl-change nil :background "blue3" :foreground "blue3") + (set-face-attribute 'diff-hl-delete nil :background "red3" :foreground "red3") + (set-face-attribute 'diff-hl-insert nil :background "green3" :foreground "green3")) + +;;; Theme Management +(defvar jens-themes + '(developer-dark + modus-vivendi + modus-operandi) + "List of preferred themes.") + +(defun load-jens-dark-theme () + "Load the custom jens-dark theme." + (interactive) + (load-theme 'jens-dark t) + (message "Jens Dark theme loaded")) + +(defun switch-theme (theme) + "Switch to a different theme interactively." + (interactive + (list (intern (completing-read "Load theme: " + (mapcar #'symbol-name (custom-available-themes)))))) + (mapc #'disable-theme custom-enabled-themes) + (load-theme theme t) + (message "Switched to %s theme" theme)) + +;; Load default theme +(when (member 'developer-dark (custom-available-themes)) + (load-theme 'developer-dark t)) + +;;; Icons +(use-package all-the-icons + :ensure t + :defer t) + +(use-package all-the-icons-dired + :ensure t + :after all-the-icons + :hook (dired-mode . all-the-icons-dired-mode)) + +;;; Which-key for discovering keybindings +(use-package which-key + :ensure t + :init (which-key-mode) + :config + (setq which-key-idle-delay 0.3) + (setq which-key-popup-type 'side-window)) + +(provide 'init-ui) +;;; init-ui.el ends here \ No newline at end of file diff --git a/lisp/init-utils.el b/lisp/init-utils.el new file mode 100644 index 0000000..fe80efb --- /dev/null +++ b/lisp/init-utils.el @@ -0,0 +1,117 @@ +;;; init-utils.el --- Utility functions and configurations -*- lexical-binding: t -*- +;;; Commentary: +;;; Custom utility functions and helper configurations + +;;; Code: + +;;; Custom Functions +(defun kill-current-buffer-no-confirm () + "Kill the current buffer without confirmation, unless it has unsaved changes." + (interactive) + (kill-buffer (current-buffer))) + +(defun reload-emacs-config () + "Reload the Emacs configuration file and all dependent configs." + (interactive) + ;; First reload the main init.el + (load-file (expand-file-name "init.el" user-emacs-directory)) + + ;; Reload development config if it exists + (let ((dev-config (expand-file-name "emacs-dev-config.el" user-emacs-directory))) + (when (file-exists-p dev-config) + (load-file dev-config))) + + ;; Reload SHR config if it exists + (let ((shr-config (expand-file-name "shr-config.el" user-emacs-directory))) + (when (file-exists-p shr-config) + (load-file shr-config))) + + ;; Reload elfeed config if it exists + (let ((elfeed-config (expand-file-name "elfeed-config.el" user-emacs-directory))) + (when (file-exists-p elfeed-config) + (load-file elfeed-config))) + + ;; Reload mu4e config if it exists + (let ((mu4e-config (expand-file-name "mu4e-config.el" user-emacs-directory))) + (when (file-exists-p mu4e-config) + (condition-case err + (load-file mu4e-config) + (error + (message "mu4e config available but mu4e not installed"))))) + + (message "Emacs configuration fully reloaded!")) + +;;; Package management helpers +(defun package-refresh-without-proxy () + "Temporarily disable proxy and refresh packages." + (interactive) + (let ((url-proxy-services nil)) + (package-refresh-contents) + (message "Package list refreshed without proxy"))) + +(defun package-install-without-proxy (package) + "Install PACKAGE without using proxy." + (interactive + (list (intern (completing-read "Install package: " + (mapcar #'car package-archive-contents))))) + (let ((url-proxy-services nil)) + (package-install package) + (message "Package %s installed without proxy" package))) + +(defun install-dev-packages () + "Install development packages without proxy." + (interactive) + (let ((url-proxy-services nil) + (dev-packages '(lsp-mode lsp-ui lsp-treemacs + company company-box yasnippet + flycheck magit forge))) + (package-refresh-contents) + (dolist (pkg dev-packages) + (unless (package-installed-p pkg) + (condition-case err + (progn + (package-install pkg) + (message "Installed %s" pkg)) + (error + (message "Failed to install %s: %s" pkg err))))) + (message "Development packages installation complete"))) + +;;; Proxy management +(defvar url-proxy-services-backup nil + "Backup of proxy settings.") + +(defun toggle-proxy () + "Toggle proxy settings on/off." + (interactive) + (if url-proxy-services + (progn + (setq url-proxy-services-backup url-proxy-services) + (setq url-proxy-services nil) + (message "Proxy disabled")) + (progn + (setq url-proxy-services (or url-proxy-services-backup + '(("https" . "eudewerepo001:3128") + ("http" . "eudewerepo001:3128")))) + (message "Proxy enabled: %s" (cdr (assoc "http" url-proxy-services)))))) + +;;; Development mode helpers +(defun show-dev-mode-info () + "Show information about development mode." + (interactive) + (message "Development mode is available. Use M-x enable-dev-mode to activate LSP, company-mode, flycheck, and other development tools.")) + +;;; God-mode helpers +(defun enable-god-mode-config () + "Enable god-mode configuration." + (interactive) + (let ((god-config (expand-file-name "god-mode-config.el" user-emacs-directory))) + (if (file-exists-p god-config) + (condition-case err + (progn + (load-file god-config) + (message "God-mode configuration loaded. Press ESC to toggle god-mode.")) + (error (message "Failed to load god-mode config: %s" err))) + (message "God-mode config file not found at %s" god-config)))) + +(provide 'init-utils) +;;; init-utils.el ends here \ No newline at end of file diff --git a/lisp/init-vcs.el b/lisp/init-vcs.el new file mode 100644 index 0000000..b90fc0c --- /dev/null +++ b/lisp/init-vcs.el @@ -0,0 +1,78 @@ +;;; init-vcs.el --- Version control configuration -*- lexical-binding: t -*- +;;; Commentary: +;;; Configuration for version control, primarily diff-hl and Git support + +;;; Code: + +;;; Diff-hl Configuration +(use-package diff-hl + :ensure t + :hook ((prog-mode . diff-hl-mode) + (dired-mode . diff-hl-dired-mode) + (text-mode . diff-hl-mode) + (conf-mode . diff-hl-mode)) + :bind (("M-n" . diff-hl-next-hunk) + ("M-p" . diff-hl-previous-hunk) + ("C-c v r" . diff-hl-revert-hunk) + ("C-c v s" . diff-hl-diff-goto-hunk) + ("C-c v u" . diff-hl-update)) + :init + ;; Set fringe width before diff-hl loads + (setq-default left-fringe-width 8) + (setq-default right-fringe-width 8) + ;; Ensure fringes are visible + (fringe-mode 8) + :config + ;; Configure VC backend for Git + (setq vc-handled-backends '(Git)) + (setq vc-git-diff-switches '("--histogram")) + + ;; Tell diff-hl to use VC backend + (setq diff-hl-reference-revision nil) + (setq diff-hl-disable-on-remote nil) + + ;; Make diff-hl use the left fringe + (setq diff-hl-side 'left) + + ;; Ensure diff-hl draws in fringe, not margin + (setq diff-hl-draw-borders nil) + (setq diff-hl-margin-mode nil) + + ;; Set diff-hl fringe bitmaps + (setq diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-type) + + ;; Enable flydiff for real-time updates + (diff-hl-flydiff-mode 1) + + ;; Update immediately when visiting a file + (setq diff-hl-flydiff-delay 0.3) + + ;; Make sure diff-hl updates on various events + (add-hook 'after-save-hook 'diff-hl-update) + (add-hook 'after-revert-hook 'diff-hl-update) + (add-hook 'find-file-hook 'diff-hl-update) + (add-hook 'vc-checkin-hook 'diff-hl-update) + + ;; Enable globally with a slight delay to speed up initial startup + (run-with-idle-timer 2 nil #'global-diff-hl-mode)) + +;; Manual refresh command +(defun diff-hl-refresh () + "Manually refresh diff-hl indicators in all buffers." + (interactive) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when diff-hl-mode + (diff-hl-update))))) + +;; Force VC to refresh its state +(defun diff-hl-force-vc-refresh () + "Force VC to refresh state and then update diff-hl." + (interactive) + (when buffer-file-name + (vc-refresh-state) + (diff-hl-update) + (message "VC state refreshed and diff-hl updated"))) + +(provide 'init-vcs) +;;; init-vcs.el ends here \ No newline at end of file diff --git a/shr-config.el b/shr-config.el index a25ed78..f948f3b 100644 --- a/shr-config.el +++ b/shr-config.el @@ -49,6 +49,19 @@ (add-hook 'shr-after-render-hook 'my-shr-apply-serif-font)) +;; Disable the fill-column indicator (red margin line) in SHR-related modes +(defun disable-fill-column-indicator () + "Disable the fill column indicator in the current buffer." + (display-fill-column-indicator-mode -1)) + +;; Add hooks to disable fill-column indicator in SHR-using modes +(add-hook 'eww-mode-hook 'disable-fill-column-indicator) +(add-hook 'elfeed-show-mode-hook 'disable-fill-column-indicator) +(add-hook 'mu4e-view-mode-hook 'disable-fill-column-indicator) + +;; Also disable it after SHR renders content +(add-hook 'shr-after-render-hook 'disable-fill-column-indicator) + ;; Disable line numbers in all modes that use SHR (defun my-shr-disable-line-numbers () "Disable line numbers in SHR-rendered buffers."