You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Pete Ley d26123d3b4
Add yaml-mode
3 weeks ago
..
.config/systemd/user Set up EXWM 2 years ago
.emacs.d Add yaml-mode 3 weeks ago
.local/share/applications un-depend on org elpa 2 years ago
README.org Add yaml-mode 3 weeks ago

README.org

My emacs configuration

systemd service

Per EmacsWiki, you can enable emacs to run in daemon mode with this in $HOME/.config/systemd/user/emacs.service:

  [Unit]
  Description=Emacs text editor
  Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

  [Service]
  Type=forking
  ExecStart=/usr/bin/emacs --daemon
  ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
  Environment=SSH_AUTH_SOCK=%t/keyring/ssh
  Restart=on-failure

  [Install]
  WantedBy=default.target

I use a desktop file (in $HOME/.local/share/applications/emacs.desktop) to make sure files are opened in the frame that's already open:

  [Desktop Entry]
  Name=Emacs
  GenericName=Text Editor
  Comment=Edit text
  MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
  Exec=emacsclient -c %F
  Icon=emacs
  Type=Application
  Terminal=false
  Categories=Development;TextEditor;
  StartupWMClass=Emacs
  Keywords=Text;Editor;

Then enable and start with

  systemctl enable --user emacs
  systemctl start --user emacs

and open new emacs frames with

  emacsclient -c

Packages by straight.el

Inhibit package.el

early-init.el:

  (setq package-enable-at-startup nil)

Bootstrap per the instructions

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

