1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830 |
- ;;; init.el --- Configuration entry point -*- lexical-binding: t -*-
- ;;; Commentary:
- ;;; Code:
- (require 'cl-lib)
- (require 'xdg)
- ;; Some other config files
- (cl-eval-when (compile load eval)
- (add-to-list 'load-path (expand-file-name "elisp" user-emacs-directory))
- (add-to-list 'load-path (expand-file-name "third-party" user-emacs-directory)))
- ;; Set package dir to follow no-littering conventions
- (setq package-user-dir (expand-file-name "var/elpa"
- user-emacs-directory))
- ;; Use melpa
- (require 'package)
- (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
- (package-initialize)
- ;; Ensure use-package is installed
- (unless (package-installed-p 'use-package)
- (package-refresh-contents)
- (package-install 'use-package))
- ;; use-package
- (eval-when-compile
- (require 'use-package)
- (setq use-package-always-ensure t
- package-user-dir (expand-file-name "var/elpa"
- user-emacs-directory)))
- ;; no-littering
- (use-package no-littering
- :autoload (no-littering-theme-backups
- no-littering-expand-etc-file-name)
- :init
- (no-littering-theme-backups)
- (setq custom-file (no-littering-expand-etc-file-name "custom.el")))
- ;; load things saved with custom
- (load custom-file t t)
- ;; diminish
- (use-package diminish
- :config
- (diminish 'visual-line-mode)
- (diminish 'abbrev-mode))
- ;; Private config loading
- (require 'private nil t)
- (defun my/get-private (key)
- "Get the private config variable KEY from the private configuration file."
- (alist-get key my/private-config))
- ;; basic stuff
- (use-package emacs
- :hook (;;(emacs-lisp-mode . my/-emacs-lisp-mode-setup-evil-lookup)
- ;;(prog-mode . electric-pair-local-mode)
- ((text-mode tex-mode prog-mode) . auto-fill-mode)
- ((text-mode tex-mode prog-mode) . my/-enable-show-trailing-whitespace))
- :init
- (with-eval-after-load 'find-func
- (when (file-directory-p "~/src/emacs/src/")
- (setq find-function-C-source-directory "~/src/emacs/src/")))
- (defun my/-enable-show-trailing-whitespace ()
- (setq-local show-trailing-whitespace t))
- ;; (defun my/-emacs-lisp-mode-setup-evil-lookup ()
- ;; (setq-local evil-lookup-func
- ;; #'my/describe-symbol-at-point))
- (defun my/describe-symbol-at-point ()
- "Calls `describe-symbol' on the return value of `symbol-at-point'."
- (interactive)
- (let ((form (symbol-at-point)))
- (if (consp form)
- (describe-symbol (cadr form))
- (describe-symbol form))))
- ;; Increase responsiveness
- (setq gc-cons-threshold 80000000
- read-process-output-max (* 1024 1024)) ;; 1mb
- (global-so-long-mode 1)
- ;; Terminal mouse support
- (xterm-mouse-mode 1)
- ;; Make cursor more visible
- (global-hl-line-mode 1)
- (blink-cursor-mode -1)
- ;; Enable all disabled stuff
- (setq disabled-command-function nil)
- ;; Stop some annoying stuff
- (setq extended-command-suggest-shorter nil
- suggest-key-bindings nil)
- ;; Better scrolling
- (setq mouse-scroll-delay 0
- scroll-conservatively 10
- scroll-margin 2
- scroll-preserve-screen-position t)
- ;; Make show paren instant
- (setq show-paren-delay 0)
- (show-paren-mode 1)
- ;; Display line numbers
- (global-display-line-numbers-mode 1)
- ;; Allow the frame to be any size
- (setq frame-resize-pixelwise t)
- ;; Don't use a gtk file picker
- (setq use-file-dialog nil)
- ;; Make yes-or-no-p less verbose (and not use windows)
- (setq use-dialog-box nil
- use-short-answers t)
- ;; Disable startup screen
- (setq inhibit-startup-screen t
- server-client-instructions nil)
- ;; show column numbers
- (column-number-mode 1)
- ;; Disable the menu and tool bars
- (menu-bar-mode -1)
- (tool-bar-mode -1)
- ;; No scroll bars
- (scroll-bar-mode -1)
- ;; Visual line mode
- (global-visual-line-mode 1)
- ;; Make some commands easier to enter multiple times
- (repeat-mode 1)
- ;; Easier buffer navigation
- (keymap-global-set "C-c <" #'previous-buffer)
- (keymap-global-set "C-c >" #'next-buffer)
- (keymap-global-set "C-c k" #'previous-buffer)
- (keymap-global-set "C-c j" #'next-buffer)
- ;; Seems useful...
- (keymap-global-set "C-c u" #'browse-url)
- ;; Set fonts
- (add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font Mono-12"))
- (add-hook 'server-after-make-frame-hook
- (lambda ()
- (set-fontset-font t 'japanese-jisx0208 "IPAGothic")))
- ;; Enable color in compilation buffers
- (add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
- ;; Some settings for programming
- (setq-default indent-tabs-mode nil
- tab-width 4
- fill-column 80
- comment-multi-line t
- comment-empty-lines 'eol)
- (add-to-list 'auto-mode-alist '("\\.[cC][nN][fF]\\'" . conf-mode))
- (keymap-set emacs-lisp-mode-map "C-c C-r" #'eval-region)
- (defun my/-fix-emacs-lisp-mode-system-files ()
- (when (string-prefix-p lisp-directory buffer-file-name)
- ;; system Emacs files use tab characters and look weird without this.
- (setq-local tab-width 8)))
- (add-hook 'emacs-lisp-mode-hook #'my/-fix-emacs-lisp-mode-system-files)
- ;; Tree sitter download locations
- (setq treesit-language-source-alist
- '((c "https://github.com/tree-sitter/tree-sitter-c")
- (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
- (java "https://github.com/tree-sitter/tree-sitter-java")
- ;; (glsl "https://github.com/tree-sitter-grammars/tree-sitter-glsl")
- (python "https://github.com/tree-sitter/tree-sitter-python")
- (rust "https://github.com/tree-sitter/tree-sitter-rust")
- (json "https://github.com/tree-sitter/tree-sitter-json")
- (yaml "https://github.com/ikatyang/tree-sitter-yaml")
- (css "https://github.com/tree-sitter/tree-sitter-css")
- (go "https://github.com/tree-sitter/tree-sitter-go")
- (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
- (javascript "https://github.com/tree-sitter/tree-sitter-javascript")
- (typescript "https://github.com/tree-sitter/tree-sitter-typescript"
- nil "typescript/src")
- (tsx "https://github.com/tree-sitter/tree-sitter-typescript"
- nil "tsx/src")
- (bash "https://github.com/tree-sitter/tree-sitter-bash")
- (cmake "https://github.com/uyha/tree-sitter-cmake")
- (blueprint "https://github.com/huanie/tree-sitter-blueprint")
- (kdl "https://github.com/tree-sitter-grammars/tree-sitter-kdl")))
- ;; Tree sitter major mode conversions
- (dolist (ent '((c-mode . c-ts-mode)
- (c++-mode . c++-ts-mode)
- (c-or-c++-mode . c-or-c++-ts-mode)
- (python-mode . python-ts-mode)
- (java-mode . java-ts-mode)
- (rust-mode . rust-ts-mode)
- (json-mode . json-ts-mode)
- (yaml-mode . yaml-ts-mode)
- (css-mode . css-ts-mode)
- (javascript-mode . js-ts-mode)
- (cmake-mode . cmake-ts-mode)))
- (add-to-list 'major-mode-remap-alist ent))
- (defun my/treesit-compile-all (force)
- "Compile all the modules defined in `treesit-language-source-alist'.
- If FORCE, recompile all modules, even ones that are already compiled.
- Interactively, force the recompile if called with a prefix."
- (interactive "P")
- (let ((did-build nil))
- (dolist (lang treesit-language-source-alist)
- (when (or force (not (treesit-language-available-p (car lang))))
- (treesit-install-language-grammar (car lang))
- (setq did-build t)))
- (unless did-build
- (message "All defined parsers installed!")))))
- (use-package auth-source
- :ensure nil
- :custom
- (auth-sources '("~/.authinfo.gpg")))
- (use-package tramp
- :ensure nil
- :config
- (connection-local-set-profile-variables
- 'remote-direct-async-process
- '((tramp-direct-async-process . t)))
- (connection-local-set-profiles
- '(:protocol "ssh")
- 'remote-direct-async-process))
- (use-package midnight
- :ensure nil
- :config
- (add-to-list 'clean-buffer-list-kill-never-buffer-names
- "*mu4e-main*")
- (add-to-list 'clean-buffer-list-kill-never-buffer-names
- "*Async-native-compile-log*")
- (add-to-list 'clean-buffer-list-kill-never-buffer-names
- "*dashboard*")
- (add-to-list 'clean-buffer-list-kill-never-buffer-names
- "*elfeed-search*")
- (midnight-mode 1))
- (defvar my/kill-some-buffers-exclude-names
- '("*mu4e-main*" "*Async-native-compile-log*" "*dashboard*" "*elfeed-search*"
- "*Messages*" "*scratch*")
- "List of literal buffer names that `my/kill-some-buffers' should not kill.")
- (defun my/kill-some-buffers-excluded-buffer-p (buffer)
- "Return non-nil if BUFFER should be excluded from `my/kill-some-buffers'."
- (cl-find (buffer-name buffer) my/kill-some-buffers-exclude-names
- :test 'equal))
- (defun my/buffer-visible-p (buffer)
- "Return non-nil if BUFFER is visible.
- BUFFER can be a string or a buffer."
- (cond
- ((stringp buffer)
- (not (string-prefix-p " " buffer)))
- ((bufferp buffer)
- (and (stringp (buffer-name buffer))
- (my/buffer-visible-p (buffer-name buffer))))
- (t
- (signal 'wrong-type-argument `((or bufferp stringp) ,buffer)))))
- (defvar my/kill-some-buffers-default-pred 'my/buffer-visible-p
- "Default predicate for `my/kill-some-buffers'.")
- (defun my/kill-some-buffers-prompt-for (buffer)
- "Generate a prompt for BUFFER."
- (let* ((process (get-buffer-process buffer))
- (process-p (and (process-live-p process)
- (not (process-query-on-exit-flag process))))
- (modified-p (and (buffer-file-name buffer)
- (buffer-modified-p buffer))))
- (format "Buffer \"%s\" %s. Kill? "
- (buffer-name buffer)
- (cond
- ((and process-p modified-p)
- "HAS BEEN EDITED AND HAS A LIVE PROCESS")
- (modified-p
- "HAS BEEN EDITED")
- (process-p
- "HAS A LIVE PROCESS")
- (t "is unmodified")))))
- (cl-defun my/kill-some-buffers (&optional auto-unmod pred)
- "Improved version of `kill-some-buffers'.
- Ask the user weather to kill each visible buffer whose name is not in
- `my/kill-some-buffers-exclude-names'.
- When AUTO-UNMOD is non-nil, as it is with a prefix argument, automatically kill
- unmodified buffers, and then ask about the rest.
- When PRED is non-nil, it is a function that will be run in each buffer (not just
- visible ones). If it returns t, that buffer will be considered for killing. If
- PRED is nil, the value of `my/kill-some-buffers-default-pred' is used."
- (interactive "P")
- ;; we already ask, no need to do it again
- (let ((kill-buffer-query-functions nil)
- (all-action (when auto-unmod 'unmod))
- (had-valid-buffer)
- (ask-again-buffers)
- (to-kill))
- (cl-flet ((ask-about (buffer allow-unmod)
- (unless all-action
- (read-answer
- (my/kill-some-buffers-prompt-for buffer)
- `(("yes" ?y "save and kill this buffer")
- ("no" ?n "skip this buffer")
- ("all" ?! "save and kill all remaining buffers")
- ("nosave" ?l "kill this buffer without saving")
- ,@(when allow-unmod
- '(("unmod" ?a
- "kill unmodified buffers, ask about the rest")))
- ("quit" ?q "exit")))))
- (act-on (ans buffer allow-unmod)
- (when (equal ans "all")
- (setq all-action 'all))
- (when (and allow-unmod
- (equal ans "unmod"))
- (setq all-action 'unmod))
- (cond
- ((and (eq all-action 'unmod)
- (buffer-file-name buffer)
- (buffer-modified-p buffer))
- (push buffer ask-again-buffers))
- ((or (eq all-action 'all)
- (eq all-action 'unmod)
- (equal ans "yes"))
- (when (buffer-file-name buffer)
- (with-current-buffer buffer
- (save-buffer)))
- (push buffer to-kill))
- ((equal ans "nosave")
- (with-current-buffer buffer
- (set-buffer-modified-p nil))
- (push buffer to-kill))
- ;; Skip buffer
- ;; ((equal ans "no"))
- ((equal ans "quit")
- (cl-return-from my/kill-some-buffers)))))
- (dolist (buffer (buffer-list))
- (when (and (not (my/kill-some-buffers-excluded-buffer-p buffer))
- (funcall (or pred my/kill-some-buffers-default-pred) buffer))
- (setq had-valid-buffer t)
- (act-on (ask-about buffer t) buffer t)))
- (unless had-valid-buffer
- (message "Nothing to do..."))
- (setq all-action nil)
- (dolist (buffer ask-again-buffers)
- (act-on (ask-about buffer nil) buffer nil))
- ;; Do this last so that tty frames don't auto-close half way through
- (mapc 'kill-buffer to-kill))))
- (keymap-global-set "C-x K" 'my/kill-some-buffers)
- (use-package tab-bar
- :ensure nil
- :init
- (setq tab-bar-show 1
- tab-bar-tab-hints t
- icon-preference '(symbol text image emoji))
- (tab-bar-mode 1))
- ;; jinx (better flyspell)
- (use-package jinx
- :hook (emacs-startup . global-jinx-mode)
- :config
- (evil-define-key 'normal 'global
- "z=" #'jinx-correct)
- (defun my/jinx-visit-dictionary (language &optional other-window)
- "Visit the dictionary file for LANGUAGE in another window.
- With OTHER-WINDOW, visit the file in another window. Interactively, use the
- current buffer's language, prompting if there is more than one. OTHER-WINDOW is
- t with a prefix argument."
- (interactive (list
- (let ((langs (split-string jinx-languages " ")))
- (if (length= langs 1)
- (car langs)
- (completing-read "Language: " langs nil t)))
- current-prefix-arg))
- (let* ((config-dir (expand-file-name "enchant" (xdg-config-home)))
- (dict-path (expand-file-name (concat language ".dic") config-dir)))
- (if other-window
- (find-file-other-window dict-path)
- (find-file dict-path)))))
- ;; recentf
- (use-package recentf
- :init
- (setq recentf-exclude `("^/tmp/.*"
- "^~/.mail/[^/]/Drafts/.*"
- ,(format "^%svar/dape-breakpoints" user-emacs-directory)
- ,(format "^%svar/elpa/.*" user-emacs-directory)
- ,(format "^%svar/elfeed/.*" user-emacs-directory)
- ,(format "^%svar/gnus/.*" user-emacs-directory)
- ,(format "^%svar/ellama-sessions/.*" user-emacs-directory)
- ,(format "^%setc/gnus/.*" user-emacs-directory)
- ,(format "^%svar/bookmark-default.el" user-emacs-directory)))
- :bind ("C-c r" . recentf)
- :config
- (recentf-mode 1))
- ;; bookmarks
- (use-package bookmark
- :ensure nil
- :bind ("C-c B" . my/bookmark-find-file)
- :config
- (defun my/bookmark-find-file (&optional name)
- "Run `find-file' in or on bookmark NAME.
- If NAME points to a directory, run `find-file' with `default-directory' in that
- directory. Otherwise, run `find-file' on that file."
- (interactive (list (bookmark-completing-read
- "Find file in" bookmark-current-bookmark)))
- (unless name
- (error "No bookmark specified"))
- (bookmark-maybe-historicize-string name)
- (when-let ((file (bookmark-get-filename name)))
- (if (file-directory-p file)
- (let ((default-directory (file-name-as-directory file)))
- (call-interactively 'find-file))
- (find-file file)))))
- ;; kitty keyboard protocol
- (use-package kkp
- :defer nil
- :config
- (global-kkp-mode 1)
- ;; (defun my/-kkp-after-terminal-setup ()
- ;; ;; Make tab and backtab work properly
- ;; (define-key input-decode-map [(control ?i)] [tab])
- ;; (define-key input-decode-map [(control ?I)] [backtab])
- ;; (define-key input-decode-map [(control ?m)] [return]))
- ;; (defun my/-kkp-after-terminal-teardown (term)
- ;; (with-selected-frame (car (frames-on-display-list term))
- ;; (define-key input-decode-map [(control ?i)] nil t)
- ;; (define-key input-decode-map [(control ?I)] nil t)
- ;; (define-key input-decode-map [(control ?m)] nil t)))
- ;; (advice-add 'kkp--terminal-setup :after 'my/-kkp-after-terminal-setup)
- ;; (advice-add 'kkp--terminal-teardown :after 'my/-kkp-after-terminal-teardown)
- (defun my/quoted-insert (arg)
- "Insert the next character using read-key, not read-char."
- (interactive "*p")
- ;; Source: https://github.com/benjaminor/kkp/issues/11
- (let ((char (read-key)))
- ;; Ensure char is treated as a character code for insertion
- (unless (characterp char)
- (user-error "%s is not a valid character"
- (key-description (vector char))))
- (when (numberp char)
- (while (> arg 0)
- (insert-and-inherit char)
- (setq arg (1- arg))))))
- (keymap-global-set "C-q" #'my/quoted-insert)
- (defun my/-kkp-fix-map-y-or-n-p (oldfun &rest args)
- "Fix `map-y-or-n-p' when used in a terminal with kkp enabled."
- (let ((status (kkp--terminal-has-active-kkp-p)))
- (condition-case err
- (progn
- (when status (kkp-disable-in-terminal))
- (apply oldfun args))
- (quit
- ;; We won't die in this case, so just re-enable kkp
- (when (and status (not (kkp--terminal-has-active-kkp-p)))
- (kkp-enable-in-terminal))
- (signal 'quit nil))
- (t
- (when (and status (not (kkp--terminal-has-active-kkp-p)))
- ;; this does async stuff that will make kitty send characters after
- ;; Emacs exits. We prevent that by not re-enabling if this frame (or
- ;; Emacs) is about to die
- (let ((will-die))
- (mapbacktrace
- (lambda (_evald func _args _flags)
- (when (or (eq func 'save-buffers-kill-emacs)
- (eq func 'server-save-buffers-kill-terminal))
- (setq will-die t))))
- (unless will-die
- (kkp-enable-in-terminal))))
- (when err
- (signal (car err) (cdr err)))))))
- (advice-add #'map-y-or-n-p :around
- #'my/-kkp-fix-map-y-or-n-p))
- ;; mozc
- (require 'mozc nil t)
- (setq default-input-method "japanese-mozc")
- ;; migemo
- (use-package migemo
- :config
- (setq migemo-dictionary "/usr/share/migemo/utf-8/migemo-dict"
- migemo-coding-system 'utf-8-unix
- migemo-regex-dictionary nil
- migemo-user-dictionary nil
- migemo-isearch-enable-p t)
- (keymap-global-set "C-c w" 'isearch-forward)
- (keymap-global-set "C-c W" 'isearch-backward)
- (migemo-init))
- ;; undo-tree
- (use-package undo-tree
- :defer nil
- :hook (undo-tree-visualizer-mode . my/-undo-tree-visualizer-mode-setup)
- :config
- (defun my/-undo-tree-visualizer-mode-setup ()
- (visual-line-mode -1)
- (setq truncate-lines t))
- (global-undo-tree-mode))
- ;; evil
- (use-package evil
- :init
- (setq evil-want-integration t
- evil-want-C-d-scroll nil
- evil-want-keybinding nil
- evil-undo-system 'undo-tree
- evil-search-module 'isearch
- evil-respect-visual-line-mode t)
- :config
- (evil-mode 1)
- (evil-define-key '(normal visual motion) proced-mode-map
- "u" #'proced-unmark)
- (evil-define-key '(normal visual motion) dired-mode-map
- "u" #'dired-unmark)
- (evil-define-key '(normal visual motion) profiler-report-mode-map
- (kbd "TAB") #'profiler-report-toggle-entry)
- (eldoc-add-command 'evil-insert
- 'evil-append
- 'evil-insert-line
- 'evil-append-line))
- (use-package evil-collection
- :after evil
- :diminish evil-collection-unimpaired-mode
- :config
- (evil-collection-init))
- (use-package evil-surround
- :after evil
- :config
- (evil-define-key 'operator evil-surround-mode-map
- "z" #'evil-surround-edit
- "Z" #'evil-Surround-edit)
- (evil-define-key 'visual evil-surround-mode-map
- "gz" #'evil-surround-region
- "gZ" #'evil-Surround-region)
- (global-evil-surround-mode 1))
- (use-package evil-terminal-cursor-changer
- :after evil
- :config
- (evil-terminal-cursor-changer-activate))
- (use-package evil-numbers
- :after evil
- :bind (("C-c =" . evil-numbers/inc-at-pt)
- ("C-c +" . evil-numbers/inc-at-pt)
- ("C-c -" . evil-numbers/dec-at-pt)
- ("C-c C-=" . evil-numbers/inc-at-pt-incremental)
- ("C-c C-+" . evil-numbers/inc-at-pt-incremental)
- ("C-c C--" . evil-numbers/dec-at-pt-incremental)))
- (use-package evil-cleverparens
- :hook ((prog-mode . my/-enable-evil-cleverparens)
- (evil-cleverparens-mode . paredit-mode))
- :bind (:map paredit-mode-map
- ("C-<return>" . paredit-RET)
- ("C-RET" . paredit-RET)
- :map evil-cleverparens-mode-map
- ("C-c o" . evil-cp-open-below-form))
- :custom
- (evil-cleverparens-use-s-and-S nil)
- (evil-cleverparens-complete-parens-in-yanked-region t)
- :config
- (eldoc-add-command 'paredit-RET
- 'paredit-open-round
- 'paredit-open-angled
- 'paredit-open-bracket
- 'paredit-open-angled
- 'paredit-open-parenthesis
- 'delete-indentation
- 'evil-cp-insert
- 'evil-cp-append
- 'evil-cp-insert-at-beginning-of-form
- 'evil-cp-insert-at-end-of-form)
- (keymap-unset evil-cleverparens-mode-map "<normal-state> M-o" t)
- (defun my/-enable-evil-cleverparens ()
- (if (member major-mode '(lisp-mode emacs-lisp-mode
- lisp-interaction-mode))
- (evil-cleverparens-mode 1)
- (electric-pair-local-mode 1)))
- (cl-defun my/range-inside-thing-p (thing beg end &optional no-edge)
- "Return non-nil if BEG and END fall inside the bounds of THING.
- With NO-EDGE, return nil if beg or end fall on the edge of the range."
- (save-excursion
- ;; this fixes that fact that `thing-at-point-bounds-of-string-at-point'
- ;; errors if called at the end of the buffer
- (condition-case nil
- (let ((sb (progn (goto-char beg) (bounds-of-thing-at-point thing)))
- (eb (progn (goto-char end) (bounds-of-thing-at-point thing))))
- (and sb eb (equal sb eb)
- (or (not no-edge)
- (and (/= beg (car sb))
- (< beg (cdr sb))
- (/= end (car sb))
- (< end (cdr sb))))))
- ;; if the error happens, we aren't in a string
- (wrong-type-argument nil))))
- (defun my/-evil-cp-region-ok-p-no-string (oldfun beg end)
- (or
- (and (sp-point-in-comment beg)
- (sp-point-in-comment end))
- (and (sp-point-in-string beg)
- (sp-point-in-string end)
- (my/range-inside-thing-p 'string beg end t))
- (funcall oldfun beg end)))
- (defun my/column-num-at-pos (pos)
- "Return the column number at POS."
- (save-excursion
- (goto-char pos)
- (current-column)))
- (defun my/-evil-cp-block-ok-p-no-string (oldfun beg end)
- (when (> beg end) (cl-rotatef beg end))
- (or
- (save-excursion
- (goto-char beg)
- (let ((start-off (current-column))
- (end-off (my/column-num-at-pos end)))
- (cl-block nil
- (dotimes (_ (count-lines beg end) t)
- (let ((bol (pos-bol)))
- (unless (sp-region-ok-p (+ bol start-off)
- (+ bol end-off))
- (cl-return))
- (forward-line))))))
- (funcall oldfun beg end)))
- (advice-add 'sp-region-ok-p :around 'my/-evil-cp-region-ok-p-no-string)
- (advice-add 'evil-cp--balanced-block-p :around 'my/-evil-cp-block-ok-p-no-string))
- ;; be (hopefully) safer
- (require 'trusted-files)
- (keymap-global-set "C-c t" 'trusted-files-map)
- (trusted-files-modeline-mode)
- ;; better lisp editing
- (use-package adjust-parens
- :hook (prog-mode . adjust-parens-mode)
- :config
- (defun my/lisp-indent-adjust-parens ()
- "Like `lisp-indent-adjust-parens', but got to first char on line first.
- Also, this works even if the region is active (it indents every line in the
- region)."
- (interactive)
- (save-mark-and-excursion
- (let ((end (mark t))
- (line-count 1)
- (indent-cols))
- (when (and (region-active-p) end)
- (setq mark-active nil
- line-count (count-lines (point) end))
- (when (> (point) end)
- (let ((start (point)))
- (goto-char end)
- (setq end start))))
- ;; find the indentation column of each line
- (save-excursion
- (dotimes (_ line-count)
- (back-to-indentation)
- (push (- (point) (pos-bol)) indent-cols)
- (forward-line))
- (cl-callf nreverse indent-cols))
- (cl-loop repeat line-count
- for indent-col in indent-cols
- for bol = (pos-bol)
- do (back-to-indentation)
- ;; skip this line if the indentation has changed
- when (= (- (point) bol) indent-col) do
- (lisp-indent-adjust-parens)
- ;; if the indent failed, stop
- (when (= (- (point) bol) indent-col)
- (cl-return))
- do (forward-line)))))
- (defun my/lisp-dedent-adjust-parens ()
- "Like `lisp-dedent-adjust-parens', but got to first char on line first.
- Also, this works even if the region is active (it just jumps to the first line
- in the region and indents once)."
- (interactive)
- (save-mark-and-excursion
- (let ((end (mark t)))
- (when (and (region-active-p) end)
- (setq mark-active nil)
- (when (> (point) end)
- (goto-char end))))
- (back-to-indentation)
- (lisp-dedent-adjust-parens)))
- (eldoc-add-command 'my/lisp-indent-adjust-parens
- 'my/lisp-dedent-adjust-parens
- 'lisp-indent-adjust-parens
- 'lisp-dedent-adjust-parens)
- (evil-define-key '(normal visual) adjust-parens-mode-map
- (kbd "<tab>") #'my/lisp-indent-adjust-parens
- (kbd "<backtab>") #'my/lisp-dedent-adjust-parens
- (kbd "C-c C-i") #'my/lisp-indent-adjust-parens
- (kbd "C-c S-<tab>") #'my/lisp-dedent-adjust-parens))
- ;; for when the files are just too large
- (use-package vlf
- :demand t
- :config
- (require 'vlf-setup))
- ;; allow copy from terminal
- (use-package xclip
- :config
- (setq xclip-method 'wl-copy
- xclip-program (symbol-name xclip-method))
- (xclip-mode 1)
- (defun my/-xclip-detect-wl-paste-error (oldfun type)
- (if (eq xclip-method 'wl-copy)
- ;; Direct from `xclip-get-selection'
- (when (and (getenv "WAYLAND_DISPLAY")
- (memq type '(clipboard CLIPBOARD primary PRIMARY)))
- (let* ((exit-code 0)
- (output
- (with-output-to-string
- (setq exit-code
- (apply #'call-process (replace-regexp-in-string
- "\\(.*\\)copy" "\\1paste"
- xclip-program 'fixedcase)
- nil standard-output nil
- "-n" (if (memq type '(primary PRIMARY))
- '("-p")))))))
- (if (zerop exit-code)
- output
- "")))
- (funcall oldfun type)))
- (advice-add 'xclip-get-selection :around 'my/-xclip-detect-wl-paste-error))
- ;; Set the WAYLAND_DISPLAY environment variable
- (require 'xdg)
- (defun my/detect-wayland-display ()
- "Try to set the WAYLAND_DISPLAY environment variable.
- This attempts to detect a running Wayland session and set the WAYLAND_DISPLAY
- environment variable accordingly."
- (let ((found '(nil . nil)))
- (dolist (entry (directory-files-and-attributes
- (xdg-runtime-dir) nil nil 'nosort)
- (cdr found))
- (cl-destructuring-bind (name . attrs) entry
- (when-let (((string-match (rx bos "wayland-" (group (+ (any "0-9"))) eos)
- name))
- (id (string-to-number (match-string 1 name)))
- ((or (not (car found)) (< id (car found))))
- ;; socket
- ((string-prefix-p "s" (file-attribute-modes attrs))))
- (setq found (cons id name)))))))
- (unless (getenv "WAYLAND_DISPLAY")
- (setenv "WAYLAND_DISPLAY" (my/detect-wayland-display)))
- ;; which-key
- (use-package which-key
- :diminish which-key-mode
- :config
- (which-key-mode 1))
- ;; avy
- (use-package avy
- :bind (("C-c C-j" . avy-resume)
- ("M-s s" . evil-avy-goto-char-2)
- ("M-s S" . evil-avy-goto-line))
- :init
- (define-minor-mode my/evil-avy-mode
- "A minor mode for binding avy commands to s and S in evil's normal and
- visual states."
- :keymap (make-sparse-keymap))
- (evil-define-key '(normal visual operator motion) my/evil-avy-mode-map
- "s" #'evil-avy-goto-char-2
- "S" #'evil-avy-goto-line)
- (define-globalized-minor-mode my/evil-avy-global-mode my/evil-avy-mode
- (lambda () (my/evil-avy-mode 1))
- :predicate '((not magit-mode dired-mode
- proced-mode mu4e-main-mode
- mu4e-view-mode mu4e-headers-mode
- ibuffer-mode calc-mode calc-trail-mode
- gnus-group-mode) t))
- (my/evil-avy-global-mode 1)
- :config
- (avy-setup-default))
- ;; better `replace-regexp'
- (use-package visual-regexp
- :bind (("C-c q" . vr/replace)
- ("C-M-%" . vr/query-replace)))
- ;; better `align-regexp'
- (use-package ialign
- :defer t
- :custom
- (ialign-initial-repeat t))
- ;; ace-window
- (use-package ace-window
- :diminish ace-window-mode
- :bind ("M-o" . ace-window)
- :init
- (setq aw-scope 'frame
- aw-minibuffer-flag t))
- ;; savehist
- (use-package savehist
- :config
- (savehist-mode 1))
- ;; vertico
- (use-package vertico
- :bind (:map vertico-map
- ("C-RET" . vertico-exit-input)
- ("C-<return>" . vertico-exit-input)
- ("C-S-k" . kill-line)
- ("C-k" . vertico-previous)
- ("C-j" . vertico-next)
- ("RET" . vertico-directory-enter)
- ("DEL" . vertico-directory-delete-char)
- ("M-DEL" . vertico-directory-delete-word))
- :hook (minibuffer-setup . cursor-intangible-mode)
- :init
- (defun my/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 #'my/crm-indicator)
- (setq vertico-cycle t
- enable-recursive-minibuffers t
- ;; read-extended-command-predicate #'command-completion-default-include-p
- read-extended-command-predicate nil
- minibuffer-prompt-properties '(read-only t ;; noindent 3
- cursor-intangible t
- face minibuffer-prompt))
- (vertico-mode 1)
- ;; for jinx
- (require 'vertico-multiform)
- (add-to-list 'vertico-multiform-categories
- '(jinx grid (vertico-grid-annotate . 20)))
- (vertico-multiform-mode 1))
- ;; orderless
- (use-package orderless
- :autoload orderless-define-completion-style
- :hook (text-mode . my/-setup-text-mode-completion-styles)
- :init
- (defun my/-setup-text-mode-completion-styles ()
- (setq-local completion-styles '(basic)))
- (orderless-define-completion-style my/orderless-with-initialism
- (orderless-matching-styles '(orderless-initialism
- orderless-regexp)))
- (setq orderless-matching-styles '(orderless-regexp)
- completion-styles '(orderless basic)
- completion-category-defaults nil
- completion-category-overrides '((file
- (styles basic partial-completion))
- (command
- (styles my/orderless-with-initialism basic)))))
- ;; marginalia
- (use-package marginalia
- :bind (:map minibuffer-local-map
- ("M-a" . marginalia-cycle))
- :init
- (marginalia-mode 1))
- ;; embark
- (use-package embark
- :bind (("C-," . embark-act)
- ("C-;" . embark-dwim)
- :map help-map
- ("B" . embark-bindings)
- :map embark-symbol-map
- ("h" . helpful-symbol)
- :map embark-become-file+buffer-map
- ("b" . consult-buffer)
- ("B" . switch-to-buffer))
- :init
- (setq embark-quit-after-action nil
- embark-indicators '(embark-minimal-indicator
- embark-isearch-highlight-indicator
- embark-highlight-indicator))
- :config
- (defvar-keymap my/embark-string-map
- :doc "Keymap for Embark string actions."
- :parent embark-expression-map
- "R" 'repunctuate-sentences)
- (defun my/embark-target-string ()
- "Target the string at point."
- (if-let (((not (eobp))) ; prevent next line from causing errors
- (bounds (bounds-of-thing-at-point 'string)))
- (append (list 'string (buffer-substring-no-properties (car bounds)
- (cdr bounds)))
- bounds)))
- (add-to-list 'embark-around-action-hooks
- '(repunctuate-sentences embark--mark-target))
- (add-to-list 'embark-keymap-alist
- '(string my/embark-string-map))
- (add-to-list 'embark-target-finders 'my/embark-target-string)
- (add-to-list 'display-buffer-alist
- '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
- nil
- (window-parameters (mode-line-format . none))))
- (evil-define-key '(normal motion) org-mode-map
- (kbd "C-,") #'embark-act))
- ;; consult
- (use-package consult
- :bind (("C-s" . consult-line)
- ("C-x b" . consult-buffer)
- ("C-S-s" . consult-ripgrep)
- ("C-x C-S-f" . consult-fd)
- ("C-x c k" . consult-keep-lines)
- ("C-x c f" . consult-focus-lines)
- ("C-x c r" . consult-recent-file)
- ("C-x c b" . consult-bookmark)
- ("C-x c d" . consult-fd)
- ("C-c p" . consult-fd)
- ("C-x c g" . consult-ripgrep)
- ("C-c P" . consult-ripgrep)
- ("C-x c y" . consult-yank-from-kill-ring)
- ("M-g i" . consult-imenu)
- ("M-g I" . consult-imenu-multi)
- ("M-g r" . consult-imenu-multi)
- :map help-map
- ("TAB". consult-info)
- ("RET" . consult-man))
- :hook (minibuffer-setup . my/consult-setup-minibuffer-completion)
- :init
- (defun my/consult-setup-minibuffer-completion ()
- (setq-local completion-in-region-function #'consult-completion-in-region))
- (evil-declare-motion #'consult-line))
- (use-package consult-eglot
- :commands consult-eglot-symbols)
- ;; wgrep
- (use-package wgrep)
- ;; integration for embark and consult
- (use-package embark-consult
- :hook (embark-collect-mode . consult-preview-at-point-mode))
- ;; corfu (autocomplete)
- (use-package corfu
- :bind (("M-<tab>" . completion-at-point)
- :map corfu-map
- ("C-j" . corfu-next)
- ("C-k" . corfu-previous)
- ("M-SPC" . corfu-insert-separator)
- ("M-m" . my/corfu-move-to-minibuffer))
- :init
- (defun my/corfu-move-to-minibuffer ()
- (interactive)
- (when completion-in-region--data
- (let ((completion-extra-properties corfu--extra)
- (completion-cycle-threshold completion-cycling))
- (apply #'consult-completion-in-region completion-in-region--data))))
- (setq corfu-cycle t
- corfu-auto t
- corfu-on-exact-match nil
- corfu-popupinfo-delay '(1.0 . 0.5)
- completion-cycle-threshold nil
- global-corfu-minibuffer
- ;; only enable corfu in the minibuffer in graphical frames
- (lambda ()
- (and (display-graphic-p)
- (not (eq (current-local-map)
- read-passwd-map)))))
- (global-corfu-mode 1)
- (corfu-popupinfo-mode 1)
- :config
- (add-to-list 'corfu-continue-commands #'my/corfu-move-to-minibuffer)
- (defun my/help-buffer-exists-p ()
- "Return if the buffer that `help-buffer' would, or nil if it doesn't exist."
- (or (and help-xref-following (derived-mode-p 'help-mode))
- (get-buffer "*Help*")))
- (defun my/-corfu-popupinfo-close-help-buffer (oldfun &rest args)
- (if (derived-mode-p 'emacs-lisp-mode)
- (let ((help-buf (my/help-buffer-exists-p)))
- (prog1
- (apply oldfun args)
- (when-let (((not help-buf))
- (buf (help-buffer)))
- ;; Ensure that, even if `help-buffer' returns nil in the future, we
- ;; don't kill the current buffer
- (kill-buffer buf))))
- (apply oldfun args)))
- (advice-add 'corfu-popupinfo--get-documentation :around
- 'my/-corfu-popupinfo-close-help-buffer))
- (use-package corfu-terminal
- :init
- (corfu-terminal-mode 1)
- :config
- (require 'corfu-terminal-popupinfo)
- (corfu-terminal-popupinfo-mode 1))
- (use-package dabbrev
- :ensure nil
- :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))
- ;; cape (a bunch of capfs!)
- (use-package cape
- :bind (([remap dabbrev-expand] . cape-dabbrev)
- ("C-c P" . cape-line)
- ("C-c f" . cape-file))
- :hook (text-mode . my/-cape-setup-text-mode)
- :init
- (defun my/-cape-setup-text-mode ()
- ;; Only run this if we are not in `TeX-mode'
- (unless (bound-and-true-p TeX-mode-p)
- (setq-local completion-at-point-functions
- (append completion-at-point-functions (list 'cape-dict
- 'cape-dabbrev))
- corfu-auto nil))))
- ;; xref
- (use-package xref
- :init
- (evil-define-key '(normal motion) 'global
- "gr" #'xref-find-references)
- (setq xref-show-xrefs-function #'consult-xref
- xref-show-definitions-function #'consult-xref))
- ;; popup.el
- (use-package popup)
- ;; posframe
- (use-package posframe
- :init
- (defun my/posframe-tip (name msg)
- "Like `popup-tip', but with a posframe.
- NAME should be the buffer name to pass to `posframe-show'. MSG is the message to
- display."
- (unwind-protect
- (progn
- (posframe-show name
- :string msg
- :position (point)
- :max-width 80
- :border-width 2
- :border-color "white")
- (clear-this-command-keys)
- (push (read-event) unread-command-events)
- (posframe-hide name))
- (posframe-hide name))))
- (defun my/floating-tooltip (name msg)
- "If `display-graphic-p', call `my/posframe-tip', otherwise `popup-tip'.
- MSG is the message to show in the popup. NAME is the name of the buffer to pass
- to `posframe-show' if the display is graphical."
- (if (display-graphic-p)
- (my/posframe-tip name msg)
- (popup-tip msg)))
- ;; flymake
- (use-package flymake
- :config
- (require 'consult-flymake))
- ;; flycheck
- (use-package flycheck
- :hook ((sh-mode emacs-lisp-mode) . trusted-files-flycheck-mode-if-safe)
- :custom
- (flycheck-indication-mode 'left-margin)
- :init
- (setq flycheck-display-errors-function nil))
- (use-package consult-flycheck)
- (defun my/sly-notes-at-point (&optional pos buffer)
- "Return the sly notes at POS in BUFFER.
- If BUFFER is nil, the current buffer is used."
- (with-current-buffer (or buffer (current-buffer))
- (unless pos
- (setq pos (point)))
- (cl-loop for overlay in (overlays-at pos)
- for note = (overlay-get overlay 'sly-note)
- when note
- collect note)))
- (defun my/diagnostic-at-point ()
- "Show the diagnostics under point."
- (interactive)
- (let ((message))
- (when-let (((bound-and-true-p flymake-mode))
- (diag (get-char-property (point) 'flymake-diagnostic)))
- (cl-callf nconc message (string-split (flymake--diag-text diag) "\n" t)))
- (when (bound-and-true-p flycheck-mode)
- (cl-callf nconc message
- (mapcar 'flycheck-error-message (flycheck-overlay-errors-at (point)))))
- ;; sly (lazy-loaded)
- (when (featurep 'sly)
- (cl-callf nconc message (mapcar (lambda (note)
- (plist-get note :message))
- (my/sly-notes-at-point))))
- ;; jinx
- (when-let (((bound-and-true-p jinx-mode))
- (jinx-msg (jinx--get-overlays (point) (1+ (point)))))
- (push "misspelled word" message))
- (when message
- (my/floating-tooltip " *my-diagnostic-posframe*"
- (mapconcat (lambda (msg)
- (concat "•" msg))
- message "\n")))))
- (defconst my/consult-flymake-flycheck-narrow
- '((?e . "Error")
- (?w . "Warning")
- (?i . "Info")
- (?n . "Info")))
- (defun my/-consult-replace-flymake-error-level (candidates)
- "Return CANDIDATES with the flymake error level note replaced with info."
- (cl-loop for cand in candidates
- collect
- (cl-loop
- with start = nil
- for i below (length cand)
- for props = (text-properties-at i cand)
- for face = (plist-get props 'face)
- when (eq face 'compilation-info) do
- (setq start (or start i))
- else when start do
- (setf (substring cand start i)
- (propertize (string-pad "info" (- i start))
- 'face (flycheck-error-level-error-list-face
- 'info)))
- (cl-return cand)
- finally return cand)))
- (defun my/consult-flymake-flycheck-candidates (&optional project)
- "Return combined candidate list for flymake and flycheck.
- With PROJECT, return the candiadeets for that project."
- (let ((had-errors))
- (prog1
- (seq-uniq
- (append
- (when-let (((bound-and-true-p flymake-mode))
- (diags (if project (flymake--project-diagnostics
- project)
- (flymake-diagnostics))))
- (setq had-errors t)
- (my/-consult-replace-flymake-error-level
- (consult-flymake--candidates diags)))
- (when (boundp 'flycheck-mode)
- (if project
- (cl-loop for buf in (project-buffers project)
- append
- (with-current-buffer buf
- (when (and flycheck-mode flycheck-current-errors)
- (setq had-errors t)
- (consult-flycheck--candidates))))
- (when (and flycheck-mode flycheck-current-errors)
- (setq had-errors t)
- (consult-flycheck--candidates))))))
- (unless had-errors
- (user-error "No errors (Flymake: %s | Flycheck: %s)"
- (cond
- ((not (bound-and-true-p flymake-mode))
- "not running")
- ((seq-difference (flymake-running-backends)
- (flymake-reporting-backends))
- "running")
- (t "finished"))
- (if (boundp 'flycheck-last-status-change)
- flycheck-last-status-change
- "not running"))))))
- (defun my/consult-flymake-flycheck (&optional project)
- "Jump to flymake or flycheck error.
- With PROJECT, give diagnostics for all buffers in the current project."
- (interactive "P")
- (consult--read
- (consult--with-increased-gc
- (my/consult-flymake-flycheck-candidates
- (and project (project-current))))
- :prompt "Error: "
- :category 'flymake-flycheck-error
- :history t
- :require-match t
- :sort nil
- :narrow (consult--type-narrow my/consult-flymake-flycheck-narrow)
- :group (consult--type-group my/consult-flymake-flycheck-narrow)
- :lookup #'consult--lookup-candidate
- :state (consult--jump-state)))
- (with-eval-after-load 'flymake
- (keymap-set flymake-mode-map "C-c e" 'my/diagnostic-at-point)
- (keymap-set flymake-mode-map "C-c E" 'my/consult-flymake-flycheck))
- (with-eval-after-load 'flycheck
- (keymap-set flycheck-mode-map "C-c e" 'my/diagnostic-at-point)
- (keymap-set flycheck-mode-map "C-c E" 'my/consult-flymake-flycheck))
- (with-eval-after-load 'jinx
- (keymap-set jinx-mode-map "C-c e" 'my/diagnostic-at-point))
- ;; eldoc
- (use-package eldoc
- :diminish eldoc-mode
- :init
- (setq-default eldoc-echo-area-use-multiline-p nil))
- ;; eglot
- (use-package eglot
- :demand t
- :pin gnu ;; try to force Elpa version to fix warnings
- :hook ((eglot-managed-mode . my/-eglot-setup))
- :init
- ;; (defun my/eglot-in-text-mode-only ()
- ;; (when (eq major-mode 'text-mode)
- ;; (trusted-files-eglot-ensure-if-safe)))
- (defvar my/-eglot-documentation-buffer nil
- "Buffer for showing documentation for `my/eglot-documentation-at-point'.")
- (defun my/eglot-documentation-at-point ()
- "Show documentation for a symbol at point."
- (interactive)
- (if-let (server (eglot-current-server))
- (progn
- (if-let* (((not (buffer-live-p my/-eglot-documentation-buffer)))
- (name (generate-new-buffer-name "*eglot documentation*")))
- (setq my/-eglot-documentation-buffer (generate-new-buffer name)))
- (eglot-hover-eldoc-function
- (lambda (info _ _)
- (if-let (((not (seq-empty-p info)))
- (buff (current-buffer)))
- (with-current-buffer my/-eglot-documentation-buffer
- (read-only-mode -1)
- (erase-buffer)
- (insert info)
- (goto-char (point-min))
- (special-mode)
- (read-only-mode 1)
- (when (not (get-buffer-window my/-eglot-documentation-buffer nil))
- (switch-to-buffer-other-window my/-eglot-documentation-buffer t)
- (switch-to-buffer-other-window buff t)))))))))
- (defun my/-eglot-cleanup-doc-buffer (_server &optional _interactive _timeout
- preserve-buffers)
- (when (and (not preserve-buffers)
- (buffer-live-p my/-eglot-documentation-buffer)
- (cl-every (lambda (buffer)
- (with-current-buffer buffer
- (let ((server (eglot-current-server)))
- (or (not (eglot-lsp-server-p server))
- (eglot--shutdown-requested server)))))
- (buffer-list)))
- (kill-buffer my/-eglot-documentation-buffer)))
- (advice-add 'eglot-shutdown :after 'my/-eglot-cleanup-doc-buffer)
- (defun my/-eglot-setup ()
- "Setup eldoc variables for `eglot-managed-mode-hook'."
- (setq-local evil-lookup-func #'my/eglot-documentation-at-point)
- (evil-define-key '(normal motion) 'local
- "K" #'evil-lookup
- "gR" #'eglot-rename
- "gA" #'eglot-code-actions
- "gs" #'consult-eglot-symbols)
- (eglot-inlay-hints-mode -1))
- (setq eglot-autoshutdown t
- eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider))
- :config
- (add-to-list 'eglot-server-programs
- (cons '(c-mode c-ts-mode c++-mode c++-ts-mode objc-mode)
- '("clangd" "--all-scopes-completion" "--background-index"
- "--clang-tidy" "--completion-style=detailed"
- "--header-insertion=never" "--pch-storage=memory"
- "--function-arg-placeholders"
- "--compile-commands-dir=build"))))
- ;; LTeX (languagetool)
- (require 'ltex-eglot)
- ;; apheleia (code formatter)
- (use-package apheleia
- :defer nil
- :bind ("C-c o" . apheleia-format-buffer)
- :init
- (add-to-list 'auto-mode-alist `(,(rx "/.clang-format" eos) . yaml-ts-mode))
- :config
- (with-eval-after-load 'apheleia
- (setf (alist-get 'java-mode apheleia-mode-alist) 'clang-format
- (alist-get 'java-ts-mode apheleia-mode-alist) 'clang-format))
- (apheleia-global-mode +1))
- ;; gud
- (use-package gud
- :demand t
- :ensure nil
- :after (project evil)
- :bind (:map project-prefix-map
- ("U" . my/project-gdb))
- :config
- (setq gdb-debuginfod-enable-setting t)
- (defvar my/project-gdb-command nil
- "Command to use in `my/project-gdb'.")
- (put 'my/project-gdb-command 'safe-local-variable (lambda (val)
- (stringp val)))
- (defun my/project-gdb (project command-line)
- "Run gdb in the project root"
- (interactive (let* ((project (project-current t))
- (default-directory (project-root project)))
- (list project (gud-query-cmdline 'gdb))))
- (let ((default-directory (project-root project)))
- (gdb command-line)))
- (evil-set-initial-state 'gdb-locals-mode 'motion)
- (evil-collection-inhibit-insert-state 'gdb-locals-mode-map)
- (evil-define-key '(normal motion visual) gdb-locals-mode-map
- (kbd "TAB") (keymap-lookup gdb-locals-mode-map "TAB")
- (kbd "RET") #'gdb-edit-locals-value
- (kbd "<mouse-1>") #'gdb-edit-locals-value
- "q" #'kill-current-buffer)
- (evil-set-initial-state 'gdb-registers-mode 'motion)
- (evil-collection-inhibit-insert-state 'gdb-registers-mode-map)
- (evil-define-key '(normal motion visual) gdb-registers-mode-map
- (kbd "TAB") (keymap-lookup gdb-registers-mode-map "TAB")
- (kbd "RET") #'gdb-edit-register-value
- (kbd "<mouse-1>") #'gdb-edit-register-value
- "q" #'kill-current-buffer
- (kbd "C-c f") #'gdb-registers-toggle-filter
- (kbd "C-c F") (lambda ()
- "Customize the filter for the registers buffer."
- (interactive)
- (customize-option-other-window
- 'gdb-registers-filter-pattern-list)))
- (evil-set-initial-state 'gdb-frames-mode 'motion)
- (evil-collection-inhibit-insert-state 'gdb-frames-mode-map)
- (evil-define-key '(normal motion visual) gdb-frames-mode-map
- "q" #'kill-current-buffer
- (kbd "RET") #'gdb-select-frame)
- (evil-set-initial-state 'gdb-breakpoints-mode 'motion)
- (evil-collection-inhibit-insert-state 'gdb-breakpoints-mode-map)
- (evil-define-key '(normal motion visual) gdb-breakpoints-mode-map
- (kbd "TAB") (keymap-lookup gdb-breakpoints-mode-map "TAB")
- "q" #'gdb-delete-frame-or-window
- "D" #'gdb-delete-breakpoint
- (kbd "RET") #'gdb-goto-breakpoint
- (kbd "<mouse-1>") #'gdb-goto-breakpoint
- (kbd "SPC") #'gdb-toggle-breakpoint)
- (evil-set-initial-state 'gdb-threads-mode 'motion)
- (evil-collection-inhibit-insert-state 'gdb-threads-mode-map)
- (evil-define-key '(normal motion visual) gdb-threads-mode-map
- (kbd "TAB") (keymap-lookup gdb-threads-mode-map "TAB")
- "q" #'gdb-delete-frame-or-window
- "D" #'gdb-frame-disassembly-for-thread
- (kbd "C-c f") #'gdb-display-stack-for-thread
- (kbd "C-c i") #'gdb-interrupt-thread
- (kbd "C-c l") #'gdb-display-locals-for-thread
- (kbd "C-c r") #'gdb-display-registers-for-thread
- (kbd "C-c c") #'gdb-continue-thread
- (kbd "C-c d") #'gdb-display-disassembly-for-thread
- (kbd "C-c s") #'gdb-step-thread
- (kbd "C-c F") #'gdb-frame-stack-for-thread
- (kbd "C-c L") #'gdb-frame-locals-for-thread
- (kbd "C-c R") #'gdb-frame-registers-for-thread
- (kbd "RET") #'gdb-select-thread
- (kbd "<mouse-2>") #'gdb-select-thread))
- ;; dape
- (use-package dape
- :hook ((after-init . dape-breakpoint-load)
- (kill-emacs . dape-breakpoint-save)
- (dape-start . save-some-buffers)
- (dape-display-source . pulse-momentary-highlight-one-line))
- :bind (:map dape-info-parent-mode-map
- ("<tab>" . dape--info-buffer-tab))
- :init
- (setopt dape-default-breakpoints-file (no-littering-expand-var-file-name
- "dape-breakpoints"))
- :config
- (setopt dape-buffer-window-arrangement 'right)
- (dape-breakpoint-global-mode 1))
- ;; dumb-jump
- (use-package dumb-jump
- :init
- (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
- ;; yasnippet
- (use-package yasnippet
- :demand t
- :bind ("C-c s" . yas-expand)
- :config
- (yas-global-mode 1))
- ;; project.el
- (use-package project
- :bind (([remap project-compile] . my/project-compile-or-default)
- :map project-prefix-map
- ("s" . my/project-eshell)
- ("u" . my/project-run))
- :init
- (defvar eshell-buffer-name)
- (defun my/project-eshell (prompt &optional arg)
- "Switch to or create an eshell buffer in the current projects root."
- (interactive (list t current-prefix-arg))
- (if-let ((proj (project-current prompt))
- (default-directory (project-root proj))
- (eshell-buffer-name
- (concat "*eshell for project " default-directory "*")))
- (eshell arg)))
- (defun my/project-eshell-or-default (&optional arg)
- "Open an eshell for the current project, otherwise, open a normal eshell."
- (interactive "P")
- (unless (my/project-eshell nil arg)
- (eshell arg)))
- (defun my/project-compile-or-default ()
- "If in a project, run `project-compile', otherwise run `compile'."
- (interactive)
- (if (project-current)
- (call-interactively 'project-compile)
- (call-interactively 'compile)))
- (defvar my/project-run-command nil
- "Command to run with `my/project-run'.")
- (put 'my/project-run-command 'safe-local-variable (lambda (val)
- (stringp val)))
- (defvar my/project-run-dir nil
- "Directory to run project in with `my/project-run'.")
- (put 'my/project-run-dir 'safe-local-variable (lambda (val)
- (stringp val)))
- (defvar my/-project-run-history '()
- "Commands previously run with `my/project-run'")
- (defvar my/project-root-marker ".project-root"
- "Marker file to look for in non-vc backed projects.")
- (defun my/project-get-root-dir ()
- "Get the root dir for the current project"
- (let* ((proj (project-current nil))
- (default-directory (if proj
- (project-root proj)
- default-directory)))
- (if my/project-run-dir
- (expand-file-name my/project-run-dir)
- default-directory)))
- (defvar my/compile-use-comint t
- "Weather or not to use comint by default for compile buffers.")
- (defun my/-compile-use-comint-by-default (args)
- (if my/compile-use-comint
- (list (car args) t)
- args))
- (advice-add 'compile :filter-args 'my/-compile-use-comint-by-default)
- (defun my/project-run (command comint)
- "Like `project-compile', but for running a project.
- COMMAND and COMINT are like `compile'."
- (interactive
- (list
- (let ((default-directory (my/project-get-root-dir)))
- (read-shell-command "Run Command: "
- (or (car my/-project-run-history)
- my/project-run-command)
- (if (and my/project-run-command
- (equal my/project-run-command
- (car-safe my/-project-run-history)))
- '(my/-project-run-history . 1)
- 'my/-project-run-history)))
- (consp current-prefix-arg)))
- (let* ((default-directory (my/project-get-root-dir))
- (compilation-buffer-name-function (lambda (_)
- (progn "*run project*")))
- (compilation-directory default-directory)
- (compile-history nil)
- (compile-command nil))
- (compile command comint)
- (when (not my/project-run-command)
- (setq my/project-run-command command))))
- :config
- (defun my/project-try-dotfile (dir)
- (if-let (root (locate-dominating-file dir my/project-root-marker))
- (list 'vc nil root)))
- (add-hook 'project-find-functions #'my/project-try-dotfile))
- ;; comint
- (use-package comint
- :ensure nil
- :after evil
- :config
- (evil-set-initial-state 'comint-mode 'normal))
- ;; nxml
- (use-package nxml-mode
- :ensure nil
- :hook (nxml-mode . my/-nxml-setup)
- :init
- (defun my/-nxml-setup ()
- "Setup `nxml-mode'."
- (sgml-electric-tag-pair-mode 1)
- (setq-local completion-at-point-functions
- '(rng-completion-at-point cape-file)))
- (add-to-list 'auto-mode-alist
- `(,(concat
- (regexp-opt '("gschema" "gresource" "ui")) "\\'")
- . nxml-mode)))
- ;; devdocs
- (use-package devdocs
- :bind (("C-h D" . devdocs-lookup)))
- ;; Bibtex (built in)
- (require 'bibtex)
- ;; Better URL highlighting and matching
- (dolist (field '("howpublished" "url"))
- (add-to-list 'bibtex-generate-url-list
- `((,field . ,(rx "http" (? "s") "://" (+ (not "}"))))
- "%s"
- (,field
- ,(rx (? (or "\\url{" "{")) (group "http" (? "s") "://"
- (+ (not "}")))
- (? "}"))
- ,(lambda (field)
- (match-string 1 field))))))
- (defun my/bibtex-in-entry-p (&optional exclude-braces)
- "Return t is point is inside a BibTeX entry.
- When EXCLUDE-BRACES is non-nil, don't count the first and last brace of the
- entry as in the entry. That is, if the point is on the first { or last } of the
- entry, return nil."
- (save-excursion
- (when (and exclude-braces (eq ?\} (char-after)))
- (forward-char))
- ;; go to top level and check if the character at point is {
- (let ((start-pos (point))
- (last-valid (point)))
- (condition-case nil
- (while t
- (backward-up-list 1 t t)
- (setq last-valid (point)))
- (error
- (and
- (eq ?\{ (char-after last-valid))
- (or (not exclude-braces)
- (not (= start-pos last-valid)))))))))
- (defvar my/bibtex-indent-width 4
- "Width to indent for `my/bibtex-calculate-indentation'.")
- (defun my/bibtex-calculate-indentation ()
- "Calculate the column to indent to on the current line."
- (save-excursion
- (back-to-indentation)
- (if (my/bibtex-in-entry-p t)
- my/bibtex-indent-width
- 0)))
- (defun my/bibtex-empty-line-p ()
- "Return t if the current line is only blank characters."
- (save-excursion
- (beginning-of-line)
- (looking-at (rx (* blank) eol))))
- (defun my/bibtex-indent-line ()
- "Indent the current line."
- (interactive)
- (save-excursion
- (beginning-of-line)
- (when (looking-at (rx (+ blank)))
- (delete-region (point) (match-end 0)))
- (indent-to (my/bibtex-calculate-indentation)))
- (when (looking-at (rx (+ blank) eol))
- (end-of-line)))
- (defun my/bibtex-indent-or-find-text ()
- "Either indent the current line or jump to the current fields text.
- If the current line is only whitespace call `my/bibtex-calculate-indentation',
- otherwise, call `bibtex-find-text'."
- (interactive)
- (if (my/bibtex-empty-line-p)
- (my/bibtex-indent-line)
- (bibtex-find-text)))
- (defun my/bibtex-indent-or-find-text-and-insert ()
- "Like `my/bibtex-indent-or-find-text', but enter insert mode after."
- (interactive)
- (my/bibtex-indent-or-find-text)
- (if (my/bibtex-empty-line-p)
- (evil-append 1)
- (evil-insert 1)))
- (defun my/-bibtex-setup-indent ()
- "Set up `bibtex-mode' indentation stuff."
- (setq-local indent-line-function 'my/bibtex-indent-line
- electric-indent-chars '(?\n ?\{ ?\} ?,)))
- (defun my/-bibtex-fix-fill-prefix ()
- "`bivtex-mode' has a bad habbit of messing up `fill-prefix'."
- (when (eq major-mode 'bibtex-mode)
- (setq-local fill-prefix nil)))
- (advice-add 'bibtex-mode :after 'my/-bibtex-fix-fill-prefix)
- (add-hook 'bibtex-mode-hook 'my/-bibtex-setup-indent)
- (keymap-set bibtex-mode-map "RET" 'newline-and-indent)
- (keymap-set bibtex-mode-map "TAB" 'my/bibtex-indent-or-find-text)
- (evil-define-key 'normal bibtex-mode-map
- (kbd "TAB") 'my/bibtex-indent-or-find-text-and-insert)
- ;; Latex help (from elisp file)
- (require 'latex-help)
- ;; AUCTeX
- (use-package auctex
- :hook ((LaTeX-mode . turn-on-reftex)
- (LaTeX-mode . LaTeX-math-mode)
- (LaTeX-mode . my/-setup-LaTeX-mode)
- (LaTeX-mode . trusted-files-flycheck-mode-if-safe))
- :bind (:map TeX-mode-map
- ("C-c ?" . latex-help))
- :init
- (add-to-list 'major-mode-remap-alist '(plain-tex-mode . plain-TeX-mode))
- (add-to-list 'major-mode-remap-alist '(latex-mode . LaTeX-mode))
- (add-to-list 'major-mode-remap-alist '(ams-tex-mode . AmSTeX-mode))
- (add-to-list 'major-mode-remap-alist '(context-mode . ConTeXt-mode))
- (add-to-list 'major-mode-remap-alist '(texinfo-mode . Texinfo-mode))
- (add-to-list 'major-mode-remap-alist '(doctex-mode . docTeX-mode))
- (add-to-list 'auto-mode-alist '("/\\.latexmkrc\\'" . perl-mode))
- (add-to-list 'auto-mode-alist '("\\.[tT]e[xX]\\'" . LaTeX-mode))
- :config
- (defun my/-auctex-texdoc-setup-env (oldfun &rest args)
- (let ((process-environment process-environment)
- (emacs-cmd (concat "emacsclient" (and (not (display-graphic-p)) " -nw"))))
- (setenv "PDFVIEWER_texdoc" "evince")
- (setenv "MDVIEWER_texdoc" emacs-cmd)
- (setenv "PAGER_texdoc" emacs-cmd)
- (apply oldfun args)))
- (advice-add 'TeX-documentation-texdoc :around 'my/-auctex-texdoc-setup-env)
- (defun my/-setup-LaTeX-mode ()
- (setq evil-lookup-func 'latex-help-at-point))
- (setq TeX-auto-save t
- TeX-parse-self t
- reftex-plug-into-AUCTeX t)
- (evil-define-operator my/evil-LaTeX-fill (beg end)
- "Like `evil-fill', but using auctex."
- ;; The code here came straight from `evil-fill'
- :move-point nil
- :type line
- (save-excursion
- (ignore-errors (LaTeX-fill-region beg end))))
- (evil-define-operator my/evil-LaTeX-fill-and-move (beg end)
- "Like `evil-fill-and-move', but using auctex."
- ;; The code here came straight from `evil-fill-and-move'
- :move-point nil
- :type line
- (let ((marker (make-marker)))
- (move-marker marker (1- end))
- (ignore-errors
- (LaTeX-fill-region beg end)
- (goto-char marker)
- (evil-first-non-blank))))
- (evil-define-key 'normal TeX-mode-map
- "gq" 'my/evil-LaTeX-fill-and-move
- "gw" 'my/evil-LaTeX-fill)
- (setq-default TeX-master nil)
- (require 'tex)
- (TeX-global-PDF-mode 1))
- ;; blueprint
- (use-package blueprint-ts-mode
- :hook (blueprint-ts-mode . trusted-files-eglot-ensure-if-safe)
- :after eglot)
- ;; python-ts-mode
- (use-package python-ts-mode
- :ensure nil
- :hook (python-ts-mode . trusted-files-eglot-ensure-if-safe))
- ;; java-ts-mode
- (use-package java-ts-mode
- :hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe)
- (java-ts-mode . my/-setup-java-ts-mode))
- :config
- (defun my/-setup-java-ts-mode ()
- (let ((rules (car treesit-simple-indent-rules)))
- (setcdr rules
- (cons '((and (parent-is "array_initializer")
- (node-is "array_initializer"))
- parent-bol java-ts-mode-indent-offset)
- (nthcdr 1 rules))))))
- ;; c-ts-mode
- (use-package c-ts-mode
- :after evil
- :hook ((c-ts-mode c++-ts-mode) . trusted-files-eglot-ensure-if-safe)
- :init
- (setq-default c-ts-mode-indent-offset 4)
- :config
- (evil-define-key 'normal 'c-ts-mode-map
- "go" #'ff-find-other-file
- "gO" #'ff-find-other-file-other-window)
- (evil-define-key 'normal 'c++-ts-mode-map
- "go" #'ff-find-other-file
- "gO" #'ff-find-other-file-other-window)
- (evil-define-key 'normal 'objc-mode-map
- "go" #'ff-find-other-file
- "gO" #'ff-find-other-file-other-window))
- ;; GLSL
- (use-package glsl-mode)
- ;; php-mode
- (use-package php-mode
- :hook (php-mode . trusted-files-eglot-ensure-if-safe))
- ;; web-mode
- (use-package web-mode
- :hook (web-mode . trusted-files-eglot-ensure-if-safe)
- :init
- (add-to-list 'eglot-server-programs
- '(web-mode . ("vscode-html-language-server" "--stdio"))))
- ;; JavaScript
- (use-package js
- :ensure nil
- :hook (js-ts-mode . trusted-files-eglot-ensure-if-safe))
- ;; TypeScript
- (use-package typescript-ts-mode
- :ensure nil
- :hook (typescript-ts-mode . trusted-files-eglot-ensure-if-safe)
- :init
- (add-to-list 'auto-mode-alist `(,(rx ".ts" eos) . typescript-ts-mode)))
- ;; Polymode
- (use-package polymode
- :config
- (define-hostmode my/poly-web-hostmode
- :mode 'web-mode)
- (define-innermode my/poly-php-innermode
- :mode 'php-mode
- :head-matcher (regexp-quote "<?php")
- :tail-matcher (regexp-quote "?>")
- :head-mode 'body
- :tail-mode 'body)
- (define-polymode my/poly-web-mode
- :hostmode 'my/poly-web-hostmode
- :innermodes '(my/poly-php-innermode))
- (add-to-list 'auto-mode-alist '("\\.php\\|\\.phtml\\'" . my/poly-web-mode)))
- ;; shell-mode
- (use-package sh-script
- :ensure nil
- :hook (sh-mode . my/-setup-sh-mode)
- :init
- (defun my/-setup-sh-mode ()
- (add-to-list 'completion-at-point-functions #'cape-file)))
- ;; go mode
- (use-package go-mode
- :defer nil
- :hook (go-mode . trusted-files-eglot-ensure-if-safe))
- (use-package go-ts-mode
- :ensure nil
- :hook (go-ts-mode . trusted-files-eglot-ensure-if-safe))
- ;; rust
- (use-package rust-mode)
- (use-package rust-ts-mode
- :ensure nil
- :hook (rust-ts-mode . trusted-files-eglot-ensure-if-safe))
- ;; zig
- (use-package zig-mode
- :hook (zig-mode . trusted-files-eglot-ensure-if-safe))
- ;; lua
- (use-package lua-mode
- :hook (lua-mode . trusted-files-eglot-ensure-if-safe))
- ;; markdown
- (use-package markdown-mode
- :hook (markdown-mode . auto-fill-mode))
- ;; groovy
- (use-package groovy-mode)
- ;; cmake
- (require 'cmake-mode)
- (require 'cmake-ts-mode)
- (with-eval-after-load 'cmake-mode
- (setq cmake-ts-mode-indent-offset tab-width))
- ;; kdl
- (require 'kdl-ts-mode)
- ;; json
- (use-package json-ts-mode
- :hook (json-ts-mode . trusted-files-eglot-ensure-if-safe))
- (use-package json-mode)
- ;; csv
- (use-package csv-mode)
- ;; firejail
- (require 'firejail-mode)
- ;; yaml
- (use-package yaml-ts-mode
- :hook ((yaml-ts-mode . trusted-files-eglot-ensure-if-safe)
- (yaml-ts-mode . my/-setup-yaml-ts-mode))
- :init
- (defun my/-setup-yaml-ts-mode ()
- (setq indent-line-function #'yaml-indent-line)))
- (use-package yaml-mode)
- ;; yuck (config language for eww)
- (use-package yuck-mode)
- ;; Some Elisp indentation stuff
- ;; Source: https://github.com/magit/emacsql
- ;; emacsql.el line 394
- (defun my/lisp-inside-plist-p ()
- "Return t if point is inside a plist."
- (save-excursion
- (let ((start (point)))
- (beginning-of-defun)
- (when-let ((sexp (nth 1 (parse-partial-sexp (point) start))))
- (goto-char sexp)
- (looking-at (rx "(" (* (syntax whitespace)) ":"))))))
- (defun my/-calculate-indent-fix-plists (oldfun &rest args)
- "This function is meant to advise `calculate-lisp-indent'.
- It calls OLDFUN with ARGS in such an environment as to prevent the default
- indentation of plists."
- (if (and (eq major-mode 'emacs-lisp-mode)
- (save-excursion
- (beginning-of-line)
- (my/lisp-inside-plist-p)))
- (let ((lisp-indent-offset 1))
- (apply oldfun args))
- (apply oldfun args)))
- (advice-add 'calculate-lisp-indent :around
- 'my/-calculate-indent-fix-plists)
- (defvar my/max-lisp-noindent-comment-search-lines 30
- "Max lines to search for the noindent comment.")
- (defun my/-calculate-lisp-indent-noindent-comment (oldfun &rest args)
- "This function is meant to advise `calculate-lisp-indent'.
- It calls OLDFUN with ARGS, unless the line ends with the comment
- ; noindent [LINES]
- In this case, it just returns the current amount of indentation. LINES is the
- number of lines that this comment affects. This is limited by
- `my/max-lisp-noindent-comment-search-lines'.
- This only works if its on the first or second form in a block. I think this is
- because the indentation code only checks those and then assumes the same
- indentation for every following line in the same block. This is probably OK as
- I can't imagine too many instances where you need to randomly change the indent
- midway through a block, and in those cases you can just stick this on the first
- line in the block and manually deal with indentation."
- (if (and (save-excursion
- (end-of-line)
- (re-search-backward
- (rx (+ ";") (syntax whitespace) "noindent"
- (? (syntax whitespace) (group (+ num)))
- line-end)
- (pos-bol (- my/max-lisp-noindent-comment-search-lines))
- t))
- (save-excursion
- ;; if we are on a blank line, move forward a line
- (when (zerop (length (buffer-substring-no-properties
- (pos-bol) (pos-eol))))
- (beginning-of-line 2))
- (<= (count-lines (match-beginning 0) (pos-eol))
- (if-let ((match (match-string 1)))
- (string-to-number match)
- 1))))
- (save-excursion
- (beginning-of-line)
- (looking-at (rx (* blank)))
- (length (match-string 0)))
- (apply oldfun args)))
- (advice-add 'calculate-lisp-indent :around
- 'my/-calculate-lisp-indent-noindent-comment)
- ;; sly
- (use-package sly
- ;; :hook (lisp-mode . my/-lisp-mode-autoconnect-sly)
- :bind (:map sly-mode-map
- ("C-c e" . my/diagnostic-at-point))
- :autoload sly-connected-p
- :init
- (defun my/-lisp-mode-autoconnect-sly ()
- (unless (sly-connected-p)
- (trusted-files-sly-if-safe)))
- (setq inferior-lisp-program "/usr/bin/sbcl")
- (defun my/-sly-fix-special-buffers ()
- (when (string-match-p (rx bos "*" (* any) "*" eos) (buffer-name))
- (setq-local show-trailing-whitespace nil)))
- (add-hook 'lisp-mode-hook 'my/-sly-fix-special-buffers)
- :config
- (evil-define-key 'insert sly-mrepl-mode-map
- (kbd ",") 'self-insert-command)
- (evil-define-key nil sly-mrepl-mode-map
- (kbd "C-c ,") 'sly-mrepl-shortcut)
- (sly-symbol-completion-mode -1))
- ;; pdf-tools
- (use-package pdf-tools
- :hook (pdf-view-mode . my/setup-pdf-view-mode)
- :init
- (setq pdf-misc-print-program-executable "lp")
- (defun my/setup-pdf-view-mode ()
- (display-line-numbers-mode -1)
- (evil-define-key '(motion normal visual) 'local
- (kbd "C-s") #'isearch-forward
- (kbd "C-r") #'isearch-backward)
- (setq-local cursor-type nil))
- (pdf-tools-install))
- ;; doc view
- (use-package doc-view
- :ensure nil
- :hook (doc-view-mode . my/-setup-doc-view-mode)
- :init
- (defun my/-setup-doc-view-mode ()
- (display-line-numbers-mode -1)
- (evil-define-key '(motion normal visual) 'local
- (kbd "C-s") #'isearch-forward
- (kbd "C-r") #'isearch-backward)))
- ;; calc
- (use-package calc
- :ensure nil
- :bind (("C-c m" . quick-calc)
- :map calc-mode-map
- ("M-<tab>" . calc-roll-up)
- ("M-TAB" . calc-roll-up))
- :hook ((calc-mode calc-trail-mode) . my/setup-calc-calc-trail-mode)
- :init
- (defun my/setup-calc-calc-trail-mode ()
- (setq-local doom-modeline-percent-position '()
- truncate-partial-width-windows nil)
- (visual-line-mode -1)
- (display-line-numbers-mode -1)
- (toggle-truncate-lines 1))
- :config
- (evil-define-key '(normal visual motion) calc-edit-mode-map
- (kbd "RET") 'calc-edit-return
- (kbd "<return>") 'calc-edit-return)
- (defun my/-calc-float-mode-string ()
- (cl-destructuring-bind (mode prec) calc-float-format
- (concat
- (upcase-initials (symbol-name mode))
- (unless (zerop prec)
- (concat ": " (number-to-string prec))))))
- (doom-modeline-def-segment calc
- "Display calculator icons and info."
- (concat
- (doom-modeline-spc)
- (when-let ((icon (doom-modeline-icon 'faicon "nf-fa-calculator" "🖩" "")))
- (concat
- (doom-modeline-display-icon icon)
- (doom-modeline-vspc)))
- (doom-modeline--buffer-simple-name)
- (when (eq major-mode 'calc-mode)
- (concat
- (doom-modeline-spc)
- (number-to-string calc-internal-prec)
- (doom-modeline-spc)
- (upcase-initials (symbol-name calc-angle-mode))
- (doom-modeline-spc)
- (my/-calc-float-mode-string)
- (when calc-prefer-frac
- (concat
- (doom-modeline-spc)
- "Frac"))
- (cond
- (calc-algebraic-mode
- (concat
- (doom-modeline-spc)
- "Alg"))
- (calc-incomplete-algebraic-mode
- (concat
- (doom-modeline-spc)
- "IAlg"))))))))
- ;; sage (for when calc is not enough)
- (use-package sage-shell-mode
- :demand
- :bind ("C-c g" . my/run-sage)
- :hook (sage-shell-mode . my/-setup-sage-shell-mode)
- :init
- (defun my/-setup-sage-shell-mode ()
- (setq-local comint-dynamic-complete-functions
- '(comint-c-a-p-replace-by-expanded-history)))
- :config
- (defun my/run-sage (p)
- "Like `sage-shell:run-sage', but does not ask anything without a prefix
- argument."
- (interactive "P")
- (let ((sage-shell:ask-command-options p))
- (funcall-interactively #'sage-shell:run-sage
- (sage-shell:read-command)))))
- ;; fricas (because I like calculators)
- (add-to-list 'load-path "/usr/lib/fricas/emacs")
- (use-package fricas
- :ensure nil
- :custom
- (fricas-run-command "fricas -nosman")
- :init
- ;; Fix `fricas-mode' messing up `completion-at-point-functions'
- (advice-add #'fricas-mode :around
- #'(lambda (oldfun &rest r)
- (let ((temp-capfs))
- (let ((completion-at-point-functions '(t)))
- (apply oldfun r)
- (setq temp-capfs completion-at-point-functions))
- (setq-local completion-at-point-functions temp-capfs)))
- '((name . "my/-fricas-fix-capfs")))
- :config
- (face-spec-set 'fricas-type-time '((t (:foreground unspecified
- :background unspecified
- :inherit font-lock-type-face))))
- (face-spec-set 'fricas-message '((t (:foreground unspecified
- :background unspecified
- :inherit error))))
- (face-spec-set 'fricas-undefined '((t (:foreground unspecified
- :background unspecified
- :inherit nerd-icons-lblue))))
- (face-spec-set 'fricas-algebra '((t (:foreground unspecified
- :background unspecified
- :weight bold
- :inherit fricas-prompt))))
- (face-spec-set 'fricas-TeX '((t (:foreground "black"
- :background "white"
- :inherit fricas-prompt)))))
- ;; gnuplot (mostly for org-plot)
- (use-package gnuplot)
- ;; eat
- (use-package eat
- :bind (("C-c V" . my/project-eat-or-default)
- :map eat-mode-map
- ("M-o" . ace-window)
- :map eat-semi-char-mode-map
- ("M-o" . ace-window)
- :map eat-eshell-emacs-mode-map
- ("M-o" . ace-window)
- :map eat-eshell-semi-char-mode-map
- ("M-o" . ace-window))
- :config
- (defvar my/project-eat-hash-table (make-hash-table :test 'equal)
- "Hash table that maps project root dirs to eat buffers.")
- (defun my/-eat-shell-for-cwd ()
- "Return a good shell for CWD, or nil if the default shell should be used."
- (when (file-remote-p default-directory)
- "/bin/sh"))
- (defun my/project-eat (prompt &optional arg)
- "Switch to or create a eat buffer in the current projects root."
- (interactive (list t current-prefix-arg))
- (if-let ((proj (project-current prompt))
- (default-directory (project-root proj)))
- (if-let ((eat-buff (gethash default-directory
- my/project-eat-hash-table))
- ((buffer-live-p eat-buff)))
- (switch-to-buffer eat-buff)
- (let ((eat-buffer-name (concat "*eat for project " default-directory
- "*"))
- (eat-term-name (if (file-remote-p default-directory)
- "xterm-256color"
- eat-term-name)))
- (puthash default-directory
- (eat (my/-eat-shell-for-cwd) arg)
- my/project-eat-hash-table)))))
- (defun my/project-eat-or-default (&optional arg)
- "Open an eat for the current project, otherwise, open a normal eat."
- (interactive "P")
- (unless (my/project-eat nil)
- (if-let ((eat-buff (gethash nil my/project-eat-hash-table))
- ((buffer-live-p eat-buff)))
- (switch-to-buffer eat-buff)
- (puthash nil (let ((eat-term-name (if (file-remote-p default-directory)
- "xterm-256color"
- eat-term-name)))
- (eat (my/-eat-shell-for-cwd) arg))
- my/project-eat-hash-table)))))
- ;; eshell stuff
- (use-package eshell
- :ensure nil
- :defer nil
- :hook ((eshell-load . eat-eshell-visual-command-mode)
- (eshell-load . eat-eshell-mode)
- (eshell-mode . my/-eshell-mode-setup)
- (eshell-directory-change . my/-eshell-maybe-setup-remote-aliases))
- :bind (:map eshell-mode-map
- ("TAB" . completion-at-point)
- ("<tab>" . completion-at-point))
- :init
- (defun my/-eshell-filter-alias-list ()
- (cl-remove-if-not (lambda (elt)
- (or (string-match-p
- (rx bos
- (or "clear" "find-file"
- "ls" "la" "git"
- (and "eshell/" (+ (not " "))))
- (or " " eos))
- (cl-second elt))))
- eshell-command-aliases-list))
- (defun my/-eshell-maybe-setup-remote-aliases ()
- (if (file-remote-p default-directory)
- (setq-local eshell-command-aliases-list (my/-eshell-filter-alias-list))
- (kill-local-variable 'eshell-command-aliases-list)))
- (defun my/-eshell-mode-setup ()
- "Setup function run from `eshell-mode-hook'"
- (setq-local corfu-auto nil)
- (my/-eshell-maybe-setup-remote-aliases))
- (setq-default eshell-command-aliases-list
- '(("clear" "clear t")
- ("e" "find-file $1")
- ("n" "find-file $1")
- ("emacs" "find-file $1")
- ("nvim" "find-file $1")
- ("ls" "eza --git -F $*")
- ("la" "ls -a $*")
- ("l" "ls -l $*")
- ("ll" "la -l $*")
- ("gt" "git status $*")
- ("gp" "git push $*")
- ("gu" "git pull $*")
- ("gf" "git fetch $*")
- ("ga" "git add $*")
- ("gcm" "git commit -m ${string-join $* \" \"}")
- ("ldg" "ledger -f \"$HOME/docs/finance/finances.ledger\" $*")
- ("tp" "trash-put $*")
- ("trr" "trash-restore $*")
- ("tre" "trash-empty $*")
- ("tre" "trash-empty $*")
- ("trm" "trash-rm $*")
- ("rm" "echo 'rm: I''m unsafe! Don''t use me.'; false")
- ("\\rm" "eshell/rm")))
- (defvar my/eshell-bm-auto-ls t
- "Weather or not to run ls after `eshell/bm'")
- (defun eshell/bm (&optional name)
- "Change to directory of bookmark NAME.
- If no name is given, list all bookmarks instead."
- (if name
- (progn
- (eshell/cd (bookmark-get-filename name))
- (when my/eshell-bm-auto-ls
- (eshell/ls)))
- (eshell-print (string-join (bookmark-all-names) " ")))))
- (use-package esh-help
- :hook (eshell-mode . my/-setup-eshell-help-func)
- :init
- (defun my/-setup-eshell-help-func ()
- (eldoc-mode 1)
- (setq-local evil-lookup-func #'esh-help-run-help))
- (setup-esh-help-eldoc))
- (use-package eshell-syntax-highlighting
- :init
- (eshell-syntax-highlighting-global-mode 1))
- (use-package eshell-starship
- :ensure nil
- :demand t
- :hook (eshell-prompt-mode . eshell-starship-prompt-mode)
- :config
- (eshell-starship-setup-evil-keybindings)
- (set-face-attribute 'eshell-starship-icon-face nil
- :family "FiraCode Nerd Font"))
- (defun my/open-shell-dwim (&optional arg)
- "Open either an Eshell or eat terminal based on `default-directory'.
- If `default-directory' is remote, call `my/project-eat-or-default'. Otherwise,
- call `my/project-eshell-or-default'. ARG is the same as for either of the above
- functions (only eshell uses it at the time of writing)."
- (interactive "P")
- (if (file-remote-p default-directory)
- (my/project-eat-or-default)
- (my/project-eshell-or-default arg)))
- (keymap-global-set "C-c v" #'my/open-shell-dwim)
- ;; proced
- (use-package proced
- :bind ("C-x j" . proced)
- :init
- (evil-define-key '(motion visual normal) proced-mode-map
- "u" 'proced-unmark)
- (setq proced-auto-update-flag t
- proced-auto-update-interval 1)
- (defun my/-setup-proced-mode ()
- (visual-line-mode -1)
- (setq-local truncate-lines t))
- (add-hook 'proced-mode-hook 'my/-setup-proced-mode))
- ;; dired
- (use-package dired
- :ensure nil
- :custom
- (dired-listing-switches
- "-l --almost-all --human-readable --group-directories-first --no-group")
- (dired-hide-details-hide-symlink-targets nil)
- :init
- (setq-default dired-kill-when-opening-new-dired-buffer t)
- (setq delete-by-moving-to-trash t
- dired-recursive-copies 'always
- dired-recursive-deletes 'always
- dired-dwim-target t
- dired-create-destination-dirs 'ask
- dired-create-destination-dirs-on-trailing-dirsep t
- dired-isearch-filenames 'dwim
- dired-do-revert-buffer (lambda (dir)
- (not (file-remote-p dir)))
- dired-clean-up-buffers-too t
- dired-clean-confirm-killing-deleted-buffers t)
- (evil-define-key '(normal visual motion) dired-mode-map
- "u" #'dired-unmark
- "U" #'dired-unmark-all-marks))
- ;; dirvish
- (use-package dirvish
- :defer nil
- :bind (("C-c b" . dirvish-quick-access)
- :map dirvish-mode-map
- ("<mouse-1>" . dirvish-subtree-toggle-or-open)
- ("<mouse-2>" . dired-mouse-find-file-other-window)
- ("<mouse-3>" . dired-mouse-find-file))
- :hook (((dirvish-directory-view-mode dired-mode dirvish-mode) .
- my/-setup-dirvish-lines)
- ;; ((dirvish-directory-view-mode dired-mode dirvish-mode) .
- ;; auto-revert-mode)
- ((dirvish-mode dired-mode) . my/-setup-dirvish-mouse))
- :custom
- (dirvish-subtree-always-show-state t)
- (dirvish-reuse-session t)
- (dirvish-quick-access-function 'dired)
- :init
- (defun my/-setup-dirvish-lines ()
- (setq-local truncate-lines t))
- (defun my/-setup-dirvish-mouse ()
- (setq-local mouse-1-click-follows-link nil
- mouse-1-click-in-non-selected-windows nil))
- (defvar my/-dirvish-base-quick-access-entries
- `(("h" "~/" "Home")
- ("d" "~/downloads/" "Downloads")
- ("e" ,user-emacs-directory "Emacs user directory")
- ("z" "~/.config/zsh/" "Zsh user directory")
- ("o" "~/docs/" "Documents")
- ("w." "~/workspace/" "Workspace")))
- (defun my/-dirvish-build-quick-access-entries (bookmarks)
- ;; NOTE called from a variable watcher for `bookmark-alist' and so must
- ;; never set that variable
- (let (out)
- (dolist (bme bookmarks
- (append my/-dirvish-base-quick-access-entries
- (sort out
- (lambda (elt1 elt2)
- (string< (car elt1) (car elt2))))))
- (let ((name (car bme)))
- (let-alist (cdr bme)
- (when (and (file-directory-p .filename)
- (string-match (rx bos (group (any "a-z" "A-Z")) "ws" eos)
- name))
- (setf (alist-get (concat "w" (match-string 1 name)) out
- nil nil 'equal)
- (list .filename
- (concat
- (capitalize
- (car (last (string-split .filename "/" t))))
- " Workspace")))))))))
- :config
- (require 'dirvish-extras)
- (defun my/-dirvish-bookmark-alist-watcher (_sym newval oper where)
- (when (and (not where) (memq oper '(set makunbound defvaralias)))
- (setopt dirvish-quick-access-entries
- (my/-dirvish-build-quick-access-entries newval))))
- (add-variable-watcher 'bookmark-alist #'my/-dirvish-bookmark-alist-watcher)
- (defvar-local my/-dirvish-uid-name-cache nil
- "Cons of path and a hash table mapping user ids to their names.")
- (dirvish-define-attribute file-owner-mode
- "The file's owner and mode."
- :index 2
- :when (and (dirvish-prop :root) dired-hide-details-mode
- (> win-width 60))
- (let ((root (dirvish-prop :root))
- (uid (file-attribute-user-id f-attrs)))
- (unless (or (dirvish-prop :remote) (stringp uid))
- (unless (and (equal root (car my/-dirvish-uid-name-cache))
- (hash-table-p (cdr my/-dirvish-uid-name-cache)))
- (setq my/-dirvish-uid-name-cache
- (cons root (make-hash-table :test 'equal))))
- (if-let ((name (gethash uid (cdr my/-dirvish-uid-name-cache))))
- (setq uid name)
- (let* ((new-attrs (file-attributes f-name 'string))
- (new-name (file-attribute-user-id new-attrs)))
- (puthash uid new-name
- (cdr my/-dirvish-uid-name-cache))
- (setq uid new-name))))
- (cons 'right (propertize (format " %s %s" uid (file-attribute-modes f-attrs))
- 'face (or hl-face 'dirvish-file-time)))))
- (let ((cur-val dirvish-ui-setup-items))
- (cl-pushnew '("o" file-owner-mode "File owner and mode")
- cur-val :test 'equal)
- (setopt dirvish-ui-setup-items cur-val))
- (add-to-list 'dirvish-libraries '(dirvish file-owner-mode))
- (setopt dirvish-attributes
- '(vc-state subtree-state nerd-icons file-size file-owner-mode))
- (evil-define-key 'normal dirvish-mode-map
- (kbd "q") #'dirvish-quit
- (kbd "a") #'dirvish-quick-access
- (kbd "f") #'dirvish-file-info-menu
- (kbd "y") #'dirvish-yank-menu
- (kbd "N") #'dirvish-narrow
- (kbd "^") #'dirvish-history-last
- (kbd "h") #'dirvish-history-jump
- (kbd "s") #'dirvish-quicksort
- (kbd "o") #'dirvish-quicksort
- (kbd "v") #'dirvish-vc-menu
- (kbd "TAB") #'dirvish-subtree-toggle
- (kbd "M-f") #'dirvish-history-go-forward
- (kbd "M-b") #'dirvish-history-go-backward
- (kbd "M-l") #'dirvish-history-go-forward
- (kbd "M-h") #'dirvish-history-go-backward
- (kbd "M-l") #'dirvish-ls-switches-menu
- (kbd "M-m") #'dirvish-mark-menu
- (kbd "M-t") #'dirvish-layout-toggle
- (kbd "M-s") #'dirvish-setup-menu
- (kbd "M-e") #'dirvish-emerge-menu
- (kbd "M-j") #'dirvish-fd-jump
- (kbd "/") #'dirvish-fd
- (kbd "?") #'dirvish-dispatch)
- (dirvish-override-dired-mode)
- (dirvish-define-preview eza (file)
- "Use `eza' to generate directory preview."
- :require ("eza")
- (when (file-directory-p file)
- `(shell . ("eza" "-la" "--color=always" "--icons"
- "--group-directories-first" ,file))))
- (add-to-list 'dirvish-preview-dispatchers 'eza))
- ;; ibuffer
- (use-package ibuffer
- :bind ("C-x C-b" . ibuffer))
- ;; magit
- (use-package magit
- :init
- (defvar-keymap my/magit-personal-prefix-map
- :doc "Keymap for some useful Magit commands."
- "s" #'magit-stage
- "d" #'magit-diff-dwim
- "D" #'magit-diff
- "b" #'magit-blame)
- (keymap-global-set "C-c i" my/magit-personal-prefix-map)
- (evil-define-key '(normal visual motion) magit-mode-map
- "s" #'magit-stage-file
- "S" #'magit-stage-modified))
- (use-package forge
- :config
- (add-to-list 'forge-alist '("git.zander.im" "git.zander.im/api/v1"
- "git.zander.im" forge-gitea-repository)))
- ;; org-mode
- (use-package org
- :pin gnu
- :bind (("C-c c" . org-capture)
- ("C-c a" . org-agenda)
- ("C-c l" . org-store-link)
- :map org-mode-map
- ("C-c t" . org-table-create))
- :hook (org-mode . org-table-header-line-mode)
- :init
- (font-lock-add-keywords 'org-mode
- `((,(rx bol (* " ") (group "-") " ")
- (0 (prog1 nil
- (compose-region (match-beginning 1)
- (match-end 1) "•"))))))
- (setq org-directory "~/org"
- org-agenda-files '("~/org/")
- org-log-into-drawer t
- org-log-done 'time
- org-log-redeadline 'time
- org-log-reschedule 'time
- org-preview-latex-default-process 'dvisvgm
- org-highlight-latex-and-related '(native entities)
- org-startup-with-inline-images t
- org-adapt-indentation t
- org-hide-leading-stars t
- org-html-with-latex 'dvisvgm
- org-preview-latex-process-alist
- '((dvisvgm
- :image-input-type "dvi"
- :image-output-type "svg"
- :image-size-adjust (1.7 . 1.5)
- :latex-compiler ("pdflatex -interaction nonstopmode -output-format=dvi -output-directory=%o %f")
- :image-converter ("dvisvgm %o%b.dvi --no-fonts --exact-bbox --scale=%S --output=%O"))))
- (defun my/-org-allow-in-derived-mode (oldfun &rest r)
- "Allow OLDFUN to run, even if `major-mode' is only derived from `org-mode'.
- R is rest of the arguments to OLDFUN."
- (let ((major-mode (if (derived-mode-p 'org-mode)
- 'org-mode
- major-mode)))
- (apply oldfun r)))
- (advice-add 'org-element-at-point :around 'my/-org-allow-in-derived-mode)
- (advice-add 'org-table-header-line-mode :around 'my/-org-allow-in-derived-mode))
- (use-package evil-org
- :after org
- :hook (org-mode . evil-org-mode)
- :init
- (require 'evil-org-agenda)
- (evil-org-agenda-set-keys))
- ;; ledger
- (use-package ledger-mode)
- (use-package flycheck-ledger
- :hook (ledger-mode . trusted-files-flycheck-mode-if-safe))
- ;; khard contacts
- (require 'khard)
- ;; This is also in khard (see above), it's just also here so that if I remove
- ;; that file ever, other things will not break.
- (defun my/message-in-header-p (name &optional testfn)
- "If in field NAME, return the start of the header, otherwise, return nil.
- The name is compared with the field name using TESTFN (defaults to `equal')."
- (save-excursion
- (when (and (message-point-in-header-p)
- (message-beginning-of-header t))
- (beginning-of-line)
- (when (and (looking-at (rx bol (group (+? any)) ":" (? " ")))
- (funcall (or testfn 'equal) (match-string 1) name))
- (match-end 0)))))
- ;; mu4e
- (use-package mu4e
- :ensure nil
- :defer nil
- :hook ((mu4e-index-updated . my/-mu4e-enable-index-messages)
- (mu4e-main-mode . my/-mu4e-setup-main-mode)
- (mu4e-view-mode . my/-mu4e-setup-view-mode)
- (mu4e-compose-mode . my/-mu4e-setup-compose-mode))
- :bind (("C-x C-m" . mu4e)
- :map message-mode-map
- ("C-c k" . khard-insert-email-contact)
- :map mu4e-headers-mode-map
- ([remap mu4e-headers-mark-for-trash] .
- my/mu4e-headers-mark-for-trash)
- :map mu4e-view-mode-map
- ([remap mu4e-view-mark-for-trash] .
- my/mu4e-view-mark-for-trash))
- :init
- (require 'mu4e)
- (evil-define-key '(normal motion) mu4e-main-mode-map "q" #'bury-buffer)
- (evil-define-key '(normal motion) mu4e-view-mode-map "gy" #'mu4e-view-save-url)
- (defun my/-mu4e-setup-view-mode ()
- (setq-local global-hl-line-mode nil))
- (defun my/-mu4e-setup-main-mode ()
- (setq-local default-directory "~/"))
- (defun my/-mu4e-enable-index-messages ()
- (setq mu4e-hide-index-messages nil))
- (defun my/mu4e-update-mail-and-index-silent ()
- "Run `mu4e-update-mail-and-index' without any messages in the background."
- (setq mu4e-hide-index-messages t)
- (mu4e-update-mail-and-index t))
- (defun my/mu4e-headers-mark-for-trash ()
- "Move the message a point to the trash without marking it was deleted
- (trashed)."
- (interactive)
- (when (mu4e-thread-message-folded-p)
- (mu4e-warn "Cannot mark folded messages"))
- (mu4e-mark-at-point 'move mu4e-trash-folder)
- (when mu4e-headers-advance-after-mark
- (mu4e-headers-next)))
- (defun my/mu4e-view-mark-for-trash ()
- "Like `my/mu4e-headers-mark-for-trash', but for `mu4e-view-mode'."
- (interactive)
- (mu4e--view-in-headers-context
- (my/mu4e-headers-mark-for-trash)))
- (defun my/-mu4e-enable-autocomplete-in-header ()
- ;; corfu auto must be t (not the integer returned by
- ;; `my/message-in-header-p'
- (setq-local corfu-auto (and (not (window-minibuffer-p))
- (my/message-in-header-p "To")
- t)))
- (defun my/-mu4e-setup-compose-mode ()
- (add-hook 'post-command-hook 'my/-mu4e-enable-autocomplete-in-header
- nil t)
- (add-to-list
- (make-local-variable 'completion-at-point-functions)
- (cape-capf-super #'mu4e-complete-contact #'khard-message-mode-capf)))
- (defun my/-mu4e-fix-cycle-threshold ()
- (setq-local completion-cycle-threshold nil))
- (advice-add 'mu4e--compose-setup-completion :after
- 'my/-mu4e-fix-cycle-threshold)
- (defvar my/mu4e-interesting-mail-query
- (concat "flag:unread AND NOT flag:trashed AND NOT "
- "maildir:/protonmail/Trash AND NOT maildir:/protonmail/Spam")
- "Flag for mail which will appear as \"unread\" and will be notified.")
- (setq message-kill-buffer-on-exit t
- message-confirm-send t
- message-send-mail-function 'sendmail-send-it
- mu4e-change-filenames-when-moving t
- mu4e-context-policy 'pick-first
- mu4e-attachment-dir "~/downloads/"
- mu4e-last-update-buffer " *mu4e-last-update*"
- mu4e-index-update-error-warning nil
- mu4e-get-mail-command "mbsync protonmail"
- mu4e-completing-read-function #'completing-read-default
- mu4e-compose-context-policy 'ask-if-none
- mu4e-contexts
- (list (make-mu4e-context
- :name "Personal"
- :match-func (lambda (msg)
- (when msg
- (string-match-p "^/protonmail/"
- (mu4e-message-field msg
- :maildir))))
- :vars `((user-mail-address . ,(my/get-private 'mu4e-email))
- (user-full-name . ,(my/get-private 'mu4e-name))
- (message-signature . nil)
- (mu4e-refile-folder . "/protonmail/Archive")
- (mu4e-sent-folder . "/protonmail/Sent")
- (mu4e-drafts-folder . "/protonmail/Drafts")
- (mu4e-trash-folder . "/protonmail/Trash")
- (mu4e-bookmarks
- . ((:name "Inbox"
- :query "maildir:/protonmail/Inbox"
- :key ?i)
- (:name "Unread"
- :query ,my/mu4e-interesting-mail-query
- :key ?u))))))))
- (use-package mu4e-alert
- :after mu4e
- :hook (after-init . mu4e-alert-enable-notifications)
- :init
- (setq mu4e-alert-set-window-urgency nil
- mu4e-alert-interesting-mail-query my/mu4e-interesting-mail-query)
- :config
- (mu4e-alert-set-default-style 'libnotify))
- (mu4e t)
- (mu4e-context-switch nil "Personal")
- ;; mu4e compose HTML messages
- (use-package org-mime)
- (require 'org-mu4e-compose)
- (setq ;; mail-user-agent 'org-mu4e-user-agent
- org-mime-org-html-with-latex-default 'dvisvgm
- org-mime-export-options '(:with-latex dvisvgm :with-footnotes t))
- ;; (evil-define-key '(normal visual) org-mu4e-compose-mode-map
- ;; "G" #'mu4e-compose-goto-bottom
- ;; "gg" #'mu4e-compose-goto-top)
- ;; (evil-define-key 'normal org-mu4e-compose-mode-map
- ;; "ZZ" #'message-send-and-exit
- ;; "ZD" #'message-dont-send
- ;; "ZQ" #'message-kill-buffer
- ;; "ZF" #'mml-attach-file)
- ;; (evil-define-key 'normal mu4e-view-mode-map
- ;; "R" 'org-mu4e-compose-reply
- ;; "cr" 'org-mu4e-compose-reply)
- ;; (evil-define-key 'normal mu4e-headers-mode-map
- ;; "R" 'org-mu4e-compose-reply
- ;; "cr" 'org-mu4e-compose-reply)
- ;; (defun my/-setup-org-mu4e-compose-mode ()
- ;; "Setup up stuff in `org-mu4e-compose' buffers."
- ;; (setq-local ltex-eglot-variable-save-method 'file)
- ;; ;; this should come last so it can pick up the above
- ;; ;; (trusted-files-eglot-ensure-if-safe)
- ;; )
- ;; (add-hook 'org-mu4e-compose-mode-hook #'my/-setup-org-mu4e-compose-mode)
- ;; elfeed
- (use-package elfeed
- :bind (("C-c d" . elfeed))
- :custom
- (elfeed-feeds
- '(("https://archlinux.org/feeds/news/" linux arch)
- ("https://9to5linux.com/feed/atom" linux news)))
- :config
- (setq elfeed-log-buffer-name " *elfeed-log*")
- (evil-define-key '(normal motion) elfeed-search-mode-map
- "r" #'elfeed-search-fetch)
- (elfeed-db-load))
- ;; helpful
- (use-package helpful
- :hook ((emacs-lisp-mode . my/-helpful-setup-emacs-lisp-mode)
- (helpful-mode . my/-setup-helpful-mode))
- :bind (:map help-map
- ("f" . helpful-callable)
- ("v" . helpful-variable)
- ("k" . helpful-key)
- ("o" . helpful-symbol)
- ("x" . helpful-command)
- ("F" . helpful-function)
- :map helpful-mode-map
- ("<mouse-8>" . my/helpful-history-back)
- ("<mouse-9>" . my/helpful-history-forward)
- ("<normal-state><" . my/helpful-history-back)
- ("<normal-state>>" . my/helpful-history-forward))
- :init
- (defun my/-helpful-setup-emacs-lisp-mode ()
- (setq-local evil-lookup-func #'helpful-at-point))
- (defun my/-setup-helpful-mode ()
- (setq-local evil-lookup-func #'helpful-at-point
- tab-width 8))
- (defvar my/helpful-symbol-history-size 50
- "Max size of `my/helpful-symbol-history'.")
- (defvar my/helpful-symbol-history '()
- "History of helpful symbols.")
- (defvar my/-helpful-inhibit-history nil
- "If non-nil, don't add symbols to `my/helpful-symbol-history'.")
- (defvar my/-helpful-last-entry nil
- "Last entry looked up with helpful.")
- (defun my/helpful-history-back (count)
- "Go back COUNT symbols in `my/helpful-symbol-history'. If called
- interactively, COUNT defaults to 1."
- (interactive "p")
- (my/helpful-history-forward (- count)))
- (defun my/helpful-history-forward (count)
- "Move COUNT symbols in `my/helpful-symbol-history'. If COUNT is negative,
- move back. If COUNT is larger than the history, go to the newest entry. Go to
- the oldest entry if -COUNT is larger than the history."
- (interactive "p")
- (when helpful--sym
- (let* ((hist-len (length my/helpful-symbol-history))
- (current-pos (seq-position my/helpful-symbol-history
- (cons helpful--sym
- helpful--callable-p)
- 'equal))
- (new-pos (- current-pos count)))
- (cond
- ;; if already at the newest element, signal an error
- ((and (> count 0) (= current-pos 0))
- (message "%s" "No newer symbol!"))
- ;; if already at the oldest element, signal an error
- ((and (< count 0) (= (1+ current-pos) hist-len))
- (message "%s" "No older symbol!"))
- (t
- (let ((my/-helpful-inhibit-history t)
- (entry (cond
- ((<= new-pos 0)
- (seq-first my/helpful-symbol-history))
- ((>= new-pos hist-len)
- (car (last my/helpful-symbol-history)))
- (t
- (nth new-pos my/helpful-symbol-history)))))
- (if (cdr entry)
- (helpful-callable (car entry))
- (helpful-variable (car entry)))))))))
- (defun my/-helpful-switch-buffer-function (helpful-buf)
- "Like `pop-to-buffer', but kill previous helpful buffers and save the new
- buffers `helpful--sym' to `my/helpful-symbol-history'."
- (cl-loop with window = nil
- for buf in (buffer-list)
- when (and
- (not (eq buf helpful-buf))
- (eq (buffer-local-value 'major-mode buf) 'helpful-mode))
- do
- (when-let (cur-window (get-buffer-window buf nil))
- (setq window cur-window))
- (kill-buffer buf)
- finally
- (let ((entry (cons (buffer-local-value 'helpful--sym helpful-buf)
- (buffer-local-value 'helpful--callable-p
- helpful-buf))))
- (unless my/-helpful-inhibit-history
- (when-let (from-current-hist
- (member my/-helpful-last-entry
- my/helpful-symbol-history))
- (setq my/helpful-symbol-history from-current-hist))
- (cl-pushnew entry my/helpful-symbol-history :test 'equal)
- (setq my/helpful-symbol-history
- (seq-take my/helpful-symbol-history
- my/helpful-symbol-history-size)))
- (setq my/-helpful-last-entry entry))
- (if window
- (window--display-buffer helpful-buf window 'reuse)
- (pop-to-buffer helpful-buf))))
- (setq helpful-switch-buffer-function 'my/-helpful-switch-buffer-function
- helpful-max-buffers 2))
- (defun my/greyify-color (color percent &optional frame)
- "Make COLOR closer to black by PERCENT on FRAME.
- Color can be any color which can be passed to `color-values'."
- (cl-destructuring-bind (&optional r g b)
- (color-name-to-rgb color frame)
- (when (and r g b)
- (let ((scale (- 1.0 (/ percent 100.0))))
- (color-rgb-to-hex (* r scale)
- (* g scale)
- (* b scale))))))
- ;; rainbow-delimiters
- (use-package rainbow-delimiters
- :hook (prog-mode . rainbow-delimiters-mode)
- :config
- ;; generate dark version of the rainbow delimiters faces
- (defun my/-rainbow-delimiters-recalc-dark-faces (&optional frame)
- (unless frame (setq frame (selected-frame)))
- (dotimes (i 9)
- (when-let ((old-face (intern-soft
- (format "rainbow-delimiters-depth-%d-face"
- (1+ i))))
- (new-face
- (intern
- (format "my/rainbow-delimiters-depth-%d-dark-face"
- (1+ i))))
- (old-color (face-attribute old-face :foreground frame))
- (new-color (my/greyify-color old-color 50 frame)))
- (set-face-attribute new-face frame :foreground new-color))))
- (add-hook 'after-make-frame-functions
- #'my/-rainbow-delimiters-recalc-dark-faces)
- (add-hook 'server-after-make-frame-hook
- #'my/-rainbow-delimiters-recalc-dark-faces)
- (defun my/rainbow-delimiters-parinfer-pick-face (depth match loc)
- "Version of `rainbow-delimiters-default-pick-face' that colors closing
- parenthesis darker than opening ones. This function defers to
- `rainbow-delimiters-default-pick-face' and just changes the output if it returns
- one of the normal rainbow-delimiters-depth-N-face faces."
- (save-match-data
- (let* ((base-face (rainbow-delimiters-default-pick-face depth match loc))
- (base-name (symbol-name base-face)))
- (if (and evil-cleverparens-mode
- (eq ?\) (char-syntax
- (elt (buffer-substring-no-properties loc (1+ loc)) 0)))
- (string-match (rx string-start "rainbow-delimiters-depth-"
- (group (+ num))
- "-face" string-end)
- base-name))
- (or (intern-soft (format "my/rainbow-delimiters-depth-%s-dark-face"
- (match-string 1 base-name)))
- base-face)
- base-face))))
- (setopt rainbow-delimiters-pick-face-function
- 'my/rainbow-delimiters-parinfer-pick-face))
- ;; make regexp look nicer
- (use-package easy-escape
- :hook ((emacs-lisp-mode reb-mode) . easy-escape-minor-mode)
- :config
- (face-spec-set 'easy-escape-face
- '((t (:foreground unspecified
- :weight bold
- :inherit 'font-lock-regexp-grouping-backslash)))))
- ;; auto-highlight-symbol
- (use-package auto-highlight-symbol
- :hook (lisp-data-mode . auto-highlight-symbol-mode)
- :init
- (setq ahs-face 'bold
- ahs-face-unfocused 'bold
- ahs-definition-face 'bold
- ahs-definition-face-unfocused 'bold
- ahs-plugin-default-face 'bold
- ahs-plugin-default-face-unfocused 'bold)
- :config
- (keymap-unset auto-highlight-symbol-mode-map "C-x C-a" t))
- ;; Theme (doom-themes)
- (use-package doom-themes
- :config
- (load-theme 'doom-molokai t)
- (doom-themes-org-config))
- ;; solaire-mode
- (use-package solaire-mode
- :config
- (solaire-global-mode 1))
- ;; Highlight todos
- (use-package hl-todo
- :hook (prog-mode . hl-todo-mode))
- (use-package magit-todos
- :after (hl-todo magit)
- :config
- (magit-todos-mode 1))
- ;; icons
- (use-package nerd-icons)
- (use-package nerd-icons-completion
- :config
- (nerd-icons-completion-mode))
- (use-package nerd-icons-dired
- :hook (dired-mode . my/-maybe-enable-nerd-icons-dired)
- :init
- (defun my/-maybe-enable-nerd-icons-dired ()
- (unless (bound-and-true-p dirvish-override-dired-mode)
- (nerd-icons-dired-mode))))
- (use-package kind-icon
- :after corfu
- :init
- (setq kind-icon-default-face 'corfu-default
- kind-icon-default-style
- '(:padding -1 :stroke 0 :margin 0 :radius 0 :height 0.5 :scale 1))
- :config
- (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))
- ;; modeline (doom-modeline)
- (use-package doom-modeline
- :init
- (setq doom-modeline-support-imenu t)
- (doom-modeline-mode 1))
- ;; dashboard.el
- (use-package dashboard
- :config
- (defvar-local my/-dashboard-did-fix-image nil
- "Weather or not the dashboard image has been fixed in this buffer.")
- (defun my/-dashboard-fix-image ()
- (unless my/-dashboard-did-fix-image
- (dashboard-refresh-buffer)
- (setq my/-dashboard-did-fix-image t)))
- (defun my/-dashboard-setup-function ()
- (add-hook 'window-configuration-change-hook 'my/-dashboard-fix-image nil t)
- (setq-local display-line-numbers nil))
- (add-hook 'dashboard-mode-hook 'my/-dashboard-setup-function)
- (set-face-background 'dashboard-banner-logo-title nil)
- (dashboard-setup-startup-hook)
- (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))
- dashboard-force-refresh t
- dashboard-display-icons-p t
- dashboard-icon-type 'nerd-icons
- dashboard-set-file-icons t
- dashboard-projects-backend 'project-el
- dashboard-items '((recents . 5)
- (projects . 5)
- (bookmarks . 5))))
- ;; page break lines
- (use-package page-break-lines
- :config
- (global-page-break-lines-mode 1)
- (add-to-list 'page-break-lines-modes 'prog-mode)
- (add-to-list 'page-break-lines-modes 'text-mode)
- (add-to-list 'page-break-lines-modes 'helpful-mode))
- ;; fun!
- (use-package mines)
- ;;; init.el ends here
|