Skip to content

Latest commit

 

History

History
3491 lines (2575 loc) · 102 KB

configuration.org

File metadata and controls

3491 lines (2575 loc) · 102 KB

Literate Emacs Configuration


This is a copy of my literate configuration for Emacs and if you’re viewing this anywhere else, don’t! Because you can find a readable version on my website. You can also find a source-based version with the rest of my ~/.emacs.d on my sourcehut.

This was created on <2021-12-04 Sat> and the date above reflects when the last modification was made.

Other init files

These are in my ~/.emacs.d and help load this main configuration.

Early init

This creates a file, early-init.el , in ~~/.emacs.d~. This was stolen from Protesilaos some time back. It still needs work - it’s not tangled (by default) yet.

    ;;; early-init.el --- Early Init File -*- lexical-binding: t -*-

;; Copyright (c) 2021-2022 pereginator

;; Author: peregrinator <brihadeesh@protonmail.com>
;; URL: https://git.sr.ht/~peregrinator/dotfiles Version: 0.1.0
;; Package-Requires: ((emacs "28.1"))

;; This file is NOT part of GNU Emacs.

;; This file is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation, either version 3 of the License,
;; or (at your option) any later version.  This file is distributed in
;; the hope that it will be useful, but WITHOUT ANY WARRANTY; without
;; even the implied warranty of MERCHANTABILITY or FITNESS FOR A
;; PARTICULAR PURPOSE.  See the GNU General Public License for more
;; details.  You should have received a copy of the GNU General Public
;; License along with this file.  If not, see
;; <http://www.gnu.org/licenses/>.

    ;;; Commentary:

;; Prior to Emacs 27, the `init.el' was supposed to handle the
;; initialisation of the package manager, by means of calling
;; `package-initialize'.  Starting with Emacs 27, the default
;; behaviour is to start the package manager before loading the init
;; file.

    ;;; Code:

;; Don't initialise installed packages cause package.el sucks balls
(setq package-enable-at-startup nil)

;; Do not resize the frame at this early stage.  (setq
frame-inhibit-implied-resize t)

;; Disable GUI elements ; Disable menu-bar, tool-bar, and scroll-bar.
;;(if (fboundp 'menu-bar-mode) (menu-bar-mode -1)) (if (fboundp
;;'tool-bar-mode) (tool-bar-mode -1)) (if (fboundp 'scroll-bar-mode)
;;(scroll-bar-mode -1)) (setq inhibit-splash-screen t) (setq
;;use-dialog-box nil) ; only for mouse events (setq use-file-dialog
;;nil)

(setq inhibit-startup-screen t) (setq inhibit-startup-buffer-menu t)

;; for when I upgrade to emacs28 with native compilation (setq
native-comp-async-report-warnings-errors nil)

    ;;; early-init.el ends here

init.el

I’ve changed things up a little - this now includes the package management code and the code for org installation.

;;; init.el --- Initialization. -*- lexical-binding: t; -*-

;; Copyright (C) 2022 peregrinator
;; This file is NOT part of GNU Emacs.
;; This file is free software.

;; Author: peregrinator <brihadeesh@protonmail.com>
;; URL: https://git.sr.ht/~peregrinator/.emacs.d
;; Package-Requires: ((emacs "28.1"))

;;; Commentary:
;; This file provides the initialization configuration.

;;; Code:

;; Make emacs startup faster
(defvar startup/file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)
(defun startup/revert-file-name-handler-alist ()
  "Revert file name handler alist."
  (setq file-name-handler-alist startup/file-name-handler-alist))
(add-hook 'emacs-startup-hook 'startup/revert-file-name-handler-alist)

;; For performance
(setq read-process-output-max (* 1024 1024)) ;; 1mb
(setq process-adaptive-read-buffering nil)

;; Load newer .elc or .el
(setq load-prefer-newer t)

Package management

Update: <2022-11-21 Mon> I’ve moved all of this and some other stuff (garbage collection etc) to the init.el. See the initial org-mode installation section below for more.

Setup straight.el

I’ll be using use-package to organise and configure individual packages into neater code blocks although the download will be handled by straight.el. I’ve included the org-installation here since there’s somehow always an issue with version mismatch.

;;; Configure `straight.el'
;; fetch developmental version of `straight.el'
(setq straight-repository-branch "develop"

      ;; redirect all package repos and builddirs elsewhere
      straight-base-dir "~/.cache/")


;; Bootstrap straight.el
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" "~/.cache"))
      (bootstrap-version 6))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

;;; Configure straight.el (contd.)
;; make all use-package instances use straight.el
(setq straight-use-package-by-default t

      ;; clone depth (probably to save space)
      straight-vc-git-default-clone-depth 1

      ;; Define when to check for package modifications,
      ;; for improved straight.el startup time.
      straight-check-for-modifications nil

      ;; use elpa
      straight-recipes-gnu-elpa-use-mirror t

      straight-host-usernames
      '((github . "brihadeesh")
        (gitlab . "peregrinator")))

Install and configure use-package

use-package is installed and managed by straight.el and in turn packages used in this config are managed/organized by use-package. There’s something to do with integration with use-package on the straight.el readme

