nix-config/home/gburd/features/emacs/burd.org

1051 lines
36 KiB
Org Mode
Raw Normal View History

2023-09-24 18:09:05 +00:00
#+TITLE: Gregory Burd's Emacs 24 Configuration
#+AUTHOR: Gregory Burd
#+EMAIL: greg@burd.me
#+OPTIONS: toc:3 num:nil
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="http://thomasf.github.io/solarized-css/solarized-light.min.css" />
* Configuration
Emacs is a special beast. Taming it takes a lot of care. In an
attempt to document/explain/share with the rest of the world, this
is my attempt at configuration as a literate program. It also shows
off the awesome power of org-mode, which makes all of this possible.
** User details
:PROPERTIES:
:CUSTOM_ID: user-info
:END:
Emacs will normally pick this up automatically, but this way I can
be sure the right information is always present.
#+begin_src emacs-lisp
(setq user-full-name "Gregory Burd")
(setq user-mail-address "greg@burd.me")
#+end_src
** Environment
:PROPERTIES:
:CUSTOM_ID: environment
:END:
There are plenty of things installed outside of the default
=PATH=. This allows me to establish additional =PATH= information. At
the moment, the only things that added are =/usr/local/bin= for
homebrew on OS X and =.cabal/bin= for [[http://www.haskell.org/cabal/][Haskell package binaries]].
Emacs lisp is really only a subset of common lisp, and I need to
have some of the additional functionality to make the configuration
and its dependencies work properly, which we get by requiring
[[http://www.emacswiki.org/emacs/CommonLispForEmacs][Common Lisp for Emacs]].
#+begin_src emacs-lisp
;(setq debug-on-error t)
;(setq debug-on-signal t)
(setenv "PATH" (concat "/usr/local/bin:/opt/local/bin:/usr/bin:/bin" (getenv "PATH")))
(require 'cl-lib)
#+end_src
*** Define default packages
:PROPERTIES:
:CUSTOM_ID: default-packages
:END:
This is the list of packages used in this configuration.
#+begin_src emacs-lisp
(setq burd-packages '(
; clojure-mode
; clojure-test-mode
; csharp-mode
; nrepl
ac-slime
ag
2023-09-24 18:19:52 +00:00
; auto-complete
; autopair
2023-09-24 18:09:05 +00:00
cc-guess
cc-mode
cmake-mode
coffee-mode
company
company-c-headers
company-cmake
deft
2023-09-24 18:19:52 +00:00
; dockerfile-mode
2023-09-24 18:09:05 +00:00
elixir-mix
elixir-mode
eredis
erlang
feature-mode
flx
flx-ido
flx-isearch
flycheck
flycheck-google-cpplint
flycheck-ocaml
flycheck-pos-tip
flycheck-rust
gh
gist
git-commit
go-mode
graphviz-dot-mode
haml-mode
haskell-mode
htmlize
intellij-theme
lua-mode
magit
markdown-mode
marmalade
multi-web-mode
nix-mode
nodejs-repl
o-blog
org
paredit
pastebin
php-mode
puppet-mode
python-mode
python-pep8
restclient
rust-mode
rvm
scala-mode
smex
sml-mode
solarized-theme
toml-mode
web-mode
writegood-mode
ws-butler
yaml-mode))
#+end_src
** Package Management
:PROPERTIES:
:CUSTOM_ID: package-management
:END:
Since Emacs 24, Emacs includes the Emacs Lisp Package Archive
([[http://www.emacswiki.org/emacs/ELPA][ELPA]]) by default. This provides a nice way to install additional
packages. Since the default package archive doesn't include
everything necessary, the [[http://marmalade-repo.org/][marmalade]], and [[http://melpa.milkbox.net/#][melpa]] repositories are
also added.
#+begin_src emacs-lisp
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/"))
(add-to-list 'package-archives
'("org" . "http://orgmode.org/elpa/"))
;; install any packages in burd-packages, if they are not installed already
(let ((refreshed nil))
(when (not package-archive-contents)
(package-refresh-contents)
(setq refreshed t))
(dolist (pkg burd-packages)
(when (and (not (package-installed-p pkg))
(assoc pkg package-archive-contents))
(unless refreshed
(package-refresh-contents)
(setq refreshed t))
(package-install pkg))))
(defun package-list-unaccounted-packages ()
"Like `package-list-packages', but shows only the packages that
are installed and are not in `burd-packages'. Useful for
cleaning out unwanted packages."
(interactive)
(package-show-package-list
(remove-if-not (lambda (x) (and (not (memq x burd-packages))
(not (package-built-in-p x))
(package-installed-p x)))
(mapcar 'car package-archive-contents))))
#+end_src
** Start-up options
:PROPERTIES:
:CUSTOM_ID: start-up-options
:END:
*** Splash Screen
:PROPERTIES:
:CUSTOM_ID: splash-screen
:END:
I want to skip straight to the scratch buffer. This turns off the
splash screen and puts me straight into the scratch buffer. I
don't really care to have anything in there either, so turn off
the message while we're at it. Since I end up using =org-mode=
most of the time, set the default mode accordingly.
#+begin_src emacs-lisp
(setq inhibit-splash-screen t
initial-scratch-message nil
initial-major-mode 'org-mode)
#+end_src
*** Scroll bar, Tool bar, Menu bar
:PROPERTIES:
:CUSTOM_ID: menu-bars
:END:
Emacs starts up with way too much enabled. Turn off the scroll
bar, menu bar, and tool bar. There isn't really a reason to have
them on.
#+begin_src emacs-lisp
(when window-system
(scroll-bar-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1))
#+end_src
*** Marking text
:PROPERTIES:
:CUSTOM_ID: regions
:END:
There are some behaviors in Emacs that aren't intuitive. Since I
pair with others that don't know how Emacs handles highlighting,
treat regions like other text editors. This means typing when the
mark is active will write over the marked region. Also, make the
common highlighting keystrokes work the way most people expect
them to. This saves a lot of time explaining how to highlight
areas of text. Emacs also has it's own clipboard and doesn't
respond to the system clipboard by default, so tell Emacs that
we're all friends and can get along.
#+begin_src emacs-lisp
(delete-selection-mode t)
(transient-mark-mode t)
(setq x-select-enable-clipboard t)
#+end_src
*** Display Settings
:PROPERTIES:
:CUSTOM_ID: buffers
:END:
I have some modifications to the default display. First, a minor
tweak to the frame title. It's also nice to be able to see when a
file actually ends. This will put empty line markers into the left
hand side.
#+begin_src emacs-lisp
(when window-system
(setq frame-title-format '(buffer-file-name "%f" ("%b")))
(set-face-attribute 'default nil
:family "Fira Code"
:height 134
:weight 'normal
:width 'normal)
(when (functionp 'set-fontset-font)
(set-fontset-font "fontset-default"
'unicode
(font-spec :family "DejaVu Sans Mono"
:width 'normal
:size 12.4
:weight 'normal))))
(setq-default indicate-empty-lines t)
(setq-default fill-column 80)
; (setq-default auto-fill-mode nil)
(require 'newcomment)
(setq comment-auto-fill-only-comments 1)
(setq-default auto-fill-function 'do-auto-fill)
(when (not indicate-empty-lines)
(toggle-indicate-empty-lines))
#+end_src
*** Font Ligature
:PROPERTIES:
:CUSTOM_ID: ligature
:END:
Modern fonts such as Fira Code and PragmataPro provide ligatures for common useful
programming constructs (for example -> to ⟶ or lambda to λ).
https://github.com/tonsky/FiraCode/wiki/Emacs-instructions
https://emacs.stackexchange.com/questions/9586/otf-ligature-support-in-emacs
https://www.reddit.com/r/emacs/comments/4sm6fa/how_to_enable_pragmatapro_ligatures/
#+begin_src emacs-lisp
(cl-defun fira-code-mode--make-alist (list)
"Generate prettify-symbols alist from LIST."
(let ((idx -1))
(mapcar
(lambda (s)
(setq idx (1+ idx))
(let* ((code (+ #Xe100 idx))
(width (string-width s))
(prefix ())
(suffix '(?\s (Br . Br)))
(n 1))
(while (< n width)
(setq prefix (append prefix '(?\s (Br . Bl))))
(setq n (1+ n)))
(cons s (append prefix suffix (list (decode-char 'ucs code))))))
list)))
(defconst fira-code-mode--ligatures
'("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\"
"{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}"
"--" "---" "-->" "->" "->>" "-<" "-<<" "-~"
"#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_("
".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*"
"/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||="
"|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "=="
"===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">="
">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>"
"<$" "<$>" "<!--" "<-" "<--" "<->" "<+" "<+>" "<="
"<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" "<~"
"<~~" "</" "</>" "~@" "~-" "~=" "~>" "~~" "~~>" "%%"
"x" ":" "+" "+" "*"))
(defvar fira-code-mode--old-prettify-alist)
(cl-defun fira-code-mode--enable ()
"Enable Fira Code ligatures in current buffer."
(setq-local fira-code-mode--old-prettify-alist prettify-symbols-alist)
(setq-local prettify-symbols-alist (append (fira-code-mode--make-alist fira-code-mode--ligatures) fira-code-mode--old-prettify-alist))
(prettify-symbols-mode t))
(cl-defun fira-code-mode--disable ()
"Disable Fira Code ligatures in current buffer."
(setq-local prettify-symbols-alist fira-code-mode--old-prettify-alist)
(prettify-symbols-mode -1))
(define-minor-mode fira-code-mode
"Fira Code ligatures minor mode"
:lighter " Fira Code"
(setq-local prettify-symbols-unprettify-at-point 'right-edge)
(if fira-code-mode
(fira-code-mode--enable)
(fira-code-mode--disable)))
(cl-defun fira-code-mode--setup ()
"Setup Fira Code Symbols"
(set-fontset-font t '(#Xe100 . #Xe16f) "Fira Code Symbol"))
(provide 'fira-code-mode)
#+end_src
*** Indentation
:PROPERTIES:
:CUSTOM_ID: indentation
:END:
There's nothing I dislike more than tabs in my files. Make sure I
don't share that discomfort with others.
#+begin_src emacs-lisp
(setq tab-width 4
indent-tabs-mode nil)
#+end_src
*** Backup files
:PROPERTIES:
:CUSTOM_ID: backup-files
:END:
Some people like to have them. I don't. Rather than pushing them
to a folder, never to be used, just turn the whole thing off.
#+begin_src emacs-lisp
(setq make-backup-files nil)
#+end_src
*** Yes and No
:PROPERTIES:
:CUSTOM_ID: yes-and-no
:END:
Nobody likes to have to type out the full yes or no when Emacs
asks. Which it does often. Make it one character.
#+begin_src emacs-lisp
(defalias 'yes-or-no-p 'y-or-n-p)
#+end_src
*** Key bindings
:PROPERTIES:
:CUSTOM_ID: key-bindings
:END:
Miscellaneous key binding stuff that doesn't fit anywhere else.
#+begin_src emacs-lisp
(global-set-key (kbd "RET") 'newline-and-indent)
(global-set-key (kbd "C-;") 'comment-or-uncomment-region)
(global-set-key (kbd "M-/") 'hippie-expand)
(global-set-key (kbd "C-+") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
(global-set-key (kbd "C-c C-k") 'compile)
(global-set-key (kbd "C-x g") 'magit-status)
(if (eq system-type 'darwin)
(progn
(setq mac-option-modifier 'meta)))
#+end_src
*** Misc
:PROPERTIES:
:CUSTOM_ID: misc
:END:
Turn down the time to echo keystrokes so I don't have to wait
around for things to happen. Dialog boxes are also a bit annoying,
so just have Emacs use the echo area for everything. Beeping is
for robots, and I am not a robot. Use a visual indicator instead
of making horrible noises. Oh, and always highlight parentheses. A
person could go insane without that. Finally, Magit's behaviour
changed, let's ack that change and prevent an potentially bad
outcome.
#+begin_src emacs-lisp
(setq echo-keystrokes 0.1
use-dialog-box nil
visible-bell t)
(show-paren-mode t)
(setq magit-auto-revert-mode nil)
(setq magit-last-seen-setup-instructions "1.4.0")
#+end_src
*** Vendor directory
:PROPERTIES:
:CUSTOM_ID: vendor-directory
:END:
I have a couple of things that don't come from package
managers. This includes the directory for use.
#+begin_src emacs-lisp
(defvar burd/vendor-dir (expand-file-name "vendor" user-emacs-directory))
(add-to-list 'load-path burd/vendor-dir)
(dolist (project (directory-files burd/vendor-dir t "\\w+"))
(when (file-directory-p project)
(add-to-list 'load-path project)))
#+end_src
** Org
:PROPERTIES:
:CUSTOM_ID: org-mode
:END:
=org-mode= is one of the most powerful and amazing features of
Emacs. I mostly use it for task/day organization and generating
code snippets in HTML. Just a few tweaks here to make the
experience better.
*** Settings
:PROPERTIES:
:CUSTOM_ID: org-mode-settings
:END:
Enable logging when tasks are complete. This puts a time-stamp on
the completed task. Since I usually am doing quite a few things at
once, I added the =INPROGRESS= keyword and made the color
blue. Finally, enable =flyspell-mode= and =writegood-mode= when
=org-mode= is active.
#+begin_src emacs-lisp
(setq org-log-done t
org-todo-keywords '((sequence "TODO" "INPROGRESS" "DONE"))
org-todo-keyword-faces '(("INPROGRESS" . (:foreground "blue" :weight bold))))
(add-hook 'org-mode-hook
(lambda ()
(flyspell-mode)))
(add-hook 'org-mode-hook
(lambda ()
(writegood-mode)))
#+end_src
*** org-agenda
:PROPERTIES:
:CUSTOM_ID: org-agenda
:END:
First, create the global binding for =org-agenda=. This allows it
to be quickly accessed. The agenda view requires that org files be
added to it. The =personal.org= and =groupon.org= files are my
daily files for review. I have a habit to plan the next day. I do
this by assessing my calendar and my list of todo items. If a todo
item is already scheduled or has a deadline, don't show it in the
global todo list.
#+begin_src emacs-lisp
(global-set-key (kbd "C-c a") 'org-agenda)
(setq org-agenda-show-log t
org-agenda-todo-ignore-scheduled t
org-agenda-todo-ignore-deadlines t)
(setq org-agenda-files (list "~/Dropbox/org/personal.org"
"~/Dropbox/org/agenda.org"))
#+end_src
*** org-habit
:PROPERTIES:
:CUSTOM_ID: org-habit
:END:
I have severial habits that I also track. In order to take full
advantage of this feature =org-habit= has to be required and added
to =org-modules=. A few settings are also tweaked for habit mode to
make the tracking a little more palatable. The most significant of
these is =org-habit-graph-column=. This specifies where the graph
should start. The default is too low and cuts off a lot, so I start
it at 80 characters.
#+begin_src emacs-lisp
;; (require 'org)
;; (require 'org-loaddefs)
;; (require 'org-habit)
;; (add-to-list 'org-modules "org-habit")
;; (setq org-habit-preceding-days 7
;; org-habit-following-days 1
;; org-habit-graph-column 80
;; org-habit-show-habits-only-for-today t
;; org-habit-show-all-today t)
#+end_src
*** org-babel
:PROPERTIES:
:CUSTOM_ID: org-babel
:END:
=org-babel= is a feature inside of =org-mode= that makes this
document possible. It allows for embedding languages inside of an
=org-mode= document with all the proper font-locking. It also
allows you to extract and execute code. It isn't aware of
=Clojure= by default, so the following sets that up.
#+begin_src emacs-lisp
(require 'ob)
(org-babel-do-load-languages
'org-babel-load-languages
'((shell . t)
(ditaa . t)
(plantuml . t)
(dot . t)
(ruby . t)
(js . t)
(C . t)))
(add-to-list 'org-src-lang-modes (quote ("dot". graphviz-dot)))
(add-to-list 'org-src-lang-modes (quote ("plantuml" . fundamental)))
(add-to-list 'org-babel-tangle-lang-exts '("clojure" . "clj"))
(defvar org-babel-default-header-args:clojure
'((:results . "silent") (:tangle . "yes")))
(cl-defun org-babel-execute:clojure (body params)
(lisp-eval-string body)
"Done!")
(provide 'ob-clojure)
(setq org-src-fontify-natively t
org-confirm-babel-evaluate nil)
(add-hook 'org-babel-after-execute-hook (lambda ()
(condition-case nil
(org-display-inline-images)
(error nil)))
'append)
#+end_src
*** org-abbrev
:PROPERTIES:
:CUSTOM_ID: org-abbrev
:END:
#+begin_src emacs-lisp
(add-hook 'org-mode-hook (lambda () (abbrev-mode 1)))
(define-skeleton skel-org-block-elisp
"Insert an emacs-lisp block"
""
"#+begin_src emacs-lisp\n"
_ - \n
"#+end_src\n")
(define-abbrev org-mode-abbrev-table "elsrc" "" 'skel-org-block-elisp)
(define-skeleton skel-org-block-js
"Insert a JavaScript block"
""
"#+begin_src js\n"
_ - \n
"#+end_src\n")
(define-abbrev org-mode-abbrev-table "jssrc" "" 'skel-org-block-js)
(define-skeleton skel-header-block
"Creates my default header"
""
"#+TITLE: " str "\n"
"#+AUTHOR: Greg Burd\n"
"#+EMAIL: greg@burd.me\n"
"#+OPTIONS: toc:3 num:nil\n"
"#+STYLE: <link rel=\"stylesheet\" type=\"text/css\" href=\"http://thomasf.github.io/solarized-css/solarized-light.min.css\" />\n")
(define-abbrev org-mode-abbrev-table "sheader" "" 'skel-header-block)
(define-skeleton skel-org-html-file-name
"Insert an HTML snippet to reference the file by name"
""
"#+HTML: <strong><i>"str"</i></strong>")
(define-abbrev org-mode-abbrev-table "fname" "" 'skel-org-html-file-name)
(define-skeleton skel-ngx-config
"Template for NGINX module config file"
""
"ngx_addon_name=ngx_http_" str "_module\n"
"HTTP_MODULES=\"$HTTP_MODULES ngx_http_" str "_module\"\n"
"NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_" str "_module.c\"")
(define-abbrev fundamental-mode-abbrev-table "ngxcnf" "" 'skel-ngx-config)
(define-skeleton skel-ngx-module
"Template for NGINX modules"
""
"#include <nginx.h>\n"
"#include <ngx_config.h>\n"
"#include <ngx_core.h>\n"
"#include <ngx_http.h>\n\n"
"ngx_module_t ngx_http_" str "_module;\n\n"
"static ngx_int_t\n"
"ngx_http_" str "_handler(ngx_http_request_t *r)\n"
"{\n"
>"if (r->main->internal) {\n"
>"return NGX_DECLINED;\n"
"}" > \n
\n
>"ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"My new module\");\n\n"
> _ \n
>"return NGX_OK;\n"
"}" > "\n\n"
"static ngx_int_t\n"
"ngx_http_"str"_init(ngx_conf_t *cf)\n"
"{\n"
>"ngx_http_handler_pt *h;\n"
>"ngx_http_core_main_conf_t *cmcf;\n\n"
>"cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n"
>"h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);\n\n"
>"if (h == NULL) {\n"
>"return NGX_ERROR;\n"
"}" > \n
\n
>"*h = ngx_http_"str"_handler;\n\n"
>"return NGX_OK;\n"
"}" > \n
\n
"static ngx_http_module_t ngx_http_"str"_module_ctx = {\n"
>"NULL, /* preconfiguration */\n"
>"ngx_http_"str"_init, /* postconfiguration */\n"
>"NULL, /* create main configuration */\n"
>"NULL, /* init main configuration */\n"
>"NULL, /* create server configuration */\n"
>"NULL, /* merge server configuration */\n"
>"NULL, /* create location configuration */\n"
>"NULL /* merge location configuration */\n"
"};" > \n
\n
"ngx_module_t ngx_http_"str"_module = {\n"
>"NGX_MODULE_V1,\n"
>"&ngx_http_"str"_module_ctx, /* module context */\n"
>"NULL, /* module directives */\n"
>"NGX_HTTP_MODULE, /* module type */\n"
>"NULL, /* init master */\n"
>"NULL, /* init module */\n"
>"NULL, /* init process */\n"
>"NULL, /* init thread */\n"
>"NULL, /* exit thread */\n"
>"NULL, /* exit process */\n"
>"NULL, /* exit master */\n"
>"NGX_MODULE_V1_PADDING\n"
"};" >)
(require 'cc-mode)
(define-abbrev c-mode-abbrev-table "ngxmod" "" 'skel-ngx-module)
(define-skeleton skel-ngx-append-header
"Template for header appending function for NGINX modules"
""
"static void append_header(ngx_http_request_t *r)\n"
"{\n"
> "ngx_table_elt_t *h;\n"
> "h = ngx_list_push(&r->headers_out.headers);\n"
> "h->hash = 1;\n"
> "ngx_str_set(&h->key, \"X-NGINX-Hello\");\n"
> "ngx_str_set(&h->value, \"Hello NGINX!\");\n"
"}\n")
(define-abbrev c-mode-abbrev-table "ngxhdr" "" 'skel-ngx-append-header)
#+end_src
** Utilities
*** ditaa
:PROPERTIES:
:CUSTOM_ID: ditaa
:END:
There's no substitute for real drawings, but it's nice to be able
to sketch things out and produce a picture right from
=org-mode=. This sets up =ditaa= for execution from inside a babel
block.
#+begin_src emacs-lisp
(setq org-ditaa-jar-path "~/.emacs.d/vendor/ditaa0_9.jar")
#+end_src
*** plantuml
:PROPERTIES:
:CUSTOM_ID: plantuml
:END:
#+begin_src emacs-lisp
(setq org-plantuml-jar-path "~/.emacs.d/vendor/plantuml.jar")
#+end_src
*** deft
=deft= provides random note taking with history and
searching. Since I use =org-mode= for everything else, I turn that
on as the default mode for =deft= and put the files in Dropbox.
#+begin_src emacs-lisp
(setq deft-directory "~/Dropbox/deft")
(setq deft-use-filename-as-title t)
(setq deft-extension "org")
(setq deft-text-mode 'org-mode)
#+end_src
*** Smex
=smex= is a necessity. It provides history and searching on top of =M-x=.
#+begin_src emacs-lisp
(setq smex-save-file (expand-file-name ".smex-items" user-emacs-directory))
(smex-initialize)
(global-set-key (kbd "M-x") 'smex)
(global-set-key (kbd "M-X") 'smex-major-mode-commands)
#+end_src
*** Ido
=Ido= mode provides a nice way to navigate the filesystem. This is
mostly just turning it on.
#+begin_src emacs-lisp
(ido-mode t)
(setq ido-enable-flex-matching t
ido-use-virtual-buffers t)
#+end_src
*** Column number mode
Turn on column numbers.
#+begin_src emacs-lisp
(setq column-number-mode t)
#+end_src
*** Temporary file management
Deal with temporary files. I don't care about them and this makes
them go away.
#+begin_src emacs-lisp
(setq backup-directory-alist `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))
#+end_src
*** autopair-mode
This makes sure that brace structures =(), [], {}=, etc. are closed
as soon as the opening character is typed.
#+begin_src emacs-lisp
2023-09-24 18:19:52 +00:00
; (require 'autopair)
2023-09-24 18:09:05 +00:00
#+end_src
*** Power lisp
A bunch of tweaks for programming in LISP dialects. It defines the
modes that I want to apply these hooks to. To add more just add
them to =lisp-modes=. This also creates its own minor mode to
properly capture the behavior. It remaps some keys to make paredit
work a little easier as well. It also sets =clisp= as the default
lisp program and =racket= as the default scheme program.
#+begin_src emacs-lisp
(setq lisp-modes '(lisp-mode
emacs-lisp-mode
common-lisp-mode
scheme-mode
clojure-mode))
(defvar lisp-power-map (make-keymap))
(define-minor-mode lisp-power-mode "Fix keybindings; add power."
:lighter " (power)"
:keymap lisp-power-map
(paredit-mode t))
(define-key lisp-power-map [delete] 'paredit-forward-delete)
(define-key lisp-power-map [backspace] 'paredit-backward-delete)
(cl-defun burd/engage-lisp-power ()
(lisp-power-mode t))
(dolist (mode lisp-modes)
(add-hook (intern (format "%s-hook" mode))
#'burd/engage-lisp-power))
(setq inferior-lisp-program "clisp")
(setq scheme-program-name "racket")
#+end_src
*** auto-complete
Turn on auto complete.
#+begin_src emacs-lisp
2023-09-24 18:19:52 +00:00
; (require 'auto-complete-config)
; (ac-config-default)
2023-09-24 18:09:05 +00:00
#+end_src
*** Indentation and buffer cleanup
This re-indents, untabifies, and cleans up whitespace. It is stolen
directly from the emacs-starter-kit.
#+begin_src emacs-lisp
(cl-defun untabify-buffer ()
(interactive)
(untabify (point-min) (point-max)))
(cl-defun indent-buffer ()
(interactive)
(indent-region (point-min) (point-max)))
(cl-defun cleanup-buffer ()
"Perform a bunch of operations on the whitespace content of a buffer."
(interactive)
(indent-buffer)
(untabify-buffer)
(delete-trailing-whitespace))
(cl-defun cleanup-region (beg end)
"Remove tmux artifacts from region."
(interactive "r")
(dolist (re '("\\\\│\·*\n" "\W*│\·*"))
(replace-regexp re "" nil beg end)))
(global-set-key (kbd "C-x M-t") 'cleanup-region)
(global-set-key (kbd "C-c n") 'cleanup-buffer)
(setq-default show-trailing-whitespace t)
#+end_src
*** flyspell
The built-in Emacs spell checker. Turn off the welcome flag because
it is annoying and breaks on quite a few systems. Specify the
location of the spell check program so it loads properly.
#+begin_src emacs-lisp
(setq flyspell-issue-welcome-flag nil)
(if (eq system-type 'darwin)
(setq-default ispell-program-name "/usr/local/bin/aspell")
(setq-default ispell-program-name "/usr/bin/aspell"))
(setq-default ispell-list-command "list")
#+end_src
*** multi-web-mode
When editing HTML it's a jumble of languages embedded into a single
file. Emacs can choose the major-mode based on the section of the
file if you enable it.
#+begin_src emacs-lisp
(require 'multi-web-mode)
(setq mweb-default-major-mode 'html-mode)
(setq mweb-tags
'((php-mode "<\\?php\\|<\\? \\|<\\?=" "\\?>")
(js-mode "<script[^>]*>" "</script>")
(css-mode "<style[^>]*>" "</style>")))
(setq mweb-filename-extensions '("php" "htm" "html" "ctp" "phtml" "php4" "php5"))
(multi-web-global-mode 1)
#+end_src
** Language Hooks
:PROPERTIES:
:CUSTOM_ID: languages
:END:
*** Erlang Mode
:PROPERTIES:
:CUSTOM_ID: erlang-mode
:END:
#+begin_src emacs-lisp
(add-hook 'erlang-mode-hook
(lambda ()
(setq inferior-erlang-machine-options
'("-sname" "emacs"
"-pz" "ebin deps/*/ebin apps/*/ebin"
"-boot" "start_sasl"))
(imenu-add-to-menubar "imenu")))
#+end_src
*** C/C++ Mode
:PROPERTIES:
:CUSTOM_ID: c-mode
:END:
#+begin_src emacs-lisp
(semantic-mode +1)
(require 'semantic/bovine/gcc)
(flx-ido-mode 1)
(add-hook 'c-mode-hook (lambda () (
(setq flycheck-check-syntax-automatically '(save mode-enabled))
(setq flycheck-standard-error-navigation nil)
;; flycheck errors on a tooltip (doesnt work on console)
(when (display-graphic-p (selected-frame))
(eval-after-load 'flycheck
'(custom-set-variables
'(flycheck-display-errors-function #'flycheck-pos-tip-error-messages)))))))
#+end_src
*** Elixir Mode
:PROPERTIES:
:CUSTOM_ID: elixir-mode
:END:
#+begin_src emacs-lisp
#+end_src
*** GDB/GUD Mode
:PROPERTIES:
:CUSTOM_ID: gdb-mode
:END:
#+begin_src emacs-lisp
(defvar gdb-libtool-command-name "libtool"
"Pathname for executing gdb.")
(cl-defun gdb-libtool (path &optional corefile)
"Run gdb on a libtool program FILE in buffer *gdb-FILE*.
The directory containing FILE becomes the initial working
directory and source-file directory for GDB. If you wish to
change this, use the GDB commands `cd DIR' and `directory'."
(interactive "FRun gdb-libtool on file: ")
(load "gud")
(setq path (file-truename (expand-file-name path)))
(let ((file (file-name-nondirectory path)))
(switch-to-buffer (concat "*gud-" file "*"))
(setq default-directory (file-name-directory path))
(or (bolp) (newline))
(insert "Current directory is " default-directory "\n")
; M-x gud-gdb libtool --mode=execute gdb -fullname ___
(apply 'make-comint
(concat "gud-" file)
(substitute-in-file-name gdb-libtool-command-name)
nil
"--mode=execute"
(substitute-in-file-name gdb-command-name)
"-fullname"
"-cd" default-directory
file
(and corefile (list corefile)))
; (set-process-filter (get-buffer-process (current-buffer)) 'gud-filter)
; (set-process-sentinel (get-buffer-process (current-buffer)) 'gud-sentinel)
;; XEmacs change: turn on gdb mode after setting up the proc filters
;; for the benefit of shell-font.el
(gud-mode)
(gud-set-buffer)))
(setq gdb-show-main t)
#+end_src
*** shell-script-mode
:PROPERTIES:
:CUSTOM_ID: shell-script-mode
:END:
Use =shell-script-mode= for =.zsh= files.
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.zsh$" . shell-script-mode))
#+end_src
*** dockerfile-mode
:PROPERTIES:
:CUSTOM_ID: dockerfile-model
:END:
Use =dockerfile-mode= for =Dockerfile= files.
#+begin_src emacs-lisp
2023-09-24 18:19:52 +00:00
; (require 'dockerfile-mode)
; (add-to-list 'auto-mode-alist '("^Dockerflie$" . dockerfile-mode))
2023-09-24 18:09:05 +00:00
#+end_src
*** make
:PROPERTIES:
:CUSTOM_ID: make-mode
:END:
Use =shell-script-mode= for =.zsh= files.
#+begin_src emacs-lisp
;; http://stackoverflow.com/a/9059906/366692
(cl-defun get-closest-pathname (&optional (max-level 3) (file "Makefile"))
(let ((root (expand-file-name "/"))
(level 0))
(expand-file-name file
(loop
for d = default-directory then (expand-file-name ".." d)
do (setq level (+ level 1))
if (file-exists-p (expand-file-name file d))
return d
if (> level max-level)
return nil
if (equal d root)
return nil))))
(add-hook 'c-mode-hook
(lambda ()
(unless (file-exists-p "Makefile")
(set (make-local-variable 'compile-command)
(let ((file (file-name-nondirectory buffer-file-name))
(mkfile (get-closest-pathname)))
(if mkfile
(progn (format "cd %s; make -f %s"
(file-name-directory mkfile) mkfile))
(format "%s -c -o %s.o %s %s %s"
(or (getenv "CC") "gcc")
(file-name-sans-extension file)
(or (getenv "CPPFLAGS") "-DDEBUG=9")
(or (getenv "CFLAGS") "-ansi -pedantic -Wall -g")
file)))))))
(provide 'make)
#+end_src
*** conf-mode
:PROPERTIES:
:CUSTOM_ID: conf-mode
:END:
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.gitconfig$" . conf-mode))
#+end_src
*** Web Mode
:PROPERTIES:
:CUSTOM_ID: web-mode
:END:
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.hbs$" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb$" . web-mode))
#+end_src
*** YAML
Add additional file extensions that trigger =yaml-mode=.
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.yaml$" . yaml-mode))
#+end_src
*** TOML
Add additional file extensions that trigger =toml-mode=.
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.tml$" . toml-mode))
(add-to-list 'auto-mode-alist '("\\.toml$" . toml-mode))
#+end_src
*** CoffeeScript Mode
The default CoffeeScript mode makes terrible choices. This turns
everything into 2 space indentations and makes it so the mode
functions rather than causing you indentation errors every time you
modify a file.
#+begin_src emacs-lisp
(cl-defun coffee-custom ()
"coffee-mode-hook"
(make-local-variable 'tab-width)
(set 'tab-width 4))
(add-hook 'coffee-mode-hook 'coffee-custom)
#+end_src
*** JavaScript Mode
=js-mode= defaults to using 4 spaces for indentation. Change it to 2
#+begin_src emacs-lisp
(cl-defun js-custom ()
"js-mode-hook"
(setq indent-tabs-mode nil
tab-width 2
js-indent-level 2))
(add-hook 'js-mode-hook 'js-custom)
#+end_src
*** Markdown Mode
Enable Markdown mode and setup additional file extensions. Use
pandoc to generate HTML previews from within the mode, and use a
custom css file to make it a little prettier.
#+begin_src emacs-lisp
(add-to-list 'auto-mode-alist '("\\.md$" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.mdown$" . markdown-mode))
(add-hook 'markdown-mode-hook
(lambda ()
(visual-line-mode t)
(writegood-mode t)
(flyspell-mode t)))
(setq markdown-command "pandoc --smart -f markdown -t html")
(setq markdown-css-paths (expand-file-name "markdown.css" burd/vendor-dir))
#+end_src
*** CPSA Mode
Enable support for Cryptographic Protocol Shapes Analyzer. This is
a scheme-ish dialect, so it's a derived from =scheme-mode=.
#+begin_src emacs-lisp
(define-derived-mode cpsa-mode scheme-mode
(setq mode-name "CPSA")
(setq cpsa-keywords '("defmacro" "defprotocol" "defrole" "defskeleton" "defstrand"))
(setq cpsa-functions '("cat" "hash" "enc" "string" "ltk" "privk" "pubk" "invk" "send" "recv" "non-orig" "uniq-orig" "trace" "vars"))
(setq cpsa-types '("skey" "akey" "name" "text"))
(setq cpsa-keywords-regexp (regexp-opt cpsa-keywords 'words))
(setq cpsa-functions-regexp (regexp-opt cpsa-functions 'words))
(setq cpsa-types-regexp (regexp-opt cpsa-types 'words))
(setq cpsa-font-lock-keywords
`(
(,cpsa-keywords-regexp . font-lock-keyword-face)
(,cpsa-functions-regexp . font-lock-function-name-face)
(,cpsa-types-regexp . font-lock-type-face)))
(setq font-lock-defaults '((cpsa-font-lock-keywords))))
(add-to-list 'auto-mode-alist '("\\.cpsa$" . cpsa-mode))
#+end_src
*** Themes
Load solarized-light if in a graphical environment. Load the
wombat theme if in a terminal.
#+begin_src emacs-lisp
(load-theme 'tsdh-dark t)
; (load-theme 'intellij t)
; (load-theme 'wombat t)
; (load-theme 'solarized-dark t)
;(when window-system
; (load-theme 'intellij t)
; (load-theme 'tsdh-dark t))
#+end_src