(require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (package-initialize) (require 'use-package) ;; helper functions ;; ===== (defun my/format-elisp-buffer () "Format the whole elisp buffer." (interactive) (save-excursion (indent-region (point-min) (point-max)))) (defun my/setup-elisp-formatting-key () "Set up the formatting key for Emacs Lisp buffers." (local-set-key (kbd "C-c f") 'my/format-elisp-buffer)) (add-hook 'emacs-lisp-mode-hook #'my/setup-elisp-formatting-key) (defun my/visit-todo () "Visit TODO.org in home" (interactive) (find-file "~/TODO.org")) (defun my/reload-init-file () "Reload the Emacs init file." (interactive) (load-file user-init-file)) (defun my/reload-org-config () "Reload the org configuration." (interactive) (my/org-config) (message "Org configuration reloaded")) (defun my-org-agenda-format-item (item) "Format an agenda or TODO item without the filename, but keep the keyword." (let* ((marker (or (get-text-property 0 'org-marker item) (get-text-property 0 'org-hd-marker item))) (line (when marker (org-with-point-at marker (buffer-substring (line-beginning-position) (line-end-position))))) (todo-keyword (when marker (org-with-point-at marker (org-get-todo-state)))) (truncated-line (when line (replace-regexp-in-string "^\\[#[A-Z]\\] " "" line)))) (if truncated-line (let ((clean-item (replace-regexp-in-string "^\\(\\*+ \\)" "" truncated-line))) (if todo-keyword (concat todo-keyword " " (replace-regexp-in-string (concat "^" todo-keyword " ?") "" clean-item)) clean-item)) item))) (defun my/kill-all-org-agenda-buffers () "Close all org-agenda-related buffers." (interactive) (dolist (buffer (buffer-list)) (with-current-buffer buffer (when (or (derived-mode-p 'org-agenda-mode) (and (buffer-file-name) (member (buffer-file-name) (org-agenda-files)))) (kill-buffer buffer)))) (message "Closed all org-agenda-related buffers.")) (defun my/org-config () (setq org-agenda-inhibit-startup t org-todo-keywords '((sequence "HOLD(h)" "TODO(t)" "|" "DONE(d)" "CNCL(x)")) org-todo-keyword-faces '(("HOLD" . (:foreground "orange" :weight bold)) ("TODO" . (:foreground "red" :weight bold)) ("DONE" . (:foreground "green" :weight bold)) ("CNCL" . (:foreground "gray" :weight bold))) org-agenda-files (append (list "~/TODO.org") (directory-files-recursively "~/notes" "\\.org$")) org-agenda-prefix-format '((agenda . "%i %?-12t% s ") (todo . " %i ") (tags . " %i ") (search . " %i ")) org-agenda-hide-tags-regexp "." org-agenda-todo-keyword-format "%s" org-archive-location "%s_archive::datetree/" org-log-done 'note org-log-into-drawer t) (advice-add 'org-agenda-format-item-line :filter-return #'my-org-agenda-format-item) (advice-add 'org-agenda-format-item :filter-return #'my-org-agenda-format-item)) (defun my/org-refile-config () (setq org-refile-targets '((nil :maxlevel . 9) ("~/TODO.org" :maxlevel . 9)) org-refile-use-outline-path 'file org-outline-path-complete-in-steps nil org-refile-allow-creating-parent-nodes 'confirm)) (defun my/org-capture-config () (setq org-capture-templates '(("t" "TODO" entry (file "~/TODO.org") "* TODO %?\n %i" :prepend t)))) (defun my/refresh-org-agenda-files () (interactive) (setq org-agenda-files (append (list "~/TODO.org") (directory-files-recursively "~/notes" "\\.org$")))) (use-package emacs :init (tool-bar-mode -1) (require 'server) (unless (server-running-p) (server-start)) (setq backup-by-copying t completion-cycle-threshold 3 create-lockfiles nil custom-file (concat user-emacs-directory "custom.el") delete-old-versions t enable-recursive-minibuffers t focus-follows-mouse t indent-tabs-mode nil inhibit-startup-screen t kept-new-versions 20 kept-old-versions 2 minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt) mouse-autoselect-window t read-extended-command-predicate #'command-completion-default-include-p read-process-output-max (* 1024 1024) tab-always-indent 'complete tab-width 2 text-mode-ispell-word-completion nil use-short-answers t version-control t warning-minimum-level :emergency backup-directory-alist `((".*" . ,(concat user-emacs-directory "backup"))) read-extended-command-predicate #'command-completion-default-include-p text-mode-ispell-word-completion nil gc-cons-threshold 100000000) (defun crm-indicator (args) (cons (format "[CRM%s] %s" (replace-regexp-in-string "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator) (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator) ;; Do not allow the cursor in the minibuffer prompt (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (load-theme 'misterioso) (put 'erase-buffer 'disabled nil) (put 'narrow-to-region 'disabled nil) (put 'narrow-to-defun 'disabled nil) (put 'narrow-to-page 'disabled nil) (put 'widen 'disabled nil) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (with-eval-after-load 'ibuffer (define-key ibuffer-mode-map (kbd "TAB") nil)) (global-unset-key (kbd "C-x n")) (global-set-key (kbd "") 'dired-sidebar-toggle-sidebar) (global-set-key (kbd "M-/") 'dabbrev-expand) (global-set-key (kbd "C-=") 'multi-vterm-dedicated-toggle) (global-set-key (kbd "C-x r") 'replace-regexp) (global-set-key (kbd "C-x R") 'replace-string) (global-set-key (kbd "C-x c O") 'my/reload-org-config) (global-set-key (kbd "C-x c r") #'my/reload-init-file) (global-set-key (kbd "C-x E n p") #'narrow-to-page) (global-set-key (kbd "C-x E n f") #'narrow-to-defun) (global-set-key (kbd "C-x E n r") #'narrow-to-region) (global-set-key (kbd "C-x E w") #'widen) (with-eval-after-load 'lsp-mode (add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration) ;; (setq lsp-idle-delay 0.500) ;; (setq lsp-log-io nil) ;; (setq lsp-completion-provider :capf) (add-hook 'prog-mode-hook 'lsp-deferred) (add-hook 'before-save-hook #'lsp-format-buffer) (setq lsp-file-watch-threshold nil) (add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]plz-out\\'")) (when (file-exists-p custom-file) (load custom-file))) (use-package async :ensure t) (use-package async-status :ensure t) (use-package dash :ensure t :defer t) (use-package multiple-cursors :ensure t :defer t :init (multiple-cursors-mode 1) :bind (("C-x C-<" . 'mc/edit-lines) ("C->" . 'mc/mark-next-like-this) ("C-<" . 'mc/mark-previous-like-this) ("C-x C->" . 'mc/mark-all-like-this))) ;; minibuffer ;; === (use-package which-key :ensure t :defer t :init (which-key-mode)) ;; eye candy ;; === (use-package doom-modeline :ensure t :init (doom-modeline-mode 1)) (use-package nerd-icons :ensure t :defer t) (use-package nerd-icons-dired :ensure t :defer t :hook (dired-mode . nerd-icons-dired-mode)) (use-package nerd-icons-completion :ensure t :defer t) (use-package nerd-icons-ibuffer :ensure t :defer t) (use-package unicode-fonts :ensure t :defer t) ;; org-mode ;; === (use-package org :ensure t :bind (("C-x O a" . org-agenda) ("C-x O c" . org-capture) ("C-x O t" . my/visit-todo) ("C-x O k" . my/kill-all-org-agenda-buffers)) :mode ("\\.org\\'" . org-mode) :config (my/org-config) (my/org-refile-config) (my/org-capture-config) (run-with-idle-timer 300 t #'my/refresh-org-agenda-files)) ;; knowledge management ;; === (use-package denote :ensure t :bind (("C-x n f" . #'denote-open-or-create) ("C-x n n" . #'denote) ("C-x n c" . #'denote-region) ("C-x n !" . #'denote-date) ("C-x n z" . #'denote-signature) ("C-x n s" . #'denote-subdirectory) ("C-x n S-n" . #'denote-type) ("C-x n t" . #'denote-template) ("C-x n l l" . #'denote-link) ("C-x n l a" . #'denote-add-links) ("C-x n l f" . #'denote-find-link) ("C-x n b l" . #'denote-backlinks) ("C-x n b f" . #'denote-find-backlink) ("C-x n r r" . #'denote-rename-file) ("C-x n r f" . #'denote-rename-file-using-front-matter)) :config ;; Key bindings specifically for Dired. (let ((map dired-mode-map)) (define-key map (kbd "C-x n d") #'denote-dired-mode) (define-key map (kbd "C-x n i") #'denote-link-dired-marked-notes) (define-key map (kbd "C-x n k") #'denote-dired-rename-marked-files-with-keywords) (define-key map (kbd "C-x n r r") #'denote-dired-rename-files) (define-key map (kbd "C-x n r f") #'denote-dired-rename-marked-files-using-front-matter)) (setq denote-directory (expand-file-name "~/notes/") denote-save-buffers nil denote-known-keywords '("emacs" "golang" "dev") denote-infer-keywords t denote-sort-keywords t denote-file-type nil denote-prompts '(title keywords) denote-excluded-directories-regexp nil denote-excluded-keywords-regexp nil denote-rename-confirmations '(rewrite-front-matter modify-file-name) denote-date-prompt-use-org-read-date t denote-date-format nil denote-backlinks-show-context t) (denote-rename-buffer-mode 1) (with-eval-after-load 'org-capture (setq denote-org-capture-specifiers "%l\n%i\n%?") (add-to-list 'org-capture-templates '("n" "New note (with denote.el)" plain (file denote-last-path) #'denote-org-capture :no-save t :immediate-finish nil :kill-buffer t :jump-to-captured t)))) ;; version control ;; === (use-package magit :ensure t :defer t :bind (("C-x g g" . #'magit-status) ("C-x g l l" . #'magit-log) ("C-x g l a" . #'magit-log-all) ("C-x g l f" . #'magit-log-buffer-file) ("C-x g R s" . #'magit-reset-soft) ("C-x g R h" . #'magit-reset-hard) ("C-x g !" . #'magit-run) ("C-x g b b" . #'magit-branch) ("C-x g b c" . #'magit-branch-create) ("C-x g b s" . #'magit-branch-checkout) ("C-x g m m" . #'magit-merge) ("C-x g d d" . #'magit-diff)) :commands (magit-status-here magit-get-current-branch) :custom (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)) ;; diagramming ;; === (use-package graphviz-dot-mode :ensure t :defer t :config (with-eval-after-load 'org (org-babel-do-load-languages 'org-babel-load-languages '((dot . t))))) (use-package plantuml-mode :ensure t :config (setq plantuml-default-exec-mode 'jar plantuml-jar-path "/usr/share/plantuml/plantuml.jar" org-plantuml-jar-path (expand-file-name "/usr/share/plantuml/plantuml.jar")) (with-eval-after-load 'org (org-babel-do-load-languages 'org-babel-load-languages '((plantuml . t))))) ;; coding ;; === (use-package go-mode :ensure t :defer t :mode (("\\.go$" . go-mode))) (use-package yaml-mode :ensure t :defer t :mode (("\\.yml$" . yaml-mode))) (use-package json-mode :ensure t :defer t :mode (("\\.json$" . json-mode))) (use-package php-mode :ensure t :defer t :mode (("\\.php$" . php-mode))) (use-package python-mode :ensure t :defer t :mode (("BUILD\\'" . python-mode) ;; please BUILD config ("\\.py$" . python-mode))) (use-package mmm-mode :ensure t :defer t) (use-package multi-web-mode :ensure t :defer t) (use-package protobuf-mode :ensure t :defer t) (use-package rego-mode :ensure t :defer t) (use-package kotlin-mode :ensure t :defer t) (use-package fic-mode :ensure t :defer t) (use-package flycheck :ensure t :defer t) (use-package eglot :ensure t :hook ((python-mode . eglot-ensure) (rust-mode . eglot-ensure) (c-mode . eglot-ensure) (c++-mode . eglot-ensure) (js-mode . eglot-ensure) (typescript-mode . eglot-ensure) (eglot-managed-mode . (lambda () (setq-local completion-at-point-functions (list (cape-super-capf #'eglot-completion-at-point #'cape-file)))))) :config (setq eglot-autoshutdown t) (setq eglot-sync-connect 1) (setq eglot-connect-timeout 10) (setq eglot-confirm-server-initiated-edits nil) (setq eglot-autoreconnect t) (setq completion-category-defaults nil) (setq completion-category-overrides '((eglot (styles orderless)))) (setq eglot-completion-style 'orderless) (setq eglot-completion-provider :company) (with-eval-after-load 'orderless (setq orderless-matching-styles '(orderless-literal orderless-regexp orderless-initialism orderless-flex))) :bind (:map eglot-mode-map ("s-l a" . eglot-code-actions) ("s-l r" . eglot-rename) ("s-l f" . eglot-format) ("s-l d" . eglot-find-declaration) ("s-l i" . eglot-find-implementation) ("s-l t" . eglot-find-typeDefinition))) ;; navigation ;; === (use-package ace-jump-mode :ensure t :defer t :bind (("C-x j j" . ace-jump-mode) ("C-x j l" . ace-jump-line-mode) ("C-x j c" . ace-jump-char-mode) ("C-x j w" . ace-jump-word-mode) ("C-c DEL" . 'ace-jump-mode-pop-mark))) (use-package ace-mc :ensure t :defer t :bind (("C-c >" . 'ace-mc-add-multiple-cursors))) ;; api testing ;; === (use-package plz :ensure t) ;; force recompiling init.el ;; === (defun my-compile-init-file () "Compile the init file." (let ((init-file (file-truename user-init-file))) (when (string= (buffer-file-name) init-file) (byte-compile-file init-file)))) (add-hook 'after-save-hook #'my-compile-init-file) (use-package orderless :ensure t :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles . (partial-completion))))) (orderless-matching-styles '(orderless-flex orderless-prefixes orderless-initialism)) :config (setq orderless-component-separator #'orderless-escapable-split-on-space)) ;; better minibuffer (use-package vertico :ensure t :init (vertico-mode) :custom (vertico-cycle t) (vertico-count 13) :config (setq read-file-name-completion-ignore-case t read-buffer-completion-ignore-case t completion-ignore-case t completion-styles '(orderless basic) completion-category-defaults nil completion-category-overrides '((file (styles partial-completion))))) ;; it's like ace-jump in a vertico minibuffer (use-package vertico-quick :after vertico :bind (:map vertico-map ("C-q" . vertico-quick-insert) ("M-q" . vertico-quick-exit))) ;; save history (use-package savehist :ensure t :init (savehist-mode)) ;; inline completion (use-package corfu :ensure t :demand t :bind ("C-." . completion-at-point) :init (global-corfu-mode) :hook (eglot-managed-mode . corfu-mode) :config ;; Enable Orderless for Corfu (setq completion-styles '(orderless basic) completion-category-defaults nil completion-at-point-functions nil completion-category-overrides '((file (styles . (partial-completion))))) (setq corfu-auto t) ;; Enable auto completion (setq corfu-separator ?\s) ;; Orderless field separator (setq corfu-quit-at-boundary nil) ;; Never quit at completion boundary (setq corfu-quit-no-match nil) ;; Never quit, even if there is no match (setq corfu-preview-current nil) ;; Disable current candidate preview (setq corfu-on-exact-match nil) ;; Configure handling of exact matches (setq corfu-echo-documentation nil) ;; Disable documentation in the echo area (setq corfu-scroll-margin 5) ;; Use scroll margin ;; Set up dabbrev as the primary completion source ;; (setq-local completion-at-point-functions '(dabbrev-completion)) ;; Enable kind-icon (use-package kind-icon :ensure t :after corfu :custom (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly :config (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)) ;; Add Cape for additional completion sources (use-package cape :ensure t :init ;; Add `completion-at-point-functions', used by `completion-at-point'. (add-to-list 'completion-at-point-functions #'cape-file) (add-to-list 'completion-at-point-functions #'cape-elisp-block) (add-to-list 'completion-at-point-functions #'cape-dabbrev) ;;(add-to-list 'completion-at-point-functions #'cape-history) ;;(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) )) ;; better search (use-package consult :ensure t :demand t :bind (("C-s" . consult-line) ("C-r" . consult-line-multi) ("C-c h" . consult-history) ("C-c m" . consult-mode-command) ("C-c k" . consult-kmacro) ;; Add keybindings for ripgrep-related commands ("C-c s" . consult-ripgrep) ("C-c C-s" . consult-ripgrep-multi)) :custom (consult-preview-key nil) ; Disable preview :config ;; Configure ripgrep for consult (setq consult-ripgrep-command "rg --null --line-buffered --color=ansi --max-columns=1000 --no-heading --line-number . -e ARG OPTS") ;; Use ripgrep for `consult-grep` as well (setq consult-grep-command "rg --null --line-buffered --color=ansi --max-columns=1000 --no-heading --line-number . -e ARG OPTS") ;; Configure mode-specific histories (setq consult-mode-histories '((comint-mode . comint-input-ring) (eshell-mode . eshell-history-ring) (geiser-repl-mode . geiser-repl--history) (inferior-python-mode . comint-input-ring) (minibuffer-local-map . consult-history))) (consult-customize consult-theme :preview-key '(:debounce 0.2 any) consult-ripgrep consult-git-grep consult-grep consult-bookmark consult-recent-file consult-xref consult--source-bookmark consult--source-recent-file consult--source-project-recent-file :preview-key '(:debounce 0.4 any)) ;; Configure async search for better performance (setq consult-async-min-input 2) (setq consult-async-refresh-delay 0.15) (setq consult-async-input-throttle 0.2) (setq consult-async-input-debounce 0.1) ;; Use Consult to select xref locations with preview (setq xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref)) (use-package projectile :ensure t :config (projectile-mode +1) :bind-keymap ("C-x p" . projectile-command-map) :bind (:map projectile-command-map ("f" . consult-projectile-find-file) ("d" . consult-projectile-find-dir) ("b" . consult-projectile-switch-to-buffer) ("s" . consult-projectile-switch-project) ("p" . consult-projectile) ("/" . consult-ripgrep))) (use-package consult-projectile :ensure t :after (consult projectile) :config ;; Override projectile functions with consult-projectile functions (advice-add 'projectile-find-file :override #'consult-projectile-find-file) (advice-add 'projectile-find-dir :override #'consult-projectile-find-dir) (advice-add 'projectile-switch-to-buffer :override #'consult-projectile-switch-to-buffer) (advice-add 'projectile-switch-project :override #'consult-projectile-switch-project) ;; Custom function to always use consult-projectile-find-file (defun my-consult-projectile-find-file () "Use consult-projectile-find-file, falling back to consult-projectile if not in a project." (interactive) (if (projectile-project-p) (consult-projectile-find-file) (consult-projectile))) ;; Replace the keybinding in projectile-command-map (define-key projectile-command-map (kbd "f") #'my-consult-projectile-find-file)) ;; util ;; ===== (use-package elfeed :ensure t :defer t) (use-package eshell :ensure t :defer t :bind (("C-" . #'eshell-toggle)) :config (use-package eshell-autojump :ensure t :defer t) (use-package eshell-bookmark :ensure t :defer t) (use-package eshell-git-prompt :ensure t :defer t) (use-package eshell-toggle :ensure t :defer t)) (use-package dired-sidebar :ensure t :defer t) (use-package surround :ensure t :defer t :bind-keymap ("M-'" . surround-keymap)) (use-package wrap-region :ensure t :defer t) (use-package eat :ensure t :defer t) (use-package scratch :ensure t :defer t :bind (("C-;" . scratch))) (defun my-set-scratch-mode () (funcall 'text-mode)) (add-hook 'scratch-create-buffer-hook 'my-set-scratch-mode) (use-package go-playground :ensure t :defer t) (use-package persistent-scratch :ensure t :init (setq persistent-scratch-directory "~/.local/state/emacs/") (unless (file-exists-p persistent-scratch-directory) (make-directory persistent-scratch-directory t)) :config (persistent-scratch-setup-default) :custom (persistent-scratch-autosave-interval 30) (persistent-scratch-save-file (expand-file-name "scratch" persistent-scratch-directory)) (persistent-scratch-what-to-save '(((:current . "*scratch*") . (major-mode point narrowing)))) :hook (after-init . persistent-scratch-autosave-mode) :bind (("C-c s" . persistent-scratch-save) ("C-c r" . persistent-scratch-restore))) (defun my/dired-duplicate-file (arg) "Duplicate the current file in Dired." (interactive "p") (let ((filename (dired-get-filename))) (setq target (concat (file-name-sans-extension filename) "-old" (if (> arg 1) (number-to-string arg)) (file-name-extension filename t))) (if (file-directory-p filename) (copy-directory filename target) (copy-file filename target)) ) ) (define-key dired-mode-map (kbd "C-c d") 'my/dired-duplicate-file) (use-package lsp-mode :ensure t :commands lsp :custom (lsp-completion-provider :none) ;; we use Corfu! :init (defun my/lsp-mode-setup-completion () (lsp-enable-which-key-integration) (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults)) '(orderless))) :config (setq lsp-prefer-flymake nil) :hook (lsp-completion-mode . my/lsp-mode-setup-completion)) (add-to-list 'auto-mode-alist '("BUILD\\'" . python-mode)) ;; Less chatty for unsupported modes (setq lsp-warn-no-matched-clients nil) (setq lsp-file-watch-threshold nil)