;; Install use-package with straight.el
(straight-use-package 'use-package)

DISABLED Use-package v2 related changes

Need to figure this out - I think maybe use-package might not be updated

(eval-when-compile
  (require 'use-package))
(require 'diminish)
(require 'bind-key)

DISABLED Minimal package.el setup only to browse packages

Running package-list-packages includes them only for browsing

(require 'package)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/"))

Initial Org-mode installation

I use the latest version of Org, pulled from the gnu-elpa repositories and have to install it early on so straight.el / use-package don’t give me trouble with version conflicts because it loads the built in (older) version first.

;; install org & org-contrib
(straight-use-package 'org)
;; (require 'org)
(straight-use-package 'org-contrib)


;; Load configuration.org
(when (file-readable-p
       (concat user-emacs-directory "configuration.org"))
  (org-babel-load-file
   (concat user-emacs-directory "configuration.org")))

End

;;; init.el ends here

Header

Just the usual header for elisp files that Emacs keeps complaining about.

    ;;; configuration.el --- Initialisation. -*- lexical-binding: t; -*-

;; Copyright (C) 2022 peregrinator

;; Author: peregrinator <brihadeesh@protonmail.com>
;; URL: https://git.sr.ht/~peregrinator/.emacs.d
;; Package-Requires: ((emacs "28.1"))

;; This file is NOT part of GNU Emacs.

;; This file is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published
;; by the Free Software Foundation, either version 3 of the License,
;; or (at your option) any later version.  This file is distributed in
;; the hope that it will be useful, but WITHOUT ANY WARRANTY; without
;; even the implied warranty of MERCHANTABILITY or FITNESS FOR A
;; PARTICULAR PURPOSE.  See the GNU General Public License for more
;; details.  You should have received a copy of the GNU General Public
;; License along with this file.  If not, see
;; <http://www.gnu.org/licenses/>.


    ;;; Commentary:
;; This file provides the initialization configuration tangled this
;; file.


    ;;; Code:

Prerequisites

Reload Emacs configuration

I’m not sure I understand how this works entirely but joseph8th’s repo suggests using M-: (load-file user-init-file) RET or evaluating that same function interactively. I’ve modified the sanemacs reload config function below hoping that it works but in that doesn’t happen, this first code block can be evaluated using C-c C-c:

(defun reload-config ()
  (interactive)
  (load-file user-init-file))

Ensure UTF-8

(set-language-environment 'utf-8)
(prefer-coding-system 'utf-8)

Whoami

(setq user-full-name "peregrinator"
      user-mail-address "brihadeesh@protonmail.com")

Writing

Org-mode

  • [X] Get the damn thing first
  • [ ] Organise the thing - needs splitting into multiple code blocks.

Moved the installation to init.el along with the straight.el bootstrap to avoid conflicts with the bundled version of the package. I think this can go back to being a regular use-package function but I’m desperately avoiding having to debug init any further.

(require 'org)
(setq initial-major-mode 'org-mode
      org-display-inline-images t
      org-redisplay-inline-images t
      org-image-actual-width nil
      org-startup-with-inline-images "inlineimages"
      org-catch-invisible-edits 'smart

      ;; sub-headings inherit properties set at parent level
      ;; headings
      org-use-property-inheritance t

      ;; org-ellipsis " ▾"
      ;; hide markers for bold, italic, etc and trailing stars
      org-hide-emphasis-markers t

      ;; fontify code in code blocks
      org-src-fontify-natively t
      org-fontify-quote-and-verse-blocks t
      ;; org-src-tab-acts-natively t

     ;; org-edit-src-content-indentation 2
     org-hide-block-startup nil
     ;; org-src-preserve-indentation nil

     ;; allow for increased space between org
     ;; org-cycle-separator-lines -1

     ;; hard indentation
     ;; org-adapt-indentation t

     ;; increase indentation by using odd header levels only
     ;; org-odd-levels-only t

     ;; org-startup-folded 'content
     org-capture-bookmark nil
     org-hide-leading-stars t

     ;; org-modern
     org-tags-column -80
     org-ellipsis ""
     org-special-ctrl-a/e t
     org-insert-heading-respect-content t

     ;; display numbers instead of bullets for headings
     ;; org-num-mode t

     ;; faster (single-key) navigation in org-mode
     ;; type `?' for help
     ;; org-use-speed-commands t
     )

    ;;(setq org-modules
    ;;  '(org-crypt
    ;;      org-habit
    ;;      org-bookmark
    ;;      org-eshell
    ;;      org-irc))

    (setq org-refile-targets '((nil :maxlevel . 5)
                               (org-agenda-files :maxlevel . 5)))

    (setq org-outline-path-complete-in-steps nil)
    (setq org-refile-use-outline-path t)

    ;; get something like this for regular emacs bindings
    ;;(evil-define-key '(normal insert visual) org-mode-map (kbd "C-j") 'org-next-visible-heading)
    ;;(evil-define-key '(normal insert visual) org-mode-map (kbd "C-k") 'org-previous-visible-heading)
    ;;(evil-define-key '(normal insert visual) org-mode-map (kbd "M-j") 'org-metadown)
    ;;(evil-define-key '(normal insert visual) org-mode-map (kbd "M-k") 'org-metaup)

    ;; Replace list hyphen with dot
    ;; (font-lock-add-keywords 'org-mode
    ;;                         '(("^ *\\([-]\\) "
    ;;                            (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))

    ;; Make sure org-indent face is available
    (require 'org-indent)

    ;; Ensure that anything that should be fixed-pitch in Org files appears that way
    (set-face-attribute 'org-block nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-table nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-formula nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch))
    (set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch))
    (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
    (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
    (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
    (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)


    ;; block templates
    ;; This is needed as of Org 9.2
    (require 'org-tempo)

    (add-to-list 'org-structure-template-alist '("sh" . "src sh"))
    (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
    (add-to-list 'org-structure-template-alist '("li" . "src lisp"))
    (add-to-list 'org-structure-template-alist '("sc" . "src scheme"))
    (add-to-list 'org-structure-template-alist '("rr" . "src R"))
    (add-to-list 'org-structure-template-alist '("py" . "src python"))
    (add-to-list 'org-structure-template-alist '("lua" . "src lua"))
    (add-to-list 'org-structure-template-alist '("yaml" . "src yaml"))
    (add-to-list 'org-structure-template-alist '("json" . "src json"))

    ;; disable electric pairing for angle bracket

    ;; (add-hook 'org-mode-hook (lambda ()
    ;; 	   (setq-local electric-pair-inhibit-predicate
    ;; 		   `(lambda (c)
    ;; 		  (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c)))))))
Babel

It’s gotta be one of these

(org-babel-do-load-languages
         'org-babel-load-languages
         '((emacs-lisp . t)
           (R . t)
           ))
(setq org-babel-load-languages
                  '((emacs-lisp  . t)
                    (lisp        . t)
                    (org         . t)
                    (sh          . t)
                    (R           . t)))
Sources for agenda tasks

Generates an agenda from wildcarded org files from the specified directory

;; (setq org-agenda-files
;;       (file-expand-wildcards "~/org/*.org"))
Tags and todo-keywords config

Todo-keywords are things like TODO and DONE and so on. Tags are for classifying stuff by the general theme of what’s being talked about.

todo-keywords
(setq org-todo-keywords
      '((sequence "TODO(t)" "|" "DONE(d!)" "DISABLED(f!)")))
DISABLED tags
(setq org-tag-alist '((("misc" . ?m)
                      ("emacs" . ?e)
                      ("dotfiles" . ?d)
                      ("work" . ?w)
                      ("chore" . ?c)
                      ("blog" . ?b)
                      )))
Capture templates

This will need to be looked at carefully. Roughly, I need to work out if I’m going to be using org-agenda and if so, how will I be using it. Adding tasks can be made much easier with this. I can also use this for entering entries into org-journal, making it a whole deal easier. Perhaps to start off, the org-mode tutorial might be a good place to start. I’ve also got a simple enough config from a reddit post in my unused local elisp libs too.

Display features
Autoindent/autofill turned on automatically
(add-hook 'org-mode-hook 'org-indent-mode)
(setq org-startup-indented t)

;; organise paragraphs automatically
(add-hook 'org-mode-hook 'turn-on-auto-fill)
Minad’s modern UI for org-mode

Was maintaining a fork but I guess it’s too much work - should considering getting back at it sometime. I was having issues with TODO keywords and tags/categories being rendered absurdly small but apparently setting org-modern-label-border to nil should make them full sized (see this issue). I really must consider using a variable-pitch font for rich text and headers while restricting fixed-pitch fonts to header arguments, source blocks and explicitly code segments in text.

(use-package org-modern
  ;; currently outdated/unmaintained fork
  ;; :straight (:host github :repo "brihadeesh/org-modern")

  :config
  ;; Add frame borders and window dividers
  (modify-all-frames-parameters
   '((right-divider-width . 10)
     (internal-border-width . 5)))
  (dolist (face '(window-divider
                  window-divider-first-pixel
                  window-divider-last-pixel))
    (face-spec-reset-face face)
    (set-face-foreground face (face-attribute 'default :background)))
  (set-face-background 'fringe (face-attribute 'default :background))

  ;; `org-modern' specific config
  (setq org-modern-star ["" "" "" "" "" "" ""]
        org-modern-label-border nil)

  ;; enable the global mode
  (global-org-modern-mode t))
DISABLED Rougier’s svg-tag-mode

…to replace janky font-related issues with org-modern

  (use-package svg-tag-mode

    :config
    (defconst date-re "[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}")
(defconst time-re "[0-9]\\{2\\}:[0-9]\\{2\\}")
(defconst day-re "[A-Za-z]\\{3\\}")
(defconst day-time-re (format "\\(%s\\)? ?\\(%s\\)?" day-re time-re))

(defun svg-progress-percent (value)
  (svg-image (svg-lib-concat
              (svg-lib-progress-bar (/ (string-to-number value) 100.0)
                                nil :margin 0 :stroke 2 :radius 3 :padding 2 :width 11)
              (svg-lib-tag (concat value "%")
                           nil :stroke 0 :margin 0)) :ascent 'center))

(defun svg-progress-count (value)
  (let* ((seq (mapcar #'string-to-number (split-string value "/")))
         (count (float (car seq)))
         (total (float (cadr seq))))
  (svg-image (svg-lib-concat
              (svg-lib-progress-bar (/ count total) nil
                                    :margin 0 :stroke 2 :radius 3 :padding 2 :width 11)
              (svg-lib-tag value nil
                           :stroke 0 :margin 0)) :ascent 'center)))

(setq svg-tag-tags
      `(
        ;; Org tags
        (":\\([A-Za-z0-9]+\\)" . ((lambda (tag) (svg-tag-make tag))))
        (":\\([A-Za-z0-9]+[ \-]\\)" . ((lambda (tag) tag)))

        ;; Task priority
        ("\\[#[A-Z]\\]" . ( (lambda (tag)
                              (svg-tag-make tag :face 'org-priority
                                            :beg 2 :end -1 :margin 0))))

        ;; Progress
        ("\\(\\[[0-9]\\{1,3\\}%\\]\\)" . ((lambda (tag)
                                            (svg-progress-percent (substring tag 1 -2)))))
        ("\\(\\[[0-9]+/[0-9]+\\]\\)" . ((lambda (tag)
                                          (svg-progress-count (substring tag 1 -1)))))

        ;; TODO / DONE
        ("TODO" . ((lambda (tag) (svg-tag-make "TODO" :face 'org-todo :inverse t :margin 0))))
        ("DONE" . ((lambda (tag) (svg-tag-make "DONE" :face 'org-done :margin 0))))


        ;; Citation of the form [cite:@Knuth:1984]
        ("\\(\\[cite:@[A-Za-z]+:\\)" . ((lambda (tag)
                                          (svg-tag-make tag
                                                        :inverse t
                                                        :beg 7 :end -1
                                                        :crop-right t))))
        ("\\[cite:@[A-Za-z]+:\\([0-9]+\\]\\)" . ((lambda (tag)
                                                (svg-tag-make tag
                                                              :end -1
                                                              :crop-left t))))


        ;; Active date (with or without day name, with or without time)
        (,(format "\\(<%s>\\)" date-re) .
         ((lambda (tag)
            (svg-tag-make tag :beg 1 :end -1 :margin 0))))
        (,(format "\\(<%s \\)%s>" date-re day-time-re) .
         ((lambda (tag)
            (svg-tag-make tag :beg 1 :inverse nil :crop-right t :margin 0))))
        (,(format "<%s \\(%s>\\)" date-re day-time-re) .
         ((lambda (tag)
            (svg-tag-make tag :end -1 :inverse t :crop-left t :margin 0))))

        ;; Inactive date  (with or without day name, with or without time)
         (,(format "\\(\\[%s\\]\\)" date-re) .
          ((lambda (tag)
             (svg-tag-make tag :beg 1 :end -1 :margin 0 :face 'org-date))))
         (,(format "\\(\\[%s \\)%s\\]" date-re day-time-re) .
          ((lambda (tag)
             (svg-tag-make tag :beg 1 :inverse nil :crop-right t :margin 0 :face 'org-date))))
         (,(format "\\[%s \\(%s\\]\\)" date-re day-time-re) .
          ((lambda (tag)
             (svg-tag-make tag :end -1 :inverse t :crop-left t :margin 0 :face 'org-date))))))

      (svg-tag-mode t)
  )
Display emphasis markers on hover

This package makes it much easier to edit Org documents when org-hide-emphasis-markers is turned on. It temporarily shows the emphasis markers around certain markup elements when you place your cursor inside of them. No more fumbling around with = and * characters!

(use-package org-appear
  :hook (org-mode . org-appear-mode))
Better commenting in org-mode code-blocks

Got this from a Stack Exchange answer to work around messed up commenting using the default C-x C-; command. The older/default command messes up lines, undos, and sometimes comment syntax as well.

;; allow comment region in the code edit buffer (according to language)
(defun my-org-comment-dwim (&optional arg)
  (interactive "P")
  (or (org-babel-do-key-sequence-in-edit-buffer (kbd "M-;"))
      (comment-dwim arg)))

;; make `C-c C-v C-x M-;' more convenient
(define-key org-mode-map
  (kbd "M-;") 'my-org-comment-dwim)
TOC for org-mode files
(use-package toc-org
    :after org
    :hook (org-mode . toc-org-enable))

Alternatively

(use-package org-make-toc
  :hook (org-mode . org-make-toc-mode))

Convert all org-keywords/block identifiers to lowercase

It’s always nice to see random people online that are crazy like you and are nice enough to write elisp code for the shit you need. Stolen from Kaushal Modi

(defun peremacs/lower-case-org-keywords ()
  "Lower case Org keywords and block identifiers.

Example: \"#+TITLE\" -> \"#+title\"
         \"#+BEGIN_EXAMPLE\" -> \"#+begin_example\"

Inspiration:
https://code.orgmode.org/bzg/org-mode/commit/13424336a6f30c50952d291e7a82906c1210daf0."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((case-fold-search nil)
          (count 0))
      ;; Match examples: "#+foo bar", "#+foo:", "=#+foo=", "~#+foo~",
      ;;                 "‘#+foo’", "“#+foo”", ",#+foo bar",
      ;;                 "#+FOO_bar<eol>", "#+FOO<eol>".
      (while (re-search-forward "\\(?1:#\\+[A-Z_]+\\(?:_[[:alpha:]]+\\)*\\)\\(?:[ :=~’”]\\|$\\)" nil :noerror)
        (setq count (1+ count))
        (replace-match (downcase (match-string-no-properties 1)) :fixedcase nil nil 1))
      (message "Lower-cased %d matches" count))))

org-journal for journaling requirements

This needs better setting up and integration with either Orgzly or GitJournal for android. iOS seems to have better apps though. Or just make this workable with the termux version of Emacs.

(use-package org-journal
  :init
  ;; Change default prefix key; needs to be set before loading org-journal
  (setq org-journal-prefix-key "C-c j ")

  :bind
  ;; (("C-c t" . journal-file-today)
  ;;  ("C-c y" . journal-file-yesterday))

  :config
  ;; Journal directory and files
  (setq org-journal-dir "~/journal/entries/"
        org-journal-file-format "%Y/%m/%Y%m%d.org"
        org-journal-file-type 'daily
        org-journal-find-file 'find-file)

  ;; Journal file content
  (setq org-journal-date-format "%e %b %Y (%A)"
        org-journal-time-format "(%R)"
        org-journal-file-header "#+title: Daily Journal\n#+startup: showeverything")
  )

AUCTex for LaTex editing + completion

;; FIXME:
;; (use-package auctex
;;   :init
;;   (setq TeX-auto-save t)
;;   (setq TeX-parse-self t)
;;   (setq-default TeX-master nil))

(use-package auctex
  :demand t
  :no-require t
  :mode ("\\.tex\\'" . TeX-latex-mode)
  :config
  (defun latex-help-get-cmd-alist ()    ;corrected version:
    "Scoop up the commands in the index of the latex info manual.
       The values are saved in `latex-help-cmd-alist' for speed."
    ;; mm, does it contain any cached entries
    (if (not (assoc "\\begin" latex-help-cmd-alist))
        (save-window-excursion
          (setq latex-help-cmd-alist nil)
          (Info-goto-node (concat latex-help-file "Command Index"))
          (goto-char (point-max))
          (while (re-search-backward "^\\* \\(.+\\): *\\(.+\\)\\." nil t)
            (let ((key (buffer-substring (match-beginning 1) (match-end 1)))
                  (value (buffer-substring (match-beginning 2)
                                           (match-end 2))))
              (add-to-list 'latex-help-cmd-alist (cons key value))))))
    latex-help-cmd-alist)

  (add-hook 'TeX-after-compilation-finished-functions
            #'TeX-revert-document-buffer))

;; (use-package company-auctex)

Spellcheck

Hunspell

Finally figured this out from a reddit post from 2019.

;; flyspell + aspell??
(setq ispell-dictionary "en_GB")
(setq ispell-program-name "hunspell")
;; below two lines reset the the hunspell to it STOPS querying locale!
;; (setq ispell-local-dictionary "en_GB") ; "en_GB" is key to lookup in `ispell-local-dictionary-alist`

;; tell ispell that apostrophes are part of words
;; and select Bristish dictionary
;; (setq ispell-local-dictionary-alist
;;             (quote ("UK_English" "[[:alpha:]]" "[^[:alpha:]]" "['’]" t ("-d" "en_GB") nil utf-8)))

;; hook for text mode
(add-hook 'text-mode-hook 'flyspell-mode)
;; hook to check spelling for comments in code
(add-hook 'prog-mode-hook 'flyspell-prog-mode)

Aspell based spellchecking

… because Void linux keeps complaining about not being able to find a British English dictionary

(setq ispell-program-name "aspell")
;; Please note ispell-extra-args contains ACTUAL parameters passed to aspell
(setq ispell-extra-args '("--sug-mode=ultra" "--lang=en_GB"))

;; hook for text mode
(add-hook 'text-mode-hook 'flyspell-mode)
;; hook to check spelling for comments in code
(add-hook 'prog-mode-hook 'flyspell-prog-mode)

flyspell-correct provides a more refines UI for spelling checking

This shows a popup like for completions when it finds a misspelled word which makes it somewhat more accessible when writing, rather than having to look down at the message area.

(use-package flyspell-correct
  :after flyspell
  :bind (:map flyspell-mode-map ("C-;" . flyspell-correct-wrapper)))

(use-package flyspell-correct-popup
  :after flyspell-correct)

Something like scrivener from Mac

…cause I’m gonna become a novelist and/or write large books in the near future

(use-package binder)
;; (use-package binder-tutorial)

Blogging

I’ve defined some stuff necessary to make editing a Hugo website easier.

ox-hugo since the go-org keep wrecking up links

My personal static site was/is written with this. I might have to add additional setup to add some of this functionality for project pages but then I hope to eventually move everything to sourcehut or atleast using it to host a website on my own domain.

This source block continues into the next section.

(use-package ox-hugo
  :after ox

Additional setup for streamlining writing posts on the static site:

Blogging flow based on the capture templates in the documentation

This function is called on invoking the org-capture (see next section). This particular function adds a date below the header marked CLOSED which on export to markdown is converted to a regular date field by ox-hugo and is included in the single page view/posts list on the final export. It also changes the TODO tag to DONE. I’m still trying to figure out bundles so this might change soon.

This source block continues into the next section.

:config

(with-eval-after-load 'org-capture
     (defun org-hugo-new-subtree-post-capture-template ()
       "Returns `org-capture' template string for new Hugo post.
   See `org-capture-templates' for more information."
       (let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
              (fname (org-hugo-slug title)))
         (mapconcat #'identity
                    `(
                      ,(concat "* TODO " title)
                      ":PROPERTIES:"
                      ,(concat ":EXPORT_HUGO_BUNDLE: " fname)
                      ":EXPORT_FILE_NAME: index"
                      ":EXPORT_HUGO_AUTO_SET_LASTMOD: t"
                      ":END:"
                      "%?\n")          ;Place the cursor here finally
                    "\n")))

Add capture template

Since the provided template runs independent of my git repo for the website, I’ll have to figure out the file variable and how to point it to the /content-org/blog/posts.org file in the repo. From the original ox-hugo docs code, this is the first template provided, (from under the entry variable in the source block below):

It is assumed that below file is present in org-directory and that it has a “Blog Ideas” heading. It can even be a symlink pointing to the actual location of all-posts.org!

So I’ll change the target to point to my file in the repo directly until I can assign specific (programmatic ?) definitions for the repo in this configuration somewhere.

Capture for blog posts

(add-to-list 'org-capture-templates
              '("h"                ;`org-capture' binding + h
                "Hugo blog post"
                entry
                (file+olp "~/my_gits/brihadeesh.github.io/content-org/blog/posts.org" "Posts")
                (function org-hugo-new-subtree-post-capture-template)))))

Capture for posts on eBird analyses

(add-to-list 'org-capture-templates
                '("r"                ;`org-capture' binding + r
                  "eBird analysis"
                  entry
                  (file+olp "~/work/iiser_tpt-SOIB/ebd_biases_2/content-org/analyses.org" "Analyses")
                  (function org-hugo-new-subtree-post-capture-template)))))

This is the end of the use-package source block for ox-hugo, the parent header in this section.

DISABLED go-org based workflow

This works with the go-org backend/library which natively / directly uses org files as content source. I’m not keen on using this because it doesn’t fully support org syntax and is weirdly buggy.

  1. Hugo compatible links

Adds new link type for go-org friendly internal links.

(org-link-set-parameters
 "hugo"
 :complete (lambda ()
             (concat "{{% ref */"
                     (file-name-nondirectory
                      (read-file-name "File: "))
                     " %}}"))
 :follow #'org-hugo-follow)
  1. Following internal links within Emacs
    (defun org-hugo-follow (link)
      "Follow Hugo link shortcodes"
      (org-link-open-as-file
       (string-trim "{{% ref test.org %}}" "{{% ref " "%}}")))
    
        
  2. Automatically update files with last modified date, when #+lastmod: is available

I might need to enable a similar function for the ox-hugo scheme, perhaps in the capture template itself?

(setq time-stamp-active t
      time-stamp-start "#\\+lastmod:[ \t]*"
      time-stamp-end "$"
      time-stamp-format "%04Y-%02m-%02d")
(add-hook 'before-save-hook 'time-stamp nil)

Work

Emacs Speaks Statistics for R and python(?)

Figure out babel/org-tangle or whatever because Emacs sucks for RMarkdown and org-mode is generally better (see next bit for RMarkdown)

(use-package ess)
;; :ensure t
(require `ess-r-mode)

Polymodes for R

This helps integrate ESS into markdown so editing RMarkdown files is easy. As an added benefit syntax for regular markdown files is supported. I won’t have to install polymode itself explicitly since it’s a dependency for the poly-R modes and will be pulled automatically. I do vaguely remember some issues this caused, will have to

(use-package poly-R
  :config
  (add-to-list 'auto-mode-alist '("\\.md" . poly-markdown-mode))
  (add-to-list 'auto-mode-alist '("\\.Rmd" . poly-markdown+r-mode))

  :hook
  (poly-markdown-mode . flyspell-mode)
  (poly-markdown-mode . visual-line-mode)
  (poly-markdown-mode . auto-fill-mode))

defining org-skeleton for org-babel + R

Got this from worg documentation but I vaguely remember reading about more options for individual source blocks so will have to change / append accordingly. This defines a skeleton or a format for an org file header that’s specific to writing and running R code. It’s bound to a keybind so it can be accessed easily on opening a fresh org-babel document.

The other resource was: R source blocks in Org Mode

More info from the page:

  1. The #+INFOJS_OPT option will generate a HTML document that is foldable and follows the style of GNU/INFO document.
  2. The :session *R* option makes sure all the R code is run in the same session so objects generated in one code block can be accessed from other code blocks.
  3. the :cache yes option is used to avoid re-evaluating unchanged code blocks. This can save significant time when you revise a document with a lot of R code frequently.
  4. The :results output graphics :exports both option will put both the R code and its text and graphics output in the generated document.
  5. The :tangle yes option allows the document to be “tangled” to generate pure code file. The short-cut key for tangling is C-c C-v t, which generates a .R file with all the R code extracted.
  6. Note the –— string will generate a horizontal line in HTML file.
  7. Finally, a hotkey C-S-f4 (while pressing Ctrl and Shift keys, press F4 key) is assigned to invoke this skeleton quickly.
(define-skeleton org-skeleton
  "Header info for a emacs-org file."
  "Title: "
  "#+TITLE:" str " \n"
  "#+AUTHOR: Brihaadeesh S \n"
  "#+email: brihadeesh.santharam@gmail.com\n"
  "#+INFOJS_OPT: \n"
  "#+BABEL: :session *R* :cache yes :results output graphics :exports both :tangle yes \n"
  "-----"
 )

(global-set-key [C-S-f4] 'org-skeleton)

org-present for presentations

See dawiwil’s section on this from his literate init for more about this.

Citar for reference management?

Citar

If I ever get down to writing papers, of course, I’d write them in org-mode or LaTeX so this should be useful considering Mendeley desktop is bloat and I haven’t a clue if FreeBSD even has Zotero. This has additional setup stuff to do with Embark and the rest of that family. This particular config only works with org-mode. Needs a shit ton of work to properly setup.

Also perhaps check out org-ref - it seems a lot simpler. Introduction to org-ref - a video ontroduction

;;(use-package citar
  ;;:no-require
  ;;:custom
  ;;(org-cite-global-bibliography '("~/bib/references.bib"))
  ;;(org-cite-insert-processor 'citar)
  ;;(org-cite-follow-processor 'citar)
  ;;(org-cite-activate-processor 'citar)
  ;; optional: org-cite-insert is also bound to C-c C-x C-@
  ;;:bind
  ;;(:map org-mode-map :package org ("C-c b" . #'org-cite-insert)))

Project management and navigation - projectile

;; project management
(use-package projectile
  ;; :ensure t
  :demand t
  :init (setq projectile-completion-system 'default)
  :bind-keymap
  ("C-c p" . projectile-command-map)
  ;; :diminish projectile-mode
  :config
  (setq projectile-project-search-path '("~/my_gits/" "~/journal/"))
  (projectile-mode +1))



;; (use-package ibuffer-projectile
;;   :after ibuffer
;;   :preface
;;   (defun my/ibuffer-projectile ()
;;     (ibuffer-projectile-set-filter-groups)
;;     (unless (eq ibuffer-sorting-mode 'alphabetic)
;;       (ibuffer-do-sort-by-alphabetic)))
;;   :hook (ibuffer . my/ibuffer-projectile))

Denote for note-taking

I hope this is considerably simpler than org-roam and easier to setup. I don’t particularly like the way org-roam is unnecessarily cluttered and excruciatingly tedious to even get started with.

Basic setup

(use-package denote
    :straight (:source gnu-elpa-mirror)

  :config
  ;; Remember to check the doc strings of those variables.
  (setq denote-directory (expand-file-name "~/documents/denotes/")

        ;; keywords
        denote-known-keywords '("emacs" "r-stats" "work" "thoughts" "politics" "blog-ideas")

        ;; check
        denote-infer-keywords t

        ;; check
        denote-sort-keywords t

        ;; Org is the default, set others here
        denote-file-type nil

        ;; entry prompt asks for title and keywords
        denote-prompts '(title keywords)

        ;; Pick dates, where relevant, with Org's advanced interface:
        denote-date-prompt-use-org-read-date t

        ;; We allow multi-word keywords by default.  The author's
        ;; personal preference is for single-word keywords for a more
        ;; rigid workflow.
        denote-allow-multi-word-keywords t

        ;; read doc string
        denote-date-format nil

        ;; By default, we fontify backlinks in their bespoke buffer.
        denote-link-fontify-backlinks t)

        ;; Also see `denote-link-backlinks-display-buffer-action'
        ;; which is a bit advanced.

  ;; If you use Markdown or plain text files (Org renders links as buttons
  ;; right away)
  (add-hook 'find-file-hook #'denote-link-buttonize-buffer)

  ;; We use different ways to specify a path for demo purposes.
  (setq denote-dired-directories
        (list denote-directory
              (thread-last denote-directory (expand-file-name "attachments"))
              (expand-file-name "~/documents/denotes/books")))

  ;; Generic (great if you rename files Denote-style in lots of places):
  ;; (add-hook 'dired-mode-hook #'denote-dired-mode)
  ;;
  ;; OR if only want it in `denote-dired-directories':
  (add-hook 'dired-mode-hook #'denote-dired-mode-in-directories)

Capture template(s) for Denote

org-capture template for denote - I might want to add some more header arguments (?). I should probably define the user-level command first so I can call it with org-capture. Going by the way it automatically adds dates and tags, I could even switch to this for journaling and completely replace org-journal because it’s clunky. If I were to do that I would have to maybe write a script that renames and organises everything in that repo to a denote-like format.

Prot in the documentation

Read this manual for how to specify `denote-templates’. We do not include an example here to avoid potential confusion.

So perhaps one template per source block?

;; Here is a custom, user-level command from one of the examples we
;; showed in this manual.  We define it here and add it to a key binding
;; below.
;; (defun my-denote-journal ()
;;   "Create an entry tagged 'journal', while prompting for a title."
;;   (interactive)
;;   (denote
;;    (denote--title-prompt)
;;    '("journal")))

Finally, defining the key each of these templates are bound and referencing the templates for org-capture

(with-eval-after-load 'org-capture
  (setq denote-org-capture-specifiers "%l\n%i\n%?")
  (add-to-list 'org-capture-templates
               '("n" "New note (with denote.el)" plain
                 (file denote-last-path)
                 #'denote-org-capture
                 :no-save t
                 :immediate-finish nil
                 :kill-buffer t
                 :jump-to-captured t)))

Keybindings

Denote DOES NOT define any key bindings. It requires arguments acceptable to the bind-keys macro. I’m not entirely sure some of these are necessary since I’m using a capture template.

;; :bind
;; (("C-c n n" . denote)
;;  ("C-c n N" . denote-type)
;;  ("C-c n d" , denote-date)
;;  ("C-c n s" . denote-subdirectory)
;;  ("C-c n t" . denote-template)
;;  ;; renames don't work with `dired-mode', hence placed here
;;  ("C-c n r" . denote-rename-file)
;;  ("C-c n R" . denote-rename-file-using-front-matter)

;;  ;; org-mode specifics (group with `global-mode-map' for multiple formats
;;  ;; or add for each `markdown'/`text'/`org' if using single format)
;;  :map org-mode-map
;;  ("C-c n i" . denote-link) ; "insert" mnemonic
;;  ("C-c n I" . denote-link-add-links)
;;  ("C-c n b" . denote-link-backlinks)
;;  ("C-c n f f" . denote-link-find-file)
;;  ("C-c n f b" . denote-link-find-backlink)

;;  ;; specific to dired
;;  :map dired-mode-map
;;  ("C-c C-d C-i" . denote-link-dired-marked-notes)
;;  ("C-c C-d C-r" . denote-dired-rename-marked-files)
;;  ("C-c C-d C-R" . denote-dired-rename-marked-files-using-front-matter)
;;  ;; Also check the commands `denote-link-after-creating',
;;  ;; `denote-link-or-create'.  You may want to bind them to keys as well.
;; )

)

Version control

Git with Magit and gists with gist.el

(use-package magit
  :bind ("C-x g"    . magit-status))

gist.el to manage github gists from here

(use-package gist)

Undo tree

Helps revert to older versions of files in case I fuck up something somewhere. Hmm. I doubt I ever use it so disabling it now.

(use-package undo-tree
  :init
  (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))
        undo-tree-auto-save-history nil)
  (global-undo-tree-mode)
  :diminish undo-tree-mode)

SSH for personal packages and magit

This needs a ton of work

(use-package keychain-environment
    :config
    (keychain-refresh-environment))

;; ;; import ssh deets from profile
;; (use-package exec-path-from-shell
;;   :config
;;   (exec-path-from-shell-copy-env "SSH_AGENT_PID")
;;   (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))

Minibuffer completion

DISABLED Prescient command history with M-x

(use-package prescient
  :config
  (prescient-persist-mode 1))
;; (use-package selectrum-prescient)

Access a list of recently edited files

Helps jump back into whatever I was doing before closing Emacs. Or my laptop more like it.

(use-package recentf
  :init
  (setq recentf-max-menu-items 25
        recentf-auto-cleanup 'never
        recentf-keep '(file-remote-p file-readable-p))
  (recentf-mode 1))

DISABLED Selectrum for completions UI

If I rememeber right, this is closer to the default completion behaviour in Emacs.

(use-package selectrum
  :init
  (selectrum-mode +1)

  :config
  ;; to make sorting and filtering more intelligent
  (selectrum-prescient-mode +1)

  ;; to save your command history on disk, so the sorting gets more
  ;; intelligent over time
  (prescient-persist-mode +1))

Vertico for completions UI

;; Enable vertico
(use-package vertico
  ;; pulls extensions as well?
  ;; :straight (:host github :repo "minad/vertico")

  :init
  (vertico-mode)

  :config
  (setq
   ;; Grow and shrink the Vertico minibuffer
   vertico-resize t

   ;; No prefix with number of entries
   vertico-count-format nil)

  (advice-add #'tmm-add-prompt :after #'minibuffer-hide-completions)

(Continuing from previous block)

Completion-at-point and completion-in-region with Vertico. Use `consult-completion-in-region’ if Vertico is enabled. Otherwise use the default `completion–in-region’ function. Disabled because I use corfu for completion-at-point.

;; (setq completion-in-region-function
;;           (lambda (&rest args)
;;             (apply (if vertico-mode
;;                        #'consult-completion-in-region
;;                      #'completion--in-region)
;;                    args)))

Prefix the current candidate (See relevant section on the wiki)

(defun minibuffer-format-candidate (orig cand prefix suffix index _start)
    (let ((prefix (if (= vertico--index index)
                      ""
                    "   ")))
      (funcall orig cand prefix suffix index _start)))

  (advice-add #'vertico--format-candidate
             :around #'minibuffer-format-candidate)

Completions for M-: as well; closes the use-package function started at Vertico header.

(defun minibuffer-vertico-setup ()

  (setq truncate-lines t)
  (setq completion-in-region-function
        (if vertico-mode
            #'consult-completion-in-region
          #'completion--in-region)))

(add-hook 'vertico-mode-hook #'minibuffer-vertico-setup)
(add-hook 'minibuffer-setup-hook #'minibuffer-vertico-setup)
)

Vertico extensions

Again stolen from Karthik Chikmaglur and needs heavy work, hence not enabled

(use-package vertico-multiform
  :load-path "~/.emacs.d/lisp/vertico-extensions/"
  :commands vertico-multiform-mode
  :after vertico-flat
  :bind (:map vertico-map
              ("M-q" . vertico-multiform-grid)
              ("C-l" . vertico-multiform-unobtrusive)
              ("C-M-l" . embark-export))
  :init (vertico-multiform-mode 1)
  :config
  (setq vertico-multiform-categories
         '((file my/vertico-grid-mode reverse)
           (project-file my/vertico-grid-mode reverse)
           (imenu buffer)
           (consult-location buffer)
           (consult-grep buffer)
           (notmuch-result reverse)
           (minor-mode reverse)
           (reftex-label reverse)
           (bib-reference reverse)
           (xref-location reverse)
           (t unobtrusive)))
   (setq vertico-multiform-commands
         '((load-theme my/vertico-grid-mode reverse)
           (my/toggle-theme my/vertico-grid-mode reverse)
           (consult-dir-maybe reverse)
           (consult-dir reverse)
           (consult-history reverse)
           (consult-completion-in-region reverse)
           (completion-at-point reverse)
           (org-roam-node-find reverse)
           (embark-completing-read-prompter reverse)
           (embark-act-with-completing-read reverse)
           (embark-prefix-help-command reverse)
           (tmm-menubar reverse)))

   (defun vertico-multiform-unobtrusive ()
     "Toggle the quiet display."
     (interactive)
     (vertico-multiform--display-toggle 'vertico-unobtrusive-mode)
     (if vertico-unobtrusive-mode
         (vertico-multiform--temporary-mode 'vertico-reverse-mode -1)
       (vertico-multiform--temporary-mode 'vertico-reverse-mode 1))))
(use-package vertico-unobtrusive
  :load-path "~/.local/share/git/vertico/extensions/"
  :after vertico-flat)

#+name vertico-grid

(use-package vertico-grid
  :load-path "~/.emacs.d/lisp/vertico-extensions/"
  :after vertico
  ;; :bind (:map vertico-map ("M-q" . vertico-grid-mode))
  :config
  (defvar my/vertico-count-orig vertico-count)
  (define-minor-mode my/vertico-grid-mode
    "Vertico-grid display with modified row count."
    :global t :group 'vertico
    (cond
     (my/vertico-grid-mode
      (setq my/vertico-count-orig vertico-count)
      (setq vertico-count 4)
      (vertico-grid-mode 1))
     (t (vertico-grid-mode 0)
        (setq vertico-count my/vertico-count-orig))))
  (setq vertico-grid-separator "    ")
  (setq vertico-grid-lookahead 50))
(use-package vertico-quick
      :load-path "~/.emacs.d/lisp/vertico-extensions/"
      :after vertico
      :bind (:map vertico-map
             ("M-i" . vertico-quick-insert)
             ("C-'" . vertico-quick-exit)
             ("C-o" . vertico-quick-embark))
      :config
      (defun vertico-quick-embark (&optional arg)
        "Embark on candidate using quick keys."
        (interactive)
        (when (vertico-quick-jump)
          (embark-act arg))))
(use-package vertico-directory
  :load-path "~/.emacs.d/lisp/vertico-extensions/"
  :hook (rfn-eshadow-update-overlay vertico-directory-tidy)
  :after vertico
  :bind (:map vertico-map
         ("DEL"   . vertico-directory-delete-char)
         ("M-DEL" . vertico-directory-delete-word)
         ("C-w"   . vertico-directory-delete-word)
         ("RET"   . vertico-directory-enter)))
(use-package vertico-repeat
  :load-path "~/.emacs.d/lisp/vertico-extensions/"
  :after vertico
  :bind (("C-x ." . vertico-repeat)))
(use-package vertico-reverse
  ;; :disabled
  :load-path "~/.emacs.d/lisp/vertico-extensions/"
  :after vertico)
(use-package vertico-flat
  :load-path "~/.emacs.d/lisp/vertico-extensions/"
  ;; :bind (:map vertico-map
  ;;             ("M-q" . vertico-flat-mode))
  :after vertico)
(use-package vertico-buffer
      :load-path "~/.emacs.d/lisp/vertico-extensions/"
      :after vertico
      ;; :hook (vertico-buffer-mode . vertico-buffer-setup)
      :config
      (setq vertico-buffer-display-action 'display-buffer-reuse-window))

Orderless completion

Search for commands, buffers, etc with vertico without having to match the order of words in the command. Adding spaces between keywords can match commands with those words anywhere in them. This config was bootlegged from minad’s config at the consult wiki.

(use-package orderless
  :config
(defvar +orderless-dispatch-alist
  '((?% . char-fold-to-regexp)
    (?! . orderless-without-literal)
    (?`. orderless-initialism)
    (?= . orderless-literal)
    (?~ . orderless-flex)))

;; Recognizes the following patterns:
;; * ~flex flex~
;; * =literal literal=
;; * %char-fold char-fold%
;; * `initialism initialism`
;; * !without-literal without-literal!
;; * .ext (file extension)
;; * regexp$ (regexp matching at end)
(defun +orderless-dispatch (pattern index _total)
  (cond
   ;; Ensure that $ works with Consult commands, which add disambiguation suffixes
   ((string-suffix-p "$" pattern)
    `(orderless-regexp . ,(concat (substring pattern 0 -1) "[\x100000-\x10FFFD]*$")))
   ;; File extensions
   ((and
     ;; Completing filename or eshell
     (or minibuffer-completing-file-name
         (derived-mode-p 'eshell-mode))
     ;; File extension
     (string-match-p "\\`\\.." pattern))
    `(orderless-regexp . ,(concat "\\." (substring pattern 1) "[\x100000-\x10FFFD]*$")))
   ;; Ignore single !
   ((string= "!" pattern) `(orderless-literal . ""))
   ;; Prefix and suffix
   ((if-let (x (assq (aref pattern 0) +orderless-dispatch-alist))
        (cons (cdr x) (substring pattern 1))
      (when-let (x (assq (aref pattern (1- (length pattern))) +orderless-dispatch-alist))
        (cons (cdr x) (substring pattern 0 -1)))))))

;; Define orderless style with initialism by default
(orderless-define-completion-style +orderless-with-initialism
  (orderless-matching-styles '(orderless-initialism orderless-literal orderless-regexp)))

;; You may want to combine the `orderless` style with `substring` and/or `basic`.
;; There are many details to consider, but the following configurations all work well.
;; Personally I (@minad) use option 3 currently. Also note that you may want to configure
;; special styles for special completion categories, e.g., partial-completion for files.
;;
;; 1. (setq completion-styles '(orderless))
;; This configuration results in a very coherent completion experience,
;; since orderless is used always and exclusively. But it may not work
;; in all scenarios. Prefix expansion with TAB is not possible.
;;
;; 2. (setq completion-styles '(substring orderless))
;; By trying substring before orderless, TAB expansion is possible.
;; The downside is that you can observe the switch from substring to orderless
;; during completion, less coherent.
;;
;; 3. (setq completion-styles '(orderless basic))
;; Certain dynamic completion tables (completion-table-dynamic)
;; do not work properly with orderless. One can add basic as a fallback.
;; Basic will only be used when orderless fails, which happens only for
;; these special tables.
;;
;; 4. (setq completion-styles '(substring orderless basic))
;; Combine substring, orderless and basic.
;;
(setq completion-styles '(orderless)
      completion-category-defaults nil
      ;;; Enable partial-completion for files.
      ;;; Either give orderless precedence or partial-completion.
      ;;; Note that completion-category-overrides is not really an override,
      ;;; but rather prepended to the default completion-styles.
      ;; completion-category-overrides '((file (styles orderless partial-completion))) ;; orderless is tried first
      completion-category-overrides '((file (styles partial-completion)) ;; partial-completion is tried first
                                      ;; enable initialism by default for symbols
                                      (command (styles +orderless-with-initialism))
                                      (variable (styles +orderless-with-initialism))
                                      (symbol (styles +orderless-with-initialism)))
      orderless-component-separator #'orderless-escapable-split-on-space ;; allow escaping space with backslash!
      orderless-style-dispatchers '(+orderless-dispatch)))

Persistent command history

Persist history over Emacs restarts. Vertico sorts by history position.

(use-package savehist
    :init
    (savehist-mode))

A few more useful configurations

;; (use-package emacs
  ;; :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; Alternatively try `consult-completing-read-multiple'.
  (defun crm-indicator (args)
    (cons (concat "[CRM] " (car args)) (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)

  ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
  ;; Vertico commands are hidden in normal buffers.
  ;; (setq read-extended-command-predicate
  ;;       #'command-completion-default-include-p)

  ;; Enable recursive minibuffers
  (setq enable-recursive-minibuffers t)
  ;; )

Richer annotations in minubuffer

(use-package marginalia
  :after vertico

  ;; The :init configuration is always executed (Not lazy!)
  :init

  ;; Must be in the :init section of use-package such that the mode gets
  ;; enabled right away. Note that this forces loading the package.
  (marginalia-mode)

  ;; When using Selectrum, ensure that Selectrum is refreshed when cycling annotations.
  ;; (advice-add #'marginalia-cycle :after
  ;;             (lambda () (when (bound-and-true-p selectrum-mode) (selectrum-exhibit 'keep-selected))))

  ;; Prefer richer, more heavy, annotations over the lighter default variant.
  ;; E.g. M-x will show the documentation string additional to the keybinding.
  ;; By default only the keybinding is shown as annotation.
  ;; Note that there is the command `marginalia-cycle' to
  ;; switch between the annotators.
  ;; (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  )

Consult adds more minibuffer functionality

(use-package consult
  ;; Replace bindings. Lazily loaded due by `use-package'.
  :bind
  (("C-x B" . consult-buffer)
   ("C-x 4 b" . consult-buffer-other-window)
   ("C-x 5 b" . consult-buffer-other-frame)
   ("M-g i" . consult-imenu)
   ("M-g I" . consult-project-imenu)
   ;; searching for files
   ("M-s f" . consult-find)
   ("M-s F" . consult-git-grep)
   ("M-s g" . consult-grep)
   ("M-s r" . consult-ripgrep)
   ("C-c f r" . consult-recent-file)
   ("C-x C-" . consult-recent-file)
   ;; Isearch integration
   ("C-s" . consult-isearch-history)
   ("C-c L" . consult-outline)
   ("C-c h l" . consult-org-heading)
   ;; yank from kill-ring
   ("M-y" . consult-yank-pop)
   )

  ;; Enable automatic preview at point in the *Completions* buffer. This is
  ;; relevant when you use the default completion UI. You may want to also
  ;; enable `consult-preview-at-point-mode` in Embark Collect buffers.
  :hook (completion-list-mode . consult-preview-at-point-mode)

  :config
  ;; Configure the narrowing key.
  (setq consult-narrow-key "<") ;; (kbd "C-+")

  ;; Configure a function which returns the project
  ;; root directory - projectile.el (projectile-project-root)
  (autoload 'projectile-project-root "projectile")
  (setq consult-project-root-function #'projectile-project-root)

  ;; use consult with perspective.el
  (consult-customize consult--source-buffer :hidden t :default nil)

  (defvar consult--source-perspective
    (list :name     "Perspective"
          :narrow   ?s
          :category 'buffer
          :state    #'consult--buffer-state
          :default  t
          :items    #'persp-get-buffer-names))

  (push consult--source-perspective consult-buffer-sources)
  )

;; Optionally add the `consult-flycheck' command.
(use-package consult-flycheck
  :bind (:map flycheck-command-map
              ("!" . consult-flycheck)))

Embark - actions; reorganise

This I’ve not used yet but makes a lot of stuff easier like searchingfor the definition or the help/info page a highlighted word from within the buffer or the minibuffer or even the minibuffer completion list.

Group with the rest of the packages from this family?

(use-package embark
  :bind
  (("C-S-a" . embark-act)       ;; pick some comfortable binding
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'

  :init
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command)

  :config
  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

;; Consult users will also want the embark-consult package.
(use-package embark-consult
  :ensure t
  :after (embark consult)
  :demand t ; only necessary if you have the hook below
  ;; if you want to have consult previews as you move around an
  ;; auto-updating embark collect buffer
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

Corfu for completion in buffer

This might need some more work - integration with minad’s ~cape~ for various kinds of completions although he alleges this works well with base Emacs.

(use-package corfu
  :bind
  (:map corfu-map
         ;; ??? :states 'insert
         ("TAB" . corfu-next)
         ([tab] . corfu-next)
         ("S-TAB" . corfu-previous)
         ([backtab] . corfu-previous)
         ("<escape>" . corfu-quit)
         ("<return>" . corfu-insert)
         ("M-d" . corfu-show-documentation)
         ("M-l" . 'corfu-show-location)
         ("SPC" . corfu-insert-separator))

  :custom
  ;; Only use `corfu' when calling `completion-at-point' or
  ;; (corfu-auto t)

  ;; `indent-for-tab-command'
  ;; (corfu-auto-prefix 3)
  ;; (corfu-auto-delay 0.2)

  ;; size
  (corfu-min-width 80)

  ;; Always have the same width
  (corfu-max-width corfu-min-width)
  (corfu-count 14)
  (corfu-scroll-margin 4)
  (corfu-cycle t)

  ;; Show documentation in echo area?
  (corfu-echo-documentation t)

  ;; Preselect first candidate?
  (corfu-preselect-first nil)

  ;; Preview current candidate?
  (corfu-preview-current 'insert)

  ;; quit if no match
  (corfu-quit-no-match t)

  :init
  (global-corfu-mode))

CAPE - extensions for corfu

Corfu needs cape to provide completion backends because it’s extremely stripped down. Will have to check what other backends I’ll need to enable.

(use-package cape
    :config
    (setq cape-dabbrev-min-length 2)

    :init
    ;; Add `completion-at-point-functions', used by `completion-at-point'.

    (dolist (backend '( cape-file cape-dabbrev cape-keyword cape-abbrev
                        cape-ispell cape-dict cape-symbol cape-line ))
                     (add-to-list 'completion-at-point-functions backend)))

(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-keyword)
(add-to-list 'completion-at-point-functions #'cape-ispell)
(add-to-list 'completion-at-point-functions #'cape-dict)
(add-to-list 'completion-at-point-functions #'cape-symbol)

Look

Font configuration

Setting a font

;; first set default
(set-face-attribute 'default nil :family "Hack" :height 70)

;; then set variable-pitch fonts
(set-face-attribute 'variable-pitch nil :family "Iosevka Aile" :height 1.1)

;; finally set fixed-pitch
(set-face-attribute 'fixed-pitch nil :family "Hack" :height 1.0)

Line spacing

Usually 0, less if possible but Emacs doesn’t allow for that.

;; Line spacing, can be 0 for code and 1 or 2 for text
(setq-default line-spacing 0.1)

Editor theme

Update: <2022-11-21 Mon> Moved this up so it doesn’t throw the cryptic error with Modus themes: Debugger entered--Lisp error: (wrong-number-of-arguments (1 . 2) 8) This is based on Adam Spiers’s comment - the theme should be loaded before custom.el is pulled in to avoid issues with version mismatch like the shit with the org package.

Modus from Protesilaos!

This might need additional setting since modus themes are now included within Emacs

(use-package modus-themes
  :straight (:source gnu-elpa-mirror)

  :init
  (setq modus-themes-mixed-fonts t
        modus-themes-bold-constructs t
        modus-themes-italic-constructs t
        modus-themes-region '(no-extend)
        modus-themes-mode-line '(accented)
        modus-themes-prompts '(backgound bold intense)
        ;; modus-themes-hl-line 'accented
        modus-themes-intense-markup t
        modus-themes-region '(no-extend bg-only)
        modus-themes-subtle-line-numbers t
        modus-themes-fringes '(subtle)
        modus-themes-language-checkers '(straight-underline faint)
        modus-themes-org-blocks '(gray-background)

        modus-themes-completions
        '((matches . (bold background intense))
          (selection . (bold background intense))
          (popup . (accented))))

  (defun peremacs/call-modus-operandi ()
    (interactive)
    ;; heading backgrounds work better here
    (disable-theme 'modus-vivendi)
    (setq modus-themes-headings
          '((1 . (overline background variable-pitch bold 1.1))
            (2 . (overline background variable-pitch bold))
            (3 . (overline background variable-pitch semibold))
            (4 . (overline background variable-pitch semibold))
            (t . (overline variable-pitch semibold))))
    (modus-themes-load-operandi))

  (defun peremacs/call-modus-vivendi ()
    (interactive)
    (disable-theme 'modus-operandi)
    (setq modus-themes-headings
          '((1 . (overline monochrome variable-pitch bold 1.1))
            (2 . (overline monochrome variable-pitch bold))
            (3 . (overline monochrome variable-pitch semibold))
            (4 . (overline monochrome variable-pitch semibold))
            (t . (overline variable-pitch semibold))))
    (modus-themes-load-vivendi))


  ;; set semibold as the bold face
  ;; (for those fonts that provide this face)
  ;; (set-face-attribute 'bold nil :weight 'semibold)


  ;; :config
  ;; Load the theme files before enabling a theme
  (modus-themes-load-themes)

  ;; Load the theme of your choice:
  ;; (peremacs/call-modus-operandi)
  (peremacs/call-modus-vivendi)
)

Commentary

An elegant theme highlighting comments only

(use-package commentary-theme
  ;;:config
  ;;(load-theme 'commentary t)
  )

My themes

Neither of these work using straight.el or use-package, together or separately (afaik). If these work, I could maybe add some more of my own.

Forked from the colorless-themes macro. This includes my version of the macro, original themes from Thomas Letan, and some additional themes of my own that use this macro.

(use-package colourless-themes
    :straight (:host gitlab :repo "peregrinator/colourless-themes-el")
    ;;:config
    ;;(load-theme 'beelzebub t)
    )

DISABLED unused

DISABLED Wilmersdorf

I saw this on doom-themes but I don’t want to pull all of those just for this, so installing from it’s GitHub using straight.el. But it fails to load with use-package so I’m going to have to do it manually.

(use-package wilmersdorf
  :straight (:host github :repo "ianyepan/wilmersdorf-emacs-theme")

  ;; :config
  ;; (load-theme 'wilmersdorf t)
  )

DISABLED Tao

Monochrome theme with minimal bold highlights and boxes?

(use-package tao-theme
  :config
  ;; load theme
  (load-theme 'tao-yang t)
  ;; (load-theme 'tao-yin t)
  )

DISABLED Expresso

(use-package espresso-theme
    :straight (:host github :repo "dgutov/espresso-theme")
    ;;:config
    (load-theme 'espresso t)
    )

DISABLED Github dark

(use-package github-dark-vscode-theme
  :config
  (load-theme 'github-dark-vscode t)

  ;; fixed upstream
  ;; unrelated but the cursor colour really needs improvement
  ;; (set-cursor-color "#ffffff")
  )

DISABLED Github modern (light)

(use-package github-modern-theme
  :config
  (load-theme 'github-modern t)
  )

DISABLED Vale

(use-package vale
  :straight (:type git :repo "https://codeberg.org/ext0l/vale.el")
  :config
  ;; (load-theme 'vale t)
  )

DISABLED Parchment

Based on the screenshot of Haskell code on the Pragmata Pro website.

(use-package Parchment-theme
  :straight (:host github :repo "brihadeesh/emacs-parchment-theme")
  :config
  ;; (load-theme 'Parchment t)
  )

DISABLED Almost mono

(use-package almost-mono-themes
  :config
  ;; (load-theme 'almost-mono-black t)
  ;; (load-theme 'almost-mono-gray t)
  ;; (load-theme 'almost-mono-cream t)
  ;; (load-theme 'almost-mono-white t)
  )

DISABLED Stimmung themes for nearly monochrome appearance

(use-package stimmung-themes
  ;; :straight (stimmung-themes :host github :repo "motform/stimmung-themes") ; if you are a straight shooter
  :config
  ;; (stimmung-themes-load-dark)
  )

Highlighted line-mode

;; cursorline
(global-hl-line-mode 1)

Solid window dividers

;; (setq window-divider-default-right-width 1)
;; (setq window-divider-default-bottom-width 1)
;; (setq window-divider-default-places 'all)
;; (window-divider-mode)
(setq window-divider-default-right-width 1)
(setq window-divider-default-bottom-width 1)
(setq window-divider-default-places 'right-only)
(add-hook 'after-init-hook #'window-divider-mode)

Something about underlines

Underline line at descent position, not baseline position

(setq x-underline-at-descent-line t)

Cursor configuration

(set-default 'cursor-type  '(bar . 2))
(blink-cursor-mode 1)

Line-number format

(setq linum-format "%4d ")

Visual not audible bell

Flashes modeline for warnings from purcell

;; No sound
(setq ring-bell-function 'ignore)

(use-package mode-line-bell
  :config
  (mode-line-bell-mode))

No Tooltips

(tooltip-mode 0)

Minibuffer appearance?

As per Hamilton9508’s comment he makes a single minibuffer-only frame across the bottom of the Emacs window and so the rest of the frames have only a single buffer (i.e. the buffer being edited/used) and no minubuffer of it’s own. Not sure if this will work for me but I’ll perhaps give it a shot.

(setq minibuffer-frame-alist '(
            (name . "minibuf")
            (menu-bar-lines . 0)
            (vertical-scroll-bars . nil)
            (auto-raise . t)
            (sticky . t)
            (left . 0)
            (top . -1)
            (height . 1)
            (internal-border-width . 0)
            (minibuffer . only)))

Minimalist and ordered mode-line

People seem to use packages for this. I’ve considered using the doom-modeline but it seems to be pretty heavy in terms of dependencies and I’d like a mode-line with a much more fundamental interface although it’s still a good contender considering it’s very simple to configure. I’m also considering simple-mode-line.

Mood-line because I’m fucking tired

(use-package mood-line
  :config
  (mood-line-mode)
  )

Pulse to locate cursor with Protesilaos’s pulsar

(use-package pulsar
  :straight (:host gitlab :repo "protesilaos/pulsar")

  :custom
  (pulsar-pulse-functions ; Read the doc string for why not `setq'
   '(recenter-top-bottom
      move-to-window-line-top-bottom
      reposition-window
      ;; bookmark-jump
      ;; other-window
      ;; delete-window
      ;; delete-other-windows
      forward-page
      backward-page
      scroll-up-command
      scroll-down-command
      ;; windmove-right
      ;; windmove-left
      ;; windmove-up
      ;; windmove-down
      ;; windmove-swap-states-right
      ;; windmove-swap-states-left
      ;; windmove-swap-states-up
      ;; windmove-swap-states-down
      ;; tab-new
      ;; tab-close
      ;; tab-next
      org-next-visible-heading
      org-previous-visible-heading
      org-forward-heading-same-level
      org-backward-heading-same-level
      outline-backward-same-level
      outline-forward-same-level
      outline-next-visible-heading
      outline-previous-visible-heading
      outline-up-heading))

   :config
   (setq pulsar-pulse-on-window-change t)
   (setq pulsar-pulse t)
   (setq pulsar-delay 0.055)
   (setq pulsar-iterations 10)
   (setq pulsar-face 'pulsar-yellow)

   (pulsar-global-mode 1)

   :bind (("C-c l" . pulsar-pulse-line)
          ("C-c h l" . pulsar-highlight-line)
          ("C-l" . pulsar-recenter-middle))

   :hook
   (consult-after-jump-hook . pulsar-recenter-middle)
   (consult-after-jump-hook . pulsar-reveal-entry)
   (imenu-list-after-jump . pulsar-pulse-line))

Display complex key-binding suggestions

(use-package which-key
  :diminish which-key-mode
  :config
  (which-key-mode))

Diminish for a cleaner modeline

org-indent-mode doesn’t get disabled by the default method.

  (use-package diminish
    :diminish auto-fill-function
    :diminish flyspell-mode
    :diminish visual-line-mode
  )

(defun peremacs/diminish-org-indent ()
    (interactive)
    (diminish 'org-indent-mode ""))
(add-hook 'org-indent-mode-hook 'peremacs/diminish-org-indent)

Pixel scroll precision mode (Emacs 29+)

(pixel-scroll-precision-mode +1)

Make “Emacs” the window title

(setq-default frame-title-format '("Emacs"))

Make scratch buffer and minibuffer blank

(setq initial-scratch-message "")
(setq inhibit-startup-echo-area-message t)
(setq inhibit-startup-message t)
(setq initial-scratch-message nil)

Show keystrokes

Stolen from Karthik Chikmaglur’s emacs.d; shows what is typed immediately.

(setq echo-keystrokes 0.01)

Window Management

EXWM

This ofc doesn’t work on wayland and pgtk emacs but am I willing to learn C++ and emacs-lisp well enough to contribute to porting this to wayland/wlroots or something?

(use-package exwm
  ;; :ensure t

  :diminish

  :custom
  (exwm-workspace-number 4)

  ;; (defun exwm-start-process (command)
  ;;   "Start a process via a shell COMMAND."
  ;;   (interactive (list (read-shell-command "$ ")))
  ;;   (start-process-shell-command command nil command))

  ;; ((kbd "<s-return>") #'exwm-start-process)

  ;; (exwm-input-set-key (kbd "<s-return>") #'exwm-start-process)

  :config
  ;; This now has to be toggled separately in the `~/.xinitrc'
  ;; see https://www.reddit.com/r/emacs/comments/mjx2qd/conditional_loading_for_exwm_with_usepackage/gte7puu/
  (require 'exwm-config)
  ;; (exwm-config-default)

  ;; Effective use of EXWM requires the ability to return from char-mode to line-mode.
  ;; This will be performed with s-r.
  (exwm-input-set-key (kbd "s-r") #'exwm-reset)

  ;; Hide all windows except the current one.
  (exwm-input-set-key (kbd "s-o") #'delete-other-windows)

  ;; Close the current window and kill its buffer.
  (exwm-input-set-key (kbd "C-s-x") #'kill-buffer-and-window)

  ;; Close the current window without killing its buffer.
  (exwm-input-set-key (kbd "s-x") #'delete-window)

  ;; Open an Eshell buffer in the current buffer’s location.
  (exwm-input-set-key (kbd "C-z") #'eshell-find-eshell-here)

  ;;  Move point to the windows immediately around the current window.
  (exwm-input-set-key (kbd "s-h") #'windmove-left)
  (exwm-input-set-key (kbd "s-j") #'windmove-down)
  (exwm-input-set-key (kbd "s-k") #'windmove-up)
  (exwm-input-set-key (kbd "s-l") #'windmove-right)
  (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch))

Workspaces with perspective-el

Independent workspaces for different projects like profiles on RStudio but perhaps a lot more dynamic. This might need more work hence adding a link to the project page here.

(use-package perspective
  :demand t

  :init
  ;; apparently it's essential to define a prefix on Emacs=28
  (setq persp-mode-prefix-key (kbd "C-x x"))

  :bind
  ;; these work with selectrum/vertico i.e. `completing-read'
  ;; type completion systems that are appararently closer to
  ;; base Emacs functioning.
  (("C-x b" . persp-switch-to-buffer*)
   ;;("C-x k" . persp-kill-buffer*)
  )

  :config
  ;; Running `persp-mode' multiple times resets the perspective list...
(unless (equal (default-value 'persp-mode) t)
  (persp-mode 1)))

persp-projectile for proper workspace window management

(use-package persp-projectile
  :bind
  ("C-x x s". persp-projectile-switch-project))

Undo disrupted window/frame arrangement after using some shit

Stolen from Karthik Chikmaglur’s emacs.d

(use-package winner
  :disabled
  :commands winner-undo
  :bind (("C-x C-/" . winner-undo)
         ("s-/" . winner-undo)
         ("s-S-/" . winner-redo))
  :config
  (winner-mode +1))

Ace-window helps with navigation between multiple windows

Simpler navigation between open Emacs windows

(use-package ace-window

  :init
  (setq aw-keys '(?a ?s ?d ?f ?j ?k ?l ?o))

  :bind (("C-x o" . ace-window)
         ("M-o" . other-window)
         ("M-o" . ace-window))

  :diminish ace-window-mode)

Other actions that ace-window handles:

 (defvar aw-dispatch-alist
 '((?x aw-delete-window "Delete Window")
	(?m aw-swap-window "Swap Windows")
	(?M aw-move-window "Move Window")
	(?c aw-copy-window "Copy Window")
	(?j aw-switch-buffer-in-window "Select Buffer")
	(?n aw-flip-window)
	(?u aw-switch-buffer-other-window "Switch Buffer Other Window")
	(?c aw-split-window-fair "Split Fair Window")
	(?v aw-split-window-vert "Split Vert Window")
	(?b aw-split-window-horz "Split Horz Window")
	(?o delete-other-windows "Delete Other Windows")
	(?? aw-show-dispatch-help))
 "List of actions for `aw-dispatch-default'.")

Sane native window management

Focuses new windows when created.

;; Window management
;; focus new windows once created
(use-package window
  :straight (:type 'built-in)
  :bind (("C-x 3" . hsplit-last-buffer)
         ("C-x 2" . vsplit-last-buffer))
  :preface
  (defun hsplit-last-buffer ()
    "Gives the focus to the last created horizontal window."
    (interactive)
    (split-window-horizontally)
    (other-window 1))

  (defun vsplit-last-buffer ()
    "Gives the focus to the last created vertical window."
    (interactive)
    (split-window-vertically)
    (other-window 1)))

Better popups with popper

(use-package popper
    :bind (("C-`"   . popper-toggle-latest)
           ("M-`"   . popper-cycle)
           ("C-M-`" . popper-toggle-type))

    :init
    ;; assign windows to popper (to appear as popups)
    (setq popper-reference-buffers
          '("\\*Messages\\*"
            "Output\\*$"
            "\\*Backtrace\\*"
            "\\*Warnings\\*"
            "^Calc:"
            "^\\*ielm\\*"
            ;; terminals as popups
            "^\\*eshell.*\\*$" eshell-mode
            "^\\*shell.*\\*$" shell-mode
            "^\\*term.*\\*$" term-mode
            "^\\*vterm.*\\*$" vterm-mode
            help-mode
            compilation-mode
            ;; magit stuff
            "^magit:*" magit-mode
            "^\\*Ilist\\*$"
            ;; R console that comes with ESS
            "^\\*iESS" iESS-mode
            "^\\*R\\*"
            ;; R-help windows
            "^\\*help\\[R\\]([a-z]*)\\*"
            ))

    ;;grouping popups by projectile groups
    (setq popper-group-function #'popper-group-by-projectile)

    ;; popper UI configguration
    (setq popper-modeline nil)

    (popper-mode +1)
    ;; echo area hints?
    (popper-echo-mode +1)
    )

Garbage

from customize API

This keeps the init.el cleaner and without junk from customize.el API allows for an option to gitignore your custom.el cause it’s junk.

  ;; Offload the custom-set-variables to a separate file
  ;; (setq custom-file "~/.emacs.d/custom.el")
  (setq custom-file (concat user-emacs-directory "/custom.el"))
  (unless (file-exists-p custom-file)
    (write-region "" nil custom-file))

;; Load custom file. Don't hide errors. Hide success message
;; OR DON'T EVEN BOTHER WITH IT
;; (load custom-file nil t)

from backups and autosaves(?)

;;; Put Emacs auto-save and backup files to one folder
(defconst emacs-tmp-dir (expand-file-name (format "emacs%d" (user-uid)) temporary-file-directory))

(setq
 backup-by-copying t                                        ; Avoid symlinks
 delete-old-versions t
 kept-new-versions 6
 kept-old-versions 2
 version-control t
 auto-save-list-file-prefix emacs-tmp-dir
 auto-save-file-name-transforms `((".*" ,emacs-tmp-dir t))  ; Change autosave dir to tmp
 backup-directory-alist `((".*" . ,emacs-tmp-dir)))

;;; Lockfiles unfortunately cause more pain than benefit
(setq create-lockfiles nil)

Defaults

Primarily bootlegged from Sanemacs and changed when appropriate (and when I thought I understood what I was doing)

Disable native popups(?) and bell

;; not sure what this is about
;; (setq-default indent-tabs-mode nil)
;; disable popups?
;; (setq pop-up-windows nil)
;; Disable bell sound
(setq ring-bell-function 'ignore)

Only y or n prompts for speed

Apparently there is a short-answers variable

;; (fset 'yes-or-no-p 'y-or-n-p)

(setq-default
 use-short-answers t

 ;; Ok to visit non existent files (no confirmation reqd)
 confirm-nonexistent-file-or-buffer nil)

Merge Emacs and system clipboards

;; Merge system's and Emacs' clipboard
(setq-default select-enable-clipboard t)

Overwrite selected text

(delete-selection-mode 1)

Join line to following line

Plagiarised from pragmatic emacs. For the reverse, emacs has a slightly obscurely named command delete-indentation which is bound to M-^ which can be rather useful. From the help for the function (which you can always look up using C-h k M-^ or C-h f delete-indentation)

;; join line to next line
(global-set-key (kbd "C-j")
            (lambda ()
                  (interactive)
                  (join-line -1)))

Simpler kill buffer behaviour

(defun peremacs/kill-this-buffer ()
  (interactive) (kill-buffer (current-buffer)))
(global-set-key (kbd "C-x k") 'peremacs/kill-this-buffer)

Kill without accessing clipboard - reassess if this is really necessary

(defun peremacs/backward-kill-word ()
  (interactive "*")
  (push-mark)
  (backward-word)
  (delete-region (point) (mark)))

(global-set-key (kbd "M-DEL") 'peremacs/backward-kill-word)
(global-set-key (kbd "C-DEL") 'peremacs/backward-kill-word)

Return to last position in buffer

Opens files at last position used. Something about this on Emacs Wiki

(save-place-mode 1)

Prompt before closing Emacs

;; Confirm when killing Emacs
(setq confirm-kill-emacs (lambda (prompt)
                           (y-or-n-p-with-timeout prompt 2 nil)))

Prevent angle braces from throwing errors

(modify-syntax-entry ?< ".")
(modify-syntax-entry ?> ".")

DISABLED Don’t follow symlinks

;; don't follow symlinks? hopefully this solves the
;; `symbols function definition is void: org-file-name-concat' error
(setq vc-follow-symlinks nil)

Code utilities

Templates and snippets with minad’s tempel

Seems a lot simpler than yasnippet but will have to work on templates.

(use-package tempel
  ;; Require trigger prefix before template name when completing.
  ;; :custom
  ;; (tempel-trigger-prefix "<")

  :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand
         ("M-*" . tempel-insert))

Configuration: I’m setting the tempel-path because it defaults to ~/.config/emacs/templates which I don’t use. But I think I’ll eventually switch to something like that.

  :init
(setq tempel-path "~/.emacs.d/templates")

Setup completion at point

(defun tempel-setup-capf ()
  ;; Add the Tempel Capf to `completion-at-point-functions'.
  ;; `tempel-expand' only triggers on exact matches. Alternatively use
  ;; `tempel-complete' if you want to see all matches, but then you
  ;; should also configure `tempel-trigger-prefix', such that Tempel
  ;; does not trigger too often when you don't expect it. NOTE: We add
  ;; `tempel-expand' *before* the main programming mode Capf, such
  ;; that it will be tried first.
  (setq-local completion-at-point-functions
              (cons #'tempel-expand
                    completion-at-point-functions)))

Hooks

:hook
(prog-mode . tempel-setup-capf)
(text-mode . tempel-setup-capf)

    ;; Optionally make the Tempel templates available to Abbrev,
    ;; either locally or globally. `expand-abbrev' is bound to C-x '.
    ;; (add-hook 'prog-mode-hook #'tempel-abbrev-mode)
    ;; (global-tempel-abbrev-mode)
    )

Snippets

(use-package yasnippet
  :disabled
  :config
  (yas-global-mode 1)
  :diminish yas-minor-mode)

Syntax checking with Flycheck

(use-package flycheck
  :defer t
  :hook
  (prog-mode . flycheck-mode)
  (org-mode . flycheck-mode)
  (sh-mode . flycheck-mode)
  :diminish flycheck-mode
  )

Bash - use tabs instead of spaces

Maybe this needs to be universal but this is especially annoying when I edit void-packages ‘template’s which specifically need tabs in the custom functions below.

(add-hook 'sh-mode-hook
    (lambda ()
        (setq-default indent-tabs-mode t)
        (setq-default tab-width 8)
    (add-to-list 'write-file-functions 'delete-trailing-whitespace)))

Auto-paired parens

(electric-pair-mode 1)
(setq electric-pair-preserve-balance nil)

Show matching parens

(show-paren-mode 1)
;; Worst possible setting with this theme - it sucks balls
;; (setq show-paren-style 'expression)

CUA mode

(setq cua-enable-cua-keys nil)
;; for rectangles, CUA is nice
(cua-mode t)

Aggressive indentation coz OCD

…and I hate doing it manually and Emacs usually refuses to do it by itself

(use-package aggressive-indent
  :config (global-aggressive-indent-mode 1)
  :diminish aggressive-indent-mode)

Bug-hunter

…except those that you can eat

(use-package bug-hunter)

cl-libify

Convert all (deperecated) cl symbols to cl-lib

(use-package cl-libify
  :disabled)

Iedit

A more intuitive way to alter all the occurrences of a word/keyword at once

(use-package iedit)

Show line numbers in programming modes

(add-hook 'prog-mode-hook
                (if (and (fboundp 'display-line-numbers-mode) (display-graphic-p))
                    #'display-line-numbers-mode
                  #'linum-mode))

Files from script directories default to shell-mode

Scope for adding more such shit?

(add-to-list 'auto-mode-alist '("/bin/" . sh-mode))
(add-to-list 'auto-mode-alist '("/srcpkgs/[[:ascii:]]+/template" . sh-mode))

Editing root files & privelege escalation for TRAMP if I ever use it

(use-package su
  ;; :config
  ;; (su-mode +1)
  )

Whitespace mopup

(add-hook 'before-save-hook
          'delete-trailing-whitespace) ;; Delete trailing whitespace on save

Multiple cursors

This is like C-v, a visual mode in vim/neovim. I stole this from pragmatic emacs.

(global-set-key (kbd "C-c m c") 'peremacs/edit-lines)

Delete blank lines and whitespace interactively

Plagiarised from pragmatic emacs

(global-set-key (kbd "M-SPC") 'shrink-whitespace)

Autoupdate buffer if files has changed on disk

(global-auto-revert-mode t)

Languages

Vimscript for editing neovim init

…cause neovim sucks and I don’t like leaving Emacs in the ideal case. I might end up replacing this with a lua config

;; vimrc syntax
(use-package vimrc-mode)
;; :ensure t
(add-to-list 'auto-mode-alist '("\\.vim\\(rc\\)?\\'" . vimrc-mode))

Lua mode?

I intend to learn and use lua for my neovim config.

(use-package lua-mode)

C and C++ ???

Like really?

;; (use-package cc-mode)

El Doc for help in echo area

(use-package eldoc
  :straight (:type built-in)

  :hook
  ((emacs-lisp-mode-hook . eldoc-mode)
   (lisp-interaction-mode-hook . eldoc-mode)
   (ielm-mode-hook . eldoc-mode)
   (org-mode . eldoc-mode)))

Zig mode

(use-package zig-mode
  :config
  (add-to-list 'auto-mode-alist '("\\.zig\\'" . zig-mode)))

Terminal

Vterm ftw

(use-package vterm
  ;; :ensure t
  :load-path "/usr/lib/libvterm.so.0.0.3"

  :init
  ;;  (setq vterm-term-environment-variable "eterm-256color")
  (setq vterm-disable-bold-font t)
  (setq vterm-kill-buffer-on-exit t)
  (setq vterm-module-cmake-args "-DUSE_SYSTEM_LIBVTERM=no")
  (setq vterm-always-compile-module t)
  (setq vterm-copy-exclude-prompt t))

Make vterm behave like a guake terminal and open below the main window. This can be toggled and opens only one instance per window (afaik). Considering using this feature to not provide a dedicated buffer to vterm so it sticks to the window it was launched with.

(use-package vterm-toggle

  :bind
  (("C-M-'" . vterm-toggle-cd))

  :config

  ;; reset window layout after kill
  (setq vterm-toggle-reset-window-configration-after-exit t)

  ;; toggle behaviour - like a toggle keep it running
  (setq vterm-toggle-hide-method nil)

Show vterm in a window at the bottom

(setq vterm-toggle-fullscreen-p nil)
(add-to-list 'display-buffer-alist
         '((lambda(bufname _) (with-current-buffer bufname (equal major-mode 'vterm-mode)))
            (display-buffer-reuse-window display-buffer-at-bottom)
            ;;(display-buffer-reuse-window display-buffer-in-direction)
            ;;display-buffer-in-direction/direction/dedicated is added in emacs27
            ;;(direction . bottom)
            ;;(dedicated . t) ;dedicated is supported in emacs27
            (reusable-frames . visible)
            (window-height . 0.3)))

Make counsel use the correct function to yank in vterm buffers.

(defun vterm-counsel-yank-pop-action (orig-fun &rest args)
  (if (equal major-mode 'vterm-mode)
      (let ((inhibit-read-only t)
            (yank-undo-function (lambda (_start _end) (vterm-undo))))
        (cl-letf (((symbol-function 'insert-for-yank)
               (lambda (str) (vterm-send-string str t))))
            (apply orig-fun args)))
    (apply orig-fun args)))

(advice-add 'counsel-yank-pop-action :around #'vterm-counsel-yank-pop-action))

Multimedia

EMMS for music

(use-package emms
  :commands emms
  :config
  (require 'emms-setup)
  (emms-standard)
  (emms-default-players)
  (emms-mode-line-disable)
  (setq emms-source-file-default-directory "~/Music/")
  ;;(dw/leader-key-def
    ;;"am"  '(:ignore t :which-key "media")
    ;;"amp" '(emms-pause :which-key "play / pause")
    ;;"amf" '(emms-play-file :which-key "play file"))
  )

View ePubs and PDFs in Emacs

(use-package nov
  :mode ("\\.epub\\'" . nov-mode)
  :config (nov-text-width 75))

(use-package pdf-tools
  :magic ("%PDF" . pdf-view-mode)
  :mode ("\\.pdf\\'" . pdf-view-mode)
  :config (pdf-tools-install :no-query))

;; TODO this needs fixing idk why even
;; (use-package pdf-view
;;   :ensure nil
;;   :after pdf-tools
;;   :bind (:map pdf-view-mode-map
;;               ("C-s" . isearch-forward)
;;               ("d" . pdf-annot-delete)
;;               ("h" . pdf-annot-add-highlight-markup-annotation)
;;               ("t" . pdf-annot-add-text-annotation))
;;   :custom
;;   (pdf-view-display-size 'fit-page)
;;   (pdf-view-resize-factor 1.1)
;;   (pdf-view-use-unicode-ligther nil))

Web

Got most of these from daviwil’s literate configuration

Gemini

(use-package elpher)

mail with mu4e

see daviwil’s mail.org and the configuration in his literate config.

Browser

Elfeed for RSS

(use-package elfeed
  :commands elfeed
  :config
  (setq elfeed-feeds
        '("https://drewdevault.com/blog/index.xml"
          "https://sourcehut.org/blog/index.xml"
          "https://ambikamath.com/feed/")))

DISABLED ERC for IRC

SystemCrafters recommends this so here I am using it. Long source block split into multiple ones.

(use-package erc

    :straight (:type built-in)

    :config
    (setq erc-server "irc.libera.chat"
        erc-nick "peregrinator"    ; Change this!
        erc-user-full-name "Brihadeesh S"  ; And this!
        erc-track-shorten-start 8
        erc-autojoin-channels-alist '(("irc.libera.chat" "#voidlinux" "#xbps" "#emacs" "#bash" "#sway" "#river" "#systemcrafters"))
        erc-kill-buffer-on-part t
        erc-auto-query 'bury
        erc-server-auto-reconnect t
        erc-server-reconnect-timeout 15)

Configuring SASL for automatic authentication

;; continued from above
(add-to-list 'load-path "~/.emacs.d/lisp/erc-libera-sasl")

;; Require ERC-SASL package
(require 'erc-sasl)
(add-to-list 'erc-sasl-server-regexp-list "irc\\.freenode\\.net")

(defun erc-login ()
  "Perform user authentication at the IRC server. (PATCHED)"
  (erc-log (format "login: nick: %s, user: %s %s %s :%s"
                   (erc-current-nick)
                   (user-login-name)
                   (or erc-system-name (system-name))
                   erc-session-server
                   erc-session-user-full-name))
  (if erc-session-password
      (erc-server-send (format "PASS %s" erc-session-password))
    (message "Logging in without password"))
  (when (and (featurep 'erc-sasl) (erc-sasl-use-sasl-p))
    (erc-server-send "CAP REQ :sasl"))
  (erc-server-send (format "NICK %s" (erc-current-nick)))
  (erc-server-send
   (format "USER %s %s %s :%s"
           ;; hacked - S.B.
           (if erc-anonymous-login erc-email-userid (user-login-name))
           "0" "*"
           erc-session-user-full-name))
  (erc-update-mode-line))

Set prompt to channel name:

(setq erc-prompt  (lambda () (concat (buffer-name) " > ")))

Highlight nicks

(require 'erc-highlight-nicknames)
(add-to-list 'erc-modules 'highlight-nicknames)

Update ERC modules

(erc-update-modules))

Footer

;;; configuration.el ends here