(require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (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 ") (todo . " %i ") (tags . " %i ") (search . " %i ")) org-agenda-hide-tags-regexp "." org-agenda-todo-keyword-format "%s") (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\n %a" :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 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 text-mode-ispell-word-completion nil 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 'upcase-region 'disabled nil) (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 "") 'speedbar) (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) (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-S-c C-S-c" . 'mc/edit-lines) ("C->" . 'mc/mark-next-like-this) ("C-<" . 'mc/mark-previous-like-this) ("C-c 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-c C-g" . magit-status) :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)) :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-flex orderless-regexp orderless-initialism))) :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 p" . 'ace-jump-mode-pop-mark))) ;; 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 :bind (us (:map global-map ("C-." . completion-at-point)) :custom (corfu-auto nil) ;; Enable auto completion (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-on-exact-match nil) ;; Configure handling of exact matches (corfu-echo-documentation nil) ;; Disable documentation in the echo area (corfu-scroll-margin 5) ;; Use scroll margin :init (global-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))))) ;; 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) ("C-c C-h" . consult-mode-histories) ;; 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) ("g" . 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))