Quality of Life

Changes to core behaviour to make life better.

Preamble

;;; quality_of_life.el --- Cunene: My emacs configuration. -*- lexical-binding: t -*-
;; Author: Marco Craveiro <marco_craveiro@gmail.com> URL:
;; https://github.com/mcraveiro/prelude Version: 0.0.3 Keywords: convenience

;; This file is not part of GNU Emacs.

;;; Commentary:

;; General editor configuration

;;; License:

;; This program 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 program 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Code:

Garbage collection

Improvements to default GC. Links:

(setq-default gc-cons-threshold (* 8 1024 1024))      ; Bump up garbage collection threshold.

Garbage-collect on focus-out, Emacs should feel snappier overall.

(add-function :after after-focus-change-function
  (defun cunene/garbage-collect-maybe ()
    (unless (frame-focus-state)
      (garbage-collect))))

Better Defaults

Here are what I consider better defaults as per my own experience.

(require 'image-mode)
(setq-default
 ad-redefinition-action 'accept         ; Silence warnings for redefinition
 require-final-newline t                ; Newline at end of file
 auto-save-list-file-prefix nil         ; Prevent tracking for auto-saves
 cursor-in-non-selected-windows nil     ; Hide the cursor in inactive windows
 custom-unlispify-menu-entries nil      ; Prefer kebab-case for titles
 custom-unlispify-tag-names nil         ; Prefer kebab-case for symbols
 delete-by-moving-to-trash t            ; Delete files to trash
 fill-column 80                         ; Set width for automatic line breaks
 help-window-select t                   ; Focus new help windows when opened
 indent-tabs-mode nil                   ; Stop using tabs to indent
 inhibit-startup-screen t               ; Disable start-up screen
 initial-scratch-message ""             ; Empty the initial *scratch* buffer
 mouse-yank-at-point t                  ; Yank at point rather than pointer
 read-process-output-max (* 1024 1024)  ; Increase read size per process
 recenter-positions '(5 top bottom)     ; Set re-centering positions
 scroll-conservatively 101              ; Avoid recentering when scrolling far
 scroll-margin 2                        ; Add a margin when scrolling vertically
 select-enable-clipboard t              ; Merge system's and Emacs' clipboard
 sentence-end-double-space nil          ; Use a single space after dots
 show-help-function nil                 ; Disable help text everywhere
 tab-always-indent 'complete            ; Tab indents first then tries completions
 warning-minimum-level :error           ; Skip warning buffers
 window-combination-resize t            ; Resize windows proportionally
 vc-follow-symlinks t                   ; Follow symlinks without asking
 image-auto-resize nil                  ; Do not resize images automatically
 make-pointer-invisible nil             ; Do not make mouse invisible
 x-stretch-cursor t)                    ; Stretch cursor to the glyph width

(blink-cursor-mode 0)                   ; Prefer a still cursor
(fset 'yes-or-no-p 'y-or-n-p)           ; Replace yes/no prompts with y/n
(global-subword-mode 1)                 ; Iterate through CamelCase words
(put 'downcase-region 'disabled nil)    ; Enable downcase-region
(put 'upcase-region 'disabled nil)      ; Enable upcase-region
(set-default-coding-systems 'utf-8)     ; Default to utf-8 encoding
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(column-number-mode t)                  ; Display column numbers
(line-number-mode t)                    ; Display line numbers
(size-indication-mode t)                ; Display size indicator

;; enable narrowing commands
(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'narrow-to-defun 'disabled nil)

;; enabled change region case commands
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)

;; enable erase-buffer command
(put 'erase-buffer 'disabled nil)

;; repeat pop mark command without the need for C-u
(setq set-mark-command-repeat-pop t)
(setq image-auto-resize 0.7)

Text Size

;; Font size
(global-set-key (kbd "C-+") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)

Buffers

Identification

(require 'uniquify)
(setq uniquify-buffer-name-style 'reverse)
(setq uniquify-separator " • ")
(setq uniquify-after-kill-buffer-p t)
(setq uniquify-ignore-buffers-re "^\\*")

Killing

;; Do not ask to kill a buffer.
(global-set-key (kbd "C-x k") 'kill-this-buffer)
(setq use-short-answers t)

(defun cunene/diff-buffer-with-associated-file ()
  "View the differences between BUFFER and its associated file.
This requires the external program diff to be in your variable `exec-path'.
Returns nil if no differences found, t otherwise."
  (interactive)
  (let ((buf-filename buffer-file-name)
        (buffer (current-buffer)))
    (unless buf-filename
      (error "Buffer %s has no associated file" buffer))
    (let ((diff-buf (get-buffer-create
                     (concat "*Assoc file diff: "
                             (buffer-name)
                             "*"))))
      (with-current-buffer diff-buf
        (setq buffer-read-only nil)
        (erase-buffer))
      (let ((tempfile (make-temp-file "buffer-to-file-diff-")))
        (unwind-protect
            (progn
              (with-current-buffer buffer
                (write-region (point-min) (point-max) tempfile nil 'nomessage))
              (if (zerop
                   (apply #'call-process "diff" nil diff-buf nil
                          (append
                           (when (and (boundp 'ediff-custom-diff-options)
                                      (stringp ediff-custom-diff-options))
                             (list ediff-custom-diff-options))
                           (list buf-filename tempfile))))
                  (progn
                    (message "No differences found")
                    nil)
                (progn
                  (with-current-buffer diff-buf
                    (goto-char (point-min))
                    (if (fboundp 'diff-mode)
                        (diff-mode)
                      (fundamental-mode)))
                  (display-buffer diff-buf)
                  t)))
          (when (file-exists-p tempfile)
            (delete-file tempfile)))))))
(global-set-key (kbd "C-c C-=") 'cunene/diff-buffer-with-associated-file)

;; tidy up diffs when closing the file
(defun cunene/kill-associated-diff-buf ()
  "Kill the diff buffer when the file is killed."
  (let ((buf (get-buffer (concat "*Assoc file diff: "
                             (buffer-name)
                             "*"))))
    (when (bufferp buf)
      (kill-buffer buf))))
(add-hook 'kill-buffer-hook 'cunene/kill-associated-diff-buf)

(defun cunene/de-context-kill (arg)
  "Show differences when killing buffer.
ARG is true, always kill."
  (interactive "p")
  (if (and (buffer-modified-p)
             buffer-file-name
             (not (string-match "\\*.*\\*" (buffer-name)))
             ;; erc buffers will be automatically saved
             (not (eq major-mode 'erc-mode))
             (= 1 arg))
    (let ((differences 't))
      (when (file-exists-p buffer-file-name)
        (setq differences (cunene/diff-buffer-with-associated-file)))

      (if (y-or-n-p (format "Buffer %s modified; Kill anyway? " buffer-file-name))
          (progn
            (set-buffer-modified-p nil)
            (kill-buffer (current-buffer)))))
    (if (and (boundp 'gnuserv-minor-mode)
             gnuserv-minor-mode)
        (gnuserv-edit)
      (set-buffer-modified-p nil)
      (kill-buffer (current-buffer)))))

(global-set-key (kbd "C-x k") 'cunene/de-context-kill)

Saving

super-save https://github.com/bbatsov/super-save

Super-save auto-saves your buffers, when certain events happen - e.g. you switch between buffers, an Emacs frame loses focus, etc. You can think of it as both something that augments and replaces the standard auto-save-mode.

(use-package super-save
  :config
  (add-to-list 'super-save-triggers 'ace-window 'select-window)
  (super-save-mode +1))

;; revert buffers automatically when underlying files are changed externally
(global-auto-revert-mode t)

Themes

Doom One https://github.com/hlissner/emacs-doom-themes
(use-package doom-themes
  :config
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled
  (load-theme 'doom-dark+ t)
  ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

Doom modeline.

Links:

(use-package all-the-icons)
(use-package all-the-icons-dired)
(use-package all-the-icons-completion)
(use-package all-the-icons-ibuffer)
(use-package all-the-icons-nerd-fonts)
(add-hook 'dired-mode-hook 'all-the-icons-dired-mode)

(use-package doom-modeline
  :hook (after-init . doom-modeline-mode)
  :config
  (setq doom-modeline-buffer-file-name-style 'buffer-name)
  (setq doom-modeline-major-mode-icon t)
  ;; Whether display the buffer encoding.
  (setq doom-modeline-buffer-encoding nil))

Modeline

diminish https://github.com/emacsmirror/diminish
hide-mode-line https://github.com/hlissner/emacs-hide-mode-line
(use-package diminish)
(use-package hide-mode-line)

Whitespace

;; Give details about white space usage
(require 'whitespace)
(autoload 'whitespace-toggle-options
  "whitespace" "Toggle local `whitespace-mode' options." t)

;; limit line length
(setq whitespace-line-column 80)

;; What to highlight
(setq whitespace-style
      '(face tabs trailing lines-tail space-before-tab empty space-after-tab
             tab-mark))

;; Indicate if empty lines exist at end of the buffer
(set-default 'indicate-empty-lines t)

;; do not use global mode whitespace
(global-whitespace-mode 0)
(setq whitespace-global-modes nil)

;; Show whitespaces on these modes
(add-hook 'sh-mode-hook 'whitespace-mode)
(add-hook 'snippet-mode-hook 'whitespace-mode)
(add-hook 'tex-mode-hook 'whitespace-mode)
(add-hook 'sql-mode-hook 'whitespace-mode)
(add-hook 'ruby-mode-hook 'whitespace-mode)
(add-hook 'diff-mode-hook 'whitespace-mode)
(add-hook 'csharp-ts-mode-hook 'whitespace-mode)
(add-hook 'c-mode-common-hook 'whitespace-mode)
(add-hook 'cmake-mode-hook 'whitespace-mode)
(add-hook 'emacs-lisp-mode-hook 'whitespace-mode)
(add-hook 'dos-mode-hook 'whitespace-mode)
(add-hook 'org-mode-hook 'whitespace-mode)
(add-hook 'js-mode-hook 'whitespace-mode)
(add-hook 'js2-mode-hook 'whitespace-mode)

;; do not clean whitespace on windows.
(if (not (eq window-system 'w32))
    (add-hook 'before-save-hook 'delete-trailing-whitespace))

;;
;; Tabs
;;
(defun cunene/untabify-buffer ()
  "Remove tabs from buffer."
  (interactive)
  (untabify (point-min) (point-max)))

(defun cunene/build-tab-stop-list (width)
  "Create a tab stop list.
WIDTH is the size of the list."
  (let ((num-tab-stops (/ 80 width))
        (counter 1)
        (ls nil))
    (while (<= counter num-tab-stops)
      (setq ls (cons (* width counter) ls))
      (setq counter (1+ counter)))
    (nreverse ls)))

;; Spaces only for indentation
(set-default 'indent-tabs-mode nil)

;; Tab size
(setq tab-width 4)
(setq standard-indent 4)
(setq tab-stop-list (cunene/build-tab-stop-list tab-width))
(setq tab-stop-list (cunene/build-tab-stop-list tab-width))

Exiting

Links:

(require 'cl-lib)
(defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate)
  "Prevent annoying \"Active processes exist\" query when you quit Emacs."
  (cl-letf (((symbol-function #'process-list) (lambda ())))
    ad-do-it))

;; confirm exit
(global-set-key
 (kbd "C-x C-c")
 #'(lambda ()
    (interactive)
    (if (y-or-n-p-with-timeout "Do you really want to exit Emacs ?" 4 nil)
        (save-buffers-kill-emacs))))

(defun cunene/ask-before-closing-frame ()
  "Close only if y was pressed."
  (interactive)
  (if (y-or-n-p (format "Do you really want to close this frame?"))
      (delete-frame)
    (message "Canceled frame close")))

(global-set-key (kbd "C-x 5 0") 'cunene/ask-before-closing-frame)

Dashboard

emacs-dashboard https://github.com/emacs-dashboard/emacs-dashboard
(use-package dashboard
  :config
  (dashboard-setup-startup-hook)
  (setq dashboard-set-heading-icons t)
  (setq dashboard-startup-banner 'logo)
  (setq dashboard-set-file-icons t)
  (setq dashboard-set-init-info t)
  (setq dashboard-items '((recents  . 10)
                          (bookmarks . 5)
                          (projects . 5)
                          (agenda . 5))))

;; Remap Open Dashboard
;; From https://github.com/emacs-dashboard/emacs-dashboard/issues/236
(require 'dashboard)
(defun cunene/new-dashboard ()
  "Jump to the dashboard buffer, if doesn't exists create one."
  (interactive)
  (switch-to-buffer dashboard-buffer-name)
  (dashboard-mode)
  (dashboard-insert-startupify-lists)
  (dashboard-refresh-buffer))

(global-set-key (kbd "<f8>") 'cunene/new-dashboard)

Utilities

thingatpt https://www.emacswiki.org/emacs/ThingAtPoint

Assorted utility functions for which we could not yet establish a good category.

(use-package crux
  :bind (
         ("C-S-d" . crux-duplicate-current-line-or-region)
         ;; Move to beginning of line between head of line and head of text
         ("C-a" . crux-move-beginning-of-line)
         ("C-c r" . crux-rename-file-and-buffer)
         ("C-c D" . crux-delete-file-and-buffer))
  :config (crux-with-region-or-line kill-region))

(use-package uuid
  :load-path cunene/vendor-packages)

(require 'uuid)
(defun cunene/uuid-insert()
  "Insert a guid."
  (interactive)
  (insert (upcase (uuid-string))))

;; VS Code has a great feature where you can just copy a filename to the
;; clipboard.
(defun cunene/copy-file-name-to-clipboard ()
  "Copy the current buffer file name to the clipboard."
  (interactive)
  (let ((filename (if (equal major-mode 'dired-mode)
                      default-directory (buffer-file-name))))
    (when filename
      (kill-new filename)
      (message "Copied buffer file name '%s' to the clipboard." filename))))

(defun cunene/toggle-scroll(&optional arg)
  "Toggle both horizontal and vertical scroll bars.
ARG to set the direction."
  (interactive)
  (toggle-horizontal-scroll-bar arg)
  (toggle-scroll-bar arg))

(defun cunene/flush-blank-lines (start end)
  "Remove blank lines.
START and END mark the region."
  (interactive "r")
  (flush-lines "^\\s-*$" start end))

;; operations on thing at point.
(require 'thingatpt)

(defun cunene/to-excel-date (date)
  "Convert DATE to an excel date. Example date: 2024-03-01."
  (let* ((excel-epoch (encode-time '(0 0 0 1 1 1900)))
         (excel-days (floor (-
                             (float-time (date-to-time date))
                             (float-time excel-epoch))
                            86400)))
    (+ excel-days 2)
  ))

(defun cunene/open-directory-on-windows-explorer()
  "Open Windows Explorer on the current directory."
  (interactive)
  (if (eq system-type 'windows-nt)
      (start-process "EXPLORER" nil "explorer" ".")
    (message "Command only available on Windows.")))

Editing

iedit https://github.com/victorhge/iedit

This package includes Emacs minor modes (iedit-mode and iedit-rectangle-mode) based on a API library (iedit-lib) and allows you to alter one occurrence of some text in a buffer (possibly narrowed) or region, and simultaneously have other occurrences changed in the same way, with visual feedback as you type.

(use-package iedit
  :ensure t
  :bind ("C-:" . iedit-mode))

Region

drag-stuff https://github.com/rejeep/drag-stuff.el
volatile-highlights https://github.com/k-talo/volatile-highlights.el
(use-package drag-stuff
  :bind
  (:map drag-stuff-mode-map
        ("<C-s-up>" . drag-stuff-up)
        ("<C-s-down>" . drag-stuff-down)
        ("<C-s-left>" . drag-stuff-left)
        ("<C-s-right>" . drag-stuff-right))
  :diminish drag-stuff-mode
  :config
  (drag-stuff-global-mode t))

(use-package expand-region
  :bind ("C-=" . er/expand-region))

;; Replace region when inserting text
(delete-selection-mode 1)

;; brings visual feedback to some operations by highlighting portions relating
;; to the operations.
(use-package volatile-highlights
  :diminish volatile-highlights-mode
  :config (volatile-highlights-mode t))

;; note - this should be after volatile-highlights is required
;; add the ability to cut the current line, without marking it
(require 'rect)

;; WSL only. As per this post:
;; https://www.fredgruber.org/post/wsl_emacs_clipboard/
;;
(defun cunene/wsl-copy-clip(&rest _args)
  "Write the region to a file and then copy it to the Windows clipboard."
  (setq mytemp (make-temp-file "winclip"))
  (write-region (current-kill 0 t) nil mytemp)
  (shell-command (concat "clip.exe<" mytemp))
  (delete-file mytemp))
;; (advice-add 'kill-new :after #'cunene/wsl-copy-clip)

(defun cunene/wsl-copy-selected-text (start end)
  (interactive "r")
  (if (use-region-p)
      (let ((text (buffer-substring-no-properties start end)))
        (shell-command (concat "echo '" text "' | clip.exe")))))

Mark

jump-tree https://github.com/yangwen0228/jump-tree
(use-package jump-tree
  :init (global-jump-tree-mode))

Strings

(defun cunene/toggle-quotes ()
  "Toggle single quoted string to double or vice versa.
Flip the internal quotes as well. Best to run on the first
character of the string."
  (interactive)
  (save-excursion
    (re-search-backward "[\"']")
    (let* ((start (point))
           (old-c (char-after start))
           new-c)
      (setq new-c
            (cl-case old-c
              (?\" "'")
              (?\' "\"")))
      (setq old-c (char-to-string old-c))
      (delete-char 1)
      (insert new-c)
      (re-search-forward old-c)
      (backward-char 1)
      (let ((end (point)))
        (delete-char 1)
        (insert new-c)
        (replace-string new-c old-c nil (1+ start) end)))))

(defun cunene/backslash-slash-toggle ()
  "Replace backslash/slash in the current region or line.
If the current line contains more backslash char than slashes, then replace
them to slashes, else replace slashes to backslashes. If there's a text
selection, work on the selected text."
  (interactive)
  (let (li bds)
    (setq bds
          (if (region-active-p)
              (cons (region-beginning) (region-end))
            (bounds-of-thing-at-point 'line)))
    (setq li (buffer-substring-no-properties (car bds) (cdr bds)))
    (if (> (count 47 li) (count 92 li))
        (progn (replace-string "/" "\\" nil (car bds) (cdr bds)))
      (progn (replace-string "\\" "/" nil (car bds) (cdr bds))))))

(defun cunene/space-to-underscore-region (start end)
  "Replace space by underscore in region.
START and END mark the region."
  (interactive "r")
  (save-restriction
    (narrow-to-region start end)
    (goto-char (point-min))
    (while (search-forward " " nil t) (replace-match "_"))))

(defun cunene/underscore-to-space-region (start end)
  "Replace underscore by space in region.
START and END mark the region."
  (interactive "r")
  (save-restriction
    (narrow-to-region start end)
    (goto-char (point-min))
    (while (search-forward "_" nil t) (replace-match " "))))

(defun cunene/replace-underscore-space-toggle ()
  "Replace underscore/space in the current region or line.
If the current line contains more _ char than space, then replace
them to space, else replace space to _. If there's a text
selection, work on the selected text."
  (interactive)
  (let (li bds)
    (setq bds
          (if (region-active-p)
              (cons (region-beginning) (region-end))
            (bounds-of-thing-at-point 'line)))
    (setq li (buffer-substring-no-properties (car bds) (cdr bds)))
    (if (> (count 32 li) (count 95 li))
        (progn (replace-string " " "_" nil (car bds) (cdr bds)))
      (progn (replace-string "_" " " nil (car bds) (cdr bds))))))

(defun cunene/cycle-hyphen-underscore-space ()
  "Cyclically replace underscore, space, hypen chars.
Acts in current line or text selection. When called repeatedly,
this command cycles the { , _, -} characters."
  (interactive)
  ;; this function sets a property 「'state」. Possible values are 0
  ;; to length of charList.
  (let (mainText charList p1 p2 currentState nextState changeFrom
             changeTo startedWithRegion-p )

    (if (region-active-p)
        (progn
          (setq startedWithRegion-p t )
          (setq p1 (region-beginning))
          (setq p2 (region-end))
          )
      (progn (setq startedWithRegion-p nil )
             (setq p1 (line-beginning-position))
             (setq p2 (line-end-position)) ) )

    (setq charList (list " " "_" "-" ))

    (setq currentState
          (if (get 'cunene/cycle-hyphen-underscore-space 'state)
              (get 'cunene/cycle-hyphen-underscore-space 'state) 0))
    (setq nextState (% (+ currentState (length charList) 1) (length charList)))

    (setq changeFrom (nth currentState charList))
    (setq changeTo (nth nextState charList))

    (setq mainText
          (replace-regexp-in-string changeFrom changeTo
                                    (buffer-substring-no-properties p1 p2)))
    (delete-region p1 p2)
    (insert mainText)

    (put 'cunene/cycle-hyphen-underscore-space 'state nextState)

    (when startedWithRegion-p
      (goto-char p2)
      (set-mark p1)
      (setq deactivate-mark nil))))

(global-set-key (kbd "C-c C--") 'cunene/cycle-hyphen-underscore-space)

(defun cunene/string-inflection-cycle-auto ()
  "Switch by major-mode."
  (interactive)
  (cond
   ((eq major-mode 'emacs-lisp-mode)
    (string-inflection-all-cycle))
   ((eq major-mode 'java-mode)
    (string-inflection-java-style-cycle))
   ((eq major-mode 'ruby-mode)
    (string-inflection-ruby-style-cycle))
   (t
    ;; default
    (string-inflection-all-cycle))))

(use-package string-inflection
  :config
  (global-set-key (kbd "C-M-j") 'cunene/string-inflection-cycle-auto))

Filling

From Sacha Chua's config.

(defun cunene/unfill-paragraph (&optional region)
  "Take a multi-line paragraph and make it into a single line of text.
REGION to fill or unfill."
  (interactive (progn
                 (barf-if-buffer-read-only)
                 (list t)))
  (let ((fill-column (point-max)))
    (fill-paragraph nil region)))
(bind-key "M-Q" 'cunene/unfill-paragraph)

(defun cunene/fill-or-unfill-paragraph (&optional unfill region)
  "Fill paragraph (or REGION).
With the prefix argument UNFILL, unfill it instead."
  (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)))
(bind-key "M-q" 'cunene/fill-or-unfill-paragraph)

(remove-hook 'text-mode-hook #'turn-on-auto-fill)
(add-hook 'text-mode-hook 'turn-on-visual-line-mode)

(global-so-long-mode 1)

History

(require 'saveplace)
(setq save-place-file (cunene/cache-concat "saveplace/places"))
(save-place-mode 1)

;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :init
  (savehist-mode +1)
  :config
  (setq history-length t)
  (setq history-delete-duplicates t)
  (setq savehist-save-minibuffer-history 1)
  (setq savehist-autosave-interval 60)
  (setq savehist-additional-variables
        '(kill-ring
          search-ring
          regexp-search-ring))
  (setq savehist-file (cunene/cache-concat "savehist/history")))

Help

Links:

(use-package helpful
  :config
  (setq helpful-switch-buffer-function #'cunene/helpful-switch-to-buffer)

  (defun cunene/helpful-switch-to-buffer (buffer-or-name)
    "Switch to helpful BUFFER-OR-NAME.

The logic is simple, if we are currently in the helpful buffer,
reuse it's window, otherwise create new one."
    (if (eq major-mode 'helpful-mode)
        (switch-to-buffer buffer-or-name)
      (pop-to-buffer buffer-or-name)))
  :bind
  (("C-h f" . helpful-callable)
   ("C-h v" . helpful-variable)
   ("C-h k" . helpful-key)
   ("C-c C-d" . helpful-at-point)
   ("C-h C" . helpful-command)))

Prettify

Sources:

(defun cunene/configure-prettify-symbols-alist ()
  "List of pretty symbols."
  (push '("[ ]" . "☐" ) prettify-symbols-alist)
  (push '("[X]" . "☑" ) prettify-symbols-alist)
  (push '("[-]" . "❍" ) prettify-symbols-alist)
  (push '("#+BEGIN_QUOTE" . "“") prettify-symbols-alist)
  (push '("#+END_QUOTE" . "”") prettify-symbols-alist)
  (push '("#+begin_quote" . "“") prettify-symbols-alist)
  (push '("#+end_quote" . "”") prettify-symbols-alist)
  (push '("#+BEGIN_SRC" . "»") prettify-symbols-alist)
  (push '("#+END_SRC" . "«") prettify-symbols-alist)
  (push '("#+begin_src" . "»") prettify-symbols-alist)
  (push '("#+end_src" . "«") prettify-symbols-alist)
  (prettify-symbols-mode))
(add-hook 'org-mode-hook 'cunene/configure-prettify-symbols-alist)

(defun cunene/prog-mode-configure-prettify-symbols-alist ()
  "Set prettify symbols alist."
  (setq prettify-symbols-alist '(("lambda" . "λ")
                                 ("->" . "→")
                                 ("->>" . "↠")
                                 ("=>" . "⇒")
                                 ("map" . "↦")
                                 ("/=" . "≠")
                                 ("!=" . "≠")
                                 ("==" . "≡")
                                 ("<=" . "≤")
                                 (">=" . "≥")
                                 ("=<<" . "=≪")
                                 (">>=" . "≫=")
                                 ("<=<" . "↢")
                                 (">=>" . "↣")
                                 ("&&" . "∧")
                                 ("||" . "∨")
                                 ("not" . "¬")))
  (prettify-symbols-mode))

(add-hook 'prog-mode-hook 'cunene/prog-mode-configure-prettify-symbols-alist)

Processes

;; Example: (is-process-running "myprocess")
(defun cunene/is-process-running (process-name)
  "Check if a system process named PROCESS-NAME is running."
  (let* ((process-list-command
         (if (eq system-type 'windows-nt)
             "pslist"
           "ps aux"))
         (pipeline
          (concat process-list-command "| grep -v grep |" (format "grep -c '%s'" process-name)))
         (output (shell-command-to-string pipeline)))
    (not (zerop (string-to-number (string-trim output))))))

Postamble

;;; quality_of_life.el ends here