syscfg/emacs/init.el

485 lines
17 KiB
EmacsLisp

(use-package emacs
:init
;; package management
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(unless (package-installed-p 'quelpa)
(with-temp-buffer
(url-insert-file-contents
"https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
(eval-buffer)
(quelpa-self-upgrade)))
(quelpa
'(quelpa-use-package
:fetcher git
:url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package)
;; basic cfg
(tool-bar-mode -1)
(require 'server)
(unless (server-running-p) (server-start))
(setq gc-cons-threshold 100000000
read-process-output-max (* 1024 1024)
custom-file (concat user-emacs-directory "custom.el")
create-lockfiles nil
tab-always-indent 'complete
mac-command-modifier 'super
backup-directory-alist `((".*" .
,(concat user-emacs-directory "backup")))
delete-old-versions t
kept-new-versions 20
kept-old-versions 2
version-control t
delete-old-versions t
backup-by-copying t
focus-follows-mouse t
mouse-autoselect-window t
enable-recursive-minibuffers t
warning-minimum-level :emergency
inhibit-startup-screen t
completion-cycle-threshold 3
text-mode-ispell-word-completion nil
lsp-file-watch-threshold 3000
read-extended-command-predicate #'command-completion-default-include-p
minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
(put 'upcase-region 'disabled nil)
(put 'erase-buffer 'disabled nil)
(when (file-exists-p custom-file)
(load custom-file))
;;(load-theme 'doom-one)
(load-theme 'misterioso)
(add-hook 'eshell-mode-hook
(lambda ()
(setq-local corfu-auto nil)
(corfu-mode)))
(with-eval-after-load 'lsp-mode
(add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration)
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]plz-out\\'"))
(add-to-list 'auto-mode-alist
'("BUILD\\'" . python-mode))
(global-set-key (kbd "C-c C-r") 'replace-regexp)
(global-set-key (kbd "C-c C-R") 'replace-string)
(global-set-key (kbd "C-=") 'multi-vterm-dedicated-toggle)
(global-set-key (kbd "C-<f12>") 'speedbar))
(global-set-key (kbd "S-<mouse-2>") #'lsp-find-definition)
(use-package async :ensure t)
(use-package async-status :ensure t)
(use-package dash :ensure 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 embark :ensure t :defer t)
(use-package vertico :ensure t :defer t
:init (vertico-mode))
(use-package which-key :ensure t :defer t
:init (which-key-mode))
;; mass editing
;; ===
(use-package multifiles :ensure t)
(use-package wgrep :ensure t)
;; 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-corfu :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)
;; scratch buffers
;; ===
(use-package go-scratch :ensure t :defer t)
;; multimedia
;; ===
;;(use-package mingus :ensure t :defer t)
(use-package emms :ensure t :defer t
:config
(setq emms-player-list '(emms-player-mpv)
emms-info-functions '(emms-info-native)))
;; terminal / shell
;; ===
(use-package multishell :ensure t :defer t)
(use-package vterm-toggle :ensure t
:bind (("C-x C-1 C-t C-v" . #'multi-vterm)
("C-x C-1 C-t C-n" . #'multi-vterm-next)
("C-x C-1 C-t C-p" . #'multi-vterm-prev)))
(use-package eshell-toggle :ensure t
:bind (("C-x C-1 C-t C-e" . #'eshell-toggle)))
(use-package eshell-vterm :ensure t :defer t
:init (eshell-vterm-mode 1))
(use-package multi-vterm :ensure t)
;; org-mode
;; ===
(use-package org :ensure t
:mode (("\\.org$" . org-mode)))
;; Set up org-agenda-files to include ~/notes and its subdirectories
(setq org-agenda-files
(directory-files-recursively "~/notes" "\\.org$"))
;; Configure org-agenda-prefix-format to show minimal information
(setq org-agenda-prefix-format
'((agenda . " %i ")
(todo . " %i ")
(tags . " %i ")
(search . " %i ")))
;; Hide the filename in the agenda view
(setq org-agenda-hide-tags-regexp ".")
;; Remove the filename from agenda and TODO items, but keep the keyword
(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)))
;; Apply our custom formatting to agenda items
(advice-add 'org-agenda-format-item-line :filter-return #'my-org-agenda-format-item)
;; Apply our custom formatting to TODO items
(advice-add 'org-agenda-format-item :filter-return #'my-org-agenda-format-item)
;; Keep the TODO keyword visible in the agenda
(setq org-agenda-todo-keyword-format "%s")
;; Ensure org-agenda is refreshed when files are added or removed
(defun refresh-org-agenda-files ()
(interactive)
(setq org-agenda-files
(directory-files-recursively "~/notes" "\\.org$")))
(run-with-idle-timer 300 t 'refresh-org-agenda-files)
(use-package ob-go :ensure t :defer t)
(use-package ox-hugo :ensure t :defer t) ;; gitlab uses goldmark
(with-eval-after-load 'ox
(require 'ox-hugo))
(setq org-hugo-base-dir "~/tmp")
(use-package org-context :ensure t :defer t
:after (:all org))
;; knowledge management
;; ===
(use-package denote :ensure t
:config
(setq denote-directory (expand-file-name "~/notes/"))
(setq denote-save-buffers nil)
(setq denote-known-keywords '("emacs" "pascom" "golang" "dev"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-file-type nil) ; Org is the default, set others here
(setq denote-prompts '(title keywords))
(setq denote-excluded-directories-regexp nil)
(setq denote-excluded-keywords-regexp nil)
(setq denote-rename-confirmations '(rewrite-front-matter modify-file-name))
(setq denote-date-prompt-use-org-read-date t) ; use org date interface
(setq denote-date-format nil) ; read doc string
(setq denote-backlinks-show-context t)
(denote-rename-buffer-mode 1)
;; denote key bindings
(global-unset-key (kbd "C-x C-n"))
(let ((map global-map))
(define-key map (kbd "C-x C-n C-n") #'denote)
(define-key map (kbd "C-x C-n C-c") #'denote-region) ; "contents" mnemonic
(define-key map (kbd "C-x C-n C-S-n") #'denote-type)
(define-key map (kbd "C-x C-n !") #'denote-date)
(define-key map (kbd "C-x C-n C-z") #'denote-signature) ; "zettelkasten" mnemonic
(define-key map (kbd "C-x C-n C-s") #'denote-subdirectory)
(define-key map (kbd "C-x C-n C-t") #'denote-template)
;; If you intend to use Denote with a variety of file types, it is
;; easier to bind the link-related commands to the `global-map', as
;; shown here. Otherwise follow the same pattern for `org-mode-map',
;; `markdown-mode-map', and/or `text-mode-map'.
(define-key map (kbd "C-x C-n C-i") #'denote-link) ; "insert" mnemonic
(define-key map (kbd "C-x C-n C-I") #'denote-add-links)
(define-key map (kbd "C-x C-n C-S-b") #'denote-backlinks)
(define-key map (kbd "C-x C-n C-l C-f") #'denote-find-link)
(define-key map (kbd "C-x C-n C-l C-b") #'denote-find-backlink)
;; Note that `denote-rename-file' can work from any context, not just
;; Dired bufffers. That is why we bind it here to the `global-map'.
(define-key map (kbd "C-x C-n C-r") #'denote-rename-file)
(define-key map (kbd "C-x C-n C-S-r") #'denote-rename-file-using-front-matter))
(global-set-key (kbd "C-x C-n C-f") #'denote-open-or-create)
;; Key bindings specifically for Dired.
(let ((map dired-mode-map))
(define-key map (kbd "C-x C-n C-d") #'denote-dired-mode)
(define-key map (kbd "C-x C-n C-i") #'denote-link-dired-marked-notes)
(define-key map (kbd "C-x C-n C-r") #'denote-dired-rename-files)
(define-key map (kbd "C-x C-n C-k") #'denote-dired-rename-marked-files-with-keywords)
(define-key map (kbd "C-x C-n C-S-r") #'denote-dired-rename-marked-files-using-front-matter))
(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)))
)
;; projects
;; ===
(use-package projectile :ensure t :defer t
:bind (:map projectile-mode-map
("C-c C-p" . projectile-command-map)
("C-c C-p C-f" . #'projectile-find-file)
("C-c C-p C-p" . #'projectile-switch-project)
("C-c C-p C-/" . #'projectile-ripgrep)
("C-c C-p C-b" . #'projectile-switch-to-buffer)
("C-c C-p C-4 C-b" . #'projectile-switch-to-buffer-other-window)
("C-c C-p C-5 C-b" . #'projectile-switch-to-buffer-other-frame)
("C-c C-p C-d" . #'projectile-dired)
("C-c C-p C-4 C-d" . #'projectile-dired-other-window)
("C-c C-p C-4 C-d" . #'projectile-dired-other-frame)
("C-c C-p C-v" . #'multi-vterm-project))
:diminish projectile-mode
:init (projectile-mode))
(use-package denote-explore :ensure t)
(use-package go-projectile :ensure t :defer t)
(use-package projectile-ripgrep :ensure t :defer t
:after (projectile))
(use-package project-explorer :ensure t :defer 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))
(use-package dired-git :ensure t :defer t)
(use-package dired-git-info :ensure t :defer t)
(use-package git-link :ensure t :defer t)
(use-package git-lens :ensure t :defer t)
;; auto completion
;; ===
(use-package corfu :ensure t :defer t
:bind (:map corfu-map
("C-n" . corfu-next)
("C-p" . corfu-previous)
("SPC" . corfu-insert-separator))
:custom
(corfu-cycle t)
(corfu-auto nil)
(corfu-separator ?\s)
(corfu-preselect 'prompt)
(corfu-quit-at-boundary t)
(corfu-quit-no-match 'separator)
(corfu-scroll-margin 5)
:hook ((prog-mode . corfu-mode)
(shell-mode . corfu-mode)
(eshell-mode . corfu-mode))
:init (global-corfu-mode)
:config
(defun corfu-send-shell (&rest _)
"Send completion candidate when inside comint/eshell."
(cond
((and (derived-mode-p 'eshell-mode)
(fboundp 'eshell-send-input))
(eshell-send-input))
((and (derived-mode-p 'comint-mode)
(fboundp 'comint-send-input))
(comint-send-input))))
(advice-add #'corfu-insert
:after #'corfu-send-shell)
(global-set-key [remap dabbrev-expand]
'hippie-expand)
(add-to-list 'corfu-margin-formatters
#'nerd-icons-corfu-formatter))
(use-package dabbrev :ensure t :defer t
:bind (("M-/" . dabbrev-completion)
("C-M-/" . dabbrev-expand))
:config
(add-to-list 'dabbrev-ignored-buffer-regexps "\\` ")
(add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode)
(add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)
(add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))
(use-package orderless :ensure t :defer t
:init
(setq completion-styles '(orderless partial-completion basic)
completion-category-defaults nil
completion-category-overrides nil))
(setq text-mode-ispell-word-completion nil)
(setq read-extended-command-predicate
#'command-completion-default-include-p)
(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)
(setq read-extended-command-predicate
#'command-completion-default-include-p)
;; Add extensions
(use-package cape :ensure t :defer t
;; Bind dedicated completion commands
;; Alternative prefix keys: C-c p, M-p, M-+, ...
:bind (("C-c / p" . completion-at-point) ;; capf
("C-c / t" . complete-tag) ;; etags
("C-c / d" . cape-dabbrev) ;; or dabbrev-completion
("C-c / h" . cape-history)
("C-c / f" . cape-file)
("C-c / k" . cape-keyword)
("C-c / s" . cape-elisp-symbol)
("C-c / e" . cape-elisp-block)
("C-c / a" . cape-abbrev)
("C-c / l" . cape-line)
("C-c / w" . cape-dict)
("C-c / :" . cape-emoji)
("C-c / \\" . cape-tex)
("C-c / _" . cape-tex)
("C-c / ^" . cape-tex)
("C-c / &" . cape-sgml)
("C-c / r" . cape-rfc1345))
:init
;; Add to the global default value of `completion-at-point-functions' which is
;; used by `completion-at-point'. The order of the functions matters, the
;; first function returning a result wins. Note that the list of buffer-local
;; completion functions takes precedence over the global list.
(add-hook 'completion-at-point-functions #'cape-dabbrev)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-elisp-block)
(add-hook 'completion-at-point-functions #'cape-history)
(add-hook 'completion-at-point-functions #'cape-keyword))
;;(add-hook 'completion-at-point-functions #'cape-tex)
;;(add-hook 'completion-at-point-functions #'cape-sgml)
;;(add-hook 'completion-at-point-functions #'cape-rfc1345)
;;(add-hook 'completion-at-point-functions #'cape-abbrev)
;;(add-hook 'completion-at-point-functions #'cape-dict)
;;(add-hook 'completion-at-point-functions #'cape-elisp-symbol)
;;(add-hook 'completion-at-point-functions #'cape-line))
;; coding
;; ===
(use-package fic-mode :ensure t :defer t)
(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))
(use-package flycheck :ensure t :defer t)
(use-package go-mode :ensure t :defer t
:mode (("\\.go$" . go-mode))
:hook ((go-mode . lsp-deferred)
(before-save . lsp-format-buffer)
(before-save . lsp-organize-imports)))
(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 mmm-mode :ensure t :defer t)
(use-package multi-web-mode :ensure t :defer t)
(use-package graphviz-dot-mode :ensure t :defer t)
(use-package protobuf-mode :ensure t :defer t)
(use-package rego-mode :ensure t :defer 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"))
(org-babel-do-load-languages
'org-babel-load-languages
'(
(plantuml . t))))
(use-package kotlin-mode :ensure t :defer t)
(use-package flycheck-projectile :ensure t)
;; search & narrow
;; ===
(use-package prescient :ensure t :defer t)
;; navigation
;; ===
(use-package ace-jump-mode :ensure t :defer t
:bind (("C-c SPC" . 'ace-jump-mode)
("C-x SPC" . 'ace-jump-mode-pop-mark)))
(with-eval-after-load 'org
(org-babel-do-load-languages 'org-babel-load-languages
'((go . t)
(dot . t)
(plantuml . t)
)))
;; api testing
;; ===
(use-package plz :ensure t)