my-emacs-d/init.el

1923 lines
60 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; init --- Summary
;;; Commentary:
;;; Code:
(setq custom-file (concat user-emacs-directory "customizations.el"))
(load custom-file)
;; Initialize the package system and use-package
(setq load-prefer-newer t)
;; Add my own, version controlled ~lisp~ directory to ~load-path~
(add-to-list 'load-path (expand-file-name
(convert-standard-filename "lisp/")
user-emacs-directory))
;; Do the same with the local ~site-lisp~ if its there
(let ((site-lisp-dir "/usr/local/share/emacs/site-lisp"))
(when (file-directory-p site-lisp-dir)
(dolist (elt (directory-files site-lisp-dir))
(unless (or (string= elt ".") (string= elt ".."))
(add-to-list 'load-path (expand-file-name elt site-lisp-dir))))))
;; I prefer using freedesktops desktop specification everywhere
(load "xdg-paths")
;; Set up the package manager
;; Package archives
(require 'package)
;; GNU ELPA
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))
;; MELPA Stable
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
;; MELPA
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
;; Finally, initialise the package manager
(package-initialize)
;; use-package is a really convenient way to install packages. Its installed in 30.1, but lets stay on the safe side.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(require 'use-package)
(use-package use-package
:custom
(use-package-always-ensure t)
(use-package-verbose nil))
(use-package bind-key)
;; Custom functions and commands
(load "gpolonkai/utilities")
(load "gpolonkai/windows")
(load "gpolonkai/org-utils")
(load "gpolonkai/text-utils")
(load "gpolonkai/nav-utils")
(load "gpolonkai/file-utils")
(load "gpolonkai/magit-utils")
(defun crm-indicator (args)
"Generate a prompt for `completing-read-multiple'.
ARGS is a cons cell of two strings, which will be used in the prompt in reverse
order."
(cons (format "[CRM%s] %s"
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'"
""
crm-separator)
(car args))
(cdr args)))
(use-package emacs
:ensure nil
:init
;; Required for Consult/Vertico
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
(setq frame-title-format
'((:eval (concat system-name
": "
(if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b")))))
:custom
(user-full-name "Gergely Polonkai")
(user-mail-address "gergely@polonkai.eu")
(kill-read-only-ok t)
(use-dialog-box nil)
(cursor-type 'bar)
(echo-keystrokes .01)
(fill-column 120)
(initial-scratch-message "")
(minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt))
(enable-recursive-minibuffers t)
(completion-cycle-threshold 3)
(tab-always-indent 'complete)
:hook
(minibuffer-setup . cursor-intangible-mode))
;; Basic settings
;;
;; Now that we have package management configured we can set up defaults more
;; easily. This includes every builtin packages, font faces, and the like.
;; Set Orgs main directory
;;
;; Since a lot of packages (org-projectile, org-caldav, etc.) rely on it, it
;; needs to be set as early as possible.
(setq org-directory (expand-file-name "~/Nextcloud/orgmode"))
;; Set up my personal keymap
(defvar gpolonkai/pers-map (make-sparse-keymap)
"My own, personal, keymap!")
(define-prefix-command 'gpolonkai/pers-map)
(define-key ctl-x-map "t" 'gpolonkai/pers-map)
(define-key global-map (kbd "C-t") 'gpolonkai/pers-map)
;; Set up some faces
(use-package faces
:ensure nil
:custom-face
(default ((t (:family "Fira Code Retina" :foundry "simp" :slant normal :weight normal :height 100 :width normal))))
(trailing-whitespace ((t (:inherit nil :background "red3")))))
;; Fira Code comes with nice ligatures, lets use them!
(use-package ligature
:config
;; Enable the "www" ligature in every possible major mode
(ligature-set-ligatures 't '("www"))
;; Enable traditional ligature support in eww-mode, if the
;; `variable-pitch' face supports it
(ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
;; Enable all Cascadia and Fira Code ligatures in programming modes
(ligature-set-ligatures 'prog-mode
'(;; == === ==== => =| =>>=>=|=>==>> ==< =/=//=// =~
;; =:= =!=
("=" (rx (+ (or ">" "<" "|" "/" "~" ":" "!" "="))))
;; ;; ;;;
(";" (rx (+ ";")))
;; && &&&
("&" (rx (+ "&")))
;; !! !!! !. !: !!. != !== !~
("!" (rx (+ (or "=" "!" "\." ":" "~"))))
;; ?? ??? ?: ?= ?.
("?" (rx (or ":" "=" "\." (+ "?"))))
;; %% %%%
("%" (rx (+ "%")))
;; |> ||> |||> ||||> |] |} || ||| |-> ||-||
;; |->>-||-<<-| |- |== ||=||
;; |==>>==<<==<=>==//==/=!==:===>
("|" (rx (+ (or ">" "<" "|" "/" ":" "!" "}" "\]"
"-" "=" ))))
;; \\ \\\ \/
("\\" (rx (or "/" (+ "\\"))))
;; ++ +++ ++++ +>
("+" (rx (or ">" (+ "+"))))
;; :: ::: :::: :> :< := :// ::=
(":" (rx (or ">" "<" "=" "//" ":=" (+ ":"))))
;; // /// //// /\ /* /> /===:===!=//===>>==>==/
("/" (rx (+ (or ">" "<" "|" "/" "\\" "\*" ":" "!"
"="))))
;; .. ... .... .= .- .? ..= ..<
("\." (rx (or "=" "-" "\?" "\.=" "\.<" (+ "\."))))
;; -- --- ---- -~ -> ->> -| -|->-->>->--<<-|
("-" (rx (+ (or ">" "<" "|" "~" "-"))))
;; *> */ *) ** *** ****
("*" (rx (or ">" "/" ")" (+ "*"))))
;; www wwww
("w" (rx (+ "w")))
;; <> <!-- <|> <: <~ <~> <~~ <+ <* <$ </ <+> <*>
;; <$> </> <| <|| <||| <|||| <- <-| <-<<-|-> <->>
;; <<-> <= <=> <<==<<==>=|=>==/==//=!==:=>
;; << <<< <<<<
("<" (rx (+ (or "\+" "\*" "\$" "<" ">" ":" "~" "!"
"-" "/" "|" "="))))
;; >: >- >>- >--|-> >>-|-> >= >== >>== >=|=:=>>
;; >> >>> >>>>
(">" (rx (+ (or ">" "<" "|" "/" ":" "=" "-"))))
;; #: #= #! #( #? #[ #{ #_ #_( ## ### #####
("#" (rx (or ":" "=" "!" "(" "\?" "\[" "{" "_(" "_"
(+ "#"))))
;; ~~ ~~~ ~= ~- ~@ ~> ~~>
("~" (rx (or ">" "=" "-" "@" "~>" (+ "~"))))
;; __ ___ ____ _|_ __|____|_
("_" (rx (+ (or "_" "|"))))
;; Fira code: 0xFF 0x12
("0" (rx (and "x" (+ (in "A-F" "a-f" "0-9")))))
;; Fira code:
"Fl" "Tl" "fi" "fj" "fl" "ft"
;; The few not covered by the regexps.
"{|" "[|" "]#" "(*" "}#" "$>" "^="))
;; Enables ligature checks globally in all buffers. You can also do it
;; per mode with `ligature-mode'.
(global-ligature-mode t))
;; Set the default font and configure font resizing
;; Before this can be used, make sure the Symbola font
;; (https://zhm.github.io/symbola/) font is installed.
(defun gpolonkai/set-font-size (frame)
"Set default fonts and font sizes in FRAME."
(when (display-graphic-p frame)
(set-face-attribute 'default frame :font "Fira Code Retina-10")
(set-frame-font "Fira Code Retina-10" t (list frame))))
(defun --set-emoji-font (frame)
"Adjust the font setting of FRAME so Emacs can display Emoji properly."
(when (display-graphic-p frame)
(set-fontset-font t 'symbol
(font-spec :family "Symbola")
frame 'prepend)
(set-fontset-font t 'unicode
"Noto Emoji"
nil 'append)))
(add-hook 'after-make-frame-functions 'gpolonkai/set-font-size)
(add-hook 'after-make-frame-functions '--set-emoji-font)
(gpolonkai/set-font-size nil)
(--set-emoji-font nil)
;; Set UTF-8 as the default encoding
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
(use-package frame
:ensure nil
:custom
(blink-cursor-mode t))
;; I really dont want to type more than i really must…
(defalias 'yes-or-no-p 'y-or-n-p)
;; Tweak window chrome, AKA lets set up the UI!
;; Turn off the toolbar
(tool-bar-mode 0)
;; Turn off the menu bar
(menu-bar-mode 0)
;; If we are running in a graphical environment, turn off the scroll bar
;;
;; TODO: This is not done for every new frame!
(when window-system
(scroll-bar-mode -1))
;; Maximise newly created frames (AKA DE windows)
(set-frame-parameter nil 'fullscreen 'maximized)
;; Enable all the commands! These are disabled for a reason, but im a rockstar,
;; so who cares‽
;;
;; TODO: There must be a way to do it programatically
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'erase-buffer 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'set-goal-column 'disabled nil)
(put 'scroll-left 'disabled nil)
(put 'dired-find-alternate-file 'disabled nil)
(put 'Info-edit 'disabled nil)
(put 'list-timers 'disabled nil)
;; simple to always show the current column, and using visual lines in text
;; modes
(use-package simple
:ensure nil
:custom
(column-number-mode t)
:hook
(text-mode . (lambda () (visual-line-mode t))))
(use-package prog-mode
:ensure nil
:init
(setq prettify-symbols-alist
'(("lambda" . )
("function" . )
("map" . ?↦)
("not" . )
("and" . ?∧)
("or" . ?)))
:config
(global-prettify-symbols-mode t))
(use-package thingatpt
:ensure nil)
(use-package nxml-mode
:ensure nil
:custom
(nxml-attribute-indent 4)
(nxml-child-indent 4)
(nxml-outline-child-indent 4))
(use-package recentf
:ensure nil
:config
(run-at-time nil (* 5 60) 'recentf-save-list)
(add-to-list 'recentf-exclude (expand-file-name "elpa" user-emacs-directory)))
(use-package files
:ensure nil
:custom
(make-backup-file-name-function 'xah/backup-file-name))
(defun prevent-whitespace-mode-for-some ()
"Prevent `whitespace-mode' from running in some modes."
(and
(not (derived-mode-p 'magit-mode))
(not (derived-mode-p 'org-mode))))
(use-package whitespace
:demand
:config
(add-function :before-while whitespace-enable-predicate 'prevent-whitespace-mode-for-some)
(global-whitespace-mode 1)
:custom
(whitespace-line-column 120)
:bind
(([f10] . whitespace-mode)
([(shift f10)] . global-whitespace-mode)
:map gpolonkai/pers-map
("w" . whitespace-cleanup))
:custom-face
(whitespace-line ((t (:inherit nil :background "orange4")))))
;; Taken from https://ryuslash.org/posts/C-d-to-close-eshell.html
(defun eshell-C-d ()
"Either call `delete-char' interactively or quit."
(interactive)
(condition-case err
(call-interactively #'delete-char)
(error (if (and (eq (car err) 'end-of-buffer)
(looking-back eshell-prompt-regexp nil))
(kill-buffer)
(signal (car err) (cdr err))))))
(defun gpolonkai/eshell-set-c-d-locally ()
"Bind my `eshell-C-d' function locally."
(local-set-key (kbd "C-d") #'eshell-C-d))
;; Taken from https://blog.hoetzel.info/post/eshell-notifications/
(defun eshell-command-alert (process status)
"Send `alert' with severity based on STATUS when PROCESS finished."
(let* ((cmd (process-command process))
(buffer (process-buffer process))
(msg (format "%s: %s" (mapconcat 'identity cmd " ") status)))
(if (string-prefix-p "finished" status)
(alert msg :buffer buffer :severity 'normal)
(alert msg :buffer buffer :severity 'urgent))))
(use-package eshell
:bind
(:map gpolonkai/pers-map
("e" . eshell))
:hook
(eshell-mode . gpolonkai/eshell-set-c-d-locally)
(eshell-kill . eshell-command-alert))
(use-package calendar
:ensure nil
:custom
(calendar-week-start-day 1))
(use-package cal-dst
:ensure nil
:custom
(calendar-time-zone 60)
(calendar-standard-time-zone-name "CET")
(calendar-daylight-time-zone-name "CEST"))
(use-package solar
:ensure nil
:custom
(calendar-latitude 47.4)
(calendar-longitude 19.0)
(calendar-location-name "Budapest, Hungary"))
;; TODO: This :config call can be omitted if i add it to the calendar-load-hook
(use-package hungarian-holidays
:config
(hungarian-holidays-add))
(add-to-list 'holiday-other-holidays '(holiday-float 7 5 -1 "SysAdmin Day") t)
(add-to-list 'holiday-other-holidays '(holiday-fixed 10 21 "Reptile Awareness Day") t)
(use-package saveplace
:ensure nil
:config
(save-place-mode 1)
:custom
(save-place-file (expand-file-name ".places" user-emacs-directory)))
(use-package ediff
:ensure nil
:custom
(ediff-merge-split-window-function 'split-window-horizontally)
(ediff-split-window-function 'split-window-vertically)
(ediff-window-setup-function 'ediff-setup-windows-plain))
(use-package autorevert
:ensure nil
:config
(global-auto-revert-mode 1))
(use-package eww
:custom
(eww-search-prefix "https://duckduckgo.com/html/?q="))
;; TODO: This :config call can be omitted if i add it to prog-mode-hook
(use-package electric
:config
;; This seems to be the default, but lets make sure…
(electric-indent-mode 1))
(use-package savehist
:config
(savehist-mode 1))
(use-package webjump
:bind
(:map gpolonkai/pers-map
("j" . webjump)))
(defun gpolonkai/activate-which-func-mode ()
"Activate `which-func-mode' if it exists."
(if (fboundp 'which-function-mode)
(which-function-mode)
(which-func-mode)))
;; This :config call can be omitted if i add it to which-function-mode-hook
(use-package which-func
:config
(setq which-func-unknown "")
:hook
(prog-mode . gpolonkai/activate-which-func-mode))
;; “Fortunes” are from the Hungarian version an ancient MS-DOS based program called TAGLINE
(use-package cookie1
:demand t
:custom
(cookie-file (expand-file-name "fortune-cookies.txt" user-emacs-directory))
:bind
(:map gpolonkai/pers-map
("k" . cookie)))
(use-package dired
:ensure nil
:custom
(dired-dwim-target t)
(wdired-create-parent-directories t)
(wdired-allow-to-change-permissions t)
:bind
(:map dired-mode-map
("RET" . dired-find-alternate-file)
("^" . (lambda () (interactive) (find-alternate-file "..")))
("W" . wdired-change-to-wdired-mode)))
(use-package goto-addr
:hook ((compilation-mode . goto-address-mode)
(prog-mode . goto-address-prog-mode)
(eshell-mode . goto-address-mode)
(shell-mode . goto-address-mode))
:bind
(:map goto-address-highlight-keymap
("<RET>" . goto-address-at-point)
("M-<RET>" . newline))
:commands (goto-address-prog-mode goto-address-mode))
;; Close unused buffers at midnight.
(use-package midnight
:ensure nil
:custom
(clean-buffer-list-kill-never-buffer-names '("*scratch*"
"*Messages*"
"*dashboard*"))
:config
(midnight-mode t))
(use-package display-line-numbers
:bind
(:map gpolonkai/pers-map
("C-n" . display-line-numbers-mode)))
(use-package ispell
:custom
(ispell-dictionary "en_GB")
(ispell-program-name "/usr/bin/hunspell")
:hook
(mail-send . ispell-message)
(message-send . ispell-message))
(use-package speedbar)
;; A Termux-specific override for `browse-url'
(when (gpolonkai/termux-p)
(use-package browse-url
:ensure nil
:config
(advice-add 'browse-url-default-browser :override
(lambda (url &rest args)
(start-process-shell-command
"open-url"
nil
(concat "am start -a android.intent.action.VIEW --user 0 -d "
url))))))
(use-package dabbrev
:bind (("M-/" . dabbrev-completion)
("C-M-/" . dabbrev-expand)))
;; Make sure we always have the latest ELPA GPG keys
(use-package gnu-elpa-keyring-update)
;; Keep packages up to date
(use-package auto-package-update
:custom
(auto-package-update-interval 7)
(auto-package-update-delete-old-versions t)
;; Even though this might require a restart when packages are updated, since
;; `use-package' invocations update the list of installed packages, we have to
;; do this in `after-init-hook'.
:hook
(after-init . auto-package-update-maybe))
;; TODO: This :config call might be omitted if i move it to
;; ace-window-display-mode-hook
(use-package ace-window
:custom
(aw-background nil)
(aw-dispatch-always t)
:config
(add-to-list 'aw-dispatch-alist
'(?s gpolonkai/scroll-window-up " Scroll window up")
t)
(add-to-list 'aw-dispatch-alist
'(?S gpolonkai/scroll-window-down " Scroll window down")
t)
(add-to-list 'aw-dispatch-alist
'(?q gpolonkai/bury-window " Bury (quit) window")
t)
:bind
(:map ctl-x-map
("o" . ace-window))
:custom-face
(aw-leading-char-face ((t (:inherit ace-jump-face-foreground :height 2.0)))))
(use-package doom-themes
:custom
(doom-themes-enable-bold t)
(doom-themes-enable-italic t)
:config
(load-theme 'doom-nord-aurora t)
(doom-themes-visual-bell-config)
(doom-themes-org-config))
;; The package that converted me to Emacs:
;; https://gergely.polonkai.eu/blog/2014/9/17/nyanmacs.html
(use-package nyan-mode
:config
(nyan-mode t)
:custom
(nyan-bar-length 20)
(nyan-animate-nyancat t)
(nyan-wavy-trail t))
;; The list of active minor modes can easily fill my whole mode line (twice…).
;; This is one of the things to protect me from that.
(use-package delight)
(use-package minions
:config
(minions-mode 1))
(use-package doom-modeline
:init
(doom-modeline-mode 1)
:custom
(doom-modeline-continuous-word-count-modes '(markdown-mode gfm-mode org-mode rst-mode))
(doom-modeline-hud t)
(doom-modeline-minor-modes t))
(use-package beacon
:demand
:config
(beacon-mode 1)
:bind
(:map gpolonkai/pers-map
("b" . beacon-blink)))
(use-package eshell-fringe-status
:hook
(eshell-mode . eshell-fringe-status-mode))
(use-package eshell-prompt-extras
:config
(with-eval-after-load "esh-opt"
(autoload 'epe-theme-lambda "eshell-prompt-extras"))
:custom
(eshell-highlight-prompt nil)
(eshell-prompt-function 'epe-theme-lambda))
;; Some (many? most?) Emacs packages use this to do some logical separation of
;; the code. It is also used by some compilers, and Emacs help system.
(use-package form-feed
:hook
(emacs-lisp-mode . form-feed-mode)
(compilation-mode . form-feed-mode)
(help-mode . form-feed-mode))
(use-package hl-line
:config
(when window-system
(global-hl-line-mode))
:custom-face
(hl-line ((t (:inherit nil :background "gray25")))))
(use-package ace-jump-mode
:bind
(:map gpolonkai/pers-map
("SPC" . ace-jump-mode)))
(defun gpolonkai/no-blink-matching-paren ()
"Disable blinking matching parens."
(customize-set-variable 'blink-matching-paren nil))
(defun gpolonkai/blink-matching-paren ()
"Enable blinking matching parens."
(customize-set-variable 'blink-matching-paren t))
(use-package multiple-cursors
:init
(defvar gpolonkai/mc-prefix-map (make-sparse-keymap)
"Prefix keymap for multiple-cursors")
(define-prefix-command 'gpolonkai/mc-prefix-map)
(define-key global-map (kbd "C-c m") 'gpolonkai/mc-prefix-map)
:hook
(multiple-cursors-mode-enabled . gpolonkai/no-blink-matching-paren)
(multiple-cursors-mode-disabled . gpolonkai/blink-matching-paren)
:bind
(:map gpolonkai/mc-prefix-map
("t" . mc/mark-all-like-this)
("m" . mc/mark-all-like-this-dwim)
("l" . mc/edit-lines)
("e" . mc/edit-ends-of-lines)
("a" . mc/edit-beginnings-of-lines)
("n" . mc/mark-next-like-this)
("p" . mc/mark-previous-like-this)
("s" . mc/mark-sgml-tag-pair)
("d" . mc/mark-all-like-this-in-defun)
("M-<mouse-1>" . mc/add-cursor-on-click)))
;; phi-search is an incremental search compatible with multiple-cursors
(use-package phi-search)
(use-package phi-search-mc
:config
(phi-search-mc/setup-keys))
(use-package mc-extras
:demand
:bind
(:map mc/keymap
("=" . mc/compare-chars)))
(use-package ace-mc
:bind
(:map gpolonkai/mc-prefix-map
("SPC" . ace-mc-add-multiple-cursors)
("C-SPC" . ace-mc-add-single-cursor)))
(use-package smartparens
:demand
:config
(require 'smartparens-config)
(show-smartparens-global-mode t)
:hook
(prog-mode . turn-on-smartparens-strict-mode)
(markdown-mode . turn-on-smartparens-strict-mode)
:bind
(([f9] . smartparens-strict-mode)
("C-c s u" . sp-unwrap-sexp)
("C-c s k" . sp-kill-sexp)
("C-c s r" . sp-rewrap-sexp)))
(use-package which-key
:config
(which-key-mode)
(which-key-setup-minibuffer)
:custom
(which-key-idle-delay 0.3))
(use-package visual-fill-column
:custom
(visual-fill-column-center-text t)
(visual-fill-column-width 120)
:hook
(org-mode . visual-fill-column-mode))
(use-package spinner)
(use-package avy
:demand
:config
(avy-setup-default)
:bind
(("M-g c" . avy-goto-char)
("M-g C" . avy-goto-char-2)
("M-g f" . avy-goto-line)
("M-g w" . avy-goto-word-1)
("M-g e" . avy-goto-word-0)))
(use-package goto-last-change
:bind
(("M-g /" . goto-last-change)))
(use-package rainbow-mode
:hook
(css-mode . rainbow-mode)
(scss-mode . rainbow-mode)
(sass-mode . rainbow-mode))
(use-package zygospore
:bind
(:map ctl-x-map
("1" . zygospore-toggle-delete-other-windows)))
(use-package dashboard
:after
projectile
:config
(add-to-list 'dashboard-items '(projects . 5) t)
(dashboard-setup-startup-hook)
:custom
(dashboard-set-heading-icons t)
(dashboard-set-file-icons t)
(dashboard-center-content t)
(dashboard-set-navigator t)
(dashboard-items '((agenda . 5)
(projects . 5)
(recents . 5)
(bookmarks . 5))))
(use-package sr-speedbar
:after speedbar)
(use-package hungry-delete
:config
(global-hungry-delete-mode))
(use-package anzu
:delight
:config
(global-anzu-mode 1))
;; Should the fonts be missing, run `all-the-icons-install-fonts'. Its not run
;; automatically, as it requires connecting to a website and download a pretty
;; large font file.
(use-package all-the-icons)
(use-package all-the-icons-dired
:hook
(dired-mode . all-the-icons-dired-mode))
(use-package flyspell
:hook
(prog-mode . flyspell-prog-mode)
(text-mode . flyspell-mode))
(use-package ace-flyspell
:bind
(:map flyspell-mode-map
("C-M-i" . ace-flyspell-correct-word)))
;; Text objects are textual patterns like a line, a top level definition, a
;; word, a sentence or any other unit of text. When objed-mode is enabled,
;; certain editing commands (configurable) will activate objed and enable its
;; modal editing features.
;;
;; TODO: is :demand really needed here?
(use-package objed
:demand t
:bind
(:map global-map
("M-SPC" . objed-activate)))
(use-package alert
:config
(setq alert-default-style 'libnotify))
(alert-add-rule
:status '(buried)
:mode 'eshell-mode
:style 'notifications)
(when (gpolonkai/termux-p)
(use-package alert-termux
:after alert
:config
(setq alert-default-style 'termux)))
(use-package undo-tree
:config
(global-undo-tree-mode)
:custom
(undo-tree-auto-save-history nil))
(use-package ciel
:bind
(:map global-map
("C-c i" . ciel-ci)
("C-c o" . ciel-co)))
(use-package hide-mode-line
:bind (:map gpolonkai/pers-map
("h" . hide-mode-line-mode)))
(use-package string-inflection
:bind (:map gpolonkai/pers-map
("i" . string-inflection-all-cycle)))
(use-package spdx
:bind (:map gpolonkai/pers-map
("L" . spdx-insert-spdx-copyright))
:custom
(spdx-copyright-holder 'user)
(spdx-copyright-sign 'unicode))
(use-package ag
:commands (ag))
(use-package rg
:commands (rg))
(use-package outline
:ensure nil
:custom-face
(outline-1 ((t (:inherit font-lock-function-name-face
:overline t
:weight bold
:height 1.2))))
(outline-2 ((t (:inherit font-lock-variable-name-face
:overline t
:weight bold
:height 1.1))))
(outline-3 ((t (:inherit font-lock-keyword-face
:overline t
:weight bold)))))
(defun gpolonkai/setup-org-mode ()
"Setup Org related variables."
(org-indent-mode 1)
(variable-pitch-mode 1)
(auto-fill-mode 0)
(visual-line-mode 1))
;; From @suckless_shill:matrix.org (viz) in the #org-mode:matrix.org Matrix room
(defun vz/org-fontify-horizontal-break ()
"Display Orgs horizontal break (-----) as a full-width horizontal line."
(push '("^[[:space:]]*\\(------*\\)\n"
(0 (progn
(put-text-property (match-beginning 1) (match-end 1)
'display (make-string
(- (match-end 1)
(match-beginning 1))
?\s))
(put-text-property (match-beginning 1) (match-end 0)
'face '(:strike-through t :extend t)))))
org-font-lock-extra-keywords)
(setq-local font-lock-extra-managed-props
(cons 'display font-lock-extra-managed-props)))
(add-hook 'org-font-lock-set-keywords-hook #'vz/org-fontify-horizontal-break)
(use-package org
:demand
:custom-face
(org-block ((t (:inherit fixed-pitch))))
(org-code ((t (:inherit (shadow fixed-pitch)))))
(org-indent ((t (:inherit (org-hide fixed-pitch)))))
(org-verbatim ((t (:inherit (shadow fixed-pitch)))))
(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
(org-checkbox ((t (:inherit fixed-pitch))))
(org-table ((t (:inherit (shadow fixed-pitch)))))
(org-_drawer ((t (:inherit (shadow fixed-pitch)))))
:init
(require 'xdg-paths)
(defface org-checkbox-todo-text
'((t (:inherit org-todo)))
"Face for the text part of an unchecked org-mode checkbox.")
(defface org-checkbox-done-text
'((t (:inherit org-done)))
"Face for the text part of a checked org-mode checkbox.")
(font-lock-add-keywords
'org-mode
`(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?: \\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)" 1 'org-checkbox-todo-text prepend))
'append)
(font-lock-add-keywords
'org-mode
`(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)" 12 'org-checkbox-done-text prepend))
'append)
(setq-default org-default-notes-file (expand-file-name "notes.org" org-directory)
org-agenda-files `(,org-directory)
org-time-stamp-formats '("<%Y-%m-%d>" . "<%Y-%m-%d %H:%M>")
org-todo-keywords '((sequence "TODO(t)"
"DOING(w@/!)"
"BLOCKED(b@/!)"
"SOMEDAY(s!)"
"|"
"CANCELED(c@/!)"
"REVIEW(r@/!)"
"DONE(d@/!)"))
org-todo-keyword-faces '(("SOMEDAY" . (:foreground "goldenrod"))
("CANCELED" . (:foreground "#228b22"
:strike-through t)))
org-html-checkbox-types
'((unicode (on . "<span class=\"task-done\">☑</span>")
(off . "<span class=\"task-todo\">☐</span>")
(trans . "<span class=\"task-in-progress\">▣</span>"))))
:config
;; Load the markdown exporter
(require 'ox-md)
;; Handle org-protocol:// links
(require 'org-protocol)
;; Make it possible to encrypt headings
(require 'org-crypt)
;; Make it possible to use inline tasks
(require 'org-inlinetask)
;; Make sure we load Python babel stuff
(org-babel-do-load-languages
'org-babel-load-languages
'((python . t)))
;; Track habits
(require 'org-habit)
:custom
(org-log-into-drawer t)
(org-ellipsis "…#")
(org-startup-folded 'content)
(org-log-done 'time)
(org-src-preserve-indentation t)
(org-tags-column 0)
(org-startup-indented t)
(org-special-ctrl-a/e t)
(org-return-follows-link t)
(org-src-fontify-natively t)
(org-goto-interface 'outline-path-completion)
(org-goto-max-level 10)
(org-html-checkbox-type 'unicode)
(org-src-window-setup 'current-window)
(org-pretty-entities t)
(org-pretty-entities-include-sub-superscripts t)
(org-use-speed-commands t)
(org-hide-leading-stars t)
(org-enforce-todo-dependencies t)
(org-enforce-todo-checkbox-dependencies t)
(org-catch-invisible-edits 'show)
(org-log-reschedule 'time)
(org-log-redeadline 'note)
(org-refile-use-outline-path 'file)
(org-outline-path-complete-in-steps nil)
(org-refile-allow-creating-parent-nodes 'confirm)
(org-crypt-key "B0740C4C")
(org-speed-commands-user '(("m" . org-mark-subtree)))
(org-refile-targets '((nil :maxlevel . 6)
(org-agenda-files :maxlevel . 3)))
(org-agenda-custom-commands '(("c" "Simple agenda view"
((tags "PRIORITY=\"A\""
((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
(org-agenda-overriding-header "High priority unfinished tasks")))
(agenda "")
(alltodo ""
((org-agenda-skip-function
'(or (air-org-skip-subtree-if-habit)
(air-org-skip-subtree-if-priority ?A)
(gpolonkai/org-skip-subtree-if-state "SOMEDAY")
(org-agenda-skip-if nil '(scheduled deadline))))
(org-agenda-overriding-header "ALL normal priority tasks"))))
((org-agenda-compact-blocks t)))))
(org-log-note-clock-out t)
(org-capture-templates '(("L" "Org-protocol capture" entry
(file+headline
(lambda ()
(expand-file-name "index.org" org-directory))
"Captures")
"** %:description\n:PROPERTIES:\n:SOURCE: %:link\n:END:\n\n%:initial"
:empty-lines 1)
("R" "Region to Current Clocked Task" plain
(clock)
"%i"
:immediate-finish t
:empty-lines 1)
("K" "Kill-ring to Current Clocked Task" plain
(clock)
"%c"
:immediate-finish t
:empty-lines 1)
("c" "Item to current Clocked Task" item
(clock)
"%i%?"
:empty-lines 1)
("g" "GT2 note" entry
(file+headline
(lambda ()
(expand-file-name "gt2-notes.org" org-directory))
"Captures")
"** %^{Title}\n:PROPERTIES:\n:CREATED: %T\n:END:\n\n%a\n\n%i%?")
("p" "Blog post" entry
(file+olp+datetree
(lambda ()
(expand-file-name "blog.org" org-directory)))
"* %^{Title} :blog:\n:PROPERTIES:\n:CREATED: %T\n:END:\n\n%i%?")))
(org-read-date-force-compatible-dates nil)
(org-agenda-prefix-format '((agenda . " %i %-12:c%?-12t% s")
(todo . " %i %-12:c %(concat \"[ \"(org-format-outline-path (org-get-outline-path)) \" ]\") ")
(tags . " %i %-12:c %(concat \"[ \"(org-format-outline-path (org-get-outline-path)) \" ]\") ")
(timeline . " % s")
(search . " %i %-12:c")))
(org-agenda-dim-blocked-tasks t)
(org-link-descriptive t)
(org-hide-emphasis-markers t)
(org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)
(org-deadline-warning-days 7)
:hook
(ediff-select . f-ediff-org-unfold-tree-element)
(ediff-unselect . f-ediff-org-fold-tree)
(org-mode . gpolonkai/setup-org-mode)
:bind
(:map gpolonkai/pers-map
("a" . gpolonkai/org-agenda-list)
("C" . org-capture)
("l" . org-store-link)
:map org-mode-map
("SPC" . gpolonkai/org-space-key)
("C-c l" . org-toggle-link-display)
("C-a" . gpolonkai/move-to-beginning-of-line)
("C-e" . gpolonkai/move-to-end-of-line)
("C-c h" . outline-previous-heading)
("C-c ." . gpolonkai/org-insert-current-timestamp)
("C-c ;" . org-toggle-timestamp-type)))
(use-package org-roam
:after org
:ensure t
:config
(setq org-roam-node-display-template (concat "${title:*} "
(propertize "${tags:10}" 'face 'org-tag)))
(org-roam-db-autosync-mode)
(require 'org-roam-dailies)
(require 'org-roam-protocol)
:custom
(org-roam-directory (file-truename (expand-file-name "roam" org-directory)))
(org-roam-dailies-directory (file-truename (expand-file-name "roam/journal" org-directory)))
(org-roam-completion-everywhere t)
(org-roam-capture-templates '(("d" "default" plain "\n%?"
:target (file+head
"${slug}-%<%Y%m%d%H%M%S>.org"
"#+title: ${title}")
:unnarrowed t)))
(org-roam-dailies-capture-templates '(
("d" "default" entry "* %<%H:%M>: %?"
:if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))
:bind
(("C-c n c" . org-roam-capture)
("C-c n g" . org-roam-graph)
("C-c n i" . org-roam-node-insert)
("C-c n l" . org-roam-buffer-toggle)
:map org-mode-map
("C-M-i" . completion-at-point)
:map org-roam-dailies-map
("d" . org-roam-dailies-capture-date)
("j" . org-roam-dailies-capture-today)
("Y" . org-roam-dailies-capture-yesterday)
("T" . org-roam-dailies-capture-tomorrow))
:bind-keymap
("C-c n d" . org-roam-dailies-map))
(use-package org-roam-timestamps
:after org-roam
:config
(org-roam-timestamps-mode)
:custom
(org-roam-timestamps-remember-timestamps t)
(org-roam-timestamps-timestamp-parent-file t))
(use-package org-roam-ui
:after org-roam
:custom
(org-roam-ui-sync-theme t)
(org-roam-ui-follow t)
(org-roam-ui-update-on-save t)
(org-roam-ui-open-on-start nil))
(use-package org-bullets
:custom
(org-bullets-face-name 'org-bullet-face)
(org-bullets-bullet-list '("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""))
:hook
(org-mode . org-bullets-mode))
(use-package org-sticky-header
:custom
(org-sticky-header-full-path 'full)
:hook
(org-mode . org-sticky-header-mode))
(use-package org-random-todo
:demand
:config
;; Dont bug me too often…
(setq org-random-todo-how-often 3600)
:custom
(org-random-todo-skip-keywords '("SOMEDAY"))
:bind
(:map gpolonkai/pers-map
("r" . org-random-todo)))
(use-package org-autolist
:hook
(org-mode . org-autolist-mode))
(use-package secretaria
:after
alert
:hook
;; use this for getting a reminder every 30 minutes of those tasks
;; scheduled for today and which have no time of day defined.
(after-init . secretaria-unknown-time-always-remind-me))
(use-package org-clock-waybar
:ensure nil
:vc (:url "https://gitea.polonkai.eu/gergely/org-clock-waybar.git")
:config
(org-clock-waybar-setup))
(use-package consult-org-roam
:config
(consult-org-roam-mode)
:bind (:map goto-map
("r" . consult-org-roam-search)
:map global-map
("C-c n f" . consult-org-roam-file-find)))
(use-package org-appear
:hook
(org-mode . org-appear-mode)
:custom
(org-appear-autosubmarkers t)
(org-appear-autolinks t)
(org-appear-autoemphasis t))
(use-package ob-mermaid
:custom
(ob-mermaid-cli-path "/home/polesz/.local/node/bin/mmdc"))
;; Before using this, make sure the latest PlantUML JAR file is downloaded into
;; the downloads directory. It is available from here:
;; http://plantuml.com/download
(use-package plantuml-mode
:init
(setq plantuml-jar-path
(expand-file-name
;; Make sure we have a download location even if XDG is not working
(cond
((xdg-user-dir "DOWNLOAD")
(expand-file-name "plantuml.jar" (xdg-user-dir "DOWNLOAD")))
(t
"~/Downloads/plantuml.jar"))))
(defvaralias 'org-plantuml-jar-path 'plantuml-jar-path)
:config
(org-babel-do-load-languages
'org-babel-load-languages
'((plantuml . t))))
(use-package ox-rst
:after org)
(use-package orgit
:after magit org)
(use-package orgit-forge
:after magit org orgit)
;; Git related things
(use-package magit
:custom
(magit-auto-revert-mode nil)
(magit-last-seen-setup-instructions "1.4.0")
:bind
(:map ctl-x-map
("g" . magit-status))
:hook
(git-commit-mode . turn-on-flyspell))
(use-package forge)
;; In graphical mode we use ~git-gutter-finge~, ~git-gutter~ otherwise (as we have no fringe in that case).
(let ((gitgutter-package
(if (display-graphic-p)
"git-gutter-fringe"
"git-gutter")))
(eval `(use-package ,gitgutter-package
:demand
:config
(global-git-gutter-mode t)
:bind
(:map gpolonkai/pers-map
("gg" . git-gutter:update-all-windows)
("gn" . git-gutter:next-hunk)
("gp" . git-gutter:previous-hunk)))))
(use-package git-messenger
:bind
(:map gpolonkai/pers-map
("gm" . git-messenger:popup-message)))
(use-package git-timemachine
:bind
(([f6] . git-timemachine-toggle)))
;; Completion related external packages
(use-package vertico
:init
(vertico-mode))
(use-package consult
:bind (:map global-map
([remap Info-search] . consult-info)
("M-y" . consult-yank-pop)
:map ctl-x-map
("M-:" . consult-complex-command)
("4 b" . consult-buffer-other-window)
("5 b" . consult-buffer-other-frame)
("b" . consult-buffer)
("r b" . consult-bookmark)
("x b" . consult-project-buffer)
:map goto-map
("e" . consult-compile-error)
("F" . consult-flymake)
("g" . consult-goto-line)
("M-g" . consult-goto-line)
("i" . consult-imenu)
("I" . consult-imenu-multi)
("k" . consult-global-mark)
("m" . consult-mark)
("o" . consult-org-heading)
:map search-map
("d" . consult-find)
("D" . consult-locate)
("e" . consult-isearch-history)
("k" . consult-keep-lines)
("l" . consult-line)
("L" . consult-line-multi)
:map isearch-mode-map
("M-e" . consult-isearch-history)
:map minibuffer-local-map
("M-s" . consult-history)
("M-r" . consult-history))
:hook
(completion-list-mode . consult-preview-at-point-mode)
:custom
(consult-narrow-key "<")
(consult-project-function (lambda (_) (projectile-project-root)))
(register-preview-delay 0.5)
(xref-show-xrefs-function #'consult-xref)
(xref-show-definitions-function #'consult-xref)
:init
(setq register-preview-function #'consult-register-format)
(advice-add #'register-preview :override #'consult-register-window)
:config
(defalias 'consult-line-thing-at-point 'consult-line)
(consult-customize
consult-theme :preview-key '(:debounce 0.2 any)
consult-buffer :preview-key "M-."
consult-line-thing-at-point :initial (thing-at-point 'symbol)
consult-ripgrep
consult-git-grep
consult-grep
consult-bookmark
consult-recent-file
consult-xref
consult--source-bookmark
consult--source-file-register
consult--source-recent-file
consult--source-project-recent-file
:preview-key '(:debounce 0.4 any))
(autoload 'projectile-project-root "projectile"))
(use-package orderless
:init
(setq completion-category-defaults nil)
:custom
(completion-styles '(orderless basic))
(completion-category-defaults nil)
(completion-category-overrides '((file (styles basic partial-completion)))))
(use-package marginalia
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
:init
(marginalia-mode))
(use-package embark
:bind (:map global-map
("C-." . embark-act))
:config
(add-to-list 'display-buffer-alist
'("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
nil
(window-parameters (mode-line-format . none)))))
(use-package embark-consult
:hook
(embark-collect-mode . consult-preview-at-point-mode))
(use-package consult-projectile
:demand t
:after (:all projectile consult embark)
:config
(defvar-keymap embark-consult-projectile-project-map
:doc "Keymap to use for the projectile menu"
:parent embark-general-map
"g" #'magit-status)
(add-to-list 'embark-keymap-alist '(consult-projectile-project embark-consult-projectile-project-map))
(autoload 'magit-status "magit")
:bind (:map projectile-command-map
("p" . consult-projectile-switch-project)))
(use-package corfu
:init
(global-corfu-mode)
:custom
(corfu-cycle t)
(corfu-separator ?\s))
(defvar gpolonkai/completion-at-point-map (make-sparse-keymap)
"Map for traspose functions.")
(define-prefix-command 'gpolonkai/completion-at-point-map)
(use-package cape
:bind (:map gpolonkai/completion-at-point-map
("p" . completion-at-point)
:map gpolonkai/pers-map
("p" . gpolonkai/completion-at-point-map))
:init
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-elisp-block)
(add-to-list 'completion-at-point-functions #'cape-elisp-symbol))
;; Project management
(use-package projectile
:demand t
:delight '(:eval (concat " [" projectile-project-name "]"))
:config
(projectile-mode t)
:bind
(:map mode-specific-map
("p" . projectile-command-map)))
(use-package projectile-speedbar
:after (:all projectile sr-speedbar)
:bind
(:map projectile-mode-map
("C-c p B" . projectile-speedbar-toggle)))
(use-package org-projectile
:after (:all projectile org)
:bind
(:map projectile-command-map
("n" . org-projectile-project-todo-completing-read))
:custom
(org-projectile-projects-file (expand-file-name "projects.org" org-directory))
:config
(push (org-projectile-project-todo-entry) org-capture-templates))
;; Dired extras
(use-package dired-collapse)
(use-package dired-du)
(use-package dired-git-info
:bind
(:map dired-mode-map
(")" . dired-git-info-mode)))
(use-package dired-hide-dotfiles
:bind
(:map dired-mode-map
("." . dired-hide-dotfiles-mode)))
(use-package dired-rainbow
:config
(dired-rainbow-define-chmod executable-unix "Green" "-.*x.*"))
(use-package dired-k
:bind
(:map dired-mode-map
("K" . dired-k)))
;; Mailing
;; Due to my programming (and maybe a bit of OCD) needs, i set trailing
;; whitespace to have a red background so it stands out *a lot*. However, many
;; emails contain a lot of trailing whitespace which makes them really hard to
;; read in Emacs.
;;
;; The below snippet creates a trailing whitespace face specific to mu4e view
;; buffers. The accompanying function will be added to ~mu4e-view-mode-hook~.
(copy-face 'trailing-whitespace 'trailing-whitespace-mu4e)
(set-face-attribute 'trailing-whitespace-mu4e nil :background 'unspecified)
(defun gpolonkai/mu4e-trailing-whitespace-fix ()
"Change `trailing-whitespace' face for mu4e buffers."
(set (make-local-variable 'face-remapping-alist)
'((trailing-whitespace trailing-whitespace-mu4e))))
(use-package mu4e
:after vertico
:ensure nil
:config
(require 'org-mu4e)
(setq mu4e-contexts
`( ,(make-mu4e-context
:name "polonkai.eu"
:enter-func (lambda () (mu4e-message "Entering polonkai.eu Context"))
:leave-func (lambda () (mu4e-message "Leaving polonkai.eu Context"))
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches
msg
:to "gergely@polonkai.eu")))
:vars '((user-mail-address . "gergely@polonkai.eu")
(mu4e-sent-folder . "/Polonkai/[Gmail].Sent Mail")
(mu4e-drafts-folder . "/Polonkai/[Gmail].Drafts")
(mu4e-trash-folder . "/Polonkai/[Gmail].Bin")
(mu4e-refile-folder . "/Polonkai/[Gmail].Drafts")
(message-sendmail-extra-arguments . ("--account=polonkai"))))
,(make-mu4e-context
:name "Benchmark.games"
:enter-func (lambda () (mu4e-message "Entering Benchmark.games Context"))
:leave-func (lambda () (mu4e-message "Leaving Benchmark.games Context"))
:match-func (lambda (msg)
(when msg
(or
(mu4e-message-contact-field-matches
msg
:to "gergo@gt2.io")
(mu4e-message-contact-field-matches
msg
:to "gergo@benchmarked.games")
(mu4e-message-contact-field-matches
msg
:to "gergo@benchmark.games"))))
:vars '((user-mail-address . "gergely@benchmark.games")
(mu4e-sent-folder . "/GT2/[Gmail].Sent Mail")
(mu4e-drafts-folder . "/GT2/[Gmail].Drafts")
(mu4e-trash-folder . "/GT2/[Gmail].Trash")
(mu4e-refile-folder . "/GT2/[Gmail].Drafts")
(message-sendmail-extra-arguments . ("--account=gt2"))))
,(make-mu4e-context
:name "Private"
:enter-func (lambda () (mu4e-message "Entering Private Context"))
:leave-func (lambda () (mu4e-message "Leaving Private Context"))
:match-func (lambda (msg)
(when msg
(string-match-p "^/Private" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "me@gergely.polonkai.eu")
(mu4e-sent-folder . "/Private/Sent")
(mu4e-drafts-folder . "/Private/Drafts")
(mu4e-trash-folder . "/Private/Trash")
(mu4e-refile-folder . "/Private/Drafts")
(message-sendmail-extra-arguments . ("--account=private" "--read-envelope-from")))))
org-mu4e-link-query-in-headers-mode nil)
:custom
(mu4e-context-policy 'pick-first)
(mu4e-confirm-quit nil)
(mail-user-agent 'sendmail-user-agent)
:hook
(mu4e-view-mode . gpolonkai/mu4e-trailing-whitespace-fix)
:bind
(:map gpolonkai/pers-map
("m m" . mu4e)
("m i" . mu4e~headers-jump-to-maildir)
("m c" . mu4e-compose-new)
("m s" . mu4e-headers-search)))
(use-package sendmail
:custom
(sendmail-program "/usr/bin/msmtp")
(message-sendmail-f-is-evil t)
(message-sendmail-extra-arguments '("--read-envelope-from"))
(send-mail-function 'sendmail-send-it)
(message-send-mail-function 'message-send-mail-with-sendmail))
(use-package org-msg
:after mu4e
:defer t
:config
(org-msg-mode)
:custom
(org-msg-supported-mua '((sendmail-user-agent . "mu4e")))
(org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil")
(org-msg-startup "hidestars indent inlineimages")
(org-msg-greeting-fmt "\nHello,\n\n")
(org-msg-greeting-fmt-mailto nil)
(org-msg-signature "\n\nBest,\n\n,#+begin_signature\n-- *Gergely Polonkai* \\\\\n,#+end_signature"))
;; External packages to boost coding productivity
(use-package electric-operator
:config
;; Apply electric-operator-mode to vala-mode, too
(apply #'electric-operator-add-rules-for-mode 'vala-mode
(electric-operator-get-rules-for-mode 'prog-mode))
:hook
(c-mode-common . electric-operator-mode)
(python-mode . electric-operator-mode))
(use-package rainbow-delimiters
:hook
(prog-mode . rainbow-delimiters-mode))
(use-package rainbow-identifiers)
(use-package auto-highlight-symbol
:config
(global-auto-highlight-symbol-mode t))
(use-package glasses
:delight " 👓"
:hook
(prog-mode . glasses-mode))
(use-package hl-todo
:hook
(prog-mode . hl-todo-mode))
(defvar gpolonkai/bug-reference-url-bug-string "issues"
"String to insert in a `bug-reference-url-format' for bug references.")
(put 'gpolonkai/bug-reference-url-bug-string 'safe-local-variable 'stringp)
(defvar gpolonkai/bug-reference-url-patch-string "merge_requests"
"String to insert in a `bug-reference-url-format' for patch references.")
(defvar-local bug-reference-host "gitlab.com"
"The hostname to use in `bug-reference-url-format'.")
(defvar-local bug-reference-group "gamesystems"
"The group name or username to use in `bug-reference-url-format'.")
(defvar-local bug-reference-repository "game-app"
"The repository name to use in `bug-reference-url-format'.")
(put 'gpolonkai/bug-reference-url-patch-string 'safe-local-variable 'stringp)
(defun gpolonkai/bug-reference-url ()
"Return a GitLab issue or Merge Request URL.
Intended as a value for `bug-referecne-url-format'."
(format "https://%s/%s/%s/%s/%s"
bug-reference-host
bug-reference-group
bug-reference-repository
(if (string-suffix-p "!" (match-string-no-properties 1))
gpolonkai/bug-reference-url-patch-string
gpolonkai/bug-reference-url-bug-string)
(match-string-no-properties 2)))
(use-package bug-reference
:custom
(bug-reference-bug-regexp (rx (group word-boundary
(: (| (: (in ?B ?b) "ug" (? " ") (? ?#))
(: (in ?P ?p) "atch" (? " ") ?#)
(: "RFE" (? " ") ?#)
(: "PR " (+ (any "a-z+-")) "/")
(: "MR" (? " ") (? "!"))))
(group (+ (any "0-9")) (opt (: ?# (+ (any "0-9"))))))))
(bug-reference-url-format #'my-gitlab-url)
:hook
(text-mode . bug-reference-mode)
(prog-mode . bug-reference-prog-mode))
(use-package highlight-indentation
:hook
(python-mode . highlight-indentation-mode))
(use-package flycheck
:config
(global-flycheck-mode)
:custom
(flycheck-python-pylint-executable "python3"))
(use-package flycheck-pkg-config)
;; Utilities
(use-package kubernetes
:commands (kubernetes-overview))
;; Gopher is the next generation text protocol. Despite its age (40-ish, as of
;; writing), it still beats the Web in a lot of aspects.
(use-package elpher
:commands (elpher))
(use-package mastodon
:custom
(mastodon-instance-url "https://social.polonkai.eu/")
(mastodon-active-user "gergely"))
(use-package twtxt
:custom
(twtxt-file (expand-file-name "~/Nextcloud/twtxt.txt"))
(twtxt-following '(("benaiah" "https://benaiah.me/twtxt.txt")
("jomo" "https://gist.githubusercontent.com/jomo/64d6bd1b95ec0a24612b/raw/twtxt.txt")
("quite" "https://lublin.se/twtxt.txt")
("xena" "https://xena.greedo.xeserv.us/files/xena.txt")
("hecanjog" "https://hecanjog.com/twtxt.txt"))))
(use-package paradox
:custom
(paradox-lines-per-entry 2)
(paradox-automatically-star t)
(paradox-github-token (nth 1 (auth-source-user-and-password "api.github.com" "gergelypolonkai^paradox")))
:bind
(:map gpolonkai/pers-map
("C-p" . paradox-list-packages)))
(use-package tidal
:mode "\\.tidal\\'")
;; Programming-language specific packages
;; Python
(add-hook 'python-mode-hook
(lambda ()
(add-to-list 'prettify-symbols-alist
'("not in" . ?∉))
(add-to-list 'prettify-symbols-alist
'("in" . ?∈))
(add-to-list 'prettify-symbols-alist
'("def" . ))
(add-to-list 'prettify-symbols-alist
'("is not" . ?≭))
(add-to-list 'prettify-symbols-alist
'("is" . ?≍))
))
(use-package poetry
:config
(poetry-tracking-mode)
(remove-hook 'post-command-hook 'poetry-track-virtualenv)
:hook
(poetry-tracking-mode . (lambda () (remove-hook 'post-command-hook 'poetry-track-virtualenv)))
(python-mode . poetry-track-virtualenv)
(projectile-after-switch-project-hook . poetry-track-virtualenv)
:bind (:map gpolonkai/pers-map
("pp" . poetry)))
(defconst my-c-style
'((c-tab-always-indent . t)
(c-comment-only-line-offset . 4)
(c-hanging-braces-alist . ((substatement-open after)
(brace-list-open)))
(c-hanging-colons-alist . ((member-init-intro before)
(inher-intro)
(case-label after)
(label after)
(access-label after)))
(c-cleanup-list . (scope-operator
empty-defun-braces
defun-close-semi))
(c-offsets-alist . ((arglist-close . +)
(arglist-intro . +)
(substatement-open . 0)
(case-label . 4)
(block-open . 0)
(knr-argdecl-intro . -)
(comment-intro . 0)
(member-init-intro . ++)))
(c-echo-syntactic-information-p . t))
"My C Programming Style.")
(c-add-style "PERSONAL" my-c-style)
(defvaralias 'c-basic-offset 'tab-width)
(defvaralias 'cperl-indent-level 'tab-width)
(add-hook 'c-mode-common-hook
(lambda ()
(local-set-key (kbd "C-c o") 'ff-find-other-file)
(c-set-style "PERSONAL")
(customize-set-variable 'c-basic-offset 4)
(customize-set-variable 'tab-width 4)
(customize-set-variable 'indent-tabs-mode nil)
(c-toggle-auto-newline 1)))
(add-hook 'c-initialization-hook
(lambda ()
(define-key c-mode-base-map (kbd "C-m") 'c-context-line-break)))
(use-package web-mode
:mode "\\.html?\\'"
:custom
(web-mode-enable-auto-indentation nil)
(web-mode-enable-engine-detection t))
(use-package emmet-mode
:custom
(emmet-self-closing-tag-style "")
:hook
(web-mode . emmet-mode)
(css-mode . emmet-mode))
(use-package enlive)
(use-package json-mode
:mode "\\.json\\'")
(use-package yaml-mode
:mode (("\\.yml\\'" . yaml-mode)
("\\.yaml\\'" . yaml-mode))
:init
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))
(use-package dockerfile-mode)
(use-package systemd
:mode ("\\.\\(?:automount\\|mount\\|path\\|s\\(?:ervice\\|lice\\|ocket\\)\\|t\\(?:arget\\|imer\\)\\|wants\\)\\'" . systemd-mode))
(use-package terraform-mode
:mode "\\.tf\\'")
(use-package bats-mode
:mode "\\.bats\\'")
(use-package fish-mode
:hook
(fish-mode . (lambda () (add-hook 'before-save-hook 'fish_indent-before-save)))
:mode "\\.fish\\'")
(use-package gitlab-ci-mode
:mode "\\.gitlab-ci.yml\\'")
(use-package gitlab-ci-mode-flycheck
:after flycheck gitlab-ci-mode
:init
(gitlab-ci-mode-flycheck-enable))
(use-package csv-mode
:mode "\\.csv\\'")
(use-package rust-mode
:mode "\\.rs\\'")
(use-package cargo)
(use-package cargo-mode
:mode "Cargo\\.toml\\'")
(use-package arduino-mode
:mode "\\.ino\\'")
(use-package nginx-mode)
(use-package js2-mode
:pin melpa-stable
:mode "\\.js\\'")
(use-package typescript-mode
:mode "\\.ts\\'")
(use-package less-css-mode
:mode "\\.less\\'")
(use-package sass-mode
:mode "\\.sass\\'")
(use-package vue-html-mode
:mode "\\.vue\\'")
(use-package jinja2-mode
:mode "\\.j2\\'")
(use-package markdown-mode
:mode (("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode)))
(use-package vala-mode
:mode "\\.vala\\'")
(use-package po-mode
:ensure nil
:mode "\\.po\\'")
(use-package meson-mode
:mode "\\.meson\\'")
(defun gpolonkai/cond-enable-ggtags-mode ()
"Conditionally enable `ggtags-mode'."
(when (derived-mode-p 'c-mode 'c++-mode 'java-mode)
(ggtags-mode t)))
(use-package ggtags
:hook
(c-mode-common . gpolonkai/cond-enable-ggtags-mode))
(use-package yuck-mode
:mode "\\.yuck\\'")
(use-package ebnf-mode
:commands (ebnf-mode))
(use-package ansible-vault)
;; Key bindings
(defvar gpolonkai/transpose-map (make-sparse-keymap)
"Map for transpose commands.")
(define-prefix-command 'gpolonkai/transpose-map)
(defvar gpolonkai/org-clock-map (make-sparse-keymap)
"Map for Org clocking commands.")
(define-prefix-command 'gpolonkai/org-clock-map)
(bind-keys
:map global-map
("<C-return>" . wted/open-line-below)
("<C-S-return>" . wted/open-line-above)
("C-~" . gpolonkai/toggle-char-case)
("C-a" . gpolonkai/move-to-beginning-of-line)
("C-e" . gpolonkai/move-to-end-of-line)
("M-F" . gpolonkai/beginning-of-next-word)
("C-h C-l" . find-library)
("C-h C-f" . find-function)
("C-h C-k" . find-function-on-key)
("C-h C-v" . find-variable)
("M-q" . sachachua/fill-or-unfill-paragraph)
("C-r" . isearch-backward-regexp)
("C-M-r" . isearch-backward)
("C-s" . isearch-forward-regexp)
("C-M-s" . isearch-forward)
("M-t" . gpolonkai/transpose-map)
("C-z" . nil)
:map mode-specific-map
;; TODO this should be doable from consult-projectiles config
("p p" . consult-projectile-switch-project)
("r" . gpolonkai/round-number-at-point-to-decimals)
:map gpolonkai/transpose-map
("c" . transpose-chars)
("e" . transpose-sexps)
("l" . transpose-lines)
("p" . transpose-paragraphs)
("s" . transpose-sentences)
("w" . transpose-words)
("W" . gpolonkai/transpose-windows)
:map gpolonkai/org-clock-map
("g" . org-clock-goto)
("i" . org-clock-in)
("I" . org-clock-in-last)
("o" . org-clock-out)
:map gpolonkai/pers-map
("c" . gpolonkai/org-clock-map)
("C-c" . calc)
("M-C" . clean-buffer-list)
("M-o" . mbork/insert-current-file-name-at-point)
("u" . browse-url-at-point)
:map ctl-x-map
("|" . gpolonkai/toggle-window-split)
("C-b" . bury-buffer)
("C-d" . wted/delete-current-buffer-file)
("k" . kill-current-buffer)
("C-r" . wted/rename-current-buffer-file)
("C-y" . duplicate-line)
:map isearch-mode-map
("<C-return>" . ep/isearch-exit-other-end)
("<S-return>" . e-se/isearch-exit-mark-match)
:map goto-map
("SPC" . gpolonkai/goto-next-char))
;; And finally, start the Emacs server
;;
;; Sometimes i start an ~emacsclient~ process, like for editing a commit message
;; or something similar. As my startup time is pretty long, waiting for
;; everything to complete is undesirable.
(require 'server)
(unless (server-running-p)
(server-start))
;;; init.el ends here