2926 lines
123 KiB
Org Mode
2926 lines
123 KiB
Org Mode
#+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
|
|
#+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
|
|
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")
|
|
(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"))
|
|
(load custom-file)
|
|
#+end_src
|
|
|
|
* Set Language Environment
|
|
|
|
As of mu4e ~1.12~, ~mu4e-headers~ views do not display correctly without the language
|
|
environment being UTF-8. This cannot be set in a buffer-local way for mu4e buffers, so this
|
|
must be set globally for emacs.
|
|
|
|
See: https://www.djcbsoftware.nl/code/mu/mu4e/Known-issues.html#UTF_002d8-language-environment-is-required
|
|
|
|
#+begin_src emacs-lisp
|
|
(set-language-environment "UTF-8")
|
|
#+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
|
|
;; 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))
|
|
|
|
(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)
|
|
|
|
;; 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
|
|
|
|
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)))))))
|
|
#+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
|
|
|
|
* 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
|
|
|
|
** 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
|
|
|
|
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
|
|
|
|
;; 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)
|
|
(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)))
|
|
;; 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)))
|
|
;; 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"))))))
|
|
|
|
;; 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))
|
|
: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))
|
|
|
|
(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")))
|
|
|
|
(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))))
|
|
|
|
(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
|
|
|
|
** 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))))
|
|
|
|
;; 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")
|
|
(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"
|
|
"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")))))
|
|
#+end_src
|
|
|
|
** Calendar
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq calendar-latitude 43.1
|
|
calendar-longitude -80.7
|
|
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")
|
|
#+end_src
|
|
|
|
*** Provide functions to show sunset and sunrise separately in org-mode diary/agenda
|
|
|
|
#+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))
|
|
#+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
|
|
|
|
org-return-follows-link t
|
|
org-id-link-to-org-use-id 'use-existing
|
|
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)))
|
|
|
|
;; 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)
|
|
|
|
(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)))))
|
|
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)
|
|
|
|
("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
|
|
(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)
|
|
|
|
;; 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)
|
|
(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
|
|
|
|
#+begin_src emacs-lisp
|
|
;; Enable easy templates
|
|
(use-package org-tempo
|
|
:after org)
|
|
#+end_src
|
|
|
|
#+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
|
|
;; GUIX TODO: This package is not available
|
|
;; (use-package org-magit
|
|
;; :after org)
|
|
#+end_src
|
|
|
|
#+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
|
|
|
|
*** 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
|
|
|
|
*** 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)
|
|
: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))))
|
|
: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)
|
|
("C-c n g" . org-roam-graph)
|
|
("C-c n D" . org-roam-doctor)
|
|
|
|
("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)
|
|
("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)
|
|
#+end_src
|
|
|
|
**** TODO [#C] org-roam files pollute ~.emacs.d~ :open_source:
|
|
:PROPERTIES:
|
|
:Effort: 0:10
|
|
:END:
|
|
|
|
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]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package helm-org-ql
|
|
:bind (("C-c f d" . helm-org-ql-org-directory)
|
|
("C-c f a" . helm-org-ql-agenda-files)))
|
|
#+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")
|
|
(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))
|
|
|
|
(use-package treemacs-icons-dired
|
|
:config (treemacs-icons-dired-mode 1))
|
|
|
|
(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~).
|
|
|
|
#+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))
|
|
|
|
;; 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)))
|
|
|
|
;; 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")
|
|
|
|
(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"))
|
|
#+end_src
|
|
|
|
*** TODO Extensions :needs_review:
|
|
|
|
This package is unavailable for guix. Further the package is defered and likely will
|
|
never get loaded.
|
|
|
|
#+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
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package docker
|
|
:bind ("C-c D" . docker))
|
|
#+end_src
|
|
|
|
#+begin_src emacs-lisp
|
|
(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")))
|
|
|
|
;; 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
|
|
|