use-package

  (straight-use-package 'use-package)
  (use-package straight
    :custom (straight-use-package-by-default t))

exwm

  (straight-use-package 'exwm)
  (require 'exwm)
  (require 'exwm-config)
  (require 'exwm-systemtray)
  (exwm-systemtray-enable)

  (setq exwm-input-global-keys
	`((,(kbd "s-R") . exwm-reset)
	  (,(kbd "s-C") .
	   (lambda (command)
	     (interactive (list (read-shell-command "$ ")))
	     (start-process-shell-command command nil command)))
	  (,(kbd "s-W") . exwm-workspace-switch)
	  (,(kbd "s-N") . exwm-workspace-switch-create))
	exwm-workspace-number 4)

  (define-key exwm-mode-map (kbd "C-q") 'exwm-input-send-next-key)
  (define-key exwm-mode-map (kbd "M-a") 'jabber-activity-switch-to)

  ;; from EXWM wiki:
  ;; All buffers created in EXWM mode are named "*EXWM*". You may want to
  ;; change it in `exwm-update-class-hook' and `exwm-update-title-hook', which
  ;; are run when a new X window class name or title is available.  Here's
  ;; some advice on this topic:
  ;; + Always use `exwm-workspace-rename-buffer` to avoid naming conflict.
  ;; + For applications with multiple windows (e.g. GIMP), the class names of
  ;;  all windows are probably the same.  Using window titles for them makes
  ;;  more sense.
  ;; In the following example, we use class names for all windows except for
  ;; Java applications and GIMP.
  (add-hook 'exwm-update-class-hook
	    (lambda ()
	      (unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
			  (string= "gimp" exwm-instance-name))
		(exwm-workspace-rename-buffer exwm-class-name))))
  (add-hook 'exwm-update-title-hook
	    (lambda ()
	      (when (or (not exwm-instance-name)
			(string-prefix-p "sun-awt-X11-" exwm-instance-name)
			(string= "gimp" exwm-instance-name))
		(exwm-workspace-rename-buffer exwm-title))))

  (setq exwm-input-simulation-keys
	'(
	  ;; movement
	  ([?\C-b] . [left])
	  ([?\M-b] . [C-left])
	  ([?\C-f] . [right])
	  ([?\M-f] . [C-right])
	  ([?\C-p] . [up])
	  ([?\C-n] . [down])
	  ([?\C-a] . [home])
	  ([?\C-e] . [end])
	  ([?\M-v] . [prior])
	  ([?\C-v] . [next])
	  ([?\C-d] . [delete])
	  ([?\C-k] . [S-end delete])
	  ;; cut/paste.
	  ([?\C-w] . [?\C-x])
	  ([?\M-w] . [?\C-c])
	  ([?\C-y] . [?\C-v])
	  ;; search
	  ([?\C-s] . [?\C-f])
	  ;; c-g
	  ([?\C-g] . [escape])))

Basic setup

Turn off annoying parts of the frame:

  (menu-bar-mode -1)
  (when window-system
    (tool-bar-mode -1)
    (scroll-bar-mode -1))

Unclutter directories of backups:

  (setq backup-directory-alist '(("." . "~/.emacs.d/autosave")))

Other bits and pieces:

  (column-number-mode)
  (setq inhibit-splash-screen t
        split-height-threshold 80
        split-width-threshold 160
        display-time-default-load-average nil
        display-time-24hr-format t
        fill-column 90)
  (display-time-mode t)
  (delete 'display-time-string global-mode-string)
  (setq custom-file "~/.config/emacs-custom.el")
  (load custom-file)
  (customize-set-variable 'mode-line-format
                          '("%e"
                            display-time-string
                            mode-line-modified
                            mode-line-remote
                            mode-line-buffer-identification
                            " "
                            mode-line-position
                            (vc-mode vc-mode)
                            " "
                            mode-line-modes
                            mode-line-misc-info))
  (use-package markdown-mode)
  (use-package yaml-mode)

Theme shuffler

  (defcustom theme-shuffler-idle-timer-interval (* 60 60)
    "Time interval in seconds for theme-shuffler idle timer")

  (defvar theme-shuffler--current-theme nil "Current theme")

  (defvar theme-shuffler--theme-list nil "Available themes")

  (defmacro theme-shuffler-use-themes (&rest themes)
    "Add themes to theme shuffler."
    (let ((themes (reverse themes)))
      `(progn
         (setq theme-shuffler--theme-list nil)
         ,@(mapcar
            (lambda (theme)
              (let (package-name themes add-theme-form)
                (if (listp theme)
                    (setq package-name (car theme)
                          themes (cdr theme)
                          add-theme-form `(mapc
                                           (apply-partially 'add-to-list 'theme-shuffler--theme-list)
                                           ',themes))
                  (setq package-name theme
                        add-theme-form `(add-to-list 'theme-shuffler--theme-list ',theme)))
                (setq package-name
                      (car (read-from-string (concat (symbol-name package-name) "-theme"))))
                `(progn (use-package ,package-name) ,add-theme-form)))
            themes))))

  (defun theme-shuffler-change-theme (theme)
    "Disables all other themes and enable a new one"
    (interactive
     (list
      (intern (completing-read "Change to theme: "
                               (mapcar #'symbol-name
                                       theme-shuffler--theme-list)))))
    (mapc 'disable-theme custom-enabled-themes)
    (load-theme theme t)
    (setq theme-shuffler--current-theme theme)
    (message "Loaded theme: %s" (symbol-name theme)))

  (defun theme-shuffler-shuffle-theme ()
    "Shuffles the currently loaded theme."
    (interactive)
    (theme-shuffler-change-theme
     (random-choice
      (remove theme-shuffler--current-theme theme-shuffler--theme-list))))

  (defvar theme-shuffler--idle-timer nil)

  (defun theme-shuffler-start-idle-timer ()
    "Start shuffler idle timer"
    (interactive)
    (message "Starting theme shuffler idle timer with %d second interval" theme-shuffler-idle-timer-interval)
    (setq theme-shuffler--idle-timer (run-with-idle-timer theme-shuffler-idle-timer-interval t #'shuffle-theme)))

  (defun theme-shuffler-cancel-idle-timer ()
    "Cancel shuffler idle timer"
    (interactive)
    (if (not theme-shuffler--idle-timer)
        (message "No idle timer set")
      (message "Cancelling %d second theme shuffler idle timer" (elt theme-shuffler--idle-timer 2))
      (cancel-timer theme-shuffler--idle-timer)
      (setq theme-shuffler--idle-timer nil)))

  (theme-shuffler-use-themes
   (ample ample ample-flat)
   anti-zenburn
   cyberpunk
   (darktooth darktooth-dark)
   dracula
   gruber-darker
   inkpot
   material
   monokai
   (solarized solarized-dark-high-contrast))

Elisp util

Just standard library type stuff I've found useful

  (defmacro when-user-file-exists (file-name &rest body)
    (declare (indent 1))
    `(let ((it ,(expand-file-name file-name user-emacs-directory)))
       (when (file-exists-p it)
         ,@body)))

  (defun plistp (obj)
    "Non-nil if OBJ is a list with an even number of elements"
    (and (listp obj)
         (cl-evenp (length obj))))

  (defun plist-keys (plist)
    "A list of keys in property list PLIST or nil if PLIST is not a property list"
    (if (plistp plist)
        (mapcar #'(lambda (i) (nth i plist))
                (number-sequence 0 (/ (length plist) 2) 2))))

  (defun random-choice (lst)
    "Return random element of LST"
    (nth (random (length lst)) lst))

  (defun unfill-paragraph ()
    (interactive)
    (let ((fill-column (point-max)))
      (fill-paragraph nil)))

  (defun unfill-region ()
    (interactive)
    (let ((fill-column (point-max)))
      (fill-region (region-beginning) (region-end) nil)))

  (defun read-file-to-string (filename)
    "Return the contents of file named `filename' as a string.

  Why is this necessary for me to define? The world may never know."
    (with-temp-buffer
      (insert-file-contents filename)
      (buffer-string)))

gnus

Email login is handled by ~/.authinfo

  (setq gnus-select-method '(nnimap "mail.hover.com")
        gnus-use-full-window nil)

helm

Use helm for some basic tasks:

  (use-package helm
    :demand t
    :bind (("C-x b" . helm-mini)
           ("M-x" . helm-M-x)
           ("M-X" . execute-extended-command)
           ("C-h a" . helm-apropos)
           ("C-x C-f" . helm-find-files))
    :custom (helm-split-window-preferred-function split-window-preferred-function)
    :config (helm-mode 1))

org-mode

Basic defaults config:

  (use-package org
    :bind (("C-c a" . org-agenda)
           ("C-c c" . org-capture)
           ("C-c C-x C-o" . org-clock-out)
           ("C-c C-x C-x" . org-clock-in-last)
           :map org-mode-map
           ("C-c C-t" . my/org-todo))
    :hook (org-mode . auto-revert-mode)
    :init
    (defvar org-main-file "~/org/core.org"
      "The single org file that I use for planning")
    <<override-org-effective-time>>
    :custom
    (org-agenda-files (list org-main-file))
    (org-agenda-file-regexp "\\.org$")
    (org-modules '(org-habit))
    (org-startup-folded t)
    (org-startup-indented t)
    (org-pretty-entities t)
    (org-hide-leading-stars t)
    (org-todo-repeat-to-state t)
    (org-log-into-drawer t)
    (org-blank-before-new-entry '((heading . nil)(plain-list-item . nil)))
    (org-refile-targets '((org-agenda-files . (:maxlevel . 4))))
    (org-agenda-restore-windows-after-quit t)
    (org-archive-location "%s_archive::datetree/")
    (org-archive-subtree-save-file-p t)
    (org-src-window-setup 'current-window)
    (org-todo-keywords '(;; tasks
                         (sequence "todo(t)" "|"
                                   "done(d!)" "notdone(n@)")
                         ;; projects
                         (sequence "future(f)" "inprogress(i!)" "waiting(w@)" "|"
                                   "completed(c!)" "canceled(q@)")
                         ;; events
                         (sequence "event(E)" "going(G!)" "|"
                                   "notgoing(N!)" "went(W@)" "skipped(S!)")))
    (org-capture-templates '(("i" "Generic inbox item"
                              entry (file+headline org-main-file "Inbox")
                              "* %?\n%U")
                             ("t" "Task"
                              entry (file+headline org-main-file "Inbox")
                              "* todo %?\n%U")
                             ("c" "Generic calendar item"
                              entry (file org-main-file)
                              "* %?\n%^t")))
    (org-agenda-custom-commands '(("d" "day agenda " agenda "" ((org-agenda-span 'day)))
                                  ("w" "week agenda" agenda "")
                                  ("m" "month agenda " agenda ""
                                   ((org-agenda-span 'month)
                                    (org-agenda-use-time-grid nil)))
                                  ("t" "unscheduled todo list"
                                   todo "todo"
                                   ((org-agenda-todo-ignore-scheduled t)))
                                  ("c" "completed tasks"
                                   todo "done|complete"
                                   ((org-agenda-todo-ignore-scheduled t))))))

Overriding org-current-effective-time

The built-in `org-current-effective-time` is a hacky joke that uses `org-extend-today-until`, which is dumb and doesn't need to exist. From the docstring of `org-extend-today-until`:

IMPORTANT: This is a feature whose implementation is and likely will remain incomplete. Really, it is only here because past midnight seems to be the favorite working time of John Wiegley :-)

See also: `org-todo-yesterday`.

It makes way more sense to me that using "effective time" means prompting for a timestamp rather than arcane config options.

  (defun my/org-current-effective-time ()
    "If `org-use-effective-time' is T, prompt for date+time.

  Otherwise, use current time."
    (if org-use-effective-time
        (org-read-date nil t)
      (org-current-time)))
  (advice-add 'org-current-effective-time :override #'my/org-current-effective-time)

  (defun my/org-todo (arg)
    "Modify org-todo prefix arg behavior"
    (interactive "P")
    (let ((org-use-effective-time current-prefix-arg))
      (funcall #'org-todo nil)))

Coding

format-all

   (use-package format-all
     :hook (c-mode . format-all-mode))

magit

  (use-package magit
    :bind (("C-c m s" . magit-status)
           ("C-c m F" . magit-pull)
           ("C-c m P" . magit-push)
           ("C-c m r" . magit-rebase)
           ("C-c m c" . magit-clone)))

AutoCAD/AutoLISP

  (add-to-list 'auto-mode-alist '("\\.dcl\\'" . c-mode))

  (define-derived-mode autolisp-mode lisp-mode "AutoLISP"
    "Major mode for editing AutoLISP files"
    (set (make-local-variable 'indent-tabs-mode) nil)
    (slime-mode -1))
  (add-hook 'autolisp-mode-hook (lambda() (slime-mode -1)))
  (add-to-list 'auto-mode-alist '("\\.lsp\\'" . autolisp-mode))
  (font-lock-add-keywords 'autolisp-mode
    '(
      ("setq" . font-lock-keyword-face)
      ("vl[a-zA-Z0-9>\\-]+" . font-lock-keyword-face)
      ("defun-q-list-ref" . font-lock-keyword-face)
      ("defun-q-list-set" . font-lock-keyword-face)
      ("declare-late" . font-lock-keyword-face)
      ("defun-q" . font-lock-keyword-face)
      ("defun-r" . font-lock-comment-face)
      ("defun-q[\n[:blank:]]+\\([*_<>:&a-zA-Z0-9#!\\-]+\\)" 1 font-lock-function-name-face)))
  (add-hook 'autolisp-mode-hook (lambda() (setq fill-column 90)))

Python

Jinja2 templates

  (use-package jinja2-mode
    :mode ("\\.jinja\\'" . jinja2-mode))

elpy

  (use-package elpy
    :config (elpy-enable))

Django

  (use-package web-mode
    :mode ("\\.html\\'" . web-mode)
    :custom
    (web-mode-css-indent-offset 1)
    (web-mode-code-indent-offset 2)
    (web-mode-markup-indent-offset 1)
    (web-mode-engines-alist '(("django" . "\\.html\\'"))))

Lua

  (use-package lua-mode)

Projectile

  (use-package projectile)