my-emacs-d/configuration.org

3272 lines
92 KiB
Org Mode
Raw Permalink 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.

* Pre-init
** Add my own, version controlled ~lisp~ directory to ~load-path~
#+begin_src emacs-lisp
(add-to-list 'load-path (expand-file-name
(convert-standard-filename "lisp/")
user-emacs-directory))
#+end_src
** Do the same with the local ~site-lisp~ if its there
#+begin_src emacs-lisp
(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))))))
#+end_src
** Load ~xdg-paths~
I prefer using freedesktops desktop specification everywhere.
#+begin_src emacs-lisp
(load "xdg-paths")
#+end_src
* Set up the package manager
Im a ~package.el~ user. Dont package-shame me!
** Set up the package archives
#+begin_src emacs-lisp
(require 'package)
(add-to-list 'package-archives
'("gnu" . "https://elpa.gnu.org/packages/"))
(add-to-list 'package-archives
'("melpa-stable" . "https://stable.melpa.org/packages/") t)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives
'("org" . "https://orgmode.org/elpa/") t)
(package-initialize)
#+end_src
** Set up ~use-package~
It is built-in since 29.1, and lets hope I never get back using older versions, but lets stay on the safe side for now.
#+begin_src emacs-lisp
(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)
#+end_src
*** Install Quelpa so we can add packages not present in MELPA yet
#+begin_src emacs-lisp
(unless (package-installed-p 'quelpa)
(with-temp-buffer
(url-insert-file-contents "https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
(eval-buffer)
(quelpa-self-upgrade)))
#+end_src
*** Finally, combine the powers of use-package and quelpa
#+begin_src emacs-lisp
(quelpa
'(quelpa-use-package
:fetcher git
:url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package)
#+end_src
* Custom functions and commands
This is a collection of functions and commands i wrote or stole from all around the internet.
** Utilities
*** Make a backup filename under ~user-emacs-cache-directory~
Taken from [[http://ergoemacs.org/emacs/emacs_set_backup_into_a_directory.html][Xahs site]].
#+begin_src emacs-lisp
(defun xah/backup-file-name (fpath)
"Return a new file path for FPATH under `user-emacs-cache-directory'"
(let* ((backup-root-dir (expand-file-name "backup" user-emacs-cache-directory))
(file-path (replace-regexp-in-string "[A-Za-z]:" "" fpath))
(backup-file-path (replace-regexp-in-string "//" "/" (concat backup-root-dir file-path "~"))))
(make-directory (file-name-directory backup-file-path) (file-name-directory backup-file-path))
backup-file-path))
#+end_src
*** Check if were running under Termux
We need to do things differently, if so.
Theres probably a better way, though, other than checking the path of our home directory.
#+begin_src emacs-lisp
(defun gpolonkai/termux-p ()
"Check if Emacs is running under Termux."
(string-match-p
(regexp-quote "/com.termux/")
(expand-file-name "~")))
#+end_src
*** Find the first number on line
#+begin_src emacs-lisp
(defun gpolonkai/find-number-on-line ()
"Find the first number on the current line."
(save-excursion
(without-restriction
(goto-char (pos-bol))
(when (re-search-forward "[0-9]+" (pos-eol) t)
(number-at-point)))))
#+end_src
*** Round number at point to the given decimals
#+begin_src emacs-lisp
(defun gpolonkai/round-number-at-point-to-decimals (decimal-count)
(interactive "NDecimal count: ")
(let ((mult (expt 10 decimal-count)))
(replace-match (number-to-string
(/
(fround
(*
mult
(number-at-point)))
mult)))))
#+end_src
*** Leave ~isearch~ at the other end of the matched string
From [[http://endlessparentheses.com/leave-the-cursor-at-start-of-match-after-isearch.html][Endless Parentheses]].
#+begin_src emacs-lisp
(defun ep/isearch-exit-other-end ()
"Exit isearch, at the opposite end of the string."
(interactive)
(isearch-exit)
(goto-char isearch-other-end))
#+end_src
*** Mark the current match after leaving ~isearch~
From [[http://emacs.stackexchange.com/a/31321/507][Emacs SE]].
#+begin_src emacs-lisp
(defun e-se/isearch-exit-mark-match ()
"Exit isearch and mark the current match."
(interactive)
(isearch-exit)
(push-mark isearch-other-end)
(activate-mark))
#+end_src
*** Copy the prototype of the current C function
#+begin_src emacs-lisp
(defun gpolonkai/copy-func-prototype ()
"Copy the current function's prototype to the kill ring."
(interactive)
(save-excursion
(beginning-of-defun)
(let ((protocopy-begin (point)))
(forward-list)
(let ((protocopy-end (point)))
(kill-ring-save protocopy-begin protocopy-end)))))
#+end_src
** Window manipulation
*** Bury a window
#+begin_src emacs-lisp
(defun gpolonkai/bury-window (window)
"Quit WINDOW without killing it."
(interactive)
(quit-window nil window))
#+end_src
*** Scroll a specific window up or down
#+begin_src emacs-lisp
(defun gpolonkai/scroll-window-up (window)
"Scroll WINDOW up as `scroll-up-command' would."
(interactive)
(save-selected-window
(select-window window)
(scroll-up)))
(defun gpolonkai/scroll-window-down (window)
"Scroll WINDOW down as `scroll-down-command' would."
(interactive)
(save-selected-window
(select-window window)
(scroll-down)))
#+end_src
*** Transpose windows
#+begin_src emacs-lisp
(defun gpolonkai/transpose-windows (arg)
"Transpose the buffers shown in two windows."
(interactive "p")
(let ((selector (if (>= arg 0) 'next-window 'previous-window)))
(while (/= arg 0)
(let ((this-win (window-buffer))
(next-win (window-buffer (funcall selector))))
(set-window-buffer (selected-window) next-win)
(set-window-buffer (funcall selector) this-win)
(select-window (funcall selector)))
(setq arg (if (plusp arg) (1- arg) (1+ arg))))))
#+end_src
*** Toggle window split between horizontal and vertical
#+begin_src emacs-lisp
(defun gpolonkai/toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))
(error "This works only for two windows!")))
#+end_src
** Org-mode related
*** Wrapper around ~org-agenda~ to open my own custom list
#+begin_src emacs-lisp
(defun gpolonkai/org-agenda-list (&optional arg)
(interactive "P")
(org-agenda arg "c"))
#+end_src
*** Insert the current timestamp
#+begin_src emacs-lisp
(defun gpolonkai/org-insert-current-timestamp (&optional arg)
"Insert the current timestamp"
(interactive "P")
(org-time-stamp '(16) arg))
#+end_src
*** Insert two spaces after certain marks
#+begin_src emacs-lisp
(defcustom gpolonkai/org-dbl-space-punct-marks (list ?. ?! ?? ?…)
"Punctuation marks after which the space key should insert two space characters."
:type '(set character))
(defun gpolonkai/org-space-key (&optional arg)
"Insert two spaces after certain markers.
ARG will be passed down verbatim to `self-insert-command'."
(interactive "p")
(when (and
(not (org-in-block-p '("src")))
(looking-back (rx-to-string `(any,@ gpolonkai/org-dbl-space-punct-marks)) nil))
(call-interactively 'self-insert-command arg))
(call-interactively 'self-insert-command arg))
#+end_src
*** Filter out tasks from the Org agenda if they have a specific priority
The whole idea comes from [[https://blog.aaronbieber.com/2016/09/24/an-agenda-for-life-with-org-mode.html][here]] which i use almost verbatim, hence the ~air-~ prefix.
#+begin_src emacs-lisp
(defun air-org-skip-subtree-if-priority (priority)
"Skip an agenda subtree if it has a priority of PRIORITY.
PRIORITY may be one of the characters ?A, ?B, or ?C."
(let ((subtree-end (save-excursion (org-end-of-subtree t)))
(pri-value (* 1000 (- org-lowest-priority priority)))
(pri-current (org-get-priority (thing-at-point 'line t))))
(if (= pri-value pri-current)
subtree-end
nil)))
#+end_src
*** Filter out habits from the Org agenda
#+begin_src emacs-lisp
(defun air-org-skip-subtree-if-habit ()
"Skip an agenda entry if it has a STYLE property equal to \"habit\"."
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(if (string= (org-entry-get nil "STYLE") "habit")
subtree-end
nil)))
#+end_src
*** Filter out entries from the Org agenda with a specific state
#+begin_src emacs-lisp
(defun gpolonkai/org-skip-subtree-if-state (state)
"Skip an agenda entry if its state is STATE."
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(if (string= (org-get-todo-state) state)
subtree-end
nil)))
#+end_src
*** Insert a heading with CREATED set to the current time
This emulates how Orgzly work with my current settings.
#+begin_src emacs-lisp
(defun gpolonkai/org-insert-heading-created (&optional arg)
(interactive "p")
(let* ((org-insert-heading-respect-content t)
(format-string (concat "[" (substring (cdr org-time-stamp-formats) 1 -1) "]"))
(timestamp (format-time-string format-string (current-time))))
(if (> arg 1)
(org-insert-subheading '(4))
(org-insert-heading))
(org-set-property "CREATED" timestamp)))
#+end_src
*** Unfold everything during ~ediff~ sessions
EDiff and Org-mode files dont play nice together…
From [[https://list.orgmode.org/loom.20130801T011342-572@post.gmane.org/][the org-mode mailing list]].
#+begin_src emacs-lisp
(defun f-ediff-org-showhide (buf command &rest cmdargs)
"If buffer BUF exists and in `org-mode', execute COMMAND with CMDARGS."
(when buf
(when (eq (buffer-local-value 'major-mode (get-buffer buf)) 'org-mode)
(save-excursion
(set-buffer buf)
(apply command cmdargs)))))
(defun f-ediff-org-unfold-tree-element ()
"Unfold tree at diff location."
(f-ediff-org-showhide ediff-buffer-A 'org-reveal)
(f-ediff-org-showhide ediff-buffer-B 'org-reveal)
(f-ediff-org-showhide ediff-buffer-C 'org-reveal))
(defun f-ediff-org-fold-tree ()
"Fold tree back to top level."
(f-ediff-org-showhide ediff-buffer-A 'hide-sublevels 1)
(f-ediff-org-showhide ediff-buffer-B 'hide-sublevels 1)
(f-ediff-org-showhide ediff-buffer-C 'hide-sublevels 1))
#+end_src
*** Add code references to Org documents
From the [[http://www.howardism.org/Technical/Emacs/capturing-content.html][Howardism blog]].
#+begin_src emacs-lisp
(defun ha/org-capture-fileref-snippet (f type headers func-name)
(let* ((code-snippet
(buffer-substring-no-properties (mark) (- (point) 1)))
(file-name (buffer-file-name))
(file-base (file-name-nondirectory file-name))
(line-number (line-number-at-pos (region-beginning)))
(initial-txt (if (null func-name)
(format "From [[file:%s::%s][%s]]:"
file-name line-number file-base)
(format "From ~%s~ (in [[file:%s::%s][%s]]):"
func-name file-name line-number
file-base))))
(format "
%s
,#+begin_%s %s
%s
,#+end_%s" initial-txt type headers code-snippet type)))
(defun ha/org-capture-clip-snippet (f)
"Given a file, F, this captures the currently selected text
within an Org EXAMPLE block and a backlink to the file."
(with-current-buffer (find-buffer-visiting f)
(ha/org-capture-fileref-snippet f "example" "" nil)))
(defun ha/org-capture-code-snippet (f)
"Given a file, F, this captures the currently selected text
within an Org SRC block with a language based on the current mode
and a backlink to the function and the file."
(with-current-buffer (find-buffer-visiting f)
(let ((org-src-mode (replace-regexp-in-string "-mode" "" (format "%s" major-mode)))
(func-name (which-function)))
(ha/org-capture-fileref-snippet f "src" org-src-mode func-name))))
#+end_src
** Text manipulation
*** Fill or unfill a paragraph
From Sacha Chuas [[http://pages.sachachua.com/.emacs.d/Sacha.html][blog]].
#+begin_src emacs-lisp
(defun sachachua/fill-or-unfill-paragraph (&optional unfill region)
"Fill (or unfill, if UNFILL is non-nil) paragraph (or REGION)."
(interactive (progn
(barf-if-buffer-read-only)
(list (if current-prefix-arg 'unfill) t)))
(let ((fill-column (if unfill (point-max) fill-column)))
(fill-paragraph nil region)))
#+end_src
*** Toggle case of character at point
Based on [[http://ergoemacs.org/emacs/modernization_upcase-word.html][Xahs toggle letter case defun version 2015-12-22]]
#+begin_src emacs-lisp
(defun gpolonkai/toggle-char-case (arg-move-point)
"Toggle the case of the char after point.
If prefix argument ARG-MOVE-POINT is non-nil, move point after the char."
(interactive "P")
(let ((case-fold-search nil))
(cond
((looking-at "[[:lower:]]") (upcase-region (point) (1+ (point))))
((looking-at "[[:upper:]]") (downcase-region (point) (1+ (point)))))
(cond
(arg-move-point (right-char)))))
#+end_src
*** Sort lines by the first number on it
#+begin_src emacs-lisp
(defun gpolonkai/numeric-sort-lines (reverse beg end)
"Sort lines in region by version.
Interactively, REVERSE is the prefix argument, and BEG and END are the region.
Non-nil REVERSE means to sort in reverse order."
(interactive "P\nr")
(save-excursion
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
(let ((indibit-field-text-motion t))
(sort-subr reverse 'forward-line 'end-of-line #'gpolonkai/find-number-on-line)))))
#+end_src
*** Open a new line above
#+begin_src emacs-lisp
(defun wted/open-line-above ()
"Open a new line above point."
(interactive)
(beginning-of-line)
(newline)
(forward-line -1)
(let ((tab-always-indent t)
(c-tab-always-indent t))
(indent-for-tab-command)))
#+end_src
*** Open a new line below
Copied from [[http://whattheemacsd.com/editing-defuns.el-01.html][whattheemacsd.com]].
#+begin_src emacs-lisp
(defun wted/open-line-below ()
"Open a new line below point."
(interactive)
(end-of-line)
(newline)
(let ((tab-always-indent t)
(c-tab-always-indent t))
(indent-for-tab-command)))
#+end_src
*** Insert the current files name at point
From [[http://mbork.pl/2019-02-17_Inserting_the_current_file_name_at_point][Marcin Borkowski]].
#+begin_src emacs-lisp
(defun mbork/insert-current-file-name-at-point (&optional full-path)
"Insert the current filename at point.
With prefix argument, use full path."
(interactive "P")
(let* ((buffer
(if (minibufferp)
(window-buffer
(minibuffer-selected-window))
(current-buffer)))
(filename (buffer-file-name buffer)))
(if filename
(insert (if full-path filename (file-name-nondirectory filename)))
(error (format "Buffer %s is not visiting a file" (buffer-name buffer))))))
#+end_src
*** Swap occurences of strings
From [[http://emacs.stackexchange.com/a/27170/507][Emacs SE]].
#+begin_src emacs-lisp
(defun e-se/query-swap-strings (from-string
to-string
&optional delimited start end)
"Swap occurrences of FROM-STRING and TO-STRING.
DELIMITED, START, and END are passed down verbatim to `perform-replace'."
(interactive
(let ((common
(query-replace-read-args
(concat "Query swap"
(if current-prefix-arg
(if (eq current-prefix-arg '-) " backward" " word")
"")
(if (use-region-p) " in region" ""))
nil)))
(list (nth 0 common) (nth 1 common) (nth 2 common)
(if (use-region-p) (region-beginning))
(if (use-region-p) (region-end)))))
(perform-replace
(concat "\\(" (regexp-quote from-string) "\\)\\|" (regexp-quote to-string))
`(replace-eval-replacement replace-quote
(if (match-string 1)
,to-string
,from-string))
t t delimited nil nil start end))
#+end_src
** Navigation
*** Move to different beginnings/ends of the current line
Inspired by Bozhidar Batsov's [[http://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/][solution]].
#+begin_src emacs-lisp
(defun gpolonkai/move-to-beginning-of-line ()
"Move to different beginnings of the line.
These are, in order:
- beginning of the visual line if `visual-line-mode' is active,
- the first non-whitespace (indentation),
- the actual beginning of the line.
This function will jump between the first character and the
indentation if used multiple times."
(interactive)
(let ((last-pos (point)))
(when visual-line-mode
(beginning-of-visual-line))
(when (= (point) last-pos)
(back-to-indentation))
(when (= (point) last-pos)
(beginning-of-line))
(when (and (eq major-mode 'org-mode)
(= (point) last-pos))
(org-beginning-of-line))
(when (= (point) last-pos)
(back-to-indentation))))
(defun gpolonkai/move-to-end-of-line ()
"Move to the end of the line.
If `visual-line-mode' is active, jump to the end of the visual
line first. Then jump to the actual end of the line."
(interactive)
(let ((last-pos (point)))
(when visual-line-mode
(end-of-visual-line))
(when (= (point) last-pos)
(end-of-line))
(when (and (eq major-mode 'org-mode)
(= (point) last-pos))
(org-end-of-line))))
#+end_src
*** Move to the next occurence of a character within the same line
#+begin_src emacs-lisp
(defun gpolonkai/goto-next-char (chr)
(interactive "c")
(when (search-forward (char-to-string chr) (pos-eol) t)
(backward-char)))
#+end_src
*** Move to the beginning of the next word
#+begin_src emacs-lisp
(defun gpolonkai/beginning-of-next-word ()
(interactive)
(let ((current-point (point)))
(forward-word 1)
(backward-word 1)
(when (<= (point) current-point)
(forward-word 2)
(backward-word 1))))
#+end_src
** File manipulation
*** Rename the current file
From [[http://whattheemacsd.com/file-defuns.el-01.html][whattheemacsd.org]].
#+begin_src emacs-lisp
(defun wted/rename-current-buffer-file ()
"Renames current buffer and file it is visiting."
(interactive)
(let ((name (buffer-name))
(filename (buffer-file-name)))
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(let ((new-name (read-file-name "New name: " filename)))
(if (get-buffer new-name)
(error "A buffer named '%s' already exists!" new-name)
(rename-file filename new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
; TODO: this is suspicious for me…
(set-buffer-modified-p nil)
(message "File '%s' successfully renamed to '%s'"
name (file-name-nondirectory new-name)))))))
#+end_src
*** Delete the current file
From [[http://whattheemacsd.com/file-defuns.el-02.html][whattheemacsd.org]].
#+begin_src emacs-lisp
(defun wted/delete-current-buffer-file ()
"Remove file connected to current buffer and kill the buffer."
(interactive)
(let ((filename (buffer-file-name))
(name (buffer-name))
(buffer (current-buffer)))
(if (not (and filename (file-exists-p filename)))
(kill-buffer buffer)
(when (yes-or-no-p "Are you sure you want to remove this file? ")
(delete-file filename)
(kill-buffer buffer)
(message "File '%s' successfully removed" filename)))))
#+end_src
* Set up the very basics
Now that we have package management configured we can set up defaults more easily. This includes every builtin packages, font faces, and the like.
#+begin_src emacs-lisp
(use-package emacs
:ensure nil
:init
;; Required for Consult/Vertico
(defun crm-indicator (args)
(cons (format "[CRM%s] %s"
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
(setq 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))
#+end_src
** 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.
#+begin_src emacs-lisp
(setq org-directory (expand-file-name "NextCloud/orgmode" user-documents-directory))
#+end_src
** Set up my personal keymap
I set it up at the beginning so i can use it with ~use-package~ invocations from early on
This might (should?) become a [[*general][general]] map later.
#+begin_src emacs-lisp
(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)
#+end_src
** Set up some faces
#+begin_src emacs-lisp
(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")))))
#+end_src
*** Fira Code comes with nice ligatures, lets use them!
#+begin_src emacs-lisp
(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))
#+end_src
** Set the default font and configure font resizing
Before this can be used, make sure the [[https://zhm.github.io/symbola/][Symbola]] font is installed.
#+begin_src emacs-lisp
(defun gpolonkai/set-font-size (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)
#+end_src
** Set UTF-8 as the default encoding
Just to make sure, although most Linux DEs do this for me.
#+begin_src emacs-lisp
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
#+end_src
** Default frame settings
#+begin_src emacs-lisp
(use-package frame
:ensure nil
:custom
;; Make that cursor blink!
(blink-cursor-mode t))
#+end_src
** I really dont want to type more than i really must…
#+begin_src emacs-lisp
(defalias 'yes-or-no-p 'y-or-n-p)
#+end_src
** Tweak window chrome, AKA lets set up the UI
Turn off the scroll bar (thats why Nyan-cat is here), the toolbar (I dont really use it), and
the menu bar (I rarely use it, and in those rare occasions I can simply turn it on.)
Also, maximise the frame.
#+begin_src emacs-lisp
(tool-bar-mode 0)
(menu-bar-mode 0)
(when window-system
(scroll-bar-mode -1))
(set-frame-parameter nil 'fullscreen 'maximized)
#+end_src
** Enable all the commands!
These are disabled for a reason, but im a rockstar, so who cares‽
#+begin_src emacs-lisp
(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)
#+end_src
* Global built-in packages
** ~simple~, to always show the current column, and using visual lines in text modes
Column numbers help a lot in debugging, while visual line mode is easier on the eye when writing long text.
#+begin_src emacs-lisp
(use-package simple
:ensure nil
:custom
(column-number-mode t)
:hook
(text-mode . (lambda () (visual-line-mode t))))
#+end_src
** ~prog-mode~
Plus prettify all the symbols!
#+begin_src emacs-lisp
(use-package prog-mode
:ensure nil
:init
(setq prettify-symbols-alist
'(("lambda" . )
("function" . )
("map" . ?↦)
("not" . )
("and" . ?∧)
("or" . ?)))
:config
(global-prettify-symbols-mode t))
#+end_src
** ~thingatpt~
#+begin_src emacs-lisp
(use-package thingatpt
:ensure nil)
#+end_src
** ~nxml~
#+begin_src emacs-lisp
(use-package nxml-mode
:ensure nil
:custom
(nxml-attribute-indent 4)
(nxml-child-indent 4)
(nxml-outline-child-indent 4))
#+end_src
** ~recentf~
#+begin_src emacs-lisp
(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)))
#+end_src
** ~files~
#+begin_src emacs-lisp
(use-package files
:ensure nil
:custom
(make-backup-file-name-function 'xah/backup-file-name))
#+end_src
** ~whitespace~
~whitespace-mode~ is turned on by default, and can be toggled with ~F10~.
#+begin_src emacs-lisp
(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")))))
#+end_src
** ~eshell~
This is a function to delete a character, or close ~eshell~ if theres nothing to delete. Taken
from [[https://ryuslash.org/posts/C-d-to-close-eshell.html][here]].
#+begin_src emacs-lisp
(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))))))
#+end_src
Function to bind it locally to =C-d=.
#+begin_src emacs-lisp
(defun gpolonkai/eshell-set-c-d-locally ()
(local-set-key (kbd "C-d") #'eshell-C-d))
#+end_src
Now set up eshell.
#+begin_src emacs-lisp
(use-package eshell
:bind
(:map gpolonkai/pers-map
("e" . eshell))
:hook
(eshell-mode . gpolonkai/eshell-set-c-d-locally))
#+end_src
** ~calendar~ & Co.
#+begin_src emacs-lisp
(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"))
#+end_src
*** Add Hungarian holidays to the calendar
I know this is the builtin packages section. Sorry for cheating.
#+begin_src emacs-lisp
(use-package hungarian-holidays
:config
(hungarian-holidays-add))
#+end_src
*** Add some other important dates to the calendar
#+begin_src emacs-lisp
(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)
#+end_src
** ~saveplace~
#+begin_src emacs-lisp
(use-package saveplace
:ensure nil
:config
(save-place-mode 1)
:custom
(save-place-file (expand-file-name ".places" user-emacs-directory)))
#+end_src
** ~ediff~
#+begin_src emacs-lisp
(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))
#+end_src
** ~autorevert~
…unless they are modified, of course.
#+begin_src emacs-lisp
(use-package autorevert
:ensure nil
:config
(global-auto-revert-mode 1))
#+end_src
** ~eww~
For in-Emacs browsing needs.
#+begin_src emacs-lisp
(use-package eww
:custom
(eww-search-prefix "https://duckduckgo.com/html/?q="))
#+end_src
** ~electric~, for automatic indentation
#+begin_src emacs-lisp
(use-package electric
:config
;; This seems to be the default, but lets make sure…
(electric-indent-mode 1))
#+end_src
** ~savehist~
#+begin_src emacs-lisp
(use-package savehist
:config
(savehist-mode 1))
#+end_src
** ~webjump~
#+begin_src emacs-lisp
(use-package webjump
:bind
(:map gpolonkai/pers-map
("j" . webjump)))
#+end_src
** ~which-func~
#+begin_src emacs-lisp
(defun gpolonkai/activate-which-func-mode ()
(if (fboundp 'which-function-mode)
(which-function-mode)
(which-func-mode)))
#+end_src
Enable ~which-func-mode~ in every ~prog-mode~ derived mode.
#+begin_src emacs-lisp
(use-package which-func
:config
(setq which-func-unknown "")
:hook
(prog-mode . gpolonkai/activate-which-func-mode))
#+end_src
** ~cookie1~, AKA fortune cookies
“Fortunes” are from the Hungarian version an ancient MS-DOS based program called ~TAGLINE~.
#+begin_src emacs-lisp
(use-package cookie1
:demand t
:custom
(cookie-file (expand-file-name "fortune-cookies.txt" user-emacs-directory))
:bind
(:map gpolonkai/pers-map
("k" . cookie)))
#+end_src
** ~dired~
#+begin_src emacs-lisp
(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)))
#+end_src
** ~goto-addr~, actionable URLs
#+begin_src emacs-lisp
(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))
#+end_src
** Do things at ~midnight~
Since my machine (and thus, my Emacs) is turned on pretty much 24/7, its a pretty good idea.
By default, it closes a bunch of unused buffers. I might add some more things there later.
#+begin_src emacs-lisp
(use-package midnight
:ensure nil
:config
(setq clean-buffer-list-kill-never-buffer-names '("*scratch*"
"*Messages*"
"*dashboard*"))
(midnight-mode t))
#+end_src
** ~display-line-numbers~
I usually dont want to see them, but there are occasions when theyre useful.
#+begin_src emacs-lisp
(use-package display-line-numbers
:bind
(:map gpolonkai/pers-map
("C-n" . display-line-numbers-mode)))
#+end_src
** ~ispell~
#+begin_src emacs-lisp
(use-package ispell
:custom
(ispell-dictionary "en_GB")
(ispell-program-name "/usr/bin/hunspell")
:hook
(mail-send . ispell-message)
(message-send . ispell-message))
#+end_src
** ~speedbar~
This is basically just a dependency for [[*projectile-speedbar][projectile-speedbar]].
#+begin_src emacs-lisp
(use-package speedbar)
#+end_src
** ~browse-url~
This is a Termux-specific override.
#+begin_src emacs-lisp
(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))))))
#+end_src
** ~dabbrev~
I simply remap ~dabbrev-expand~ to a different key and replace the default binding with ~dabbrev-completion~.
#+begin_src emacs-lisp
(use-package dabbrev
:bind (("M-/" . dabbrev-completion)
("C-M-/" . dabbrev-expand)))
#+end_src
* Project management
** ~projectile~
#+begin_src emacs-lisp
(use-package projectile
:demand t
:delight '(:eval (concat " [" projectile-project-name "]"))
:config
(projectile-mode t)
:bind
(:map mode-specific-map
("p" . projectile-command-map)))
#+end_src
** ~projectile-speedbar~
#+begin_src emacs-lisp
(use-package projectile-speedbar
:after (:all projectile sr-speedbar)
:bind
(:map projectile-mode-map
("C-c p B" . projectile-speedbar-toggle)))
#+end_src
** ~org-projectile~ for per-repository ToDo files using Org
#+begin_src emacs-lisp
(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))
#+end_src
* External packages to boost usability
** ~gnu-elpa-keyring-update~, to make sure we always have the latest ELPA GPG keys
#+begin_src emacs-lisp
(use-package gnu-elpa-keyring-update)
#+end_src
** ~auto-package-update~, to automatically upgrade packages every week
#+begin_src emacs-lisp
(use-package auto-package-update
:custom
(auto-package-update-interval 7)
(auto-package-update-delete-old-versions t)
;; Lets do this in after-init-hook, as use-package invocations may modify
;; the list of installed packages
:hook
(after-init . auto-package-update-maybe))
#+end_src
** ~ace-window~, for better window navigation
Besides its standard functionality i also add key bindings for burying or scrolling other windows.
#+begin_src emacs-lisp
(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)))))
#+end_src
** ~doom-themes~
#+begin_src emacs-lisp
(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))
#+end_src
** Nyanyanyanyanya
*** ~nyan-mode~: Nyan-cat style position marker
The package that [[https://gergely.polonkai.eu/blog/2014/9/17/nyanmacs.html][made me]] switch to Emacs.
#+begin_src emacs-lisp
(use-package nyan-mode
:config
(nyan-mode t)
:custom
(nyan-bar-length 20)
(nyan-animate-nyancat t)
(nyan-wavy-trail t))
#+end_src
** ~delight~ to de-light some minor modes
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.
#+begin_src emacs-lisp
(use-package delight)
#+end_src
** ~minions~, to put all minor modes into a menu
#+begin_src emacs-lisp
(use-package minions
:config
(minions-mode 1))
#+end_src
** ~doom-modeline~, because its much, much more beautiful than the vanilla one
#+begin_src emacs-lisp
(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))
#+end_src
** ~beacon~ to find the cursor
Sometimes it disappears, or just too hard to see.
#+begin_src emacs-lisp
(use-package beacon
:demand
:config
(beacon-mode 1)
:bind
(:map gpolonkai/pers-map
("b" . beacon-blink)))
#+end_src
** ~eshell~ extras
*** Display the status of the last command in the fringe of EShell
#+begin_src emacs-lisp
(use-package eshell-fringe-status
:hook
(eshell-mode . eshell-fringe-status-mode))
#+end_src
*** Extras for the EShell prompt
#+begin_src emacs-lisp
(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))
#+end_src
** ~form-feed~, to show form feed characters as a horizontal line
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.
#+begin_src emacs-lisp
(use-package form-feed
:hook
(emacs-lisp-mode . form-feed-mode)
(compilation-mode . form-feed-mode)
(help-mode . form-feed-mode))
#+end_src
** ~hl-line~, to highlight the current line
#+begin_src emacs-lisp
(use-package hl-line
:config
(when window-system
(global-hl-line-mode))
:custom-face
(hl-line ((t (:inherit nil :background "gray25")))))
#+end_src
** ~ace-jump-mode~, to jump to a specific character
#+begin_src emacs-lisp
(use-package ace-jump-mode
:bind
(:map gpolonkai/pers-map
("SPC" . ace-jump-mode)))
#+end_src
** Multiple cursors, because one is often not enough
*** ~multiple-cursors~
#+begin_src emacs-lisp
(defun gpolonkai/no-blink-matching-paren ()
(customize-set-variable 'blink-matching-paren nil))
(defun gpolonkai/blink-matching-paren ()
(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)))
#+end_src
*** ~phi-search~, incremental search compatible with ~multiple-cursors~
#+begin_src emacs-lisp
(use-package phi-search)
(use-package phi-search-mc
:config
(phi-search-mc/setup-keys))
#+end_src
*** ~mc-extras~
#+begin_src emacs-lisp
(use-package mc-extras
:demand
:bind
(:map mc/keymap
("=" . mc/compare-chars)))
#+end_src
*** Add extra cursors with ~ace-jump~
#+begin_src emacs-lisp
(use-package ace-mc
:bind
(:map gpolonkai/mc-prefix-map
("SPC" . ace-mc-add-multiple-cursors)
("C-SPC" . ace-mc-add-single-cursor)))
#+end_src
** ~smartparens~ for easier parentheses insertion
#+begin_src emacs-lisp
(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)))
#+end_src
** ~which-key~ to display available key bindings
#+begin_src emacs-lisp
(use-package which-key
:config
(which-key-mode)
(which-key-setup-minibuffer)
:custom
(which-key-idle-delay 0.3))
#+end_src
** ~visual-fill-column~, width limited text view
Its much easier on the eye when writing text, not code.
#+begin_src emacs-lisp
(use-package visual-fill-column
:custom
(visual-fill-column-center-text t)
:hook
(org-mode . visual-fill-column-mode))
#+end_src
** ~spinner~, to display a progress bar for e.g. background tasks
#+begin_src emacs-lisp
(use-package spinner)
#+end_src
** ~avy~ to jump to things
#+begin_src emacs-lisp
(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)))
#+end_src
** ~goto-last-change~
#+begin_src emacs-lisp
(use-package goto-last-change
:bind
(("M-g /" . goto-last-change)))
#+end_src
** ~rainbow-mode~ to highlight colours based on their name/hex code
#+begin_src emacs-lisp
(use-package rainbow-mode
:hook
(css-mode . rainbow-mode)
(scss-mode . rainbow-mode)
(sass-mode . rainbow-mode))
#+end_src
** ~zygospore~ to toggle other windows for maximum focus
When focus is no longer needed, they can be toggled back.
#+begin_src emacs-lisp
(use-package zygospore
:bind
(:map ctl-x-map
("1" . zygospore-toggle-delete-other-windows)))
#+end_src
** ~dashboard~
#+begin_src emacs-lisp
(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))))
#+end_src
** ~sr-speedbar~, speed bar in the same frame
#+begin_src emacs-lisp
(use-package sr-speedbar
:after speedbar)
#+end_src
** ~hungry-delete~ to delete all the whitespace
#+begin_src emacs-lisp
(use-package hungry-delete
:config
(global-hungry-delete-mode))
#+end_src
** ~anzu~, to show number of matches while searching
#+begin_src emacs-lisp
(use-package anzu
:delight
:config
(global-anzu-mode 1))
#+end_src
** ~all-the-icons~
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.
#+begin_src emacs-lisp
(use-package all-the-icons)
#+end_src
And apply it to dired, too.
#+begin_src emacs-lisp
(use-package all-the-icons-dired
:hook
(dired-mode . all-the-icons-dired-mode))
#+end_src
** ~flyspell~ for all my spell-checking needs
#+begin_src emacs-lisp
(use-package flyspell
:hook
(prog-mode . flyspell-prog-mode)
(text-mode . flyspell-mode))
#+end_src
** ~ace-flyspell~
#+begin_src emacs-lisp
(use-package ace-flyspell
:bind
(:map flyspell-mode-map
("C-M-i" . ace-flyspell-correct-word)))
#+end_src
** ~objed~, text object manipulation
From the package description:
#+begin_quote
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.
#+end_quote
#+begin_src emacs-lisp
(use-package objed
:demand t
:bind
(:map global-map
("M-SPC" . objed-activate)))
#+end_src
** ~alert~ to send alerts to a notification system
#+begin_src emacs-lisp
(use-package alert
:config
(setq alert-default-style
(if (gpolonkai/termux-p)
(progn
;; TODO Remove this as soon as my PR gets merged
;; https://github.com/jwiegley/alert/pull/41
(unless (fboundp 'alert-termux-notify)
(defcustom alert-termux-command (executable-find "termux-notification")
"Path to the termux-notification command.
This is found in the termux-api package, and it requires the Termux
API addon app to be installed."
:type 'file
:group 'alert)
(defun alert-termux-notify (info)
"Send INFO using termux-notification.
Handles :TITLE and :MESSAGE keywords from the
INFO plist."
(if alert-termux-command
(let ((args (nconc
(when (plist-get info :title)
(list "-t" (alert-encode-string (plist-get info :title))))
(list "-c" (alert-encode-string (plist-get info :message))))))
(apply #'call-process alert-termux-command nil
(list (get-buffer-create " *termux-notification output*") t)
nil args))
(alert-message-notify info)))
(alert-define-style 'termux :title "Notify using termux"
:notifier #'alert-termux-notify))
'termux)
'libnotify)))
#+end_src
** ~undo-tree~
#+begin_src emacs-lisp
(use-package undo-tree
:config
(global-undo-tree-mode)
:custom
(undo-tree-auto-save-history nil))
#+end_src
** ~ciel~ to mimic ViMs ~ci~ functionality
#+begin_src emacs-lisp
(use-package ciel
:bind
(:map global-map
("C-c i" . ciel-ci)
("C-c o" . ciel-co)))
#+end_src
** ~hide-mode-line~ to hide the modeline occasionally
#+begin_src emacs-lisp
(use-package hide-mode-line
:bind (:map gpolonkai/pers-map
("h" . hide-mode-line-mode)))
#+end_src
** ~string-inflection~
#+begin_src emacs-lisp
(use-package string-inflection
:bind (:map gpolonkai/pers-map
("i" . string-inflection-all-cycle)))
#+end_src
** ~spdx~ to insert SPDX compatible license text
#+begin_src emacs-lisp
(use-package spdx
:bind (:map gpolonkai/pers-map
("L" . spdx-insert-spdx-copyright))
:custom
(spdx-copyright-holder 'user)
(spdx-copyright-sign 'unicode))
#+end_src
** ~ag~ to use the silver searcher
#+begin_src emacs-lisp
(use-package ag
:commands (ag))
#+end_src
** ~rg~ to use ripgrep
#+begin_src emacs-lisp
(use-package rg
:commands (rg))
#+end_src
* Dired extras
** ~dired-collapse~, to collapse directories that contain a single file somewhere deep
#+begin_src emacs-lisp
(use-package dired-collapse)
#+end_src
** ~dired-du~ to show directory sizes
#+begin_src emacs-lisp
(use-package dired-du)
#+end_src
** ~dired-git-info~ to show Git version information
#+begin_src emacs-lisp
(use-package dired-git-info
:bind
(:map dired-mode-map
(")" . dired-git-info-mode)))
#+end_src
** ~dired-hide-dotfiles~ to show/hide hidden (dot) files
#+begin_src emacs-lisp
(use-package dired-hide-dotfiles
:bind
(:map dired-mode-map
("." . dired-hide-dotfiles-mode)))
#+end_src
** ~dired-rainbow~ to color code file types
Rainbow to the stars…
#+begin_src emacs-lisp
(use-package dired-rainbow
:config
(dired-rainbow-define-chmod executable-unix "Green" "-.*x.*"))
#+end_src
** ~dired-k~ to highlight dired buffers by file size, mtime, git status, etc.
#+begin_src emacs-lisp
(use-package dired-k
:bind
(:map dired-mode-map
("K" . dired-k)))
#+end_src
* Git related things
** ~magit~
#+begin_src emacs-lisp
(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))
#+end_src
** ~forge~ to make Magit work with Git forges
#+begin_src emacs-lisp
(use-package forge)
#+end_src
** ~git-gutter~ to see changed lines
In graphical mode we use ~git-gutter-finge~, ~git-gutter~ otherwise (as we have no fringe in that case).
It also provides some nice commands to navigate between change sets.
#+begin_src emacs-lisp
(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)))))
#+end_src
** ~git-messenger~, aka annotate current line
#+begin_src emacs-lisp
(use-package git-messenger
:bind
(:map gpolonkai/pers-map
("gm" . git-messenger:popup-message)))
#+end_src
** ~git-timemachine~, to see previous versions of the current file
#+begin_src emacs-lisp
(use-package git-timemachine
:bind
(([f6] . git-timemachine-toggle)))
#+end_src
* Completion related external packages
** ~vertico~
#+begin_src emacs-lisp
(use-package vertico
:init
(vertico-mode))
#+end_src
** ~consult~
#+begin_src emacs-lisp
(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"))
#+end_src
** ~orderless~
#+begin_src emacs-lisp
(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)))))
#+end_src
** ~marginalia~
#+begin_src emacs-lisp
(use-package marginalia
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
:init
(marginalia-mode))
#+end_src
** ~embark~
#+begin_src emacs-lisp
(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))
#+end_src
** ~consult-projectile~
#+begin_src emacs-lisp
(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)))
#+end_src
** ~corfu~
#+begin_src emacs-lisp
(use-package corfu
:init
(global-corfu-mode)
:custom
(corfu-cycle t)
(corfu-separator ?\s))
#+end_src
** ~cape~
#+begin_src emacs-lisp
(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))
#+end_src
* Org mode
** ~outline~
This is the mode Org is based on. Its technically a built-in, but i configure it here so it remains together with other Org setup.
#+begin_src emacs-lisp
(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)))))
#+end_src
** Function to set up Org-mode buffers
#+begin_src emacs-lisp
(defun gpolonkai/setup-org-mode ()
(org-indent-mode 1)
(variable-pitch-mode 1)
(auto-fill-mode 0)
(visual-line-mode 1))
#+end_src
** Display horizontal rulers as a full-width line
From [[https://matrix.to/#/@suckless_shill:matrix.org][viz]] in the [[https://matrix.to/#/#org-mode:matrix.org][org-mode]] Matrix room.
#+begin_src emacs-lisp
(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)
#+end_src
** Main ~org~ configuration
#+begin_src emacs-lisp
(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)))
#+end_src
** ~org-roam~
Lets see if i can get my brain more organised this way…
#+begin_src emacs-lisp
(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))
#+end_src
** ~org-roam-timestamps~
#+begin_src emacs-lisp
(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))
#+end_src
** ~org-roam-ui~
#+begin_src emacs-lisp
(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))
#+end_src
** ~org-bullets~ for more beautiful bullet points
#+begin_src emacs-lisp
(use-package org-bullets
:custom
(org-bullets-face-name 'org-bullet-face)
(org-bullets-bullet-list '("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""))
:hook
(org-mode . org-bullets-mode))
#+end_src
** ~org-sticky-header~ so i always know where i am
#+begin_src emacs-lisp
(use-package org-sticky-header
:custom
(org-sticky-header-full-path 'full)
:hook
(org-mode . org-sticky-header-mode))
#+end_src
** ~org-random-todo~ to show a random ToDo every hour
#+begin_src emacs-lisp
(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)))
#+end_src
** ~org-autolist~ for better list management
#+begin_src emacs-lisp
(use-package org-autolist
:hook
(org-mode . org-autolist-mode))
#+end_src
** ~secretaria~ to remind me of my tasks
And because even secretaries need a secretary today.
#+begin_src emacs-lisp
(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))
#+end_src
** ~org-clock-waybar~ to export currently clocked task for status bar display
#+begin_src emacs-lisp
(use-package org-clock-waybar
:ensure nil
:quelpa (org-clock-waybar
:fetcher git
:url "https://gitea.polonkai.eu/gergely/org-clock-waybar.git")
:config
(org-clock-waybar-setup))
#+end_src
** ~consult-org-roam~
#+begin_src emacs-lisp
(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)))
#+end_src
** ~org-appear~, to display markup only when its actually needed
#+begin_src emacs-lisp
(use-package org-appear
:hook
(org-mode . org-appear-mode)
:custom
(org-appear-autosubmarkers t)
(org-appear-autolinks t)
(org-appear-autoemphasis t))
#+end_src
** ~ob-mermaid~, to generate Mermaid diagrams
#+begin_src emacs-lisp
(use-package ob-mermaid
:custom
(ob-mermaid-cli-path "/home/polesz/.local/node/bin/mmdc"))
#+end_src
** ~plantuml-mode~ to edit PlantUML files in and out of Org documents
Before using this, make sure the latest PlantUML JAR file is downloaded into the downloads
directory. It is available from [[http://plantuml.com/download][here]].
#+begin_src emacs-lisp
(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))))
#+end_src
** ~ox-rst~ to export to ReSTructured text
#+begin_src emacs-lisp
(use-package ox-rst
:after org)
#+end_src
** ~orgit~ and ~orgit-forge~ to store link to Git stuff
#+begin_src emacs-lisp
(use-package orgit
:after magit org)
(use-package orgit-forge
:after magit org orgit)
#+end_src
* Mailing
** Set up whitespace handling in mu4e buffers
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~.
#+begin_src emacs-lisp
(copy-face 'trailing-whitespace 'trailing-whitespace-mu4e)
(set-face-attribute 'trailing-whitespace-mu4e nil :background 'unspecified)
(defun gpolonkai/mu4e-trailing-whitespace-fix ()
(set (make-local-variable 'face-remapping-alist)
'((trailing-whitespace trailing-whitespace-mu4e))))
#+end_src
** ~mu4e~
Now thats out of the way, lets configure mu4e itself.
#+begin_src emacs-lisp
(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)))
#+end_src
** ~sendmail~
#+begin_src emacs-lisp
(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))
#+end_src
** ~org-msg~ to write messages using Org-mode
#+begin_src emacs-lisp
(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"))
#+end_src
* External packages to boost coding productivity
** ~electric-operator~ to automatically add spaces around operators
#+begin_src emacs-lisp
(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))
#+end_src
** ~rainbow-delimiters~
#+begin_src emacs-lisp
(use-package rainbow-delimiters
:hook
(prog-mode . rainbow-delimiters-mode))
#+end_src
** ~rainbow-identifiers~
#+begin_src emacs-lisp
(use-package rainbow-identifiers)
#+end_src
** ~auto-highlight-symbol~ to hightlight current symbol
Great help during refactoring.
#+begin_src emacs-lisp
(use-package auto-highlight-symbol
:config
(global-auto-highlight-symbol-mode t))
#+end_src
** ~glasses~, to make ReallyLongCamelCaseWords more readable
#+begin_src emacs-lisp
(use-package glasses
:delight " 👓"
:hook
(prog-mode . glasses-mode))
#+end_src
** ~hl-todo~ to highlight TODO comments
#+begin_src emacs-lisp
(use-package hl-todo
:hook
(prog-mode . hl-todo-mode))
#+end_src
** ~bug-reference~ to turn bug/patch references to links
#+begin_src emacs-lisp
(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))
#+end_src
** ~highlight-indentation~ to highlight indentation levels
#+begin_src emacs-lisp
(use-package highlight-indentation
:hook
(python-mode . highlight-indentation-mode))
#+end_src
** ~flycheck~
#+begin_src emacs-lisp
(use-package flycheck
:config
(global-flycheck-mode)
:custom
(flycheck-python-pylint-executable "python3"))
#+end_src
** ~flycheck-pkg-config~ to aid FlyCheck using ~pkg-config~
#+begin_src emacs-lisp
(use-package flycheck-pkg-config)
#+end_src
* Utilities
** ~kubernetes~, a kubernetes dashboard
#+begin_src emacs-lisp
(use-package kubernetes
:commands (kubernetes-overview))
#+end_src
** ~elpher~, a Gopher/Gemini client
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.
#+begin_src emacs-lisp
(use-package elpher
:commands (elpher))
#+end_src
** ~mastodon~
#+begin_src emacs-lisp
(use-package mastodon
:custom
(mastodon-instance-url "https://social.polonkai.eu/")
(mastodon-active-user "gergely"))
#+end_src
** ~twtxt~
#+begin_src emacs-lisp
(use-package twtxt
:custom
(twtxt-file (expand-file-name "NextCloud/twtxt.txt" user-documents-directory))
(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"))))
#+end_src
** ~paradox~ for better package management
I dont always use the package menu, but when i do, i want to do it in style…
#+begin_src emacs-lisp
(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)))
#+end_src
** ~tidal~ for improvising music
#+begin_src emacs-lisp
(use-package tidal
:mode "\\.tidal\\'")
#+end_src
* Programming-language specific packages
** Python
*** Set up pretty symbols
Because they are fancy.
#+begin_src emacs-lisp
(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" . ?≍))
))
#+end_src
*** ~poetry~
#+begin_src emacs-lisp
(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)))
#+end_src
** C
Because thats still my favourite language.
*** Set up my indentation style
#+begin_src emacs-lisp
(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)
#+end_src
*** Set indentation levels to the same as the tab width
#+begin_src emacs-lisp :tangle no
(defvaralias 'c-basic-offset 'tab-width)
(defvaralias 'cperl-indent-level 'tab-width)
#+end_src
*** Some common initialisation for C mode
#+begin_src emacs-lisp
(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)))
#+end_src
** Web
*** ~web-mode~
#+begin_src emacs-lisp
(use-package web-mode
:mode "\\.html?\\'"
:custom
(web-mode-enable-auto-indentation nil)
(web-mode-enable-engine-detection t))
#+end_src
*** ~emmet-mode~ for easier HTML/CSS writing
#+begin_src emacs-lisp
(use-package emmet-mode
:custom
(emmet-self-closing-tag-style "")
:hook
(web-mode . emmet-mode)
(css-mode . emmet-mode))
#+end_src
*** ~enlive~, to query HTML tags by CSS selectors
#+begin_src emacs-lisp
(use-package enlive)
#+end_src
** ~json-mode~
#+begin_src emacs-lisp
(use-package json-mode
:mode "\\.json\\'")
#+end_src
** ~yaml-mode~
#+begin_src emacs-lisp
(use-package yaml-mode
:mode (("\\.yml\\'" . yaml-mode)
("\\.yaml\\'" . yaml-mode))
:init
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))
#+end_src
** ~dockerfile-mode~
#+begin_src emacs-lisp
(use-package dockerfile-mode)
#+end_src
** ~systemd~, for editing systemd unit files
#+begin_src emacs-lisp
(use-package systemd
:mode ("\\.\\(?:automount\\|mount\\|path\\|s\\(?:ervice\\|lice\\|ocket\\)\\|t\\(?:arget\\|imer\\)\\|wants\\)\\'" . systemd-mode))
#+end_src
** ~terraform-mode~
#+begin_src emacs-lisp
(use-package terraform-mode
:mode "\\.tf\\'")
#+end_src
** ~bats-mode~
#+begin_src emacs-lisp
(use-package bats-mode
:mode "\\.bats\\'")
#+end_src
** ~fish-mode~
#+begin_src emacs-lisp
(use-package fish-mode
:hook
(fish-mode . (lambda () (add-hook 'before-save-hook 'fish_indent-before-save)))
:mode "\\.fish\\'")
#+end_src
** ~gitlab-ci-mode~ and ~gitlab-ci-mode-flycheck~
#+begin_src emacs-lisp
(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))
#+end_src
** ~csv-mode~
#+begin_src emacs-lisp
(use-package csv-mode
:mode "\\.csv\\'")
#+end_src
** Rust
*** ~rust-mode~
#+begin_src emacs-lisp
(use-package rust-mode
:mode "\\.rs\\'")
#+end_src
*** ~cargo~ and ~cargo-mode~ for Cargo usage
#+begin_src emacs-lisp
(use-package cargo)
(use-package cargo-mode
:mode "Cargo\\.toml\\'")
#+end_src
** ~arduino-mode~
#+begin_src emacs-lisp
(use-package arduino-mode
:mode "\\.ino\\'")
#+end_src
** ~nginx-mode~
#+begin_src emacs-lisp
(use-package nginx-mode)
#+end_src
** ~js2-mode~
#+begin_src emacs-lisp
(use-package js2-mode
:pin melpa-stable
:mode "\\.js\\'")
#+end_src
** ~typescript-mode~
#+begin_src emacs-lisp
(use-package typescript-mode
:mode "\\.ts\\'")
#+end_src
** ~less-css-mode~
#+begin_src emacs-lisp
(use-package less-css-mode
:mode "\\.less\\'")
#+end_src
** ~sass-mode~
#+begin_src emacs-lisp
(use-package sass-mode
:mode "\\.sass\\'")
#+end_src
** ~vue-html-mode~
#+begin_src emacs-lisp
(use-package vue-html-mode
:mode "\\.vue\\'")
#+end_src
** ~jinja2-mode~
#+begin_src emacs-lisp
(use-package jinja2-mode
:mode "\\.j2\\'")
#+end_src
** ~markdown-mode~
#+begin_src emacs-lisp
(use-package markdown-mode
:mode (("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode)))
#+end_src
** ~vala-mode~
#+begin_src emacs-lisp
(use-package vala-mode
:mode "\\.vala\\'")
#+end_src
** ~po-mode~
#+begin_src emacs-lisp
(use-package po-mode
:ensure nil
:mode "\\.po\\'")
#+end_src
** ~meson-mode~
#+begin_src emacs-lisp
(use-package meson-mode
:mode "\\.meson\\'")
#+end_src
** ~ggtags~, an interfaco for GNU Globals
#+begin_src emacs-lisp
(defun gpolonkai/cond-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))
#+end_src
** ~yuck-mode~
#+begin_src emacs-lisp
(use-package yuck-mode
:mode "\\.yuck\\'")
#+end_src
** ~ebnf-mode~
#+begin_src emacs-lisp
(use-package ebnf-mode
:commands (ebnf-mode))
#+end_src
* Key bindings
#+begin_src emacs-lisp
(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-this-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))
#+end_src
* 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.
#+begin_src emacs-lisp
(require 'server)
(unless (server-running-p)
(server-start))
#+end_src