Pete Ley
d26123d3b4
|
3 weeks ago | |
---|---|---|
.. | ||
.config/systemd/user | 2 years ago | |
.emacs.d | 3 weeks ago | |
.local/share/applications | 2 years ago | |
README.org | 3 weeks ago |
README.org
- systemd service
- Packages by straight.el
- exwm
- Basic setup
- Theme shuffler
- Elisp util
- gnus
- helm
- org-mode
- Coding
- Projectile
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)