Welcome to my attempt at literate configuration of the one and only Emacs. My goal is to edit this into a readable state that describes and illustrates one possible way of driving this wonderful system.
(defvar my-packages
'(
;; utilities
all-the-icons
all-the-icons-ivy
company
company-box
counsel ;; includes ivy & swiper
counsel-projectile
docker
eglot
flycheck
ivy-hydra
magit
perspective
projectile
smartparens
smex
undo-tree
which-key
xah-fly-keys
;; languages
;; Common Lisp
sly
;; Emacs Lisp
flycheck-package
package-lint
;; Ruby
enh-ruby-mode
inf-ruby
minitest
))
(defvar my-themes
'(
goose-theme
kaolin-themes
lab-themes
modus-operandi-theme
modus-vivendi-theme
sublime-themes
sunburn-theme
tao-theme
zenburn-theme
))
(straight-use-package
'(colorless-themes :type git
:repo "https://git.sr.ht/~lthms/colorless-themes.el"))
The theme files do not copy to the build directory, and the themes do not show up in my list of themes to load.
with straight.el i can just simply map over the list, it handles the logic of what to do if a package is already installed, etc.
(mapc 'straight-use-package (append my-packages my-themes))
straight-check-all
straight-pull-all
straight-rebuild-all
would be nice to have a way to automatically compile packages i’ve selected for it. i don’t really know what the best way to go about deciding which packages should get compiled this way.
- [ ] ask gccemacs author about this
- [ ] write a script to compile things that need it (after install, update, etc)
After experiencing some crippling Emacs pinky, i started looking at options for different key bindings. one of the first things i came across was xah-fly-keys, written by Xah Lee, a long time Emacs user who has written extensively about his experiences and findings. fly keys seemed worth a try, and didn’t clobber existing bindings, so i could always fall back on muscle memory while learning.
it took some getting used to, but after a month i was proficient and have not looked back. muscle memory changes, and this one is for the better. i’ve been using it alongside Spacemacs, but it made it so i had several keymaps to learn and doubling of bindings that aren’t necessary. this config will attempt to use xah’s bindings while enhancing it with my own as needed.
by default, fly keys will use the control key for certain common bindings (CUA) but i prefer to have it off. i set the variable to nil, require the package (perhaps it could use some autoloads?), use the qwerty layout and turn it on:
(setq xah-fly-use-control-key nil)
(require 'xah-fly-keys)
(xah-fly-keys-set-layout "qwerty")
(xah-fly-keys 1)
I like to configure a few visual cues to let me know if I’m in command or insert mode. Here’s one function each for when each mode turns on, which are called with the appropriate hook. These toggle the line highlight and change the cursor color and shape. Additionally, the way fly keys currently works is that it erases xah-fly-key-map for insert mode (so everything works like vanilla) and then remaps everything when command mode is turned on. this has the effect of resetting any keys i want to bind in xah-fly-key-map. so when command mode is turned on, i also set a few keys to call Ivy commands.
(defun xah-fly-keys-command-mode-on ()
(global-hl-line-mode 1)
(set-cursor-color "deep pink")
(setq cursor-type 'box)
(xah-fly--define-keys
xah-fly-key-map
'(
("a" . counsel-M-x)
("b" . swiper)
)))
(defun xah-fly-keys-insert-mode-on ()
(global-hl-line-mode 0)
(set-cursor-color "Dark Turquoise")
(setq cursor-type 'bar))
(add-hook 'xah-fly-command-mode-activate-hook 'xah-fly-keys-command-mode-on)
(add-hook 'xah-fly-insert-mode-activate-hook 'xah-fly-keys-insert-mode-on)
Setting up a personal key map to put my own shortcuts in. Currently bound to “SPC SPC” and “M-m” (like spacemacs). Currently, I’m associating keybindings with the section where they apply, but it may make more sense to have a complete map represented somewhere.
(defvar personal-key-map (make-sparse-keymap))
(define-prefix-command 'personal-key-map)
(define-key xah-fly-leader-key-map (kbd "SPC") personal-key-map)
(global-set-key (kbd "M-m") personal-key-map)
y7 Now I can bind commands
This config file
(defun my-configuration ()
(interactive)
(find-file (string-join `(,user-emacs-directory "configuration.org"))))
(define-key personal-key-map (kbd "c") 'my-configuration)
There’s no good way to just bind some other key sequence to “C-c” which is the prefix for most major modes. So I’m trying out some advice functions here to check the mode and then bind a different personal major mode map to a key in my personal key map. Its a bit buggy yet, but appears to work so far. Each major mode i care about will get its own keymap with my most often used keys.
(defvar my-major-mode-maps nil)
(setq-default my-major-mode-maps
'((org-mode my-org-keymap)
(enh-ruby-mode my-ruby-keymap)
(lisp-mode my-sly-keymap)
(sly-mrepl-mode my-sly-keymap)))
(defun set-my-major-mode-map (&rest args)
(let ((map (cadr (assoc major-mode my-major-mode-maps))))
(define-key personal-key-map (kbd ",") map)))
(advice-add 'find-file :after #'set-my-major-mode-map)
(advice-add 'switch-to-buffer :after #'set-my-major-mode-map)
(advice-add 'xah-next-window-or-frame :after #'set-my-major-mode-map)
(add-hook 'persp-switch-hook #'set-my-major-mode-map)
(straight-use-package 'lakota-input)
(require 'lakota-input)
If you put a file into enriched-mode
it seems like the text properties are saved
along with the buffer content. This allows for faces applied to text to be saved.
Its possible using the highlight.el
package will be good enough for this.
I’m wondering if I should define a special lakota-mode
to insert blocks into
org files, or if org-mode and enriched-mode can coexist.
Seems like highlight.el
has some good ideas, but it will be more useful to me
to have a set of quick keybindings for adding faces to make examples of grammar
in emacs.
(defface topic
'((t (:background "red" :foreground "white")))
"Face for marking the topic grammar structures.")
(defface comment
'((t (:background "blue" :foreground "white")))
"Face for marking the comment grammar structures.")
(defun mark-topic ()
(interactive)
(add-face-text-property (region-beginning) (region-end) 'topic))
(defun mark-comment ()
(interactive)
(add-face-text-property (region-beginning) (region-end) 'comment))
This is a feature I enjoyed in Spacemacs, so I borrowed it for my own config. This modifies the face for whitespace in all frames to highlight the background.
(set-face-attribute 'trailing-whitespace nil
:background
(face-attribute 'font-lock-comment-face
:foreground))
And this adds a hook to programming modes to toggle the show-trailing-whitespace
variable on. You can add it to any mode you choose.
(add-hook 'prog-mode-hook (lambda () (setq show-trailing-whitespace t)))
been having issues in javascript modes where tabs are being inserted. this may or may not be a solution
(setq-default indent-tabs-mode nil)
(setq inferior-lisp-program "/usr/bin/sbcl")
Trying out sly instead of slime, since i’m all “modern” now with emacs.
(xah-fly--define-keys
(define-prefix-command 'my-sly-keymap)
'(
;; ("." . slime-eval-buffer)
("a" . sly-apropos-all)
;; ("e" . slime-compile-defun)
;; ("E" . slime-edit-value)
("h" . sly-documentation-lookup)
;; ("j" . slime-compile-and-load-file)
("m" . sly-compile-defun)
;; ("M" . slime-eval-last-expression-display-output)
;; ("p" . slime-pprint-eval-last-expression)
;; ("r" . slime-interactive-eval)
;; ("u" . slime-eval-region)
))
Put your source directory here:
"/home/shoshin/picolisp"
ln -s $installdir/man/man1/picolisp.1 /usr/share/man/man1 &&
ln -s $installdir/man/man1/pil.1 /usr/share/man/man1 &&
ln -s $installdir /usr/share/picolisp
(add-hook 'js-mode-hook #'flycheck-mode)
(add-hook 'ruby-mode-hook #'enh-ruby-mode)
;; (add-hook 'enh-ruby-mode-hook 'eglot-ensure)
(add-hook 'enh-ruby-mode-hook 'flycheck-mode)
;; (add-hook 'enh-ruby-mode-hook #'lsp)
(straight-use-package 'haml-mode)
(add-hook 'haml-mode-hook 'flycheck-mode)
https://gist.github.com/mbreit/229d2528604af2f8db37
This works, but doesn’t respect project directory, which means no local rubocop.yml
(flycheck-def-config-file-var flycheck-haml-lintrc haml-lint “.haml-lint.yml” :safe #’stringp)
(defun flycheck-haml-lint–find-project-root (checker) (expand-file-name (flycheck-ruby–find-project-root checker)))
(flycheck-define-command-checker ‘haml-lint “A haml-lint syntax checker” :command ‘(“haml-lint” (config-file “–config” flycheck-haml-lintrc) source) :error-patterns ‘((warning line-start (file-name) “:” line ” [W] ” (message) line-end)) :modes ‘(haml-mode) :next-checker ‘haml ;; maybe this fixes it? (it does not) :working-directory #’flycheck-haml-lint–find-project-root )
;; this does work, perhaps i could get it to work in the checker code? (setenv “HAML_LINT_RUBOCOP_CONF” ”home/shoshin/unabridged/n2/pub_maintainer.rubocop.yml”)
(add-to-list ‘flycheck-checkers ‘haml-lint)
(defvar my-ruby-keymap (make-sparse-keymap))
(define-prefix-command 'my-ruby-keymap)
(define-key my-ruby-keymap (kbd "t") 'minitest-verify)
(define-key my-ruby-keymap (kbd "s") 'minitest-verify-single)
this seems to work, but would be nice to figure out how to “officially” extend flycheck to work with linter binaries that are running inside a container
;; an example. there must be a better way :sob:
;; (setq flycheck-rubocop-docker-app-path "/var/www/print_shop")
;; perhaps just docker would be preferable?
(flycheck-def-executable-var ruby-docker-rubocop "docker-compose")
;; not sure what a good default for this would be. perhaps there's a good
;; way to use docker commands to find out. or parsing the dockerfile
(defcustom flycheck-rubocop-docker-app-path "/var/www/app"
"Set to project root for app inside the container")
;; this provides the path for the config file /inside/ the container
;; it exists locally too, but doesn't resolve properly :|
(flycheck-def-config-file-var flycheck-docker-rubocoprc
ruby-docker-rubocop
(concat (file-name-as-directory flycheck-rubocop-docker-app-path) flycheck-rubocoprc))
(flycheck-define-command-checker 'ruby-docker-rubocop
"A Ruby syntax and style checker using the RuboCop tool inside a docker container.
You need at least RuboCop 0.34 for this syntax checker.
See URL `http://batsov.com/rubocop/'."
:command `("docker-compose"
"exec"
;; not a TTY
"-T"
;; this would need to be configured for a different app name
"app"
"bin/rubocop"
;; copied from basic rubocop config
"--display-cop-names"
"--force-exclusion"
"--format" "emacs"
;; Explicitly disable caching to prevent Rubocop 0.35.1 and earlier
;; from caching standard input. Later versions of Rubocop
;; automatically disable caching with --stdin, see
;; https://github.com/flycheck/flycheck/issues/844 and
;; https://github.com/bbatsov/rubocop/issues/2576
"--cache" "false"
;; would be nice to get this to work with (config-file ..) if possible
"--config" ,flycheck-docker-rubocoprc
(option-flag "--lint" flycheck-rubocop-lint-only)
;; Rubocop takes the original file name as argument when reading
;; from standard input
"--stdin" source-original)
:standard-input t
:working-directory #'flycheck-ruby--find-project-root
:error-patterns flycheck-ruby-rubocop-error-patterns
:modes '(enh-ruby-mode ruby-mode)
:next-checkers '((warning . ruby-reek)
(warning . ruby-rubylint)))
(add-to-list 'flycheck-checkers 'ruby-docker-rubocop)
mu 1.4.13
package installed from source tarball and installed with
sudo make install
Rather than using the mu4e
package from github, I’m using the version
included with mu
(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e")
(require 'mu4e)
(setq mu4e-mu-binary "/usr/local/bin/mu")
(setq user-full-name "Grant Shoshin Shangreaux"
user-mail-address "grant@churls.world")
(require 'smtpmail)
(setq message-send-mail-function 'smtpmail-send-it
smtpmail-stream-type 'ssl
smtpmail-smtp-server "smtp.migadu.com"
smtpmail-smtp-service 465)
this sets the mail command and runs the mu4e update.
M-x mu4e-update-mail-and-index
will run the command, or press U
in the mu4e main buffer
(setq mu4e-get-mail-command "offlineimap -o")
allows notifications from mu4e
doom-mode-line has a custom option to support this
(straight-use-package 'mu4e-alert)
(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)
(setq mu4e-alert-interesting-mail-query
(concat
"flag:unread"
" AND NOT flag:trashed"
" AND maildir:"
"\"/ChurlsWorld/INBOX\""))
(defvar my-org-keymap (make-sparse-keymap))
(define-prefix-command 'my-org-keymap)
(define-key my-org-keymap (kbd "s") 'org-insert-structure-template)
(define-key my-org-keymap (kbd "'") 'org-edit-special)
(add-to-list 'org-structure-template-alist '("se" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("sr" . "src ruby"))
(straight-use-package 'ob-restclient)
(org-babel-do-load-languages
'org-babel-load-languages
(quote ((emacs-lisp . t)
(picolisp . t)
(dot . t)
(ruby . t)
(shell . t)
(js . t)
(restclient . t))))
(straight-use-package 'org-tree-slide)
(setq company-minimum-prefix-length 2
company-idle-delay 0.3) ;; default is 0.2
(add-hook 'after-init-hook 'global-company-mode)
(add-hook 'company-mode-hook 'company-box-mode)
- [ ] shell modes it can be quite annoying
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format "(%d/%d) ")
(setq all-the-icons-ivy-file-commands
'(counsel-find-file counsel-file-jump counsel-recentf counsel-projectile-find-file counsel-projectile-find-dir))
(all-the-icons-ivy-setup)
(define-key xah-fly-c-keymap (kbd "e") 'counsel-find-file)
(which-key-mode 1)
Right now I only really need magit status:
(define-key 'personal-key-map (kbd "g") 'magit-status)
Eventually I might turn this into its own prefix map
Took some code from the modernemacs guy that replaces strings in magit buffers with pretty icons. Its a bit funky though, and you have to pick the right icons, otherwise they’ll get replaced with another one for some unknown reason. There was some report about it on the all-the-icons repo, so perhaps its fixable.
(defmacro pretty-magit (WORD ICON PROPS &optional NO-PROMPT?)
"Replace sanitized WORD with ICON, PROPS and by default add to prompts."
`(prog1
(add-to-list 'pretty-magit-alist
(list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":"))))
,ICON ',PROPS))
(unless ,NO-PROMPT?
(add-to-list 'pretty-magit-prompt (concat ,WORD ": ")))))
(setq pretty-magit-alist nil)
(setq pretty-magit-prompt nil)
(pretty-magit "Feature" ? (:foreground "slate gray" :height 1.2))
(pretty-magit "Add" ? (:foreground "#375E97" :height 1.2))
(pretty-magit "Fix" ? (:foreground "#FB6542" :height 1.2))
(pretty-magit "Clean" ? (:foreground "#FFBB00" :height 1.2))
(pretty-magit "Docs" ? (:foreground "#3F681C" :height 1.2))
(pretty-magit "main" ? (:foreground "LightSeaGreen" :box t :height 1.2) t)
(pretty-magit "origin" ? (:foreground "LightSeaGreen" :box t :height 1.2) t)
(defun add-magit-faces ()
"Add face properties and compose symbols for buffer from pretty-magit."
(interactive)
(with-silent-modifications
(--each pretty-magit-alist
(-let (((rgx icon props) it))
(save-excursion
(goto-char (point-min))
(while (search-forward-regexp rgx nil t)
(compose-region
(match-beginning 1) (match-end 1) icon)
(when props
(add-face-text-property
(match-beginning 1) (match-end 1) props))))))))
(advice-add 'magit-status :after 'add-magit-faces)
(advice-add 'magit-refresh-buffer :after 'add-magit-faces)
(setq projectile-completion-system 'ivy)
(projectile-mode 1)
(define-key 'personal-key-map (kbd "p") 'projectile-command-map)
(straight-use-package 'persp-projectile)
(define-key projectile-command-map (kbd "l") 'projectile-persp-switch-project)
NOTE: this overwrites the binding for projectile-find-file-in-directory
Perspective creates “workspaces” with isolated buffer lists. It also interacts with projectile, allowing easy opening of perspectives for a particular project.
(persp-mode 1)
(define-key 'xah-fly-leader-key-map (kbd "f") 'persp-counsel-switch-buffer)
Binding the perspective-map to my personal key map:
(define-key 'personal-key-map (kbd "l") 'perspective-map)
And customizing the selected perspective face, the default is often poor:
(set-face-foreground 'persp-selected-face "indian red")
(require 'smartparens-config)
(smartparens-global-mode 1)
(sp-local-pair 'sly-mrepl-mode "'" nil :actions nil)
(define-key 'personal-key-map (kbd "d") 'docker)
(global-undo-tree-mode 1)
I’m a sucker for the pretty icons everywhere.
(straight-use-package 'all-the-icons-dired)
(add-hook 'dired-mode 'all-the-icons-dired-mode)
(add-to-list 'auto-mode-alist '("\\.http\\'" . restclient-mode))
(straight-use-package 'emms)
(require 'emms-setup)
(emms-all)
(emms-default-players)
(setq emms-source-file-default-directory "~/Music/")
(add-to-list 'emms-info-functions #'emms-info-metaflac)
For my use case of mass editing tags on audio files, it appears that I have to enable markable playlists. It isn’t clear in the docstrings for the functions but, the info manual has this:
emms-mark is also intent to provide a way for user to select tracks for other command to operate on them. Currently, ‘emms-tag-editor.el’ uses the emms-mark to edit the tags of selected tracks
(require 'emms-mark)
This allows marking items in playlists similarly to how dired works.
Marking several items and pressing E will open them all up for tag edits,
and then you can M-x emms-tag-editor-set-all
or C-c C-r
to set the value
of a tag on all of the marked tracks.
getting frustrated with LSP taking so much cpu, but missing jump-to-def, this package comes highly recommended
(straight-use-package 'dumb-jump)
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
(straight-use-package 'elpher)
I’m putting this at the end, because its most likely to have order dependent side effects than other configurations.
I prefer to turn off several things from the default gui:
(blink-cursor-mode 0)
(tool-bar-mode 0)
(menu-bar-mode 0)
(scroll-bar-mode 0)
And with larger screens I can dedicate 16 pixels to the left fringe, which allows larger icons to show up for things like flycheck:
(fringe-mode '(16 . 0))
- [ ] make a list of favored themes to pick randomly from
- [ ] perhaps choose light/dark theme based on time of day
(load-theme 'kaolin-temple)
The default behavior of loading/enabling a theme in Emacs is to combine all the
faces defined in custom-enabled-themes
, with the front of the list having
precedence. This started annoying me when certain faces from one theme would
interfere with the one I was trying to load. There are no hooks around loading
or enabling themes (that i could find), so I wrote an advice function to disable
any currently enabled theme before loading the next one.
(defun theme-switch (&rest args)
"Function to advise `load-theme' to ensure only a single theme is enabled."
(mapc 'disable-theme custom-enabled-themes))
(advice-add 'load-theme :before #'theme-switch)
(define-key 'personal-key-map (kbd "t") 'load-theme)
I’ve moved to doom-mode-line because it is beautiful and effective.
(straight-use-package 'doom-modeline)
(setq
doom-modeline-buffer-encoding nil
doom-modeline-persp-name t
doom-modeline-height 35
doom-modeline-minor-modes t
doom-modeline-mu4e t) ;; requires ‘mu4e-alert’ package.
(doom-modeline-mode)
Because I’ve enabled minor modes and perpective names, i sometimes have an issue with space. delight helps minimize space taken by minor modes. I should also do something with perspectives as well.
On the other hand, its relatively rare that the minor modes i have running are revealed to me by the modeline anyway, and persp names are generally obvious to me. perhaps that clutter on the modeline is not necessary ;)
Part of it is certainly aesthetics, but perhaps i should consider what use the modeline might be best put towards.
(delight SPEC &optional VALUE FILE)
Modify the lighter value displayed in the mode line for the given mode SPEC if and when the mode is loaded.
(straight-use-package 'delight)
(delight
'(
(company-box-mode " ☐" company-box)
(company-mode " ©" company)
(ivy-mode " ❦" ivy) ;;
(projectile-mode " ⁋" projectile)
(undo-tree-mode " ⎌" undo-tree)
(xah-fly-keys " ∑")
))
helps highlight cursor when switching windows
(straight-use-package 'beacon)
(beacon-mode 1)
(defun screenshot-svg ()
(interactive)
(let* ((filename (make-temp-file "Emacs" nil ".svg"))
(data (x-export-frames nil 'svg)))
(with-temp-file filename
(insert data))
(kill-new filename)
(message filename)))
Well… it is 2020 after all 😷
(straight-use-package 'emojify)
(add-hook 'org-mode-hook #'emojify-mode)
(add-hook 'mu4e-view-mode-hook #'emojify-mode)
I’ve really enjoyed Victor Mono for coding, especially the cursive italics. Its a clean, classy font that brings me a bit of joy :)
(setq my-font "Victor Mono-14")
;; (set-face-attribute 'default t :font my-font)
(set-frame-font my-font nil t)
Deja Vu Sans Mono was suggested as a good monospace font for handling special chars in different orthographies, and it does seem ok.
Been searching for a nice looking font for Lakota work, and the one I’m set on currently is Linux Libertine O, which handles all characters failry well. The macron on top of consonants can still be a bit funky on t̄ and k̄. but it is ok otherwise
(set-face-attribute 'variable-pitch nil :family "Linux Libertine O")
(straight-use-package ‘composer)