dotfiles/user-config/emacs/.emacs.d/config.org

2914 lines
123 KiB
Org Mode
Raw Normal View History

2022-01-31 16:41:17 +00:00
#+TITLE: Emacs Configuration
#+AUTHOR: [[https://www.blog.rekahsoft.ca/contact.html][Collin J. Doering]]
#+SETUPFILE: ~/.guix-home/profile/var/org-themes/org-html-themes/theme-readtheorg-local.setup
2022-01-31 16:41:17 +00:00
#+HTML_HEAD: <style> #content{max-width: 100%;} .tag{background-color: #0000;} pre.src:hover::before{position: relative; float: right;}</style>
#+PROPERTY: header-args:emacs-lisp :tangle yes :tangle-mode (identity #o444)
#+CATEGORY: emacs
* Overview
This is a literate emacs configuration which leverages [[https://orgmode.org/][org-mode]].
- Sections marked with ~non_guix~ will only be enabled on machines using the guix system
distribution.
- Sections marked with ~not_tangled~ will not appear/execute (be tangled) as part of this
emacs configuration or any related source file that is generated. Instead these code blocks
are meant for reference purposes.
- All emacs lisp code blocks are tangled unless their section is marked with ~not_tangled~.
Other language code blocks may or may not be tangled. See their [[https://www.gnu.org/software/emacs/manual/html_node/org/Using-Header-Arguments.html][code block header]].
- Works with emacs version ~26+~.
* Usage :not_tangled:
To use this configuration, it must be 'tangled' then evaluated by emacs upon startup. This
can be facilitated by placing the following in ~.emacs.d/init.el~.
#+begin_src emacs-lisp :tangle no
(let* ((filename (concat (file-name-directory load-file-name) "config"))
(config (concat filename ".org")))
(when (file-exists-p config)
(org-babel-load-file config t)))
#+end_src
Because the source version of this file is tangled upon emacs startup, it must exist so that
it can be tangled, leaving us with a bootstrapping problem. There are a few options to
overcome this:
1. Have a step that runs an initial tangle over this document, which results in the
appropriate ~.emacs.d/init.el~ file. In this case, if this file is ever removed, it will
need to be manually regenerated.
2. Manage ~.emacs.d/init.el~ outside of this configuration. That is, expect the user has
placed the above snippet in place at ~.emacs.d/init.el~ (which is the case in the
repository this is distributed in).
Option ~2~ is what is currently used, but both could be supported in the future.
** Test Drive
It is useful for testing, debugging an experimentation purposes to use this configuration
without modifying an existing emacs configuration that is already in place. To achieve this,
the following can be used.
#+begin_src shell
emacs -q --eval '(load-file "/path/to/dotfiles/clone/user-config/emacs/.emacs.d/init.el")'
#+end_src
On guix systems, all emacs packages should already be installed via a [[file:~/.dotfiles/.guix/rekahsoft/guix-config/home.scm::(define-public %emacs-manifest][guix manifest]], so the
2022-01-31 16:41:17 +00:00
above will work without an issue. However, on non-guix systems, the emacs package manager
can be used to fetch missing packages. Later in this configuration ~use-package~ is setup,
and in the future the fetching of missing packages will be automated (even in this case,
see: [[*Automatically Fetch Packages][Automatically Fetch Packages]]). Until then, the above will result in an error when run
on a non-guix system, as ~use-package~ will be missing (as well as all other required
packages). Once ~use-package~ is installed in the instance of emacs launched by the above
command, running the command again should result in a working 'test drive' instance of
emacs. Alternatively, on non-guix distributions where the required packages have already
been installed for an existing configuration, simply sym-link the existing package directory
to ~~/.emacs.d/elpa~.
* Conditions
This configuration must take differing forms depending on the system it is executed on. Here
we set some variables that can be later used to modify what code blocks are executed at run
time. An alternative to this would be to generate (or 'tangle' in org-babel speak) different
versions of this file. Yet another alternative would be to use both methods interchangeably.
However, for this configuration I have chosen to handles these conditions at emacs run time,
instead of at ~org-babel~ tangle time (which occurs during emacs startup).
#+begin_src emacs-lisp
(defvar is-guix-system-p (or (file-exists-p "/run/current-system/provenance")
(string-prefix-p (expand-file-name "~/.guix-home/profile")
2022-01-31 16:41:17 +00:00
(locate-file "emacs" exec-path)))
"Variable that is t if on a guix system, nil otherwise.")
(defvar is-work-system-p (string-equal (system-name) "rekahsoft-work")
"Variable that is t if on a work system, nil otherwise.")
#+end_src
* Turn off GC for Startup
As [[https://github.com/nilcons/emacs-use-package-fast#a-trick-less-gc-during-startup][a startup performance enhancement]], turn off emacs garbage collection during
initialization. Turn it back on once initialization has completed.
#+begin_src emacs-lisp
(setq gc-cons-threshold 64000000)
(add-hook 'after-init-hook
#'(lambda ()
;; Restore default after startup
(setq gc-cons-threshold 800000)))
#+end_src
* Clean ~.emacs.d~
Use [[https://github.com/emacscollective/no-littering][no-littering]] to keep ~.emacs.d~ clean.
#+begin_src emacs-lisp
(require 'no-littering)
#+end_src
* Setup ~package.el~ :non_guix:
Use the [[https://wikemacs.org/wiki/Package.el][package.el]] emacs package manager on non-guix systems. It is not needed (or wanted) on
guix systems as instead emacs packages should be installed via the package manager.
#+begin_src emacs-lisp
(when (not is-guix-system-p)
;; Set repos for package.el
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")
("melpa-stable" . "https://stable.melpa.org/packages/")))
;; This needs to be a the start of ~/.emacs since package-initialize is run after the user
;; init file is read but before after-init-hook. It autoloads packages installed by
;; package.el including updating the load-path and running/loading/requiring
;; pkgname-autoload.el for all packages (where pkgname is replaced with the appropriate
;; package name). To disable package.el's autoloading functionality use:
;; (setq package-enabled-at-startup nil)
;; The reason to use package-initialize here is so one can modify the installed modules
;; later in this .emacs file but still retain the autoloading functionality of package.el
(package-initialize))
#+end_src
* Leverage ~use-package~
This configuration makes use of [[https://github.com/jwiegley/use-package][use-package]].
#+BEGIN_SRC emacs-lisp
;; Use use-package to autoload packages for a faster emacs start-up
(require 'use-package)
#+END_src
Sometimes its useful to turn this on for debugging purposes.
#+begin_src emacs-lisp
;; Enable use-package verbosity
;; (setq use-package-verbose t)
#+end_src
** Automatically Fetch Packages :non_guix:
When on non-guix systems, that may or may not have the appropriate packages, it is useful to
use the ensure option of ~use-package~.
*Note:* This currently expects ~use-package~ to already be installed. This will be resolved
as a TODO item.
#+begin_src emacs-lisp :tangle no
(when (not is-guix-system-p)
(require 'use-package-ensure)
(setq use-package-always-ensure t))
#+end_src
* Activate modes with a set of hooks
*This can likely be improved/removed - see TODOs*
#+begin_src emacs-lisp
;; Variables and functions that will be used in files in config folder
;; Note: This should be put into another file (eg. lib.el) that could be loaded from here
;; adds the given function mode to each element of the given-hooks
(defun activate-mode-with-hooks (mode given-hooks)
(while given-hooks
(add-hook (car given-hooks)
mode)
(setq given-hooks (cdr given-hooks))))
;; code-modes is a list of mode hooks (for programming langs only)
(defvar code-modes '(yaml-mode-hook sql-mode-hook sml-mode-hook scheme-mode-hook emacs-lisp-mode-hook c-mode-hook c++-mode-hook python-mode-hook lua-mode-hook python-mode-hook haskell-mode-hook php-mode-hook perl-mode-hook lisp-mode-hook clojure-mode-hook ruby-mode-hook erlang-mode-hook sh-mode-hook java-mode-hook scala-mode-hook js-mode-hook vue-mode-hook yaml-mode vhdl-mode rust-mode-hook graphql-mode makefile-mode-hook terraform-mode-hook asm-mode-hook go-mode-hook))
#+end_src
* Setup Custom
#+begin_src emacs-lisp
;; Load customization's made by customize
(setq custom-file (no-littering-expand-var-file-name "custom.el"))
2022-01-31 16:41:17 +00:00
(load custom-file)
#+end_src
* Setup auth-source Backend
Use password store backend disabling netrc and gpg encrypted netrc options. See [[info:auth#Help for
users][info:auth#Help for users]] for more details.
#+begin_src emacs-lisp
(auth-source-pass-enable)
(setq auth-sources '(password-store))
#+end_src
* Theme
#+begin_src emacs-lisp
;; TODO: this may not be needed after emacs 28
2022-01-31 16:41:17 +00:00
;; Set emoji font
(set-fontset-font t '(#x1f000 . #x1faff)
(font-spec :family "Noto Color Emoji"))
;; Turn off menu-bar when running in terminal
;; Note: When run as a Xwindow, .Xresources turns off the menu-bar and tool-bar
(unless window-system (menu-bar-mode -1))
;; Stop startup screen
(setq inhibit-startup-screen t)
(defun doom|init-theme ()
(load-theme 'doom-vibrant t)
(doom-modeline-mode 1))
2022-01-31 16:41:17 +00:00
(defun doom|init-theme-in-frame (frame)
(with-selected-frame frame
(doom|init-theme))
;; Unregister this hook once its run
(remove-hook 'after-make-frame-functions
'doom|init-theme-in-frame))
(use-package doom-themes
:config (progn
(setq doom-themes-enable-bold t ; if nil, bold is universally disabled
doom-themes-enable-italic t) ; if nil, italics is universally disabled
(if (daemonp)
(add-hook 'after-make-frame-functions
'doom|init-theme-in-frame)
(doom|init-theme))
;; Enable flashing mode-line on errors
(doom-themes-visual-bell-config)
;; Enable doom treemacs theme
(setq doom-themes-treemacs-theme "all-the-icons")
(doom-themes-treemacs-config)
2022-01-31 16:41:17 +00:00
;; Corrects (and improves) org-modes's native fontification
(doom-themes-org-config)))
(use-package doom-modeline
:defer t)
#+end_src
* EXWM
Define some interactive helper functions to launch various programs
#+begin_src emacs-lisp
(defun launch-browser ()
(interactive)
(start-process "kitty" "browser-output" "icecat"))
(defun launch-kitty ()
(interactive)
(start-process "kitty" "kitty-output" "kitty" "-1"))
(global-set-key (kbd "C-s-f") 'launch-browser)
(global-set-key (kbd "<C-s-return>") 'launch-kitty)
#+end_src
Define a function ~start-as-window-manager~ that allows a user to optionally start emacs as a
window manager.
#+begin_src emacs-lisp
(defun start-as-window-manager ()
(use-package exwm
:config (setq exwm-workspace-show-all-buffers t
exwm-layout-show-all-buffers t))
(use-package exwm-config
:after (exwm))
(use-package exwm-randr
:after (exwm exwm-config)
:config
(progn
(exwm-randr-enable)
(exwm-config-default))))
#+end_src
To start emacs as a window manager, add something like this to ~~/.xinitrc~.
#+begin_src shell
exec emacs -f start-as-window-manager
#+end_src
** System Tray
#+begin_src emacs-lisp
(use-package exwm-systemtray
:config (exwm-systemtray-enable))
#+end_src
* Window Management
#+begin_src emacs-lisp
(use-package ace-window
:bind
(:map global-map
("M-o" . ace-window)))
#+end_src
#+begin_src emacs-lisp
;; Make C-x O cycle backwards a pane (oposite to C-x o)
(global-set-key "\C-xO" #'(lambda ()
(interactive)
(other-window -1)))
#+end_src
#+begin_src emacs-lisp
;; Use this function to create a X11 emacs frame with a static name so it can
;; be treated specially by xmonad (as a scratch-pad in this case). This is expected
;; to be run from command-line like so:
;; emacsclient --eval '(make-frame-with-static-name "emacs-scratch")'
(defun make-frame-with-static-name (given-name)
"Makes a (X11) frame with a unchanging name for the purposes of finding it with
a window manager, and treating it specially."
(make-frame-on-display ":0" `((name . ,given-name))))
;; Toggles windows split orientation of 2 adjecent windows
;; Thanks to http://www.emacswiki.org/cgi-bin/wiki?ToggleWindowSplit
(defun toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))))
;; TODO: this breaks treemacs when its the buffer thats rotated
;; Rotates windows
;; See: http://www.emacswiki.org/emacs/TransposeWindows
(defun rotate-windows (arg)
"Rotate your windows; use the prefix argument to rotate the other direction"
(interactive "P")
(if (not (> (count-windows) 1))
(message "You can't rotate a single window!")
(let* ((rotate-times (if (and (numberp arg) (not (= arg 0))) arg 1))
(direction (if (or (< rotate-times 0) (equal arg '(4)))
'reverse
(lambda (x) x)))
(i 0))
(while (not (= rotate-times 0))
(while (< i (- (count-windows) 1))
(let* ((w1 (elt (funcall direction (window-list)) i))
(w2 (elt (funcall direction (window-list)) (+ i 1)))
(b1 (window-buffer w1))
(b2 (window-buffer w2))
(s1 (window-start w1))
(s2 (window-start w2))
(p1 (window-point w1))
(p2 (window-point w2)))
(set-window-buffer-start-and-point w1 b2 s2 p2)
(set-window-buffer-start-and-point w2 b1 s1 p1)
(setq i (1+ i))))
(setq i 0
rotate-times
(if (< rotate-times 0) (1+ rotate-times) (1- rotate-times)))))))
;; Assign keybinding to toggle split orientation of 2 adjacent windows, and to rotate windows
(define-key ctl-x-4-map "t" 'toggle-window-split)
(global-set-key "\C-cr" 'rotate-windows)
#+end_src
* Features
** Scratches
#+begin_src emacs-lisp
(defvar scratch-buffer-alist '((python-mode . "# This buffer is for notes you don't want to save, and for Lisp evaluation.\n# If you want to create a file, visit that file with C-x C-f,\n# then enter the text in that file's own buffer.")))
(defun open-scratch-buffer (&optional buf-mode buf-name msg)
"Opens a scratch buffer; if none exists creates one. When called with the universal argument (C-u) will ask what mode to use for the scratch buffer."
(interactive
(cond ((equal current-prefix-arg nil) ;; universal argument not called
(list initial-major-mode "*scratch*" initial-scratch-message))
((equal current-prefix-arg '(4)) ;; Universal argument called (C-u)
(let* ((buf-mode (read-command "Mode: " initial-major-mode))
(buf-name (if (equal buf-mode initial-major-mode)
"*scratch*"
(concat "*scratch:" (symbol-name buf-mode) "*")))
(msg ""))
(list buf-mode buf-name msg)))))
(let* ((scratch-buffer (get-buffer buf-name)))
;; check if the scratchpad is open. If not create it, change its mode and insert message text at the top of the buffer
(if (null scratch-buffer)
(with-current-buffer (get-buffer-create buf-name)
(funcall buf-mode)
(insert msg)))
(switch-to-buffer buf-name)))
;; Bind a key to grab a scratchpad
(define-key ctl-x-4-map "s" 'open-scratch-buffer)
#+end_src
** Pastebins
Setup access to ix.io pastebin
#+begin_src emacs-lisp
(use-package ix)
#+end_src
** Flyspell
#+begin_src emacs-lisp
;; Thanks to: http://www.emacswiki.org/emacs/FlySpell#toc11
(defun flyspell-emacs-popup-textual (event poss word)
"A textual flyspell popup menu."
(require 'popup)
(let* ((corrects (if flyspell-sort-corrections
(sort (car (cdr (cdr poss))) 'string<)
(car (cdr (cdr poss)))))
(cor-menu (if (consp corrects)
(mapcar (lambda (correct)
(list correct correct))
corrects)
'()))
(affix (car (cdr (cdr (cdr poss)))))
show-affix-info
(base-menu (let ((save (if (and (consp affix) show-affix-info)
(list
(list (concat "Save affix: " (car affix))
'save)
'("Accept (session)" session)
'("Accept (buffer)" buffer))
'(("Save word" save)
("Accept (session)" session)
("Accept (buffer)" buffer)))))
(if (consp cor-menu)
(append cor-menu (cons "" save))
save)))
(menu (mapcar
(lambda (arg) (if (consp arg) (car arg) arg))
base-menu)))
(cadr (assoc (popup-menu* menu :scroll-bar t) base-menu))))
(use-package flyspell
:after auto-complete auto-complete-config
:config (progn
(ac-flyspell-workaround)
(activate-mode-with-hooks 'flyspell-prog-mode code-modes)
(activate-mode-with-hooks 'flyspell-mode '(text-mode-hook markdown-mode-hook latex-mode-hook org-mode-hook magit-log-edit-mode-hook mu4e-compose-mode-hook))
(fset 'flyspell-emacs-popup 'flyspell-emacs-popup-textual)))
#+end_src
** Helm
#+begin_src emacs-lisp
;; Setup helm
(use-package helm
:bind (("C-c y" . helm-show-kill-ring)
("C-x b" . helm-mini)
("C-x C-f" . helm-find-files)
("M-x" . helm-M-x)
("C-x c i" . helm-semantic-or-imenu)
("C-x c o" . helm-occur)
("C-x c t" . helm-top)
("C-x c r" . helm-register)
("C-x c g" . helm-do-grep)
("C-x c f" . helm-for-files)
("C-x c m" . helm-man-woman)
("C-x c a" . helm-apropos)
("C-x c u" . helm-surfraw) ;; TODO: this keybinding is unused
("C-x c c" . helm-colors)
("C-c h i" . helm-info)
("s-p" . helm-run-external-command)
:map helm-map
("<tab>" . helm-execute-persistent-action) ;; Rebind tab to run persistent action
("C-i" . helm-execute-persistent-action) ;; Make TAB works in terminal
("C-z" . helm-select-action) ;; List actions using C-z
)
:config (progn
(setq helm-quick-update t
helm-buffers-fuzzy-matching t
helm-split-window-inside-p t ; open helm buffer inside current window, not occupy whole other window
helm-move-to-line-cycle-in-source t ; move to end or beginning of source when reaching top or bottom of source.
helm-ff-search-library-in-sexp t ; search for library in `require' and `declare-function' sexp.
helm-scroll-amount 8 ; scroll 8 lines other window using M-<next>/M-<prior>
helm-ff-file-name-history-use-recentf t
helm-follow-mode-persistent t)
(helm-mode 1)
(helm-adaptive-mode)))
;; See: https://github.com/emacs-helm/helm-org/issues/3
(use-package helm-org
:after helm
:config (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags)))
(use-package helm-rg
:after helm
:config (setq helm-rg-default-extra-args "--hidden"))
;; Setup helm-swoop
;; See: https://github.com/ShingoFukuyama/helm-swoop
(use-package helm-swoop
:after helm
:bind (("C-x c s" . helm-swoop)))
;; Setup helm-ls-git to quickly select files from the current vc dir
(use-package helm-ls-git
:after helm
:bind (("C-x C-d" . helm-browse-project)))
(use-package helm-descbinds
:after helm
:commands helm-descbinds-mode
:config (helm-descbinds-mode)
:bind (("C-h k" . helm-descbinds)))
(use-package helm-unicode
:after helm
:bind (("C-x 8 RET" . helm-unicode)))
;; TODO: this appears to be broken with the latest version of mu4e
;; Likely due to the fact that the packaged version installed via guix
;; is old compared to upstream: https://github.com/emacs-helm/helm-mu
(use-package helm-mu
:after helm mu4e
:bind (("C-x c M" . helm-mu)
("C-x c C" . helm-mu-contacts)
:map mu4e-main-mode-map
("s" . helm-mu)
:map mu4e-headers-mode-map
("s" . helm-mu)
:map mu4e-view-mode-map
("s" . helm-mu)))
(use-package helm-tramp
:after helm
:bind (("C-x c d" . helm-tramp)))
(use-package helm-exwm
:after helm
:bind (("C-x c e" . helm-exwm)))
#+end_src
** TODO Projectile
- [ ] helm-projectile is not available until explicitly run
#+begin_src emacs-lisp
;; Use projectile to manage projects (with helm completion)
(use-package projectile
:init (setq
projectile-indexing-method 'native
projectile-switch-project-action 'projectile-vc
projectile-project-search-path '("~/Code/" "~/work/" "~/.scratch"))
:bind (("C-c p" . 'projectile-command-map))
:config (projectile-mode 1))
(use-package helm-projectile
:after helm projectile
:config (helm-projectile-on))
#+end_src
*** TODO Fix (or remove/deprecate for the [[help:treemacs-projectile][treemacs-projectile]] function) projectile->treemacs sync function
2022-01-31 16:41:17 +00:00
It is useful to sync projectile projects into treemacs if they are not there already. The
~sync-projectile-projects-to-treemacs~ below can be used interactively to achieve this.
#+begin_src emacs-lisp
(defun sync-projectile-projects-to-treemacs ()
"Sync all projectile projects into treemacs."
(interactive)
(with-current-buffer (find-file-noselect projectile-known-projects-file)
(goto-char (point-min))
(cl-loop for i in (read (current-buffer))
for filepath = (expand-file-name i) do
(letf ((treemacs-override-workspace (treemacs--find-workspace filepath)))
(unless (treemacs--find-project-for-path filepath)
(message "Adding %s to treemacs workspace %s" i (treemacs-workspace->name (treemacs-current-workspace)))
(treemacs-add-project-to-workspace i (file-name-sans-extension i)))))))
2022-01-31 16:41:17 +00:00
#+end_src
** TODO Perspectives :not_tangled:
This is currently disabled as it has not been used thoroughly and is still has usage issues
that require investigation.
#+begin_src emacs-lisp :tangle no
;; Taken and modified from spacemacs
;; See: https://github.com/syl20bnr/spacemacs/blob/master/layers/+spacemacs/spacemacs-layouts/funcs.el#L329
(defun helm-persp-switch-project (arg)
"Switch to a projectile perspective using helm.
ARG is unused"
(interactive "P")
(helm
:sources
(helm-build-in-buffer-source "*Helm Switch Project Layout*"
:data (lambda ()
(if (projectile-project-p)
(cons (abbreviate-file-name (projectile-project-root))
(projectile-relevant-known-projects))
projectile-known-projects))
:fuzzy-match helm-projectile-fuzzy-match
:mode-line helm-read-file-name-mode-line-string
:action '(("Switch to Project Perspective" .
(lambda (project)
(let ((persp-reset-windows-on-nil-window-conf t)
(project-name (file-name-nondirectory (directory-file-name project))))
(persp-switch project)
(persp-rename project-name)
(let ((projectile-completion-system 'helm))
(projectile-switch-project-by-name project)))))))
:buffer "*Helm Projectile Layouts*"))
;; Taken and modified from spacemacs
;; See: https://github.com/syl20bnr/spacemacs/blob/master/layers/+spacemacs/spacemacs-layouts/funcs.el#L313
(defun helm-persp-kill ()
"Kill perspectives with all their buffers."
(interactive)
(helm
:buffer "*Helm Kill Perspectives with all their buffers*"
:sources (helm-build-in-buffer-source
(s-concat "Current Perspective: "
(persp-name (get-current-persp)))
:data (seq-filter (lambda (n) (not (string-equal n "main"))) (persp-names))
:fuzzy-match t
:action
'(("Kill perspective(s)" .
(lambda (candidate)
(mapcar 'persp-kill
(helm-marked-candidates))))))))
(use-package persp-mode
:diminish
:init (progn
(setq persp-nil-name "main")
(add-hook 'after-init-hook (lambda () (persp-mode +1)))))
(use-package persp-projectile
:after persp-mode
:bind (("C-x x P" . projectile-persp-switch-project)
("C-c p p" . helm-persp-switch-project)
("C-x x c" . helm-persp-kill)))
(use-package persp-mode-projectile-bridge
:disabled ;; TODO: BROKEN
:after projectile persp-mode
:commands (persp-mode-projectile-bridge-find-perspectives-for-all-buffers
persp-mode-projectile-bridge-kill-perspectives)
:hook ((persp-mode . persp-mode-projectile-bridge-mode)
(persp-mode-projectile-bridge-mode
. (lambda ()
(if persp-mode-projectile-bridge-mode
(persp-mode-projectile-bridge-find-perspectives-for-all-buffers)
(persp-mode-projectile-bridge-kill-perspectives))))))
#+end_src
** TODO Desktop Save
#+begin_src emacs-lisp
;; TODO: Disabled as this causes frames to no longer open in daemon-mode
;; ;; Enable desktop-save-mode.
;; ;; When running as a daemon, defer loading of saved desktop until first frame is created
;; (add-hook 'after-make-frame-functions
;; (lambda ()
;; (if (daemonp)
;; (progn
;; (desktop-save-mode 1)
;; (desktop-read))
;; (desktop-save-mode 1))))
#+end_src
** IBuffer
#+begin_src emacs-lisp
(use-package ibuffer
:bind ("C-x C-b" . ibuffer)
:config (progn
(use-package ibuf-ext)
(use-package ibuffer-vc)
(use-package ibuffer-projectile)
;; Require ibuffer extentions (used for ibuffer-never-show-predicates)
(add-to-list 'ibuffer-never-show-predicates "^\\*slime-events\\*$")
(add-to-list 'ibuffer-never-show-predicates "^\\*Completions\\*$")
(add-to-list 'ibuffer-never-show-predicates "^\\*tramp/.*\\*$")
;; Add vc-status line to ibuffer
(setq ibuffer-formats '((mark modified read-only vc-status-mini " "
(name 18 18 :left :elide) " "
(size 9 -1 :right) " "
(mode 16 16 :left :elide) " "
(vc-status 16 16 :left) " "
filename-and-process)
(mark " " (name 16 -1) " " filename)))
;; Merge the results of filtering by creating filter groups first for projectile
;; then for each major mode (of currently open buffers
(add-hook 'ibuffer-mode-hook
(lambda ()
(setq ibuffer-filter-groups
(append
(ibuffer-projectile-generate-filter-groups)
(mapcar (lambda (mode)
(cons (format "%s" mode) `((mode . ,mode))))
(let ((modes
(ibuffer-remove-duplicates
(mapcar (lambda (buf)
(buffer-local-value 'major-mode buf))
(buffer-list)))))
(if ibuffer-view-ibuffer
modes
(delq 'ibuffer-mode modes))))))
(let ((ibuf (get-buffer "*Ibuffer*")))
(when ibuf
(with-current-buffer ibuf
(pop-to-buffer ibuf)
(ibuffer-update nil t))))))))
#+end_src
*** ibuffer Icons
Leverage [[https://github.com/seagle0128/all-the-icons-ibuffer][all-the-icons-ibuffer]] to show pretty icons beside buffers in iBuffer.
#+begin_src emacs-lisp
(use-package all-the-icons-ibuffer
:after all-the-icons
:config (all-the-icons-ibuffer-mode 1))
#+end_src
** Insert Templates
#+begin_src emacs-lisp
;;
;; Considering phasing this out in-place of yasnippet
;;
(setq auto-insert-query nil ;; If you don't want to be prompted before insertion
autoinsert-tpl-author "Collin J. Doering"
autoinsert-tpl-email "collin.doering@rekahsoft.ca"
auto-insert-directory (no-littering-expand-etc-file-name "templates/") ;; Trailing slash important
auto-insert-alist
'(("\\.c$" . ["c-template.c" auto-update-generic-template])
("\\.cc\\|cpp$" . ["cpp-template.cc" auto-update-generic-template])
("\\.php$" . ["php-template.php" auto-update-generic-template])
("\\.rb$" . ["ruby-template.rb" auto-update-generic-template])
("\\.lua$" . ["lua-template.lua" auto-update-generic-template])
("\\.erl$" . ["erlang-template.erl" auto-update-generic-template])
("\\.sh$" . ["shell-template.sh" auto-update-generic-template])
("\\.rkt$" . ["racket-template.rkt" auto-update-generic-template])
("\\.scm$" . ["scheme-template.scm" auto-update-generic-template])
("\\.clj$" . ["clojure-template.clj" auto-update-generic-template])
("\\.lisp$" . ["lisp-template.lisp" auto-update-generic-template])
("\\.el$" . ["emacs-lisp-template.el" auto-update-generic-template])
("\\.hs$" . ["haskell-template.hs" auto-update-generic-template])
("\\.ml$" . ["ocaml-template.ml" auto-update-generic-template])
("\\.sml$" . ["sml-template.sml" auto-update-generic-template])
("\\.py$" . ["python-template.py" auto-update-generic-template])
("\\.java$" . ["java-template.java" auto-update-generic-template])
("\\.scala$" . ["scala-template.scala" auto-update-generic-template])
("\\.htm\\|html$" . ["html-template.html" auto-update-generic-template])
("\\.js$" . ["java-script-template.js" auto-update-generic-template])
("\\.css$" . ["css-template.css" auto-update-generic-template])
("\\.scss$" . ["scss-template.scss" auto-update-generic-template])
("\\.sass$" . ["sass-template.sass" auto-update-generic-template])
("\\.haml$" . ["haml-template.haml" auto-update-generic-template])
("\\.markdown$" . ["markdown-template.markdown" auto-update-generic-template])
("\\.tex$" . ["latex-template.tex" auto-update-generic-template]))
auto-insert 'other)
;; auto-insert options template and auto-completion
(add-hook 'find-file-hooks 'auto-insert)
;; TODO: remove interactive-ness from auto-update-generic-template as it's not needed
;; and there only as a workaround. Python and PHP templates are not filled for
;; some unknown reason.
(defun auto-update-generic-template ()
(interactive)
(save-excursion
;; Replace @!@FILENAME@!@ with file name sans suffix
(while (search-forward "@!@FILENAME@!@" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(replace-match (file-name-sans-extension (file-name-nondirectory buffer-file-name)) t))))
(save-excursion
;; Replace @!@FILE@!@ with file name
(while (search-forward "@!@FILE@!@" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(replace-match (file-name-nondirectory buffer-file-name) t))))
(save-excursion
;; replace @!@DATE@!@ with today's date
(while (search-forward "@!@DATE@!@" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(replace-match "")
(insert-date))))
(save-excursion
;; Replace @!@YEAR@!@ with the current year
(while (search-forward "@!@YEAR@!@" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(replace-match (format-time-string "%Y" (current-time))))))
(save-excursion
;; Replace @!@AUTHOR@!@ with the current year
(while (search-forward "@!@AUTHOR@!@" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(replace-match "")
(insert-author))))
(save-excursion
;; Replace @!@EMAIL@!@ with the current year
(while (search-forward "@!@EMAIL@!@" nil t)
(save-restriction
(narrow-to-region (match-beginning 0) (match-end 0))
(replace-match "")
(insert-author-email)))))
;; Insert current date at cursor in the currently active buffer
(defun insert-date ()
"Insert today's date into buffer"
(interactive)
(insert (format-time-string "%b %e, %Y" (current-time))))
(defun insert-author ()
"Insert author name at point"
(interactive)
(insert autoinsert-tpl-author))
(defun insert-author-email ()
"Insert author email at point"
(interactive)
(insert autoinsert-tpl-email))
#+end_src
** Terminal
#+begin_src emacs-lisp
;; Setup multi-term
;; See: http://www.emacswiki.org/MultiTerm
(use-package multi-term
:init ;; Fixes issue with tab key
;; See: https://github.com/capitaomorte/yasnippet/issues/289&sa=U&ei=8HJ2VLnbFvHhsASa0oGYBw&ved=0CBgQFjAB&sig2=3GORWPQWDpRVgReLr40nkw&usg=AFQjCNFvbeuG4bRq4nbWTPwuu7-5S4UChA
(add-hook 'term-mode-hook (lambda ()
(yas-minor-mode -1)))
:bind (("C-c t" . multi-term)
("C-c T" . multi-term-dedicated-toggle)
("C-c F" . multi-term-next)
("C-c B" . multi-term-prev)))
(use-package vterm
:config
(setq vterm-kill-buffer-on-exit t
vterm-max-scrollback 100000 ;; Max 100000
vterm-eval-cmds '(("man" man)
("info" info-other-window)
("find-file" find-file)
("message" message)
("vterm-clear-scrollback" vterm-clear-scrollback))))
#+end_src
** Hydra
#+begin_src emacs-lisp
(use-package hydra)
#+end_src
** direnv Support
#+begin_src emacs-lisp
(use-package envrc
:config (envrc-global-mode))
#+end_src
** Which key?
#+begin_src emacs-lisp
(use-package which-key
:config (which-key-mode))
#+end_src
2022-01-31 16:41:17 +00:00
* Programs
** IRC - rcirc
#+begin_src emacs-lisp
;; Use rcirc for irc; see: http://www.emacswiki.org/emacs/rcirc
;; Use rcirc-notify extension; see: http://www.emacswiki.org/emacs/rcircNotify
(use-package rcirc
:demand t
:if (daemonp)
:bind ("C-c I" . rcirc)
:hook ((rcirc-mode . flyspell-mode)) ;; Turn on flyspell mode in rcirc buffers
:config (progn
(use-package dbus
:config (progn
(defun nm-is-connected()
(equal 70 (dbus-get-property
:system "org.freedesktop.NetworkManager" "/org/freedesktop/NetworkManager"
"org.freedesktop.NetworkManager" "State")))))
(use-package rcirc-notify
:config (rcirc-notify-add-hooks))
;; Change user info
(setq rcirc-default-nick "rekahsoft"
rcirc-default-user-name "rekahsoft"
rcirc-default-full-name "rekahsoft"
rcirc-time-format "%Y-%m-%d %H:%M "
rcirc-log-flag t
rcirc-server-alist
'(("irc.libera.chat" :port 6697 :encryption tls
:channels ("#emacs" "#haskell" "#racket" "#xmonad" "#guix" "#guile"))
("localhost" :port 6667 :channels () :method 'bitlbee)))
;; Advice rcirc function to read credentials from via auth-source api
(defun rcirc--read-from-auth-source (&optional args)
(unless args
(setq rcirc-authinfo
(seq-reduce (lambda (rst x)
(let* ((host (car x))
(rec (cdr x))
(user (or (plist-get rec :user)
rcirc-default-user-name))
(method (or (plist-get rec :method)
'nickserv))
(p (auth-source-search :host host :user user :require '(:user :secret) :max 1))
(secret (if p (plist-get (car p) :secret))))
(cons (list host method user (if (functionp secret)
(funcall secret)
secret)) rst)))
rcirc-server-alist nil))))
(advice-add 'rcirc :before #'rcirc--read-from-auth-source)
;; TODO: this function uses the first password and encryption in rcirc-server-alist
;; where it should use the one for server being reconnected to
;; Thanks to: http://www.emacswiki.org/emacs/rcircReconnect
(defun-rcirc-command reconnect (arg)
"Reconnect the server process."
(interactive "i")
(unless process
(error "There's no process for this target"))
(let* ((server (car (process-contact process)))
(port (process-contact process :service))
(nick (rcirc-nick process))
channels query-buffers)
(dolist (buf (buffer-list))
(with-current-buffer buf
(when (eq process (rcirc-buffer-process))
(remove-hook 'change-major-mode-hook
'rcirc-change-major-mode-hook)
(if (rcirc-channel-p rcirc-target)
(setq channels (cons rcirc-target channels))
(setq query-buffers (cons buf query-buffers))))))
(delete-process process)
(rcirc-connect server port nick
rcirc-default-user-name
rcirc-default-full-name
channels
(plist-get (cdr rcirc-server-alist) :password)
(plist-get (cdr rcirc-server-alist) :encryption))))
;; Thanks to: http://www.emacswiki.org/emacs/rcircAll
(defun-rcirc-command all (input)
"Run the arguments as a command for all connections.
Example use: /all away food or /all quit zzzz."
(interactive "s")
(let ((buffers (mapcar 'process-buffer (rcirc-process-list))))
(dolist (buf buffers)
(with-current-buffer buf
(goto-char (point-max))
(insert "/" input)
(rcirc-send-input)))))
;; Turn on rcirc tracking to be displayed in mode-line
(rcirc-track-minor-mode)
;; Connect to servers auto-matically if in daemon-mode
(if (and (daemonp) (nm-is-connected))
(rcirc nil))))
#+end_src
** Feed Reader - elfeed
#+begin_src emacs-lisp
(use-package elfeed
:bind (("C-x w" . elfeed)))
#+end_src
*** Setup elfeed-org
#+begin_src emacs-lisp
(use-package elfeed-org
:after elfeed
:config (elfeed-org)
:custom rmh-elfeed-org-files (list "~/.org/roam/20230422115510-feeds.org"))
#+end_src
2022-01-31 16:41:17 +00:00
** Email - Mu4e
#+begin_src emacs-lisp
;; Setup email using mu4e (mbsync in the background) and smtpmail
(use-package mu4e
:init (setq mu4e~main-buffer-name "*mu4e*")
:config (progn
(setq mail-user-agent 'mu4e-user-agent
mu4e-maildir "~/.mail"
mu4e-get-mail-command "mbsync -a"
mu4e-update-interval 300
message-send-mail-function 'smtpmail-send-it
message-kill-buffer-on-exit t
mu4e-use-fancy-chars t
mu4e-confirm-quit nil
mu4e-headers-date-format "%d/%b/%Y %H:%M"
message-signature-insert-empty-line t
mu4e-view-show-images t
mu4e-view-image-max-width 800
;; Enable org-mode support (enabled by default)
mu4e-support-org t
;; When in headers view, link to the query, not the message at point
mu4e-org-link-query-in-headers-mode t
2022-01-31 16:41:17 +00:00
mml-secure-openpgp-sign-with-sender t
message-cite-style message-cite-style-gmail
;; Change the filename when moving mail; this is required to avoid duplicate uids with mbsync
mu4e-change-filenames-when-moving t
2022-01-31 16:41:17 +00:00
;; Configure Gmail style citation
message-citation-line-format "On %d %b %Y at %R, %f wrote:\n"
message-citation-line-function 'message-insert-formatted-citation-line
mu4e-view-actions '(("capture message" . mu4e-action-capture-message)
("pdf view" . mu4e-action-view-as-pdf)
("browser view" . mu4e-action-view-in-browser)
("show this thread" . mu4e-action-show-thread))
mu4e-contexts
`( ,(make-mu4e-context
:name "Personal"
:enter-func (lambda () (mu4e-message "Entering Personal context"))
:leave-func (lambda () (mu4e-message "Leaving Personal context"))
;; we match based on the contact-fields of the message
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches msg :to "collin@rekahsoft.ca")))
:vars `((user-mail-address . "collin@rekahsoft.ca")
(user-full-name . "Collin J. Doering")
(mu4e-sent-folder . "/collin@rekahsoft.ca/Sent")
(mu4e-drafts-folder . "/collin@rekahsoft.ca/Drafts")
(mu4e-trash-folder . "/collin@rekahsoft.ca/Trash")
(mu4e-refile-folder . "/collin@rekahsoft.ca/Archive")
(mu4e-maildir-shortcuts . (("/collin@rekahsoft.ca/Inbox" . ?i)
("/collin@rekahsoft.ca/Archive" . ?a)
("/collin@rekahsoft.ca/Sent" . ?s)
("/collin@rekahsoft.ca/Drafts" . ?d)
("/collin@rekahsoft.ca/Trash" . ?t)
("/collin@rekahsoft.ca/Junk" . ?J)
("/collin@rekahsoft.ca/Mailing Lists" . ?m)
("/collin@rekahsoft.ca/Mailing Lists/Gnu/Guix" . ?g)))
(mu4e-bookmarks . ((:name "Unread messages"
:query "flag:unread AND NOT flag:trashed AND maildir:/collin@rekahsoft.ca/*"
:key ?u)
(:name "Today's messages"
:query "date:today..now AND maildir:/collin@rekahsoft.ca/*"
:key ?t)
(:name "Last 7 days"
:query "date:7d..now AND maildir:/collin@rekahsoft.ca/*"
:key ?w)
(:name "Messages with images"
:query "mime:image/* AND maildir:/collin@rekahsoft.ca/*"
:key ?p)))
;; Move emails to sent folder upon being successfully sent
(mu4e-sent-messages-behavior . sent)
2022-01-31 16:41:17 +00:00
(mu4e-compose-signature .
(concat
"Collin J. Doering\n\n"
"http://rekahsoft.ca\n"
"http://blog.rekahsoft.ca\n"
"http://git.rekahsoft.ca\n"))))
,(make-mu4e-context
:name "GMail"
:enter-func (lambda () (mu4e-message "Entering GMail context"))
:leave-func (lambda () (mu4e-message "Leaving GMail context"))
;; we match based on the contact-fields of the message
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches msg :to "collin.doering@gmail.com")))
:vars `((user-mail-address . "collin.doering@gmail.com")
(user-full-name . "Collin J. Doering")
(mu4e-sent-folder . "/collin.doering@gmail.com/[Gmail]/.Sent Mail")
(mu4e-drafts-folder . "/collin.doering@gmail.com/[Gmail]/.Drafts")
(mu4e-trash-folder . "/collin.doering@gmail.com/[Gmail]/.Trash")
(mu4e-refile-folder . "/collin.doering@gmail.com/[Gmail]/.All Mail")
(mu4e-maildir-shortcuts . (("/collin.doering@gmail.com/Inbox" . ?i)
("/collin.doering@gmail.com/[Gmail]/.All Mail" . ?a)
("/collin.doering@gmail.com/[Gmail]/.Sent Mail" . ?s)
("/collin.doering@gmail.com/[Gmail]/.Drafts" . ?d)
("/collin.doering@gmail.com/[Gmail]/.Starred" . ?f)
("/collin.doering@gmail.com/[Gmail]/.Trash" . ?t)
("/collin.doering@gmail.com/[Gmail]/.Spam" . ?J)))
(mu4e-bookmarks . ((:name "Unread messages"
:query "flag:unread AND NOT flag:trashed AND maildir:/collin.doering@gmail.com/*"
:key ?u)
(:name "Today's messages"
:query "date:today..now AND maildir:/collin.doering@gmail.com/*"
:key ?t)
(:name "Last 7 days"
:query "date:7d..now AND maildir:/collin.doering@gmail.com/*"
:key ?w)
(:name "Messages with images"
:query "mime:image/* AND maildir:/collin.doering@gmail.com/*"
:key ?p)))
2022-01-31 16:41:17 +00:00
;; GMail automatically adds messages to the users sent folder, so successfully sent messages should be deleted to avoid duplicates
(mu4e-sent-messages-behavior . delete)
(mu4e-compose-signature .
(concat
"Collin J. Doering\n\n"
"http://rekahsoft.ca\n"
"http://blog.rekahsoft.ca\n"
"http://git.rekahsoft.ca\n"))))
,(make-mu4e-context
:name "Rekahsoft-GMail"
:enter-func (lambda () (mu4e-message "Entering Rekahsoft-GMail context"))
:leave-func (lambda () (mu4e-message "Leaving Rekahsoft-GMail context"))
;; we match based on the contact-fields of the message
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches msg :to "rekahsoft@gmail.com")))
:vars `((user-mail-address . "rekahsoft@gmail.com")
(user-full-name . "Collin J. Doering")
(mu4e-sent-folder . "/rekahsoft@gmail.com/[Gmail]/.Sent Mail")
(mu4e-drafts-folder . "/rekahsoft@gmail.com/[Gmail]/.Drafts")
(mu4e-trash-folder . "/rekahsoft@gmail.com/[Gmail]/.Trash")
(mu4e-refile-folder . "/rekahsoft@gmail.com/[Gmail]/.All Mail")
(mu4e-maildir-shortcuts . (("/rekahsoft@gmail.com/Inbox" . ?i)
("/rekahsoft@gmail.com/[Gmail]/.All Mail" . ?a)
("/rekahsoft@gmail.com/[Gmail]/.Sent Mail" . ?s)
("/rekahsoft@gmail.com/[Gmail]/.Drafts" . ?d)
("/rekahsoft@gmail.com/[Gmail]/.Starred" . ?f)
("/rekahsoft@gmail.com/[Gmail]/.Trash" . ?t)
("/rekahsoft@gmail.com/[Gmail]/.Spam" . ?J)))
(mu4e-bookmarks . ((:name "Unread messages"
:query "flag:unread AND NOT flag:trashed AND maildir:/rekahsoft@gmail.com/*"
:key ?u)
(:name "Today's messages"
:query "date:today..now AND maildir:/rekahsoft@gmail.com/*"
:key ?t)
(:name "Last 7 days"
:query "date:7d..now AND maildir:/rekahsoft@gmail.com/*"
:key ?w)
(:name "Messages with images"
:query "mime:image/* AND maildir:/rekahsoft@gmail.com/*"
:key ?p)))
2022-01-31 16:41:17 +00:00
;; GMail automatically adds messages to the users sent folder, so successfully sent messages should be deleted to avoid duplicates
(mu4e-sent-messages-behavior . delete)
(mu4e-compose-signature .
(concat
"Collin J. Doering\n\n"
"http://rekahsoft.ca\n"
"http://blog.rekahsoft.ca\n"
"http://git.rekahsoft.ca\n"))))
,(make-mu4e-context
:name "NoRedInk"
:enter-func (lambda () (mu4e-message "Entering NoRedInk context"))
:leave-func (lambda () (mu4e-message "Leaving NoRedInk context"))
;; we match based on the contact-fields of the message
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches msg :to "collin@noredink.com")))
:vars `((user-mail-address . "collin@noredink.com")
(user-full-name . "Collin J. Doering")
(mu4e-sent-folder . "/collin@noredink.com/[Gmail]/.Sent Mail")
(mu4e-drafts-folder . "/collin@noredink.com/[Gmail]/.Drafts")
(mu4e-trash-folder . "/collin@noredink.com/[Gmail]/.Trash")
(mu4e-refile-folder . "/collin@noredink.com/[Gmail]/.All Mail")
(mu4e-maildir-shortcuts . (("/collin@noredink.com/Inbox" . ?i)
("/collin@noredink.com/[Gmail]/.All Mail" . ?a)
("/collin@noredink.com/[Gmail]/.Sent Mail" . ?s)
("/collin@noredink.com/[Gmail]/.Drafts" . ?d)
("/collin@noredink.com/[Gmail]/.Starred" . ?f)
("/collin@noredink.com/[Gmail]/.Trash" . ?t)
("/collin@noredink.com/[Gmail]/.Spam" . ?J)))
(mu4e-bookmarks . ((:name "Unread messages"
:query "flag:unread AND NOT flag:trashed AND maildir:/collin@noredink.com/*"
:key ?u)
(:name "Today's messages"
:query "date:today..now AND maildir:/collin@noredink.com/*"
:key ?t)
(:name "Last 7 days"
:query "date:7d..now AND maildir:/collin@noredink.com/*"
:key ?w)
(:name "Messages with images"
:query "mime:image/* AND maildir:/collin@noredink.com/*"
:key ?p)))
;; GMail automatically adds messages to the users sent folder, so successfully sent messages should be deleted to avoid duplicates
(mu4e-sent-messages-behavior . delete)
(mu4e-compose-signature .
(concat
"Collin Doering\n"
"Engineering Manager, NoRedInk\n"
"P: 519-500-0931\n"
"F: 844-667-3346\n"
"https://www.noredink.com\n\n"))))))
2022-01-31 16:41:17 +00:00
;; Use imagemagick to display images (if available)
(when (fboundp 'imagemagick-register-types)
(imagemagick-register-types))
;; Verify gpg signatures for known keys
(setq mm-verify-option 'known)
;; Sign outgoing mail by default
(add-hook 'message-send-hook #'mml-secure-message-sign-pgpmime))
2022-01-31 16:41:17 +00:00
:hook ((mu4e-compose-mode . (lambda () (auto-save-mode -1)))
(mu4e-compose-mode . turn-off-auto-fill)
(mu4e-compose-mode . visual-line-mode))
:bind (("C-x M" . mu4e)))
(use-package mu4e-icalendar
:after mu4e
:config (mu4e-icalendar-setup))
2022-01-31 16:41:17 +00:00
(use-package mu4e-alert
:after mu4e
:commands mu4e-alert-set-default-style
:config (progn
(setq mu4e-alert-interesting-mail-query "flag:unread AND (maildir:/collin@rekahsoft.ca/Inbox OR maildir:/collin.doering@gmail.com/Inbox)")
(mu4e-alert-set-default-style 'libnotify))
:hook ((after-init . mu4e-alert-enable-notifications)
(after-init . mu4e-alert-enable-mode-line-display)))
(use-package smtpmail
:config (progn
;; Setup smtp defaults
(setq user-full-name "Collin J. Doering"
smtpmail-smtp-server "smtp.migadu.com"
smtpmail-smtp-service 587
smtpmail-stream-type 'starttls
smtpmail-debug-info t)
(setq smtp-accounts
'(("collin@rekahsoft.ca" "Collin J. Doering" "smtp.migadu.com")
("collin.doering@gmail.com" "Collin J. Doering" "smtp.gmail.com")
("rekahsoft@gmail.com" "rekahsoft" "smtp.gmail.com")
("collin@noredink.com" "Collin J. Doering" "smtp.gmail.com")))
2022-01-31 16:41:17 +00:00
(defun my-change-smtp ()
(save-excursion
(cl-loop with from = (save-restriction
(message-narrow-to-headers)
(message-fetch-field "from"))
for (addr fname server) in smtp-accounts
when (string-match addr from)
do (setq user-mail-address addr
user-full-name fname
smtpmail-smtp-server server
smtpmail-smtp-user addr))))
2022-01-31 16:41:17 +00:00
(defadvice smtpmail-via-smtp
(before change-smtp-by-message-from-field (recipient buffer &optional ask) activate)
(with-current-buffer buffer (my-change-smtp)))
(ad-activate 'smtpmail-via-smtp)))
;; Attach files using dired using 'C-c RET C-a'
;; Thanks to: http://www.djcbsoftware.nl/code/mu/mu4e/Attaching-files-with-dired.html#Attaching-files-with-dired
(use-package gnus-dired
:config (progn
;; make the `gnus-dired-mail-buffers' function also work on
;; message-mode derived modes, such as mu4e-compose-mode
(defun gnus-dired-mail-buffers ()
"Return a list of active message buffers."
(let (buffers)
(save-current-buffer
(dolist (buffer (buffer-list t))
(set-buffer buffer)
(when (and (derived-mode-p 'message-mode)
(null message-sent-message-via))
(push (buffer-name buffer) buffers))))
(nreverse buffers)))
(setq gnus-dired-mail-mode 'mu4e-user-agent)
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)))
#+end_src
*** HTML Email using org-mode
Use the org-mime package, to enable sending html email.
#+begin_src emacs-lisp
(use-package org-mime)
#+end_src
2022-01-31 16:41:17 +00:00
** PDF Viewer
Emacs comes with a built in pdf reader that leverages imagemagic to convert the pdf to images
which are then read off disk. This is not optimal from the perspective of performance
however, but luckily the [[https://github.com/politza/pdf-tools][pdf-tools]] package resolves this, as well as adds a variety of nice
features (eg search, text annotation, highlighting, and more..) on top of providing a much
nicer user experience.
#+begin_src emacs-lisp
(use-package pdf-tools
:config (pdf-tools-install))
#+end_src
* TODO Editing :needs_review:
This section should be reviewed as this has grown over time. Also, some if not many of these
settings can likely be replaced by the [[https://github.com/hrs/sensible-defaults.el][sensible-defaults]] package.
#+begin_src emacs-lisp
;; Turn off indentation (use spaces instead)
(setq-default indent-tabs-mode nil)
;; Turn on global pretty-ification of symbols
(global-prettify-symbols-mode 1)
(setq
;; Make cursor the width of the character it is under
x-stretch-cursor t
;; Single spaces denote end sentences for use with sentence commands
sentence-end-double-space nil)
;; Show column number in mode-line
(column-number-mode)
;; Show size of file in mode-line
(size-indication-mode)
;; Set the fill-column for text filling
(setq-default fill-column 93)
;; TODO: guix - this currently does not load in daemon mode
;; Turn hl-line-mode on globally
;;(global-hl-line-mode)
;; Activate linum-mode in all buffers used for programming
(activate-mode-with-hooks (lambda () (display-line-numbers-mode 1)) code-modes)
;; Add little indicators to fringe indicating the location of point in the given window
;; See: https://www.gnu.org/software/emacs/manual/html_node/emacs/Displaying-Boundaries.html
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Useless-Whitespace.html
(setq-default indicate-buffer-boundaries 'left
indicate-empty-lines t)
;; Don't scroll the buffer to center when using follow-mode
(add-hook 'follow-mode-hook (lambda ()
(make-local-variable 'scroll-conservatively)
(set 'scroll-conservatively 101)))
;; Indicate empty space at the end of a line
;;(setq-default show-trailing-whitespace nil)
;; Do not display error when killing from a read-only buffer; instead just show a warning
;; Note: in both cases the killed text is copied to the kill-ring
;; See: https://www.gnu.org/software/emacs/manual/html_node/emacs/Kill-Options.html#Kill-Options
(setq kill-read-only-ok t)
;; Enable the disabled narrowing commands
(put 'narrow-to-defun 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'narrow-to-region 'disabled nil)
;; Setup cursor-blink-mode
(setq blink-cursor-blinks 3)
(blink-cursor-mode)
;; Setup electric-pair mode globally
(electric-pair-mode)
;; Use view-mode aggressively for read-only files
;; See: http://www.emacswiki.org/emacs/ViewMode
(setq view-read-only t)
;; Stop renaming of saved files to filename~ which ends up breaking hardlinks
(setq backup-by-copying-when-linked t)
;; fixes color output issues; see: http://wiki.archlinux.org/index.php/Emacs#Colored_output_issues
(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)
;; Automatically open some config files with an associated major mode
;; Note: regexp's used to match buffer filenames are intentionally left
;; unbounded (without '$') to catch cases where the filename may
;; take the format: filename~
(setq auto-mode-alist
(append auto-mode-alist
'(("\\.conkerorrc" . js-mode))
'(("\\.xmobarrc" . haskell-mode))
'(("\\.screenrc" . conf-mode))
'(("\\.stumpwmrc" . lisp-mode))
'(("\w*\\.service" . conf-mode))
'(("\w*\\.socket" . conf-mode))
'(("\\.mpdconf" . conf-mode))
'(("dunstrc" . conf-mode))))
2022-01-31 16:41:17 +00:00
;; Thanks to: http://ergoemacs.org/emacs/emacs_byte_compile.html
(defun byte-compile-if-elisp ()
"`byte-compile' current buffer if it's emacs-lisp-mode and compiled file exists."
(interactive)
(when (and (eq major-mode 'emacs-lisp-mode)
(file-exists-p (byte-compile-dest-file buffer-file-name)))
(byte-compile-file buffer-file-name)))
(add-hook 'after-save-hook 'byte-compile-if-elisp)
(use-package string-inflection)
(use-package fill-column-indicator
:config (turn-on-fci-mode))
;; Highlight indentation
(use-package highlight-indent-guides
:init (progn
(setq highlight-indent-guides-method 'character
highlight-indent-guides-character ?\|
highlight-indent-guides-auto-odd-face-perc 15
highlight-indent-guides-auto-even-face-perc 15
highlight-indent-guides-auto-character-face-perc 20))
:config (add-hook 'prog-mode-hook 'highlight-indent-guides-mode))
#+end_src
Enable some commands that are by default disabled.
#+begin_src elisp
(put 'set-goal-column 'disabled nil)
(put 'upcase-region 'disabled nil)
#+end_src
** Ace Jump Mode
#+begin_src emacs-lisp
(use-package ace-jump-mode
:bind ("C-c SPC" . ace-jump-mode))
#+end_src
** Link Navigation
[[https://github.com/abo-abo/avy][avy]] is a package that assist with jumping to visible text using a char-based decision tree.
The [[https://github.com/abo-abo/ace-link][ace-link]] package leverages avy to provide quick navigation of links in various contexts.
#+begin_src emacs-lisp
(use-package ace-link
:config (ace-link-setup-default))
#+end_src
** Expand Region
#+begin_src emacs-lisp
(use-package expand-region
:bind ("C-=" . er/expand-region))
#+end_src
** Multiple Cursors
#+begin_src emacs-lisp
(use-package multiple-cursors
:bind (("C-S-c C-S-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C-<" . mc/mark-all-like-this)))
(use-package ace-mc
:after multiple-cursors ace-jump-mode
:bind (("C-c m m" . ace-mc-add-multiple-cursors)
("C-c m s" . ace-mc-add-single-cursor)))
#+end_src
** Auto Complete
Setup fancy auto-complete
#+begin_src emacs-lisp
(use-package auto-complete)
(use-package auto-complete-config
:after auto-complete
:config (progn
(ac-config-default)
;; Set trigger keys so yasnippet and auto-complete play nicely. If tab is pressed
;; and the word at point exists in yasnippet, then yassippet will be used;
;; otherwise auto-complete will be used.
(ac-set-trigger-key "TAB")
(ac-set-trigger-key "<tab>")))
;;(add-to-list 'ac-dictionary-directories "/usr/share/emacs/site-lisp/auto-complete/ac-dict")
#+end_src
** Yaml
#+begin_src emacs-lisp
(use-package yaml-mode
:config (add-hook 'yaml-mode-hook
(lambda ()
(make-variable-buffer-local 'yas-indent-line)
(setq yas-indent-line nil))))
#+end_src
** Yasnippet
#+begin_src emacs-lisp
;; Courtesy of https://github.com/makp/emacs-configs/blob/master/mk_yasnippet-setup.el
(defun shk-yas/helm-prompt (prompt choices &optional display-fn)
"Use helm to select snippet choices."
(interactive)
(setq display-fn (or display-fn 'identity))
(if (require 'helm-config)
(let (tmpsource cands result rmap)
(setq cands (mapcar (lambda (x) (funcall display-fn x)) choices))
(setq rmap (mapcar (lambda (x) (cons (funcall display-fn x) x)) choices))
(setq tmpsource
(list
(cons 'name prompt)
(cons 'candidates cands)
'(action . (("Expand" . (lambda (selection) selection))))
))
(setq result (helm-other-buffer '(tmpsource) "*helm-select-yasnippet"))
(if (null result)
(signal 'quit "user quit!")
(cdr (assoc result rmap))))
nil))
(use-package yasnippet
:defer t
:config (progn
(if is-guix-system-p
(yas-load-directory "~/.guix-home/profile/share/emacs/site-lisp/yasnippet-snippets-1.0/snippets")
2022-01-31 16:41:17 +00:00
(yas-load-directory "~/.emacs.d/elpa/yasnippet-snippets-20200802.1658"))
(yas-load-directory (no-littering-expand-etc-file-name "snippets"))
(setq-default
ac-sources (push 'ac-source-yasnippet ac-sources)
yas-prompt-functions '(shk-yas/helm-prompt yas-dropdown-prompt))
(yas-global-mode 1)))
(use-package helm-c-yasnippet
:bind (("C-<tab>" . helm-yas-complete))
:config (setq helm-yas-space-match-any-greedy t))
(use-package auto-yasnippet
:bind (("C-c w" . aya-create)
("C-c e" . aya-expand)))
#+end_src
** Rebox2
#+begin_src emacs-lisp
;; TODO: this is breaks multiple cursors (rebox replaces yank and other commands)
;; url: http://www.emacswiki.org/emacs/rebox2
(use-package rebox2
:init (setq rebox-style-loop '(10 11 12 13 15 16 17 20 21 22 23 26 27))
:config (define-globalized-minor-mode global-rebox-mode rebox-mode rebox-mode))
#+end_src
** God Mode
#+begin_src emacs-lisp
(use-package god-mode
:config (progn
;;(require 'god-mode-isearch)
;;(define-key isearch-mode-map (kbd "<escape>") 'god-mode-isearch-activate)
;;(define-key god-mode-isearch-map (kbd "<escape>") 'god-mode-isearch-disable)
(define-key god-local-mode-map (kbd ".") 'repeat)
(define-key god-local-mode-map (kbd "i") 'god-local-mode)
(setq god-exempt-major-modes
(append god-exempt-major-modes
'(eshell-mode term-mode rcirc-mode mu4e-main-mode
mu4e-view-mode mu4e-headers-mode mu4e-compose-mode)))
;; (setq god-exempt-predicates nil)
(defun c/god-mode-update-cursor ()
(cond (god-local-mode (progn
(set-face-background 'mode-line "grey20")
(set-face-foreground 'mode-line "#1ddeaa")
(set-face-background 'mode-line-inactive "grey3")
(set-face-foreground 'mode-line-inactive "grey80")))
(t (progn
(set-face-background 'mode-line "grey20")
(set-face-foreground 'mode-line "#00afff")
(set-face-background 'mode-line-inactive "grey5")
(set-face-foreground 'mode-line-inactive "grey80")))))
(add-hook 'god-mode-enabled-hook 'c/god-mode-update-cursor)
(add-hook 'god-mode-disabled-hook 'c/god-mode-update-cursor))
;; This kills the ESC prefix keymap, so I may replace it with a different binding
:bind ("<escape>" . god-mode-all))
#+end_src
** Dired
#+begin_src emacs-lisp
;; Enable dired-find-alternative-file
(put 'dired-find-alternate-file 'disabled nil)
;; Use human readable file sizes
(setq dired-listing-switches "-alh")
;; Setup omit-mode in dired
(require 'dired-x)
(setq-default dired-omit-files-p t) ; Buffer-local variable
(setq dired-omit-files (concat dired-omit-files "\\|^\\..+$"))
;; Use dired-subtree from dired-hacks
(use-package dired-subtree
:config (bind-keys :map dired-mode-map
("i" . dired-subtree-insert)
(";" . dired-subtree-remove)))
;; Use dired-narrow from dired-hacks to narrow dired to match filter
(use-package dired-narrow
:bind (:map dired-mode-map
("/" . dired-narrow)))
#+end_src
** Undo Tree
#+begin_src emacs-lisp
;; Setup undo-tree
(use-package undo-tree
:config (progn
(global-undo-tree-mode)))
#+end_src
** Code Folding
#+begin_src emacs-lisp
(use-package vimish-fold
:config (vimish-fold-global-mode 1)
:bind (("C-x n f" . vimish-fold)
("C-x n F" . vimish-fold-refold-all)
("C-x n k" . vimish-fold-delete)
("C-x n u" . vimish-fold-unfold-all)))
#+end_src
** TODO Debugging
Currently this is broken and results in a non-blocking error upon emacs startup.
#+begin_src emacs-lisp
(use-package realgud
:disabled)
#+end_src
** Swiper
#+begin_src emacs-lisp
(use-package swiper
:bind (("C-c s" . swiper)))
#+end_src
* Modes
** Winner
#+begin_src emacs-lisp
(use-package winner
:init (winner-mode +1)
:bind (("<M-left>" . winner-undo)
("<M-right>" . winner-redo)))
#+end_src
** Magit
#+begin_src emacs-lisp
(defun magit-status-with-prefix ()
"Call magit-status with the prefix key"
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively 'magit-status)))
(use-package magit
:init (setq magit-commit-signoff t
magit-status-buffer-switch-function 'switch-to-buffer
magit-last-seen-setup-instructions "1.4.0"
vc-follow-symlinks t)
:bind (("C-x g" . magit-status)
("C-x G" . magit-status-with-prefix)))
#+end_src
Use [[https://github.com/magit/forge][magit-forge]] to interact with git forges.
#+begin_src emacs-lisp
(use-package forge
:after magit
:config (setq forge-alist
(append forge-alist
'(("git.rekahsoft.ca:2222" "git.rekahsoft.ca/api/v1"
2022-01-31 16:41:17 +00:00
"git.rekahsoft.ca" forge-gitea-repository)))))
#+end_src
Use [[https://github.com/magit/orgit][orgit]] to allow links to magit from org-mode files.
#+begin_src emacs-lisp
(use-package orgit
:after org
:config
;; Allow org-export to work with magit links to repositories in my personal git forges
(setq orgit-export-alist
(append orgit-export-alist
'(("git.home.rekahsoft.ca[:/]\\(.+\\)$"
"https://git.home.rekahsoft.ca/%n" "https://git.home.rekahsoft.ca/%n/commits/%r" "https://git.home.rekahsoft.ca/%n/commit/%r")
("git.rekahsoft.ca[:/]\\(.+\\)$"
"https://git.rekahsoft.ca/%n" "https://git.rekahsoft.ca/%n/commits/%r" "https://git.rekahsoft.ca/%n/commit/%r")))))
2022-01-31 16:41:17 +00:00
#+end_src
** Calendar
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
(setq calendar-latitude 43.1
calendar-longitude -80.7
2022-01-31 16:41:17 +00:00
calendar-location-name "Woodstock, ON")
#+end_src
#+begin_src emacs-lisp
(setq calendar-time-zone -300
calendar-standard-time-zone-name "EST"
calendar-daylight-time-zone-name "EDT")
2022-01-31 16:41:17 +00:00
#+end_src
*** Provide functions to show sunset and sunrise separately in org-mode diary/agenda
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
;; Taken from: https://www.reddit.com/r/orgmode/comments/a1z26t/sunrise_sunset_as_separate_entries_on_agenda_view/
(use-package solar)
(defun solar-sunrise-string (date &optional nolocation)
"String of *local* time of sunrise and daylight on Gregorian DATE."
(let ((l (solar-sunrise-sunset date)))
(format
"%s (%s hours daylight)"
(if (car l)
(concat "Sunrise " (apply 'solar-time-string (car l)))
"no sunrise")
(nth 2 l)
)))
;; To be called from diary-list-sexp-entries, where DATE is bound.
;;;###diary-autoload
(defun diary-sunrise ()
"Local time of sunrise as a diary entry.
Accurate to a few seconds."
(or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(solar-sunrise-string date))
(defun solar-sunset-string (date &optional nolocation)
"String of *local* time of sunset and daylight on Gregorian DATE."
(let ((l (solar-sunrise-sunset date)))
(format
"%s (%s hours daylight)"
(if (cadr l)
(concat "Sunset " (apply 'solar-time-string (cadr l)))
"no sunset")
(nth 2 l)
)))
;; To be called from diary-list-sexp-entries, where DATE is bound.
;;;###diary-autoload
(defun diary-sunset ()
"Local time of sunset as a diary entry.
Accurate to a few seconds."
(or (and calendar-latitude calendar-longitude calendar-time-zone)
(solar-setup))
(solar-sunset-string date))
2022-01-31 16:41:17 +00:00
#+end_src
** Org Mode
Use [[http://gnuplot.sourceforge.net/][gnuplot]] alongside org-mode to generate plots.
#+begin_src emacs-lisp
(use-package gnuplot
:defer t)
#+end_src
#+begin_src emacs-lisp
(use-package org
:custom org-modules (append '(org-habit) org-modules)
:config (progn
(setq org-directory "~/.org"
org-attach-directory (concat (file-name-as-directory org-directory) "data")
org-todo-keywords '((sequence "TODO(t)" "NEXT(n!/!)" "DOING(s!/!)" "|" "DONE(d!/@)")
(sequence "WAIT(w@/!)" "PAUSE(p@/!)" "|" "CANCELED(c@/@)" "PHONE(P!)")
(sequence "REPLY(r!)" "|" "SENT(S!)"))
org-todo-keyword-faces '(("TODO" . org-warning)
("DOING" . "cyan")
("DONE" . org-done)
("WAIT" . "orange red")
("PAUSE" . "yellow")
("CANCELED" . (:foreground "orange" :weight bold))
("PHONE" . org-done)
("REPLY" . org-warning)
("SENT" . org-done))
org-agenda-sorting-strategy '((agenda time-up priority-down category-keep)
(todo priority-down category-keep)
(tags priority-down category-keep)
(search category-keep))
org-agenda-window-setup 'current-window
org-agenda-clockreport-parameter-plist '(:link t :maxlevel 3 :hidefiles t :properties ("CATEGORY"))
;; Use org-indent-mode globally
org-startup-indented t
;; Ensure that org-add-note puts notes after drawers
;; See: https://emacs.stackexchange.com/questions/17282/org-mode-logbook-note-entry-without-logbook-drawer
org-log-state-notes-insert-after-drawers t
;; Set org-jump default interface. The org-jump outline can still be
;; accessed using a universal prefix
;; See: https://emacs.stackexchange.com/questions/32617/how-to-jump-directly-to-an-org-headline
org-goto-interface 'outline-path-completion
;; Do not save archive file automatically (this allows for a nicer recursive archiving, with large encrypted org files)
org-archive-subtree-save-file-p nil
2022-01-31 16:41:17 +00:00
org-return-follows-link t
org-id-link-to-org-use-id 'use-existing
2022-01-31 16:41:17 +00:00
org-log-done 'time
org-src-fontify-natively t
org-enforce-todo-dependencies t
org-refile-use-outline-path t
org-global-properties '(("Effort_ALL" . "0 0:10 0:15 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00"))
org-columns-default-format "%65ITEM(Task) %TODO %3PRIORITY %8Effort(Effort){:} %CLOCKSUM %SCHEDULED %DEADLINE %TAGS"
org-complete-tags-always-offer-all-agenda-tags t
;; Setup external applications used when org-mode opens files
;; See: https://orgmode.org/worg/org-faq.html#external-application-launched-to-open-file-link
org-file-apps '((auto-mode . emacs)
(directory . emacs)
("\\.mm\\'" . default)
("\\.x?html?\\'" . "xdg-open %s")
("\\.pdf\\'" . default)))
2022-01-31 16:41:17 +00:00
;; Setup org-crypt
(setq org-crypt-key "E05BFEC8"
org-crypt-disable-auto-save 'encrypt)
(org-crypt-use-before-save-magic)
;; Enable markdown export
(require 'ox-md)
;; Enable koma-letter support
;; See: https://orgmode.org/worg/exporters/koma-letter-export.html
(require 'ox-koma-letter)
2022-01-31 16:41:17 +00:00
(setq org-agenda-files
(let ((agenda-dir "~/.org/roam/agenda")
(agenda-files '()))
(append
;; Include manually specified agenda-files
agenda-files
;; Include all *.org{,.gpg} files in agenda-dir
(if (file-directory-p agenda-dir)
(let* ((all-org-agenda-dir-files (directory-files-recursively
agenda-dir
"^[.-a-zA-Z]+\.org\\(\.gpg\\)?$" t t))
(all-org-agenda-dir-archive-files (seq-filter (apply-partially #'string-match (concat "^" agenda-dir "[.-a-zA-Z]+\-archive.org\\(\.gpg\\)?$")) all-org-agenda-dir-files))
(all-org-agenda-dir-files-sans-archive-files (cl-set-difference all-org-agenda-dir-files all-org-agenda-dir-archive-files)))
(append all-org-agenda-dir-files-sans-archive-files
all-org-agenda-dir-archive-files)))))
2022-01-31 16:41:17 +00:00
org-refile-targets `((nil :maxlevel . 5)
(,org-agenda-files :maxlevel . 3)))
;; This provides a much more usable experience with org-refile from helm
(setq org-outline-path-complete-in-steps nil
org-refile-use-outline-path t)
;; Set org-timer options
(setq org-timer-default-timer "0:25:00"
org-clock-sound (no-littering-expand-etc-file-name "assets/beep.wav")
org-show-notification-timeout 5)
;; Persist org-mode clocks between emacs sessions
(setq org-clock-persist 'history)
(org-clock-persistence-insinuate)
;; Enable org-mode capture
(setq org-default-notes-file "~/.org/roam/agenda/capture.org")
(setq org-capture-templates
'(("t" "Templates for tasks")
("tt" "Task" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* TODO %?\n %i\n")
("tT" "Immediate task" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* DOING %?\n%T\n%i\n" :clock-in t :clock-resume t)
("te" "Task regarding a particular email" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* TODO Follow up on %a from '%:fromname' %?\n%U\n")
("tE" "Immediately start task regarding a particular email" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* Doing Follow up on %a from %:fromname %?\n%U\n" :clock-in t :clock-resume t)
("tf" "Task with regards to a particular file" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* TODO %?\nFile: [[%F]]\n")
("tF" "Task with regards to a particular file" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* TODO %?\nFile: [[%F]]\n" :clock-in t :clock-keep t)
("a" "Templates for appointments")
("aa" "Appointment" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* %?\n%i%^T")
("aA" "Immediate appointment" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* %?\n %i\n%i%T" :clock-in t :clock-resume t)
("ab" "Immediate break" entry
(file+olp "~/.org/roam/agenda/noredink-agenda.org.gpg" "Breaks")
"* %?\n%i%i%T" :clock-in t :clock-resume t)
2022-01-31 16:41:17 +00:00
("r" "Templates for replies")
("re" "Reply to a particular email" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* REPLY to '%:fromname' regarding %a%?\n%U\n")
("rE" "Immediately reply to a particular email" entry
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* SENT to %:fromname regarding %a%?\n%U\n" :clock-in t :clock-resume t)
("c" "Phone call" entry
2022-01-31 16:41:17 +00:00
(file+olp "~/.org/roam/agenda/capture.org" "Triage")
"* PHONE %?\n %U\n" :clock-in t :clock-resume t)
;; ;; TODO: these should likely be moved into org-roam-dailies capture templates
;; ("l" "Templates for logging")
;; ("ll" "Log" entry
;; (file+datetree "~/.org/logbook.org")
;; "* %?\n Entered on %U\n %i\n")
;; ("lf" "Log with file link" entry
;; (file+datetree "~/.org/logbook.org")
;; "* %?\n Entered on %U\n %i\n %a")
))
;; Automatically give all capture entries an id
(add-hook 'org-capture-mode-hook #'org-id-get-create)
2022-01-31 16:41:17 +00:00
;; Use minted (requires python package pygments) for exported source code listings when using
;; pdflatex
;; See: https://emacs.stackexchange.com/questions/27154/exporting-highlighted-source-code-to-pdf-in-org-mode
(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted
org-latex-pdf-process
'("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
;; Add additional languages for org-babel (now part of org-mode)
(org-babel-do-load-languages
'org-babel-load-languages
'((haskell . t)
(gnuplot . t)
(emacs-lisp . t)
(C . t)
(python . t)
(awk . t)
(clojure . t)
(ditaa . t)
(dot . t)
(groovy . t)
(java . t)
(python . t)
(lisp . t)
(ledger . t)
(makefile . t)
2022-01-31 16:41:17 +00:00
(scheme . t)
(plantuml . t)
(octave . t)
(rec . t)
(sql . t)
(sqlite . t)
(shell . t)))
(setq org-plantuml-exec-mode 'plantuml))
:bind (("C-c c" . org-capture)
("C-c l" . org-store-link)
("C-c a" . org-agenda)
("C-c b" . org-switchb)
("C-c L" . org-insert-link-global)
("C-c o" . org-open-at-point-global)
("C-c C-/" . org-decrypt-entries)))
#+end_src
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
2022-01-31 16:41:17 +00:00
;; Enable easy templates
(use-package org-tempo
:after org)
#+end_src
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
(use-package org-expiry
:config (progn
(org-expiry-insinuate)
(setq org-expiry-inactive-timestamps t))
:after org
:hook ((org-capture-mode-hook org-expiry-insert-created)))
#+end_src
#+begin_src emacs-lisp
2022-01-31 16:41:17 +00:00
;; GUIX TODO: This package is not available
;; (use-package org-magit
;; :after org)
#+end_src
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
;; TODO: this package is not available on guix
;; (use-package org-ac
;; :after org
;; :config (org-ac/config-default))
#+end_src
2022-01-31 16:41:17 +00:00
*** Setup [[https://github.com/IvanMalison/org-projectile][org-projectile]]
#+begin_src emacs-lisp
(use-package org-projectile
:bind (("C-c f p" . org-projectile-project-todo-completing-read))
:config
(progn
(setq org-projectile-projects-file
"~/.org/roam/20210111004034-projects.org")
(setq org-agenda-files (append org-agenda-files (org-projectile-todo-files))
org-refile-targets `((nil :maxlevel . 5)
(,org-agenda-files :maxlevel . 3)))
(add-to-list 'org-capture-templates (org-projectile-project-todo-entry) t)))
#+end_src
2022-01-31 16:41:17 +00:00
*** Setup [[https://github.com/abo-abo/org-download][org-download]]
#+begin_src emacs-lisp
(use-package org-download
:after org
:config (setq org-download-screenshot-method "scrot -s %s")
:custom
(org-download-method 'attach)
2022-01-31 16:41:17 +00:00
:bind (:map org-mode-map
("C-c d y" . org-download-yank)
("C-c d s" . org-download-screenshot)
("C-c d e" . org-download-edit)))
#+end_src
*** Setup [[https://www.orgroam.com][org-roam]]
#+begin_src emacs-lisp
(use-package org-roam
:init
(setq org-roam-v2-ack t)
:custom
;; TODO: these settings need to be reviewed after the upgrade to v2
(org-roam-directory (expand-file-name "~/.org/roam"))
(org-roam-graph-viewer nil)
(org-roam-db-update-method 'immediate)
(org-roam-index-file "Index") ; Default
(org-roam-node-display-template
(concat "${title:*} " (propertize "${tags:45}" 'face 'org-tag)))
(org-roam-capture-templates
;; TODO: ROAM_ref (formally roam_key) can not be set in the file PROPERTIES in roam v2
;; See: https://github.com/org-roam/org-roam/issues/1920
'(("d" "default" plain "%?"
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}
")
:unnarrowed t)
("p" "person" entry "* %^{name}%?"
:target (node "863ddb0b-ed30-4cf6-86c7-915afda0c0d1"))
("b" "book" entry "* %^{name}%?"
:target (node "64a98813-bdf8-4813-9f4e-e1153170ffa9"))
("s" "software" entry "* %^{name}
%?"
:target (node "d0c93085-3e96-4f4b-ad83-ced92cf4de33"))))
(org-roam-dailies-directory "")
(org-roam-dailies-capture-templates
'(("d" "default" entry "* %?" :target (file+datetree "dailies.org.gpg" week))))
2022-01-31 16:41:17 +00:00
:bind (("C-c n l" . org-roam-buffer-toggle)
("C-c n f" . org-roam-node-find)
("C-c n F" . org-roam-ref-find)
2022-01-31 16:41:17 +00:00
("C-c n g" . org-roam-graph)
("C-c n D" . org-roam-doctor)
2022-01-31 16:41:17 +00:00
("C-c n i" . org-roam-node-insert)
("C-c n I" . org-roam-node-insert-section)
("C-c n a" . org-roam-alias-add)
("C-c n A" . org-roam-alias-remove)
("C-c n ." . org-roam-tag-add)
("C-c n <" . org-roam-tag-remove)
("C-c n r" . org-roam-ref-add)
("C-c n R" . org-roam-ref-remove)
("C-c n c" . org-roam-capture)
2022-01-31 16:41:17 +00:00
("C-c n y" . org-roam-dailies-goto-yesterday)
("C-c n t" . org-roam-dailies-goto-today)
("C-c n T" . org-roam-dailies-goto-tomorrow)
("C-c n d" . org-roam-dailies-goto-date))
:config
(org-roam-db-autosync-enable))
(use-package org-roam-protocol
:after org-roam)
(use-package org-roam-export
:after org-roam)
2022-01-31 16:41:17 +00:00
#+end_src
**** TODO [#C] org-roam files pollute ~.emacs.d~ :open_source:
:PROPERTIES:
:Effort: 0:10
:END:
2022-01-31 16:41:17 +00:00
This can be adjusted via the customize variable ~org-roam-db-location~. However, this should
also be pushed upstream to the ~no-littering~ package. The default value is
~~/.emacs.d/org-roam.db~.
**** TODO Turn on org-roam encryption
Encryption via gpg can be enabled transparently for org-roam org files. This can be enabled
via the customize variable ~org-roam-encrypt-files~.
**** Setup [[https://github.com/org-roam/org-roam-ui][org-roam-ui]]
#+begin_src emacs-lisp
(use-package websocket
:after org-roam)
(use-package org-roam-ui
:after org-roam
:config
(progn
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start nil)
(when (daemonp)
(org-roam-ui-mode))))
#+end_src
*** Setup [[https://github.com/alphapapa/org-ql][org-ql]]
#+begin_src emacs-lisp
(use-package org-ql
:bind (("C-c f D" . org-ql-find-in-org-directory)
("C-c f A" . org-ql-find-in-agenda)
("C-c f s" . org-ql-search)
("C-c f v" . org-ql-view))
:after org
:custom
(org-ql-search-directories-files-recursive t)
(org-ql-views
(list (cons "Projects: All tasks"
(list :buffers-files #'org-agenda-files
:query '(and (tags "project") (todo))
:sort '(todo deadline priority date)
:super-groups '((:auto-outline-path))
:title "All Project Tasks"))
(cons "Projects: This Project's Tasks"
(lambda ()
"Show current projects tasks."
(interactive)
(let ((project-name (file-name-nondirectory
(directory-file-name
(project-root (project-current))))))
(org-ql-search (list org-projectile-projects-file)
`(and (todo) (category ,project-name))
:sort '(todo deadline priority date)
:super-groups '((:auto-outline-path))
:title (concat "Project Tasks (" project-name ")")))))
(cons "Overview: Agenda-like"
(list :buffers-files #'org-agenda-files
:query '(and (not (done))
(or (habit)
(deadline auto)
(scheduled :to today)
(ts-active :on today)))
:sort '(todo priority date)
:super-groups 'org-super-agenda-groups
:title "Agenda-like"))
(cons "Overview: NEXT tasks"
(list :buffers-files #'org-agenda-files
:query '(todo "NEXT")
:sort '(date priority)
:super-groups 'org-super-agenda-groups
:title "Overview: NEXT tasks"))
(cons "Calendar: Today"
(list :buffers-files #'org-agenda-files
:query '(ts-active :on today)
:title "Today"
:super-groups 'org-super-agenda-groups
:sort '(priority)))
(cons "Calendar: This week"
(lambda ()
"Show items with an active timestamp during this calendar week."
(interactive)
(let* ((ts (ts-now))
(beg-of-week (->> ts
(ts-adjust 'day (- (ts-dow (ts-now))))
(ts-apply :hour 0 :minute 0 :second 0)))
(end-of-week (->> ts
(ts-adjust 'day (- 6 (ts-dow (ts-now))))
(ts-apply :hour 23 :minute 59 :second 59))))
(org-ql-search (org-agenda-files)
`(ts-active :from ,beg-of-week
:to ,end-of-week)
:title "This week"
:super-groups 'org-super-agenda-groups
:sort '(priority)))))
(cons "Calendar: Next week"
(lambda ()
"Show items with an active timestamp during the next calendar week."
(interactive)
(let* ((ts (ts-adjust 'day 7 (ts-now)))
(beg-of-week (->> ts
(ts-adjust 'day (- (ts-dow (ts-now))))
(ts-apply :hour 0 :minute 0 :second 0)))
(end-of-week (->> ts
(ts-adjust 'day (- 6 (ts-dow (ts-now))))
(ts-apply :hour 23 :minute 59 :second 59))))
(org-ql-search (org-agenda-files)
`(ts-active :from ,beg-of-week
:to ,end-of-week)
:title "Next week"
:super-groups 'org-super-agenda-groups
:sort '(priority)))))
(cons "Review: Recently timestamped" #'org-ql-view-recent-items)
(cons (propertize "Review: Dangling tasks"
'help-echo "Tasks whose ancestor is done")
(list :buffers-files #'org-agenda-files
:query '(and (todo)
(ancestors (done)))
:title (propertize "Review: Dangling tasks"
'help-echo "Tasks whose ancestor is done")
:sort '(todo priority date)
:super-groups '((:auto-parent t))))
(cons (propertize "Review: Stale tasks"
'help-echo "Tasks without a timestamp in the past 2 weeks")
(list :buffers-files #'org-agenda-files
:query '(and (todo)
(not (ts :from -14)))
:title (propertize "Review: Stale tasks"
'help-echo "Tasks without a timestamp in the past 2 weeks")
:sort '(todo priority date)
:super-groups '((:auto-parent t))))
(cons (propertize "Review: Stuck projects"
'help-echo "Tasks with sub-tasks but no NEXT sub-tasks")
(list :buffers-files #'org-agenda-files
:query '(and (todo)
(descendants (todo))
(not (descendants (todo "NEXT"))))
:title (propertize "Review: Stuck projects"
'help-echo "Tasks with sub-tasks but no NEXT sub-tasks")
:sort '(date priority)
:super-groups 'org-super-agenda-groups)))))
#+end_src
**** Setup [[https://github.com/alphapapa/org-ql/blob/master/helm-org-ql.el][helm-org-ql]]
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
2022-01-31 16:41:17 +00:00
(use-package helm-org-ql
:bind (("C-c f d" . helm-org-ql-org-directory)
("C-c f a" . helm-org-ql-agenda-files)))
2022-01-31 16:41:17 +00:00
#+end_src
*** Setup [[https://github.com/weirdNox/org-noter][org-noter]]
#+begin_src emacs-lisp
(use-package org-noter)
#+end_src
*** Setup [[https://github.com/alphapapa/org-super-agenda][org-super-agenda]]
#+begin_src emacs-lisp
(use-package org-super-agenda
:config
(progn
(setq org-super-agenda-groups
'((:name "Today"
:time-grid t)
(:name "Important"
:priority "A")
(:name "In Progress"
:todo "DOING")
(:name "Next Up"
:todo "NEXT")
(:name "Paused"
:todo "PAUSE")
(:name "Waiting"
:todo "WAIT")
(:auto-category t))
; This will be supported in 1.3:
; :org-super-agenda-hide-empty-groups
org-agenda-custom-commands (append
org-agenda-custom-commands
'(("xp" "All TODOs groups by parent" alltodo ""
((org-super-agenda-groups '((:auto-parent t)))))
("xd" "All TODOs groups by scheduled date or deadline" alltodo ""
((org-super-agenda-groups '((:auto-planning t)))))
("xc" "All TODOs groups by category" alltodo ""
((org-super-agenda-groups '((:auto-category t)))))))
org-super-agenda-date-format "%d %B %Y")
2022-01-31 16:41:17 +00:00
(org-super-agenda-mode)))
#+end_src
** treemacs
#+begin_src emacs-lisp
(use-package treemacs
:defer t
:init
(with-eval-after-load 'winum
(define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
:config
(progn
(setq treemacs-collapse-dirs (if treemacs-python-executable 3 0)
treemacs-deferred-git-apply-delay 0.5
treemacs-directory-name-transformer #'identity
treemacs-display-in-side-window t
treemacs-eldoc-display t
treemacs-file-event-delay 5000
treemacs-file-extension-regex treemacs-last-period-regex-value
treemacs-file-follow-delay 0.2
treemacs-file-name-transformer #'identity
treemacs-follow-after-init t
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-indentation 2
treemacs-indentation-string " "
treemacs-is-never-other-window nil
treemacs-max-git-entries 5000
treemacs-missing-project-action 'ask
treemacs-move-forward-on-expand nil
treemacs-no-png-images nil
treemacs-no-delete-other-windows t
treemacs-project-follow-cleanup nil
treemacs-position 'left
treemacs-recenter-distance 0.1
treemacs-recenter-after-file-follow nil
treemacs-recenter-after-tag-follow nil
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-show-cursor nil
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-asc
treemacs-space-between-root-nodes t
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 1.5
treemacs-user-mode-line-format nil
treemacs-user-header-line-format nil
treemacs-width 35
treemacs-workspace-switch-cleanup nil)
;; TODO: temporary workaround as in these fonts are missing
(add-to-list 'all-the-icons-data/file-icon-alist '("dart" . "\xEB29"))
(add-to-list 'all-the-icons-data/file-icon-alist '("fsharp" . "\xEB29"))
;; The default width and height of the icons is 22 pixels. If you are
;; using a Hi-DPI display, uncomment this to double the icon size.
;;(treemacs-resize-icons 44)
(treemacs-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode t)
(pcase (cons (not (null (executable-find "git")))
(not (null treemacs-python-executable)))
(`(t . t)
(treemacs-git-mode 'deferred))
(`(t . _)
(treemacs-git-mode 'simple))))
:bind
(:map global-map
("M-0" . treemacs-select-window)
("C-x t 1" . treemacs-delete-other-windows)
("C-x t t" . treemacs)
("C-x t B" . treemacs-bookmark)
("C-x t C-t" . treemacs-find-file)
("C-x t M-t" . treemacs-find-tag)))
(use-package treemacs-projectile
:after treemacs projectile)
(use-package treemacs-all-the-icons
:after treemacs all-the-icons
:config (treemacs-load-theme 'all-the-icons))
2022-01-31 16:41:17 +00:00
(use-package treemacs-icons-dired
:config (treemacs-icons-dired-mode 1))
2022-01-31 16:41:17 +00:00
(use-package treemacs-magit
:after treemacs magit)
;; (use-package treemacs-persp ;;treemacs-persective if you use perspective.el vs. persp-mode
;; :after treemacs persp-mode ;;or perspective vs. persp-mode
;; :config (treemacs-set-scope-type 'Perspectives))
#+end_src
** Ledger
#+begin_src emacs-lisp
(use-package ledger-mode
:config (setq ledger-binary-path "ledger"))
(use-package flycheck-ledger
:after flycheck)
#+end_src
** Dashboard
Use [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]].
#+begin_src emacs-lisp
(use-package dashboard
:custom (dashboard-startup-banner 'logo)
:init (setq dashboard-set-heading-icons t
dashboard-set-file-icons t
dashboard-set-navigator t
dashboard-items '((recents . 5)
(bookmarks . 5)
(projects . 5)
(agenda . 5)
(registers . 5)))
:config
(progn
(dashboard-setup-startup-hook)
(defun dashboard ()
(interactive)
(let ((buffer "*dashboard*"))
(when (not (get-buffer buffer))
(dashboard-insert-startupify-lists))
(switch-to-buffer buffer))))
:bind (("C-c d" . dashboard)))
#+end_src
** Rust
#+begin_src emacs-lisp
(use-package rust-mode)
#+end_src
** Nix
#+begin_src emacs-lisp
(use-package nix-mode
:mode "\\.nix\\'")
#+end_src
** DHall
#+begin_src emacs-lisp
(use-package dhall-mode
:mode "\\.dhall\\'")
#+end_src
** TODO Lispy
Slime has been temporarily commented out below as it broke during an upgrade from ~2.27~ ->
~2.28~ (likely an issue specific to guix). The error was:
#+begin_quote
File is missing: Cannot open load file, No such file or directory, ../lib/macrostep
#+end_quote
Briefly looking at the guix package, it appears that the ~emacs-macrostep~ package is not
specified as a native-dependency (which may be a new dependency in ~2.28~).
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
;; Set default lisp program
(setq inferior-lisp-program "/usr/bin/sbcl")
;; Since there is no support for the kawa implementation of scheme
(defun run-kawa ()
"Run Kawa Scheme in an Emacs buffer."
(interactive)
(require 'cmuscheme) ;; Built-in
(let ((scheme-program-name "/usr/bin/kawa"))
(run-scheme scheme-program-name)))
;; Function to start and/or connect to slime
(defun start-slime ()
(interactive)
(unless (slime-connected-p)
(save-excursion (slime))))
;; Setup slime mode
;(require 'slime-autoloads) ;; package
;(slime-setup '(slime-fancy))
2022-01-31 16:41:17 +00:00
;; Set usable lisp implementations
(setq slime-lisp-implementations
'((sbcl ("/usr/bin/sbcl" ""))
(clisp ("/usr/bin/clisp" "-K base"))))
;; Setup enhanced scheme/racket mode consisting of geiser, quack and paredit
;; Setup geiser
(use-package geiser
;; GUIX TODO: ac-geiser is not packaged upstream and causes this to break
; :config (use-package ac-geiser)
)
;; GUIX TODO: This breaks geiser on guix
;; ;; Setup auto-completion for geiser (ELPA)
;; (add-hook 'geiser-mode-hook 'ac-geiser-setup)
;; (add-hook 'geiser-repl-mode-hook 'ac-geiser-setup)
;; (eval-after-load "auto-complete"
;; '(add-to-list 'ac-modes 'geiser-repl-mode))
;; Make struct stand out in scheme-mode for racket
(defun racket-faces ()
(font-lock-add-keywords nil
'(("(struct \\(\\sw+\\)" 1 font-lock-function-name-face)
("(\\(struct\\)" 1 font-lock-keyword-face)
("(\\(λ\\)" 1 font-lock-function-name-face))))
(add-hook 'scheme-mode-hook 'racket-faces)
(add-hook 'geiser-repl-mode-hook 'racket-faces)
;; Setup scribble mode
(use-package scribble-mode)
;; Setup quack
(use-package quack)
(defvar lispy-langs-hooks '(lisp-mode-hook lisp-interaction-mode-hook emacs-lisp-mode-hook scheme-mode-hook c-mode-hook c++-mode-hook python-mode-hook geiser-repl-mode-hook))
;; Setup paredit
(use-package paredit
:config (activate-mode-with-hooks (lambda () (paredit-mode 1)) lispy-langs-hooks))
;; Highlight sexp's in lispy languages
(use-package highlight-sexp
:config (activate-mode-with-hooks (lambda () (highlight-sexp-mode)) lispy-langs-hooks))
;; Paredit binds to C-j globally and thus disables the binding to
;; eval-print-last-sexp in emacs-lisp-mode (e.g *scratch*, etc..)
(add-hook 'emacs-lisp-mode-hook
#'(lambda ()
(define-key emacs-lisp-mode-map "\C-xj" 'eval-print-last-sexp)))
2022-01-31 16:41:17 +00:00
;; Match paren's in given modes [to apply globally do (show-paren-mode 1)]
(activate-mode-with-hooks (lambda () (show-paren-mode)) lispy-langs-hooks)
;; Highlight paren's near point
(use-package highlight-parentheses
:config (activate-mode-with-hooks (lambda () (highlight-parentheses-mode)) lispy-langs-hooks))
;; Setup rainbow-delimiters
(use-package rainbow-delimiters
:config (activate-mode-with-hooks 'rainbow-delimiters-mode-enable lispy-langs-hooks))
;;(global-rainbow-delimiters-mode) ;; TODO: breaks font coloring in erc for some reason?
(use-package rainbow-mode
:defer t
:hook ((css-mode . rainbow-mode)))
;; upcomming functionallity: toggle paredit-mode due to annoying things like wrapping parens when a mistake is made
;; known issue..if paredit-mode is turned on when there are unbalanced parens an error is reported
(defun toggle-paredit-mode ()
(let ((active-minor-modes (list)))
(mapatoms (lambda (sym)
(when (and (symbolp sym) (assq sym minor-mode-alist) (symbol-value sym))
(push sym active-minor-modes))))
(if (member 'paredit-mode active-minor-modes) (paredit-mode -1) (paredit-mode 1))))
(setq geiser-repl-use-other-window nil
geiser-active-implementations '(racket guile))
#+end_src
** Haskell
#+begin_src emacs-lisp
;; Thanks to: https://github.com/haskell/haskell-mode/wiki/Haskell-Interactive-Mode-Setup
;; https://github.com/haskell/haskell-mode/blob/master/examples/init.el
(custom-set-variables
;; Use notify.el (if you have it installed) at the end of running
;; Cabal commands or generally things worth notifying.
'(haskell-notify-p t)
;; To enable tags generation on save.
'(haskell-tags-on-save t)
;; To enable stylish on save.
'(haskell-stylish-on-save t)
'(haskell-process-suggest-remove-import-lines t)
'(haskell-process-auto-import-loaded-modules t)
'(haskell-process-log t))
(add-hook 'haskell-mode-hook 'haskell-hook)
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)
(add-hook 'haskell-cabal-mode-hook 'haskell-cabal-hook)
;; Haskell main editing mode key bindings.
(defun haskell-hook ()
;; Use indentation.
;;(haskell-indent-mode)
;; Use electric-quote-mode
(electric-quote-local-mode)
;; Load the current file (and make a session if not already made).
(define-key haskell-mode-map [?\C-c ?\C-l] 'haskell-process-load-file)
(define-key haskell-mode-map [f5] 'haskell-process-load-file)
;; Switch to the REPL.
(define-key haskell-mode-map [?\C-c ?\C-z] 'haskell-interactive-switch)
;; “Bring” the REPL, hiding all other windows apart from the source
;; and the REPL.
(define-key haskell-mode-map (kbd "C-`") 'haskell-interactive-bring)
;; Build the Cabal project.
(define-key haskell-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
;; Interactively choose the Cabal command to run.
(define-key haskell-mode-map (kbd "C-c c") 'haskell-process-cabal)
;; Get the type and info of the symbol at point, print it in the
;; message buffer.
(define-key haskell-mode-map (kbd "C-c C-t") 'haskell-process-do-type)
(define-key haskell-mode-map (kbd "C-c C-i") 'haskell-process-do-info)
;; Contextually do clever things on the space key, in particular:
;; 1. Complete imports, letting you choose the module name.
;; 2. Show the type of the symbol after the space.
;; NOTE: the haskell-mode-contextual-space function has been depreciated
;;(define-key haskell-mode-map (kbd "SPC") 'haskell-mode-contextual-space)
;; Jump to the imports. Keep tapping to jump between import
;; groups. C-u f8 to jump back again.
(define-key haskell-mode-map [f8] 'haskell-navigate-imports)
;; Jump to the definition of the current symbol.
(define-key haskell-mode-map (kbd "M-.") 'haskell-mode-tag-find)
;; Indent the below lines on columns after the current column.
(define-key haskell-mode-map (kbd "C-<right>")
(lambda ()
(interactive)
(haskell-move-nested 1)))
;; Same as above but backwards.
(define-key haskell-mode-map (kbd "C-<left>")
(lambda ()
(interactive)
(haskell-move-nested -1)))
;; Interactive keybindings
(define-key haskell-mode-map (kbd "C-c C-l") 'haskell-process-load-or-reload)
(define-key haskell-mode-map (kbd "C-`") 'haskell-interactive-bring)
(define-key haskell-mode-map (kbd "C-c C-t") 'haskell-process-do-type)
(define-key haskell-mode-map (kbd "C-c C-i") 'haskell-process-do-info)
(define-key haskell-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
(define-key haskell-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
(define-key haskell-mode-map (kbd "C-c c") 'haskell-process-cabal)
;; Interactive keybindings that are useful in cabal-mode
(define-key haskell-cabal-mode-map (kbd "C-`") 'haskell-interactive-bring)
(define-key haskell-cabal-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
(define-key haskell-cabal-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
(define-key haskell-cabal-mode-map (kbd "C-c c") 'haskell-process-cabal))
;; Useful to have these keybindings for .cabal files, too.
(defun haskell-cabal-hook ()
(define-key haskell-cabal-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
(define-key haskell-cabal-mode-map (kbd "C-c c") 'haskell-process-cabal)
(define-key haskell-cabal-mode-map (kbd "C-`") 'haskell-interactive-bring)
(define-key haskell-cabal-mode-map [?\C-c ?\C-z] 'haskell-interactive-switch))
;; Set inferior haskell default executable
(setq haskell-program-name "/usr/bin/ghci")
;; TODO: this is rarely if ever used
(use-package hamlet-mode)
#+end_src
** PlantUML
#+begin_src emacs-lisp
(use-package plantuml-mode
:config (setq plantuml-default-exec-mode 'executable
plantuml-output-type "png"))
#+end_src
** Graphviz Dot
#+begin_src emacs-lisp
(use-package graphviz-dot-mode)
#+end_src
** TODO Hex :deprecate:
This is rarely if ever used. Consider removal.
#+begin_src emacs-lisp
(use-package "intel-hex-mode")
#+end_src
** TODO Web :needs_review:
#+begin_src emacs-lisp
;; Setup web browsing
(setq browse-url-browser-function 'browse-url-generic
browse-url-generic-program "xdg-open")
2022-01-31 16:41:17 +00:00
(defun browse-url-before-point ()
"Find the first url before point and open it in a browser using browse-url"
(interactive)
(save-excursion
(search-backward-regexp "\(file\|ftp\\|http\\|https\)://.*\.")
(browse-url-at-point)))
(global-set-key (kbd "C-c g") 'browse-url-at-point)
(global-set-key (kbd "C-c G") 'browse-url-before-point)
(use-package php-mode
:defer t
:mode "/*.\.php[345]?$")
;; Setup zencoding-mode (now emmet-mode)
(use-package emmet-mode
:defer t
:bind (:map emmet-mode-keymap
;; Disable C-j keybinding set by zencoding-mode and replace it with 'C-c j'
("C-j" . nil)
("C-c j" . emmet-expand-line))
:hook ((sgml-mode . emmet-mode) ;; Auto-start on any markup modes
(css-mode . emmet-mode) ;; Enable Emmet's css abbreviation.
(emmet-mode . (lambda () (setq emmet-indentation 2))) ;; Indent 2 spaces.
))
(use-package graphql-mode
:defer t
:mode "\\.gql\\'"
:hook ((graphql-mode)))
;; Setup mmm-mode for multiple mode regions in the same buffer
(use-package mmm-mode
:defer t)
;;(setq mmm-global-mode 'maybe)
(use-package restclient
:defer t)
(use-package elpher
:defer t)
#+end_src
** TODO Python :needs_review:
Both of these packages are not yet available on guix. Further more, the use of ein needs to
be further investigated.
#+begin_src emacs-lisp
(use-package ipython
:defer t)
(use-package ein
:defer t)
#+end_src
** TODO Recutils
#+begin_src emacs-lisp
(use-package rec-mode)
#+end_src
** Latex
#+begin_src emacs-lisp
(setq-default TeX-master nil)
;; Use auto-complete in latex buffers
(eval-after-load "auto-complete"
'(add-to-list 'ac-modes 'latex-mode))
(use-package auctex
:functions TeX-global-PDF-mode
:hook ((LaTeX-mode . visual-line-mode)
(LaTeX-mode . LaTeX-math-mode)
(LaTeX-mode . turn-on-reftex))
:config (progn
(setq TeX-auto-save t
TeX-parse-self t
reftex-plug-into-AUCTeX t)
(TeX-global-PDF-mode t)))
#+end_src
** Tramp
#+begin_src emacs-lisp
;; Set default tramp method to ssh (for security purposes)
(setq tramp-default-method "ssh")
;; Set tramp terminal type to tramp instead of the default dumb
;; This avoids forcing a simple PS1 on remote systems that are using zsh
;; via a dumb term. However this requires all remotes to disable fancy PS1's
;; by checking if the terminal type is "tramp"
(setq tramp-terminal-type "tramp")
;; This fixes sudo tramp access
(setq tramp-remote-path '(tramp-default-remote-path "/bin" "/usr/bin" "/sbin" "/usr/sbin" "/usr/local/bin" "/usr/local/sbin" "/local/bin" "/local/freeware/bin" "/local/gnu/bin" "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin" "/opt/bin" "/opt/sbin" "/opt/local/bin" "/run/current-system/profile/bin" "/run/setuid-programs"))
2022-01-31 16:41:17 +00:00
#+end_src
*** TODO Extensions :needs_review:
This package is unavailable for guix. Further the package is defered and likely will
never get loaded.
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
(use-package vagrant-tramp
:defer t)
#+end_src
** Guix
#+begin_src emacs-lisp
(use-package guix
:bind ("C-c x g" . guix))
#+end_src
** TODO Archlinux - Mode for ~PKGBUILD~ files :needs_review:
This is not yet packaged for guix.
#+begin_src emacs-lisp
(use-package pkgbuild-mode
:defer t)
#+end_src
** TODO Docker :needs_review:
#+begin_src emacs-lisp
(use-package dockerfile-mode)
#+end_src
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
(use-package docker
:bind ("C-c D" . docker))
#+end_src
2022-01-31 16:41:17 +00:00
#+begin_src emacs-lisp
2022-01-31 16:41:17 +00:00
(use-package docker-compose-mode
:disabled ;; TODO: auto-completion does not seem to work using this package
:defer t)
#+end_src
** eshell
#+begin_src emacs-lisp
;; TODO: make a function to toggle the eshell; given a the universal argument the following can occur:
;; - if numerical then opens the nth scratch buffer "*eshell*<n>"
;; - if no args then open a new eshell
;;
;; (defun toggle-eshell (arg)
;; (interactive "P")
;; (cond (((equal arg '(4)) ))))
(defun eshell/catbuf (buffer-name)
"Given a buffer-name returns the contents of said buffer"
(interactive "bBuffer: ")
(save-excursion
(let ((code-buf (get-buffer buffer-name)))
(if (null code-buf) (concat "The buffer given \"" buffer-name "\" does not exist")
(set-buffer code-buf)
(buffer-string)))))
(defun eshell/find-file-ext (fp)
"Finds a single file or a list of files matching a regxp and returns a list of their respective buffers"
(interactive)
(if (listp fp) (mapcar #'find-file fp)
(list (find-file fp))))
(defun eshell/ff (fp &rest other-fps)
"A FP is either a file path (relative or absolute) or a regexp which eshell converts to a
list of stings (file paths) which match the regexp (likely using file-expand-widcards).
eshell/ff takes one or more file paths and opens them in the current buffer returning a list
consisting of lists of buffers opened by each respective FP argument."
(interactive)
(mapcar #'eshell/find-file-ext (cons fp other-fps)))
(defun eshell/clear ()
"04Dec2001 - sailor, to clear the eshell buffer."
(interactive)
(let ((inhibit-read-only t))
(erase-buffer)))
(use-package eshell
:bind (:map ctl-x-4-map
("e" . eshell))
:config (setq
eshell-scroll-to-bottom-on-input t
eshell-visual-commands '("vi" "screen" "top" "less" ;; Commands to run without dumb-term
"more" "lynx" "ncftp" "vim" "htop"
"ncmpcpp" "irssi" "mc" "alsamixer")))
2022-01-31 16:41:17 +00:00
;; TODO: this doesn't belong in this file and should be relocated
;; Force ediff sessions to run in the same frame
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
#+end_src