It’s been said “there are many ways to skin a cat”. The same can be said of Emacs. Probably.
Don’t be shy - introduce yourself to emacs. If you are copying this config, make sure you use your name. We don’t want to confuse my mother.
(setq user-full-name "Alex Recker" user-mail-address "email@example.com")
Ensure that the system
PATH is the one used by emacs.
(use-package exec-path-from-shell :ensure t :config (exec-path-from-shell-initialize))
Helper function for sniffing OS.
(defun recker/macos-p () "Returns T if running on MacOS." (string-equal system-type "darwin"))
(defun recker/path (dir &optional subpath) (let* ((macos-p (string-equal system-type "darwin")) (dir (pcase dir ('home "~") ('desktop (if macos-p "~/Desktop" "~/desktop")) ('docs (if macos-p "~/Documents" "~/docs")) ('pics (if macos-p "~/Pictures" "~/pics")) ('public (if macos-p "~/Public" "~/public")) ('src "~/src") ('emacs user-emacs-directory) (_ (error (format "no %s directory!" dir))))) (subdir (or subpath ""))) (expand-file-name (concat (file-name-as-directory dir) subpath))))
Register some files I visit often.
(set-register ?b '(file . "/ssh:bedroom:/home/alex")) (set-register ?d `(file . ,(recker/path 'desktop))) (set-register ?e `(file . ,(recker/path 'emacs "README.org"))) (set-register ?f '(file . "/ssh:firstname.lastname@example.org:/mnt/")) (set-register ?l `(file . ,(recker/path 'emacs "local.el"))) (set-register ?o `(file . ,(recker/path 'docs "opsat.org")))
On MacOS, I like my emacs extra thicc.
(when (and (recker/macos-p) (display-graphic-p)) (setq initial-frame-alist '((width . 99) (height . 53)) default-frame-alist '((width . 99) (height . 53))))
Set the font to monaco if I’m on a mac. Otherwise, use inconsolata.
(when (display-graphic-p) (if (recker/macos-p) (set-default-font "Monaco 16" nil t) (set-default-font "Inconsolata 13" nil t)))
Emacs comes with some obnixious defaults. “Not on my watch!”, yelled Alex as he disabled them.
(setq make-backup-files nil auto-save-default nil indent-tabs-mode nil ns-confirm-quit 1) (global-auto-revert-mode 1) (menu-bar-mode 0) (delete-selection-mode t) (tool-bar-mode -1) (when (display-graphic-p) (scroll-bar-mode -1)) (setq vc-follow-symlinks t)
Because the command
C-x C-c is easier to type by accident than you’d
think, enable this so Emacs says “are you sure?”
(setq confirm-kill-emacs #'yes-or-no-p)
Disable goal column warning.
(put 'set-goal-column 'disabled nil)
(put 'upcase-region 'disabled nil)
I overwrite the build-in
comment-dwim with its superior sequel.
(use-package comment-dwim-2 :ensure t :bind ("M-;" . comment-dwim-2))
Hide all minor modes from the modeline (since there are usually like a hundred).
(use-package rich-minority :ensure t :init (rich-minority-mode 1) :config (setq rm-blacklist ""))
Automatically save the bookmark file each time it is modified. This prevents losing bookmarks created in separate emacs clients.
(setq bookmark-save-flag 1 bookmark-default-file (recker/path 'docs "emacs/bookmarks.el"))
Better File Manager
By default, hide dot files. They can be shown by disabling
Another nice side effect of
dired-x is suddenly gaining the ability
of jumping to the current file in dired with
(require 'dired-x) (setq-default dired-omit-files-p t) (setq dired-omit-files (concat dired-omit-files "\\|^\\..+$"))
-h switch to the dired output to show prettier filenames.
(setq dired-listing-switches "-alh")
Don’t ask permission to delete the buffer of a deleted file.
(setq dired-clean-confirm-killing-deleted-buffers nil)
Better Text Selection
expand-region to incrementally grab larger portions of text
based on where the cursor is. It’s a brilliant tool.
(use-package expand-region :ensure t :bind ("C-=" . er/expand-region))
(use-package company :ensure t :init (global-company-mode) :config (setq company-tooltip-align-annotations t company-dabbrev-downcase nil)) (use-package company-quickhelp :ensure t :init (company-quickhelp-mode))
Yasnippet - I don’t use this nearly as much as I should be.
(use-package yasnippet :ensure t :init (yas-global-mode 1))
Completion and filtering with ivy, supported by counsel.
(use-package ivy :ensure t :config (setq ivy-use-selectable-prompt t) :init (ivy-mode 1)) (use-package counsel :ensure t :bind ("C-c i" . counsel-imenu) ("C-c s" . swiper) ("C-c g" . counsel-git-grep) ("C-x C-y" . counsel-yank-pop))
Use projectile for finding things within projects.
(use-package projectile :ensure t :config (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) (setq projectile-completion-system 'ivy) :init (projectile-mode t))
Magit. Seriously. Just try it you heathen.
(use-package magit :ensure t :bind ("C-x g" . magit-status) ("C-c m" . magit-blame) :config (magit-add-section-hook 'magit-status-sections-hook 'magit-insert-unpushed-to-upstream 'magit-insert-unpushed-to-upstream-or-recent 'replace))
Use personal dictionary from docs for ispell.
(setq ispell-personal-dictionary (recker/path 'docs "emacs/ispell.dict"))
ispell explicitly, so it doesn’t accidentally pick up a
different program like
aspell on a mac.
(setq ispell-program-name (executable-find "ispell"))
(use-package flycheck :ensure t :init (global-flycheck-mode))
The slash screen displayed on startup is a little too noisy for
*scratch* buffer is a lot more low key.
(setq inhibit-startup-message 't)
Here is a collection of pithy quotes I like to display on my scratch screen.
|Sanity and happiness are an impossible combination.||Mark Twain|
|Trust thyself only, and another shall not betray thee.||Thomas Fuller|
|Fear has its uses but cowardice has none.||Mahatma Ghandi|
|Happiness can exist only in acceptance.||George Orwell|
|Seek respect mainly from thyself, for it comes first from within.||Steven H. Coogler|
|Conscience is the dog that can’t bite, but never stops barking.||Proverb|
|In general, pride is at the bottom of all great mistakes.||Steven H. Coogler|
|Anger as soon as fed is dead – tis starving makes it fat.||Emily Dickinson|
|Make no judgements where you have no compassion.||Anne McCaffrey|
|Isolation is a self-defeating dream.||Carlos Salinas de Gortari|
|Doubt must be no more than vigilance, otherwise it can become dangerous.||George C. Lichtenberg|
|Love is a willingless to sacrifice.||Michael Novak|
|The value of identity is that so often with it comes purpose.||Richard R. Grant|
|Discontent is the first necessity of progress.||Thomas Edison|
|Some of us think holding on makes us strong, but sometimes it is letting go.||Herman Hesse|
|Let not a man guard his dignity but let his dignity guard him.||Ralph Waldo Emerson|
|Guilt: the gift that keeps on giving.||Erma Bombeck|
|Be here now.||Ram Dass|
|The master understands that the universe is forever out of control.||Lao Tzu|
|Our biggest problems arise from the avoidance of smaller ones.||Jeremy Caulfield|
|The truth will set you free, but first it will make you miserable||James A. Garfield|
|The thing that lies at the foundation of positive change is service to a fellow human being||Lee Iacocca|
|Honesty and transparency make you vulnerable. Be honest and transparent anyway||Mother Teresa|
|If you do not ask the right questions, you do not get the right answers.||Edward Hodnett|
|Resentment is like taking poison and waiting for the other person to die.||Malachy McCourt|
|If we knew each other’s secrets, what comfort should we find.||John Churton Collins|
|The mistake is thinking that there can be an antidote to the uncertainty.||David Levithan|
|Cure sometimes, treat often, comfort always.||Hippocrates|
|Suspicion is a heavy armor and with its weight it impedes more than it protects.||Robert Burns|
|Sincerity, even if it speaks with a stutter, will sound eloquent when inspired.||Eiji Yoshikawa|
|I have little shame, no dignity - all in the name of a better cause.||A.J. Jacobs|
|Truth may sometimes hurt, but delusion harms.||Vanna Bonta|
|Intuition is more important to discovery than logic.||Henri Poincare|
|How weird was it to drive streets I knew so well. What a different perspective.||Suzanne Vega|
|There can be no progress without head-on confrontation.||Christopher Hitchens|
|Sometimes it’s necessary to go a long distance out of the way to come back a short distance correctly.||Edward Albea|
|Stagnation is death. If you don’t change, you die. It’s that simple. It’s that scary.||Leonard Sweet|
|In my opinion, actual heroism, like actual love, is a messy, painful, vulnerable business.||John Green|
|Maybe all one can do is hope to end up with the right regrets.||Arthur Miller|
|If you have behaved badly, repent, make what amends you can and address yourself to the task of behaving better next time.||Aldous Huxley|
|Sooner or later everyone sits down to a banquet of consequences.||Robert Louis Stevenson|
|We are all in the same boat, in a stormy sea, and we owe each other a terrible loyalty.||G.K. Chesterton|
|In our quest for the answers of life we tend to make order out of chaos, and chaos out of order.||Jeffrey Fry|
|There are many ways of going forward, but only one way of standing still.||Franklin D. Roosevelt|
|Truth is outside of all patterns.||Bruce Lee|
|By imposing too great a responsibility, or rather, all responsibility, on yourself, you crush yourself.||Franz Kafka|
|How few there are who have courage enough to own their faults, or resolution enough to mend them.||Benjamin Franklin|
|Resistance is useless.||Doctor Who|
|Happiness does not depend on outward things, but on the way we see them.||Leo Tolstoy|
|Being president is like being a jackass in a hailstorm. There’s nothing to do but to stand there and take it.||Lyndon Johnson|
Pick a random one on startup, wrap it in a lisp comment box, and assign it to the scratch message variable.
(setq initial-scratch-message (let* ((choice (nth (random (length quotes)) quotes)) (text (car choice)) (attribution (car (cdr choice)))) (with-temp-buffer (lisp-mode) (newline) (insert (format "\"%s\"\n" text)) (fill-region (point-min) (point-max)) (insert (format "-- %s" attribution)) (comment-region (point-min) (point-max)) (dotimes (_ 2) (newline)) (buffer-string))))
*scratch* buffer unkillable.
(defun recker/dont-kill-scratch () "Return NIL if the current buffer is the *scratch* buffer." (not (equal (buffer-name (current-buffer)) "*scratch*"))) (add-hook 'kill-buffer-query-functions 'recker/dont-kill-scratch)
My own functions for pass.
(defun recker/pass-directory () (or (bound-and-true-p recker/active-pass-directory) (expand-file-name "~/.password-store"))) (defun recker/pass--file-to-entry (path) (file-name-sans-extension (file-relative-name path (recker/pass-directory)))) (defun recker/pass-list-entries () (mapcar #'recker/pass--file-to-entry (directory-files-recursively (recker/pass-directory) ".gpg"))) (defun recker/pass-to-string (path) (first (split-string (shell-command-to-string (format "PASSWORD_STORE_DIR=\"%s\" pass \"%s\" | head -1" (recker/pass-directory) path)) "\n"))) (defun recker/pass-to-clip (path) (interactive (list (completing-read "Password: " (recker/pass-list-entries) nil t))) (shell-command (format "PASSWORD_STORE_DIR=\"%s\" pass -c \"%s\"" (recker/pass-directory) path))) (defun recker/pass-to-clip-work () (interactive) (let ((recker/active-pass-directory (expand-file-name "~/.password-store-work"))) (funcall-interactively #'recker/pass-to-clip (completing-read "Password: " (recker/pass-list-entries) nil t))))
Support for editorconfig, no matter what the mode is.
(use-package editorconfig :ensure t :config (editorconfig-mode 1))
Taken from The Linux Kernel Coding Style, which was a way better read than you’d think.
I slightly modified the provided snippet so that all of my C would obey these rules by default.
(defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (add-hook 'c-mode-common-hook (lambda () ;; Add kernel style (c-add-style "linux-tabs-only" '("linux" (c-offsets-alist (arglist-cont-nonempty c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)))))) (add-hook 'c-mode-hook (lambda () (setq indent-tabs-mode t) (setq show-trailing-whitespace t) (c-set-style "linux-tabs-only")))
;; (use-package cider ;; :ensure t) (use-package clojure-mode :ensure t)
For this to work, sbcl should be installed and in
(use-package slime :ensure t :config (setq inferior-lisp-program (executable-find "sbcl"))) (use-package slime-company :ensure t :init (slime-setup '(slime-fancy slime-company)))
;; (use-package csv-mode ;; :ensure t ;; :defer t ;; :mode "\\.csv\\'")
(use-package d-mode :ensure t :defer t :mode "\\.d\\'")
(use-package dhall-mode :ensure t :mode "\\.dhall\\'")
(use-package dockerfile-mode :ensure t :defer t :mode "\\Dockerfile\\'")
Disable those silly docstring warnings when editing elisp.
(with-eval-after-load 'flycheck (add-to-list 'flycheck-disabled-checkers 'emacs-lisp-checkdoc))
This is the really trendy part of my config.
(use-package go-mode :ensure t :defer t :mode "\\*.go\\'" :init (add-hook 'before-save-hook 'gofmt-before-save) (local-set-key (kbd "M-.") 'godef-jump) (add-hook 'go-mode-hook (lambda () (set (make-local-variable 'company-backends) '(company-go)) (company-mode)))) (use-package company-go :ensure t)
Pretty much just for Jenkins files.
(use-package groovy-mode :ensure t :defer t :mode "\\Jenkinsfile\\'")
(use-package haskell-mode :ensure t :defer t :mode "\\.hs\\'")
(use-package web-mode :ensure t :defer t :mode ("\\.html\\'" "\\.jinja\\'") :config (setq web-mode-markup-indent-offset 2 web-mode-code-indent-offset 2)) (use-package emmet-mode :ensure t :config (add-hook 'web-mode-hook 'emmet-mode))
This is the web-scale portion of my config.
(setq js-indent-level 2)
Install jsonnet mode.
(use-package jsonnet-mode :ensure t :defer t :mode ("\\.jsonnet\\'" "\\.libsonnet\\'"))
Add my own shim for formatting jsonnet because the language is in shambles and this doesn’t work out of the box.
(defun jsonnet-reformat-buffer () "Reformat entire buffer using the Jsonnet format utility, except it actually works." (interactive) (call-process-region (point-min) (point-max) "jsonnetfmt" t t nil (buffer-file-name)))
Taken from Working with Log Files in Emacs.
(use-package vlf :ensure t) (use-package log4j-mode :ensure t :defer t :mode "\\.log\\'")
(use-package lua-mode :ensure t :defer t :mode ("\\.lua\\'" "\\.p8\\'"))
(use-package markdown-mode :ensure t :commands (gfm-mode) :mode (("\\.md\\'" . gfm-mode) ("\\.gfm\\'" . gfm-mode)) :config (setq markdown-command "multimarkdown" markdown-fontify-code-blocks-natively t))
(use-package nginx-mode :ensure t :defer t)
Install virtualenvwrapper support.
(use-package virtualenvwrapper :ensure t)
Let elpy do its thing.
(use-package elpy :ensure t :init (elpy-enable))
These are very much a work in progress. I know about as much about ruby as I know about scented candles and professional football.
(setq ruby-deep-indent-paren nil)
(use-package rust-mode :ensure t :defer t :mode "\\.rs'")
Automatically “fill” text while editing.
(add-hook 'text-mode-hook 'turn-on-auto-fill)
Turn on spell check.
(add-hook 'text-mode-hook #'(lambda () (flyspell-mode t)))
(use-package terraform-mode :ensure t :defer t :mode "\\.tf\\'") (use-package company-terraform :ensure t :init (company-terraform-init))
I’m a simple man, and I use a simple shell.
(defun recker/ansi-term () (interactive) (ansi-term "/bin/bash")) (global-set-key (kbd "C-c e") 'eshell) (global-set-key (kbd "C-x t") 'recker/ansi-term)
The terminal buffer should be killed on exit.
(defadvice term-handle-exit (after term-kill-buffer-on-exit activate) (kill-buffer))
Aliases for eshell
(defalias 'ff #'find-file)
(use-package typescript-mode :ensure t :defer t :mode "\\.ts\\'")
(use-package indent-guide :ensure t :init (add-hook 'yaml-mode-hook 'indent-guide-mode)) (use-package yaml-mode :ensure t :defer t :mode ("\\.yml\\'" "\\.sls\\'" "\\.yml.j2\\'") :init (add-hook 'yaml-mode-hook 'turn-off-auto-fill))
(use-package org-mode :mode "\\.org$")
Render blank lines between collapsed headings.
(setq org-cycle-separator-lines 1)
Insert blank lines between headings by default.
(setq org-blank-before-new-entry '((heading . t) (plain-list-item . auto)))
Set attachments directory.
(setq org-attach-directory (recker/path 'docs "attachments/"))
Delete attachments when archiving something, since everything is stored in git anyway.
(setq org-attach-archive-delete 't)
Set-up org capture for quickly adding text to notes.
(setq org-capture-templates '())
org-capture to a fast, slick keybinding.
(global-set-key (kbd "C-c c") 'org-capture)
Use the local documents folder as the agenda root.
(setq org-agenda-files (list (recker/path 'docs)))
org-agenda a slick keybinding so I can quickly check it while
working on something else.
(global-set-key (kbd "C-c a") 'org-agenda)
While browsing the agenda, default to follow mode - which higlights the item in your notes while you scan through the agenda.
(setq org-agenda-start-with-follow-mode t)
By default, exclude anything that was archived.
(setq org-agenda-tag-filter-preset '("-ARCHIVE"))
Set up some custom agenda views.
(setq org-agenda-custom-commands '())
Live dangerously. Tell org to run code blocks without confirmation.
(setq org-confirm-babel-evaluate nil)
Add some languages!
(org-babel-do-load-languages 'org-babel-load-languages '((python . t) (ruby . t) (shell . t)))
Set up some publishing projects.
(setq org-publish-project-alist '())
Gnus has a steep learning curve, and learning to incorporate this mysterious program has proven to be an emotional roller coaster. I’m not even sure I know enough about it to say “it’s worth it”, but hopefully this will help you with your own journey.
Gnus requires a “primary method” from which you obtain news. Unfortunately, the program kind of explodes if this isn’t set, which proves to be kind of a pain when you want to poke around and set up things interactively.
Here’s my workaround - set the primary method to a dummy protocol that will immediately come back. In our case, this is a blank nnml stream.
(setq gnus-select-method '(nnml ""))
Default on topic mode, since it’s more helpful.
(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)
Change path to
newsrc config file.
(setq gnus-startup-file (recker/path 'docs "emacs/newsrc"))
Don’t keep a dribble file.
(setq gnus-use-dribble-file nil)
Enable the asynchronous flag.
(setq gnus-asynchronous t)
More possible placebo code to make gnus feel faster - use the cache.
(setq gnus-use-cache t)
Gnus creates a bunch of folders in your home directory that, as far as I can tell, are not needed outside of gnus. I’ve finally managed to wrangle enough variables to tell gnus to save everything in the gnus folder. I save mine off in a version controlled “docs” directory.
(setq gnus-home-directory (recker/path 'docs "emacs/gnus") nnfolder-directory (recker/path 'docs "emacs/gnus/Mail/archive") message-directory (recker/path 'docs "emacs/gnus/Mail") nndraft-directory (recker/path 'docs "emacs/gnus/Drafts") gnus-cache-directory (recker/path 'docs "emacs/gnus/cache"))
Use gmane and gwene to follow news, mailers, and tons of other syndicated things. There are even comics.
(setq gnus-secondary-select-methods '())
Add a personal IMAP account.
(add-to-list 'gnus-secondary-select-methods '(nnimap "personal" (nnimap-address "imap.gmail.com") (nnimap-server-port "imaps") (nnimap-stream ssl) (nnmail-expiry-target "nnimap+gmail:[Gmail]/Trash") (nnmail-expiry-wait immediate)))
Don’t attempt to archive outbound emails to groups.
(setq gnus-message-archive-group nil)
Keep addresses locally using
(use-package bbdb :ensure t :config (setq bbdb-file (recker/path 'docs "emacs/bbdb.el")) :init (bbdb-mua-auto-update-init 'message) (setq bbdb-mua-auto-update-p 'query) (add-hook 'gnus-startup-hook 'bbdb-insinuate-gnus))
(setq smtpmail-smtp-service 587 smtpmail-smtp-user "email@example.com" smtpmail-smtp-server "smtp.gmail.com" send-mail-function 'smtpmail-send-it)
I keep an encrypted authinfo in my docs under version control.
(add-to-list 'auth-sources (recker/path 'docs "emacs/authinfo.gpg"))
Here’s what it looks like.
machine imap.gmail.com login firstname.lastname@example.org password <password> port imaps machine smtp.gmail.com login email@example.com password <password> port 587
(use-package dictionary :ensure t) (use-package request :ensure t) (use-package transmission :ensure t)
(use-package writegood-mode :ensure t :init (add-hook 'org-mode-hook 'writegood-mode))
Start a server to listen for
(require 'server) (unless (server-running-p) (server-start))
Mutt, for work email.
(add-to-list 'auto-mode-alist '("/mutt" . mail-mode))
(setq tetris-score-file (recker/path 'docs "emacs/tetris-scores"))
These are miscellaneous functions that I’ve written (or plagiarized).
(defun recker/purge-buffers () "Delete all buffers, except for *scratch*." (interactive) (mapc #'(lambda (b) (unless (string= (buffer-name b) "*scratch*") (kill-buffer b))) (buffer-list))) (defun recker/unfill-region (beg end) "Unfill the region, joining text paragraphs into a single logical line." (interactive "*r") (let ((fill-column (point-max))) (fill-region beg end))) (defun recker/org-scratch () "Open a org mode *scratch* pad." (interactive) (switch-to-buffer "*org scratch*") (org-mode) (insert "#+TITLE: Org Scratch\n\n")) (defun recker/sudo (file-name) "find-file, as sudo." (interactive "Fsudo Find file:") (let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name)))) (find-file tramp-file-name))) (defun recker/do-fancy-equal-thingy (beg end) (interactive "r") (align-regexp beg end "\\(\\s-*\\)\\ =" 1 0 t)) (defun recker/encrypt-with-ssh (public-key-path) (interactive "fPublic Key Path: ") (let* ((pem (shell-command-to-string (format "ssh-keygen -f %s -e -m PKCS8" public-key-path))) (secret (read-passwd "Secret String: ")) (encrypt-command (format "openssl rsautl -ssl -encrypt -pubin -inkey <(echo \"%s\") -ssl -in <(echo \"%s\") | base64" pem secret)) (hash (shell-command-to-string encrypt-command)) (decrypt-command (format "echo \"%s\" | base64 -D | openssl rsautl -decrypt -inkey ~/.ssh/id_rsa" hash))) (kill-new decrypt-command nil) (message "Decrypt command added to kill ring."))) (defun recker/send-list-at-point-to-wunderlist () "Sends the org mode list at point to wunderlist. Any item not already captured in wunderlist (by title) is added." (interactive) (let* ((these-items (if (member (first (org-element-at-point)) '(plain-list item)) (mapcar #'(lambda (i) (first i)) (cdr (org-list-to-lisp))) (error "pointer not on a list"))) (headers `(("Content-Type" . "application/json") ("X-Access-Token" . ,(recker/pass-to-string "wundercron/client-secret")) ("X-Client-ID" . ,(recker/pass-to-string "wundercron/client-id")))) (url "https://a.wunderlist.com/api/v1") (list-name "groceries") ;TODO: completing-read? (list-obj (seq-find #'(lambda (i) (string-equal list-name (cdr (assoc 'title i)))) (request-response-data (request (concat url "/lists") :sync t :parser 'json-read :headers headers)))) (list-id (cdr (assoc 'id list-obj))) (current-items (mapcar #'(lambda (o) (cdr (assoc 'title o))) (request-response-data (request (concat url "/tasks") :sync t :parser 'json-read :headers headers :params `(("list_id" . ,list-id)))))) (new-items (or (remove-if #'(lambda (i) (member (format "%s" i) current-items)) these-items) (error "nothing to add!")))) (dolist (item new-items) (request (concat url "/tasks") :parser 'json-read :headers headers :type "POST" :data (json-encode-alist `(("list_id" . ,list-id) ("title" . ,(format "%s" item)))))) (message "Added to groceries: %s" new-items))) (defun recker/docs-sync () (interactive) (save-window-excursion (async-shell-command (format "git-sync -d %s" (recker/path 'docs)) nil nil))) (defun find-first-non-ascii-char () "Find the first non-ascii character from point onwards." (interactive) (let (point) (save-excursion (setq point (catch 'non-ascii (while (not (eobp)) (or (eq (char-charset (following-char)) 'ascii) (throw 'non-ascii (point))) (forward-char 1))))) (if point (goto-char point) (message "No non-ascii characters.")))) (defun recker/today () "Open today's journal entry." (interactive) (let ((target (recker/path 'src (format-time-string "blog/_posts/%Y-%m-%d-%Y-%m-%d.html.md")))) (find-file target)))
(global-set-key (kbd "C-c b") 'browse-url) (global-set-key (kbd "C-c d") 'recker/docs-sync) (global-set-key (kbd "C-c l") 'sort-lines) (global-set-key (kbd "C-c n") 'recker/org-scratch) (global-set-key (kbd "C-c r") 'replace-string) (global-set-key (kbd "C-c t") 'recker/today) (global-set-key (kbd "C-c w") 'recker/send-list-at-point-to-wunderlist) (global-set-key (kbd "C-x C-k k") 'kill-buffer) (global-set-key (kbd "C-x P") 'recker/purge-buffers) (global-set-key (kbd "C-x k") 'kill-this-buffer) (global-set-key (kbd "C-x p") 'recker/pass-to-clip) (global-set-key (kbd "C-x w") 'recker/pass-to-clip-work) (global-set-key (kbd "C-x |") 'recker/do-fancy-equal-thingy)
Emacs sometimes dumps things in
init.el. It means well, but I would
rather this be in a different file ignored by git.
(let ((custom (recker/path 'emacs "custom.el"))) (unless (file-exists-p custom) (with-temp-buffer (write-file custom))) (setq custom-file custom))
I also like to keep a file around for miscellaneous elisp that should run on startup. This is for machine specific settings or things I am still tinkering with.
(let ((local (recker/path 'emacs "local.el"))) (unless (file-exists-p local) (with-temp-buffer (insert ";; This file is for local changes") (write-file local))) (load local))