This document is my so-called dot-emacs file. Actually, it’s not -
not really. When my emacs instance starts up, it looks for and finds
a file called ~/.emacs.d/init.el
, which it loads and applies. It
could look something like this:
;; Minimal config here. The rest will be in setup.org (package-initialize) ; This needs to be early in config (require 'org) ; Load org so we can use babel (org-babel-load-file ; Load my setup file (expand-file-name "~/.emacs.d/setup.org")) ;; EOF
In reality it’s slightly more convoluted, since we can’t assume that
setup.org
resides in ~/.emacs.d
. But anyway.
It starts by loading the emacs lisp packages, and activating
them. After that, it loads org-mode
and using babel, expands the
document you’re reading now.
This makes it possible for me to write my emacs setup as an org file, interleaving comments like this sentence with actual code; in this case the emacs-lisp code used to set emacs up. Quite nifty, actually.
But I get ahead of myself. My emacs setup uses 5 files, all stored
in the directory ~/.emacs.d
:
init.el
-
The entrypoint to my setup. This file in turn loads
setup.org
. setup.org
-
This document. Contains the vast majority of my emacs configuration.
setup.el
-
When starting up, all the runnable code in
setup.org
is extracted and placed insetup.el
. This is what is actually loaded behind the scenes. I only look at this file when debugging. custom.el
-
Sometimes I’m lazy and I use emacs itself to customise its behaviour. These kinds of customisations end up in this file where its separated from my own code. I set it up in section Auto-customization.
EMACS_PERSONAL_DATA
-
This environment variable points at the file where I put all personal info. The variables are declared in section Declare personal variables.
Before we can go about fixing the fun parts of emacs, we’ll need to set up some basics.
(setq message-log-max
(- (expt 2 15) 1)) ; Make message log big
Set some constants on the environment this instance of emacs is running in.
;; We also have a sys/emacs-root defined in init.el.
;; This points at the directory this file is in.
(defconst sys/hostname
(getenv "HOSTNAME") "This machine's hostname")
(defconst sys/home
(getenv "HOME") "Home directory")
(defconst sys/windowsp
(eq system-type 'windows-nt) "Is this a Windows system?")
(defconst sys/linuxp
(eq system-type 'gnu/linux) "Is this a GNU/Linux system?")
(defconst sys/macp
(eq system-type 'darwin) "Is this a Mac system?")
(defconst sys/cygwinp
(eq system-type 'cygwin) "Is this a Cygwin system?")
(defconst sys/linux-x-p
(and (display-graphic-p) sys/linuxp) "Is this a GNU/Linux system running X?")
(defconst sys/mac-x-p
(and (display-graphic-p) sys/macp) "Is this a Mac system running X?")
Set some variables which I used here and there. These aren’t constants since I might want to change them during runtime.
(setq default-directory (concat sys/home "/"))
;; Sice I'm running with org-babel, the variable 'this-file' does not
;; contain the file filename.org; the file gets tangled into
;; filename.el
(defvar this-file (or load-file-name (buffer-file-name)))
(defvar this-file-org (replace-regexp-in-string ".el" ".org" this-file))
(setenv "PATH" (concat (getenv "PATH") ":/usr/local/bin"))
(setq exec-path (append exec-path '("/usr/local/bin")))
Don’t do automatic file backups in these directories
Note that the specialdir var is intended to be populated with path
prefixes - so if you add /
then emacs won’t make backups
anywhere…
Note to self - should put the specialdirs into custom.el
at some
point.
(defun me/my-backup-enable-predicate (name)
(let (found)
(dolist (specialdir '("~/gdrive/"
"/some/other/directory/") found)
(if (string-prefix-p specialdir name)
(setq found t)))
(if found
nil
(normal-backup-enable-predicate name))))
(setq backup-enable-predicate #'me/my-backup-enable-predicate)
Make really sure that we’re using utf-8
.
I’m so glad I don’t have to fool around with all those old character sets anymore - especially the Japanese ones…
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(prefer-coding-system 'utf-8)
(set-language-environment "UTF-8")
Here are the variables which are private to me, and are assigned in
the file pointed at by the environment variable
EMACS_PERSONAL_DATA
. No, this file is not in my vc.
(defconst me/emacs/personal-data
(getenv "EMACS_PERSONAL_DATA") "Personal info goes here")
(defvar me/fullname nil "My full name.")
(defvar me/nick nil "My nickname.")
(defvar me/mail/credentials nil "Where I store my credentials.")
(defvar me/mail/mydomain nil "My mail domainname.")
(defvar me/mail/smtp-server nil "Hostname.domainname of smtp server.")
(defvar me/mail/signature nil "My email signature.")
(defvar me/erc/server nil "Irc server hostname")
(defvar me/erc/port nil "Irc server port")
(defvar me/erc/nick nil "My nick")
(defvar me/erc/pass nil "My password")
(defvar me/erc/autojoin-alist nil "Association list of channels to join.
For example:
((\"chat.freenode.net\" \"#emacs\" \"#cooking\")
(\"another.server.org\" \"#foo\" \"#bar\" \"#baz\"))
")
(defvar me/erc/pass-query-string nil "How should ERC ask for the password?
Useful if you have multiple servers to connect to.")
;; If me/emacs/personal-data has a value, check if it points at a file. If
;; it exists, then load it.
(when me/emacs/personal-data
(when (file-exists-p me/emacs/personal-data)
(load me/emacs/personal-data)
)
)
This needs to be in place before any configurations of installed packages.
Most of this was copied from @jeekl’s emacs setup. Thanks @jeekl!
;; mkdir these and add them to load path
(dolist (path `( ,(concat sys/emacs-root "elpa/") ;; emacs-lisp package archive
,(concat sys/emacs-root "el-get/") ;; packages from el-get
,(concat sys/emacs-root "vendor/"))) ;; stuff I downloaded myself
(make-directory path t)
(let ((default-directory path))
(normal-top-level-add-subdirs-to-load-path)))
I made a lisp-learning today: In the code above, I need to use a backtick and comma in order to evaluate the functions in the list. It used to look like this:
(dolist (path '( "~/.emacs.d/elpa/" ...
As you can see, it was a list (denoted by the single-quote before the
left-paren) with strings in it. Earlier today, I replaced the full
path to elpa/
with a function concat
which concatenated the path to
the configuration directory with the string elpa/
, like so:
(dolist (path `( ,(concat sys/emacs-root "elpa/") ...
This would help me make the configuration agnostic to where in the
filesystem it was installed - a necessary prerequisite to having a
github action syntax check, where it will be installed in another
place than ~/.emacs.d
.
The backtick is a lispism to make the interpreter selectively evaluate any function prefixed with a comma symbol. Cool!
Oh, and the practice is called Quasi-quotation.
(require 'package)
;; add these sources
(eval-after-load "package"
'(progn
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/"))
(add-to-list 'package-archives '("melpa" . "http://stable.melpa.org/packages/"))
))
(eval-after-load "url-http"
'(setq url-http-attempt-keepalives nil)) ; A package.el bug. Apparently.
If this is a clean install of emacs, then it will update the package list. This updating it from the various sources takes time, we skip it otherwise.
Note to self: If this isn’t a clean install but instead a major
upgrade (like I just did from emacs26 to emacs28) you might need to
call package-refresh-contents
manually once. Or do a M-x
package-list-packages
then push U
to update.
(if (not package-archive-contents)
(package-refresh-contents))
Download these packages if they aren’t already downloaded.
(defvar elpa-packages
'(
;; Emacs theme
gruvbox-theme
;; Other packages
; muttrc-mode
; twittering-mode
adoc-mode
all-the-icons
all-the-icons-dired
all-the-icons-ibuffer
all-the-icons-ivy
; apache-mode
; auctex
blacken
column-marker
company
counsel
counsel-tramp
csv-mode
; diff-hl ;; Much too slow
dired-subtree
; docker
; docker-compose-mode
dockerfile-mode
; docker-tramp
dumb-jump
editorconfig
flycheck
graphviz-dot-mode
htmlize
ibuffer-projectile
iedit ; for renaming symbols
json-mode
js2-mode
load-theme-buffer-local
magit
mpg123
markdown-mode
olivetti
org-bullets
perl-doc
projectile
highlight-parentheses
rainbow-mode
spaceline
spaceline-all-the-icons
ssh-config-mode
swiper
tide
treemacs ; A tree style file explorer package
treemacs-all-the-icons ; all-the-icons integration for treemacs
treemacs-magit ; Magit integration for treemacs
treemacs-projectile ; Projectile integration for treemacs
typescript-mode
which-key
web-mode
yaml-mode
;; Modes for editing chrome textboxes in emacs.
; atomic-chrome
; gmail-message-mode
; edit-server
)
"These packages are installed if necessary."
)
(dolist (pkg elpa-packages)
(when (and (not (package-installed-p pkg))
(assoc pkg package-archive-contents))
(package-install pkg)))
(defun me/package-list-unaccounted-packages ()
"Like `package-list-packages', but shows only the packages that
are installed and are not in `elpa-packages'. Useful for
cleaning out unwanted packages."
(interactive)
(package-show-package-list
(remove-if-not (lambda (x) (and (not (memq x elpa-packages))
(not (package-built-in-p x))
(package-installed-p x)))
(mapcar 'car package-archive-contents))))
Sometimes, the emacs modules aren’t available on melpa, but I have the
source file. When this happens, I place it in my vendor/
folder. All
.el
files are loaded on startup.
(defun me/load-directory (dir)
(let ((load-it (lambda (f)
(load-file (concat (file-name-as-directory dir) f)))
))
(mapc load-it (directory-files dir nil "\\.el$"))))
(me/load-directory (concat sys/emacs-root "vendor/"))
Move all customization stuff to another file.
(setq custom-file (concat sys/emacs-root "custom.el"))
(load custom-file 'noerror)
The emacs server is useful if you use emacs for many things, and you want each session to share buffers and state. Startup time is minimal too.
(require 'server)
(load "server")
(unless (server-running-p) (server-start))
Setting up the User interface so that it works the way I like it.
Configuration basics.
(setq initial-major-mode 'org-mode) ; org-mode for the initial
; *scratch* window
(setq default-major-mode 'org-mode) ; default mode is org-mode
(setq fci-rule-column 80) ; fill column
(setq inhibit-startup-message t) ; no startup message
(setq initial-scratch-message nil) ; no *scratch* message
(setq line-number-mode t) ; show line number
(setq column-number-mode t) ; show current column
(global-font-lock-mode 1) ; syntax highlightning ON
(setq transient-mark-mode t) ; turn on transient-mark-mode
(setq indicate-buffer-boundaries t) ; visually show end of buffer
(setq-default indicate-empty-lines t) ; be even more obvious about it
(setq remove-help-window t) ; kill completion-window when
; leaving minibuffer
(setq insert-default-directory t) ; get default dir in commands
(setq enable-local-variables t) ; enables local variables
(setq compilation-window-height 10) ; height of compilation window.
(setq-default cursor-type 'bar) ; make cursor thin
(tool-bar-mode -1)
(menu-bar-mode -1)
(context-menu-mode 1) ; right-click to get menu
(fringe-mode '(12 . 12)) ; set default fringe
(if (boundp 'scroll-bar-mode) (scroll-bar-mode -1))
;; Look and feel for all programming modes
(require 'linum) ; necessary in newer emacsen
(add-hook 'prog-mode-hook
(lambda ()
(linum-mode 1) ; show line number in margin
(hl-line-mode 1) ; highlight the current line
(show-paren-mode t) ; show matching parens
)
)
The first section above was how emacs presents things to me. This section is how it reacts to some of my commands.
(setq case-fold-search t) ; ignore case in searches
(setq compilation-ask-about-save 0) ; dont ask to save when compiling
(setq apropos-do-all t) ; show all funcs/vars in help
(put 'downcase-region 'disabled nil); allow downcase-region commands
(put 'upcase-region 'disabled nil) ; allow downcase-region commands
(setq next-line-add-newlines t) ; C-n at eob opens new lines.
(setq scroll-step 1) ; Moving cursor down at bottom
; scrolls only a single line
(setq sentence-end-double-space nil); I don't add two spaces after a
; period, this makes M-a and M-e
; work as intended.
Generally, I don’t like programs asking me if I really want to do something I just told it to do. And if it must, I want that interaction to be as non-intrusive as possible.
(defun me/dummy-ring-bell-function () nil) ; replace beep with visible bell
(setq ring-bell-function `me/dummy-ring-bell-function)
(fset 'yes-or-no-p 'y-or-n-p) ; y or n instead of yes or no
(setq confirm-nonexistent-file-or-buffer nil) ; just open new buffers
(setq kill-buffer-query-functions ; dont ask to kill live buffers
(remq 'process-kill-buffer-query-function
kill-buffer-query-functions))
(put 'eval-expression 'disabled nil) ; no confirm on eval-expression
Link X’s primary selection and clipboard to interplay with emacs.
(if sys/linux-x-p
(progn
;; after copy Ctrl+c in Linux X11, you can paste by `yank' in emacs
(setq select-enable-clipboard t)
;; after mouse selection in X11, you can paste by `yank' in emacs
(setq select-enable-primary t)
)
)
Get the mouse to work in emacs instances running in a terminal, and other mouse configuration.
(xterm-mouse-mode t) ; Support mouse in xterms
(setq mouse-wheel-mode t) ; support mouse wheel
(setq mouse-wheel-follow-mouse t) ; scrolls mouse pointer position, not pointer
Get emacs to display time and date.
(display-time)
(setq display-time-day-and-date t)
(setq display-time-24hr-format t)
Set up time and date the way I like it.
;; I want weeks to start on Mondays rather than Sundays in
;; =M-x calendar=.
(setq calendar-week-start-day 1)
(setq calendar-date-style 'iso) ;; yyyy-mm-dd
(setq calendar-date-display-form ;; 13 Jun 2001
'((if dayname
(concat dayname ", "))
day " " monthname " " year))
(setq calendar-time-display-form
'(24-hours ":" minutes))
Generally, get emacs to indent in multiples of 2 or 4 spaces. Also - avoid inserting tabs.
(setq standard-indent 2)
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
(setq tab-width 4)
(setq-default tab-stop-list
(mapcar #'(lambda (x) (* x 4))
(cdr (reverse
(let (value)
(dotimes (number 32 value)
(setq value (cons number value))))))))
(setq perl-continued-brace-offset -2)
(setq perl-continued-statement-offset 2)
(setq perl-indent-level 2)
(setq perl-label-offset -1)
(setq sh-basic-offset 2)
(setq sh-indentation 2)
Apparently loading a theme using load-theme
overlays the new
theme onto whatever was there before. This might be useful at
times, but I find it easier when I get exactly the theme I select.
Anyway, the advice function below makes load-theme
behave the way I
like.
(defadvice load-theme (before clear-previous-themes activate)
"Clear existing theme settings instead of layering them"
(mapc #'disable-theme custom-enabled-themes))
(load-theme 'tsdh-dark)
The default look and feel of the theme I use (tsdh-dark
) has a few
details which I think doesn’t look quite right. Fixing it here.
;; Make background color a bit lighter..
(set-face-attribute 'default nil :background "gray21")
;; .. make the region a light gray
(set-face-attribute 'region nil :extend t :background "gray28")
;; and the highlight face can also be black
(set-face-attribute 'highlight nil :background "black")
;; The highlight color for isearch
(set-face-attribute 'isearch nil
:background "saddle brown"
:foreground "white smoke")
(set-face-attribute 'lazy-highlight nil :background "LightSalmon4")
;; comments in italics
(set-face-attribute font-lock-comment-face nil :slant 'italic)
;; Fringe in the same color as default.
(set-face-attribute 'fringe nil
:foreground "white"
:background "black")
Make the highlighted line a tad darker than the default background.
(eval-after-load "hl-line"
'(set-face-attribute 'hl-line nil :background "grey10"))
For the longest time, I’ve for some reason enjoyed writing much more in traditional word processors like Google Docs, Openoffice, MSWord even if I’ve been an emacs user for decades. I never really understood why until I realised that it had to do with the UI. By changing the font into something with serifs, and writing in the “middle” of the buffer window, I discovered that writing became more enjoyable for me in an emacs environment.
The code block below toggles between prose and code mode.
By the way - to use this without modification you’ll need the font Noto-serif.
(defvar me/write-state "nowrite")
(defvar me/face-cookie nil)
(defun me/write-toggle ()
"Toggles write-state of current buffer.
Write-state defaults to nil, but when activated, does the following:
- Changes the cursor to a short horizontal line
- Changes the font to Noto Serif
- Removes hl-line-mode
- Activates Olivetti-mode
Toggling again reverts the changes."
(interactive)
(if (string= me/write-state "write")
(progn
(message "write-state")
(setq cursor-type 'bar)
(variable-pitch-mode 0)
(face-remap-remove-relative me/face-cookie) ; revert to old face
(hl-line-mode 1)
(olivetti-mode -1)
(setq me/write-state "nowrite"))
(progn
(message "not write-state")
(setq cursor-type '(hbar . 2))
(variable-pitch-mode 1)
(setq me/face-cookie ; when changing face, save old
(face-remap-add-relative ; face in a cookie.
'default
'(:family "Noto Serif")))
(hl-line-mode -1)
(olivetti-mode 1)
(setq me/write-state "write"))))
which-key
is a minor mode which shows the key bindings following any
incomplete key command in a pop-up.
To page between options, hit C-h
, then you can see options in the
mini-buffer.
(require 'which-key)
(setq which-key-idle-delay 1.5)
;; With this option, I can hit ctrl-h to get which-key to activate
;; earlier than the delay timer
(setq which-key-show-early-on-C-h t)
(which-key-mode)
(which-key-setup-side-window-right-bottom)
This is just eye-candy for the most part. But icons in dired and a newer modeline just looks nice.
(require 'all-the-icons)
(add-hook 'dired-mode-hook 'all-the-icons-dired-mode)
;; make modeline a little nicer
(require 'spaceline-config)
; for a slightly fancier theme
(spaceline-all-the-icons-theme)
;; for a simpler but nice theme
;;(spaceline-emacs-theme)
;; add icons to ivy
(all-the-icons-ivy-setup)
The first time you run this on your system, you’ll need to run this command manually:
M-x all-the-icons-install-fonts
In all programming modes, make parenthesis pairs stand out.
(setq highlight-parentheses-colors '("green"
"gold"
"red"
"medium spring green"
"cyan"
"dark orange"
"deep pink"))
(add-hook 'prog-mode-hook 'highlight-parentheses-mode)
(eval-after-load "highlight-parentheses-mode"
'(set-face-attribute 'highlight-parentheses-highlight nil :weight bold))
How emacs interacts with the world outside of it.
(setq tramp-default-method "ssh")
(setq browse-url-browser-function 'browse-url-chromium)
;; use correct browser on Mac
(if sys/macp
(setq browse-url-browser-function 'browse-url-default-macosx-browser)
)
;; Use correct 'ls' program regardless of systemm
(setq ls-lisp-use-insert-directory-program
(if sys/macp "gls" ; mac
(if sys/linuxp "ls" ; linux
nil ; win or other
)))
;; Send the --dired flag to ls if it is supported
(setq dired-use-ls-dired "unspecified")
;; make scripts executable if they aren't already
(add-hook 'after-save-hook
'executable-make-buffer-file-executable-if-script-p)
Auto-set mode for these file suffixes.
(setq auto-mode-alist
(append
(list
'("Dockerfile" . dockerfile-mode )
'("\\.md" . markdown-mode )
'("\\.xml" . xml-mode )
'("\\.pp" . puppet-mode )
'("\\.html" . html-mode )
'("\\.xsl" . xml-mode )
'("\\.cmd" . cmd-mode )
'("\\.bat" . cmd-mode )
'("\\.wiki" . wikipedia-mode )
'("\\.org.txt" . org-mode )
'("\\.txt" . indented-text-mode )
'("\\.php" . php-html-helper-mode )
'("\\.fvwm2rc" . shell-script-mode )
'("tmp/mutt-" . message-mode )
'("\\.org" . org-mode )
'("\\.asciidoc" . adoc-mode )
'("\\.pm" . cperl-mode )
'("\\.pl" . cperl-mode ))
auto-mode-alist))
;; and ignore these suffixes when expanding
(setq completion-ignored-extensions
'(".o" ".elc" ".class" "java~" ".ps" ".abs" ".mx" ".~jv" ))
The above works if you only look at the file suffix - but after
loading, emacs will look at the first line of the file (if
appropriate) and see if there is a hashbang specifying an
interpreter. If that interpreter is in interpreter-mode-alist
, it
will use the mode specified there.
Since perl-mode
is the default, Perl scripts starting with the
line #!/bin/bin/perl
will be associated with that despite
the instructions in auto-mode-alist
, so we need to add the
mapping (perl . cperl-mode)
in the interpreter-mode-alist
.
(add-to-list 'interpreter-mode-alist '("perl" . cperl-mode))
I want to be able to insert the em-dash and the en-dash symbols in my writing.
(defun me/insert-em-dash ()
"Insert an em-dash"
(interactive)
(insert "—"))
(defun me/insert-en-dash ()
"Insert an em-dash"
(interactive)
(insert "–"))
In python, emacs-lisp and org-mode, replace all instances of the string ‘lambda’ with the character λ.
Not only is this pretty, it saves some space on the screen :)
;; courtesy of stefan monnier on c.l.l
(defun sm-lambda-mode-hook ()
(font-lock-add-keywords
nil `(("\\<lambda\\>"
(0 (progn (compose-region (match-beginning 0) (match-end 0)
,(make-char 'greek-iso8859-7 107))
nil))))))
(add-hook 'python-mode-hook 'sm-lambda-mode-hook)
(add-hook 'emacs-lisp-mode-hook 'sm-lambda-mode-hook)
(add-hook 'org-mode-hook 'sm-lambda-mode-hook)
A nifty tool which enables me to edit text areas in google chrome inside of an emacs frame. To get this to work, make sure you install the Atomic-chrome extension for Google chrome. Apparently there’s another extension you could use for firefox too.
;; (require 'atomic-chrome)
;; (atomic-chrome-start-server)
;; (setq atomic-chrome-buffer-open-style 'frame)
;; (setq atomic-chrome-extension-type-list '(atomic-chrome))
;;(setq atomic-chrome-default-major-mode 'markdown-mode)
Comint-mode
is essential for emacs to interact with another
process - like the shell, or a database user interface (sqsh, isql,
etc).
(ansi-color-for-comint-mode-on) ; interpret and use ansi color codes
; in shell output windows
(custom-set-variables
'(comint-scroll-to-bottom-on-input t) ; always insert at the bottom
'(comint-scroll-to-bottom-on-output t) ; always add output at the bottom
'(comint-scroll-show-maximum-output t) ; scroll to show max possible output
'(comint-completion-autolist t) ; show completion list when ambiguous
'(comint-input-ignoredups t) ; no duplicates in command history
'(comint-completion-addsuffix t) ; insert space/slash after file completion
)
This is my initial setup of company-mode, which lets me get a nice auto-completion thing going when writing code.
(defun me/setup-company-mode ()
(company-mode)
(setq company-idle-delay 1) ; small delay when proposing suggestions
(setq company-minimum-prefix-length 2) ; two characters before suggesting
(setq company-selection-wrap-around t) ; make suggestion list a ring
(setq company-tooltip-align-annotations t) ; aligns annotation to the right hand side
(set-face-attribute 'company-scrollbar-bg nil :background "#000000")
(set-face-attribute 'company-scrollbar-fg nil :background "#332222")
(set-face-attribute 'company-tooltip nil
:foreground (face-foreground 'default)
:background "grey10")
(set-face-attribute 'company-tooltip-common nil ; the text I entered, common to all tips
:inherit font-lock-constant-face
)
(set-face-attribute 'company-tooltip-selection nil ; the part which is selected
:inherit font-lock-function-name-face
:foreground "light salmon")
)
(add-hook 'prog-mode-hook 'me/setup-company-mode)
(require 'color)
Cua-mode is normally used to make emacs act more like Windows (control-c to copy, etc). I use a subset so that I can use Cua-mode’s nice rectangle functions in addition to the normal ones.
Cua’s global-mark is really cool. This is what it says in the manual:
CUA mode also has a global mark feature which allows easy moving and copying of text between buffers. Use C-S-<SPC> to toggle the global mark on and off. When the global mark is on, all text that you kill or copy is automatically inserted at the global mark, and text you type is inserted at the global mark rather than at the current position.
Really useful for copying text from one buffer to another.
(cua-mode t)
(setq cua-enable-cua-keys nil) ; go with cua, but without c-x/v/c et al
(setq shift-select-mode nil) ; do not select text when moving with shift.
(setq cua-delete-selection nil) ; dont kill selections on keypress
(setq cua-enable-cursor-indications t) ; customize cursor color
(setq cua-normal-cursor-color "white")
;; if Buffer is...
;;(setq cua-normal-cursor-color "#15FF00") ; R/W, then cursor is green
;;(setq cua-read-only-cursor-color "purple1") ; R/O, then cursor is purple
;;(setq cua-overwrite-cursor-color "red") ; in Overwrite mode, cursor is red
;;(setq cua-global-mark-cursor-color "yellow") ; in Global mark mode, cursor is yellow
In dired-mode, show directories first, then regular files. Dotfiles
before non-dotfiles. Also, open dired-mode in the simple
view. Toggle between simple and detailed view using (
.
For more keybindings, see Dired keybindings.
(setq dired-listing-switches "-aFhlv --group-directories-first")
(add-hook 'dired-mode-hook 'dired-hide-details-mode)
(add-hook 'dired-mode-hook 'toggle-truncate-lines)
This function makes it easy to toggle between showing dotfiles and
hiding them. I bound it in a section a bit further below to .
.
(defvar me/dired-dotfiles-shown t "helper var for dired-dotfiles-toggle function." )
(defun me/dired-dotfiles-toggle ()
"Toggle for displaying or hiding hidden files."
(interactive)
(setq me/dired-dotfiles-shown
(if me/dired-dotfiles-shown
(progn
(dired-sort-other "-Fhlv --group-directories-first")
nil)
(progn
(dired-sort-other "-aFhlv --group-directories-first")
t)
)
)
)
So many worthless date formats. ISO 8601 simplifies things.
(setq ls-lisp-format-time-list '("%Y-%m-%d %H:%M" "%Y-%m-%d %H:%M")
ls-lisp-use-localized-time-format t)
Enable changing permissions and creating directories using a /
in
the filename in writable dired-mode (wdired).
By the way, use C-x C-q
to enter wdired, and C-c C-c
to exit.
(setq wdired-allow-to-change-permissions t)
(setq wdired-create-parent-directories t)
I don’t use IRC as much nowadays, but used this config when I did.
;; set a max-size to a irc buffer...
(setq erc-max-buffer-size 20000)
;; Make erc prompt show channelname.
(setq erc-prompt
(lambda ()
(if (and (boundp 'erc-default-recipients) (erc-default-target))
(erc-propertize (concat (erc-default-target) ">")
'read-only t 'rear-nonsticky t 'front-nonsticky t)
(erc-propertize (concat "ERC>")
'read-only t 'rear-nonsticky t 'front-nonsticky t))))
(defun me/start-irc ()
"Connect to IRC."
(interactive)
(require 'erc)
(erc-ssl
:server me/erc/server
:port me/erc/port
:nick me/erc/nick
:password me/erc/pass ; (read-passwd me/erc/pass-query-string)
:full-name me/fullname)
(setq erc-autojoin-channels-alist me/erc/autojoin-alist)
)
Enable support for .editorconfig files as specified by editorconfig.org.
(require 'editorconfig)
(add-hook 'prog-mode-hook 'editorconfig-mode)
Once enabled, emacs looks for a .editorconfig
file (typically at the
root of a project directory) and applies the rules for all buffers
belonging to said project. Oh and afaict it’s buffer-local, so that’s
nice.
root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true max_line_length = 120 tab_width = 4 trim_trailing_whitespace = true
I’ve been using flymake
for decades, but this one seems
better. It’s smarter about where it shows the errors/warnings for
one, and feels faster than flymake
.
When troubleshooting flycheck
, flycheck-verify-setup
is a useful
command.
(add-hook 'after-init-hook #'global-flycheck-mode)
(with-eval-after-load 'flycheck
(setq-local flycheck-checker 'c/c++-gcc)
(setq flycheck-c/c++-gcc-executable "gcc")
(setq-default flycheck-disabled-checkers '(c/c++-clang))
(add-hook 'c-mode-common-hook (lambda () (setq flycheck-checker 'c/c++-gcc)))
(when sys/macp
(setq flycheck-gcc-include-path '("/opt/homebrew/include")))
)
Dependencies: Perl::Critic::
(cpan)
(setq flycheck-perl-include-path '("." "lib") )
;; Perl critic levels of severity:
;; 1 - brutal
;; 2 - cruel
;; 3 - harsh
;; 4 - stern
;; 5 - gentle
(setq flycheck-perlcritic-severity 5)
Dependencies: None
;; Use load-path's path for flycheck
(setq-default flycheck-emacs-lisp-load-path 'inherit)
;; Get rid of annoying checkdoc warnings
(with-eval-after-load 'flycheck
(setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc)))
Dependencies: shellcheck
(homebrew, apt, etc)
To get shellcheck to correctly check sourced bash files, make sure to
add the #!/bin/bash
shebang.
(add-hook 'sh-mode-hook 'flycheck-mode)
(with-eval-after-load 'flycheck
(setq flycheck-checker 'sh-shellcheck))
Spell-checker for emacs.
(setq ispell-program-name "aspell")
(setq flyspell-mark-duplications-flag nil)
(setq flyspell-consider-dash-as-word-delimiter-flag t)
A nice list-buffer replacement.
(require 'ibuffer)
(add-hook 'ibuffer-hook #'all-the-icons-ibuffer-mode)
(add-hook 'ibuffer-hook
(lambda ()
(ibuffer-projectile-set-filter-groups)
(unless (eq ibuffer-sorting-mode 'alphabetic)
(ibuffer-do-sort-by-alphabetic))))
(require 'iedit)
(add-hook 'longlines-mode-hook
(lambda()
(auto-fill-mode -1)
(longlines-show-hard-newlines)))
Let Emacs become aware of software projects. What this means in practice right now is that it looks up the directory hierarchy towards root, looking for a VC root of some kind, and sets the project there.
;; auto-load projectile upon startup
(add-hook 'after-init-hook #'projectile-mode)
;; set projectile keybindings on load of projectile
(add-hook 'projectile-mode-hook
(lambda()
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
))
There’s a lot to learn here - and integrations to other modes to configure. For example:
- ivy
- magit
- ibuffer
For about six months, I tried Ido-mode and icomplete-mode, and somehow they often made me feel more frustrated than helped. I was introduced to Swiper and friends at an emacs-meetup, and will give it a try for a while.
For keybindings, see: Swiper/Ivy/Counsel keybindings
(add-hook 'ivy-mode-hook
(lambda()
(setq ivy-use-virtual-buffers t
enable-recursive-minibuffers t
ivy-count-format "%d/%d ")
(set-face-attribute 'ivy-current-match nil
:background "black")
(set-face-attribute 'ivy-minibuffer-match-face-1 nil
:background "black"
:foreground "#887733")
(set-face-attribute 'ivy-minibuffer-match-face-2 nil
:background "grey10"
:foreground "#887733")
(set-face-attribute 'ivy-minibuffer-match-face-3 nil
:background "grey18"
:foreground "#887733")
(set-face-attribute 'ivy-minibuffer-match-highlight nil
:background "black"
:slant 'italic)
)
)
(ivy-mode 1)
Since Ivy, Counsel and Swiper always come together, their individual functionality always confuses me a bit. Here’s what they do:
- Ivy
- This is a completion framework. Given a list, it will
present them and let you limit what is shown and ultimately select
one item from the list.
Here’s an example from the docs. Evaluate the following code:
(ivy-read "My buffers: " (mapcar #'buffer-name (buffer-list)))
- Counsel
- A library which provides things to choose from. Like
counsel-find-file
orcounsel-describe-function
, which in turn uses Ivy to present the alternatives to me. - Swiper
- A tool to search through the current buffer, replacing
built-in functions like
isearch-forward
.
When mob-programming over video, it’s easier for others to follow where I am if I have a sidebar showing where I am like in many other editors. Giving treemacs a try.
There’s many, many more options to modify should I want to, and they can be found here.
(require 'treemacs)
(setq treemacs-hide-dot-git-directory nil
treemacs-recenter-after-file-follow t
treemacs-recenter-after-tag-follow t
)
(treemacs-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode 'always)
(when treemacs-python-executable (treemacs-git-commit-diff-mode t))
(pcase (cons (not (null (executable-find "git")))
(not (null treemacs-python-executable)))
(`(t . t)
(treemacs-git-mode 'deferred))
(`(t . _)
(treemacs-git-mode 'simple)))
(treemacs-hide-gitignored-files-mode nil)
It’s the little things. Quickly, I got really tired of how invoking treemacs-mode moved the cursor (and window focus) away from whatever I was doing and into the treemacs window. I understand that normally, the reason for opening treemacs was to be able to navigate the tree, and that having the cursor auto-moved there accomplishes this. But in my case, I want it open mostly for my colleagues - as a hint of where I am in the project space.
So this little function changes the default behaviour to keep the focus in place.
(defun me/toggle-treemacs ()
"Invoking treemacs moves the window selection to the treemacs
window and away from whatever I was doing. I want the sidebar to
just appear without any other changes."
(interactive)
(let ((old-win (selected-window)))
(treemacs)
(select-window old-win)
)
)
Treemacs keybindings can be found here: Treemacs keybindings.
Make it easy to set margin on visual-line-mode regardless of frame size.
(defvar visual-wrap-column nil)
(defun set-visual-wrap-column (new-wrap-column &optional buffer)
"Force visual line wrap at NEW-WRAP-COLUMN in BUFFER (defaults
to current buffer) by setting the right-hand margin on every
window that displays BUFFER. A value of NIL or 0 for
NEW-WRAP-COLUMN disables this behavior."
(interactive (list (read-number "New visual wrap column, 0 to disable: "
(or visual-wrap-column fill-column 0))))
(if (and (numberp new-wrap-column)
(zerop new-wrap-column))
(setq new-wrap-column nil))
(with-current-buffer (or buffer (current-buffer))
(visual-line-mode t)
(set (make-local-variable 'visual-wrap-column) new-wrap-column)
(add-hook 'window-configuration-change-hook 'update-visual-wrap-column nil t)
(let ((windows (get-buffer-window-list)))
(while windows
(when (window-live-p (car windows))
(with-selected-window (car windows)
(update-visual-wrap-column)))
(setq windows (cdr windows))))))
(defun update-visual-wrap-column ()
(if (not visual-wrap-column)
(set-window-margins nil nil)
(let* ((current-margins (window-margins))
(right-margin (or (cdr current-margins) 0))
(current-width (window-width))
(current-available (+ current-width right-margin)))
(if (<= current-available visual-wrap-column)
(set-window-margins nil (car current-margins))
(set-window-margins nil (car current-margins)
(- current-available visual-wrap-column))))))
First some settings to get mail to work.
(require 'smtpmail)
(require 'gnutls)
;;(setq smtpmail-auth-credentials '(("smtp.gmail.com" 25 "USERNAME" "PASSWORD")))
;;(setq smtpmail-debug-info t)
(setq message-send-mail-function 'smtpmail-send-it)
(setq send-mail-function 'smtpmail-send-it)
(setq smtpmail-debug-info t)
(setq mail-host-address me/mail/mydomain)
(setq smtpmail-local-domain me/mail/mydomain)
(setq smtpmail-sendto-domain me/mail/mydomain)
(setq smtpmail-smtp-server me/mail/smtp-server)
(setq smtpmail-auth-credentials me/mail/credentials)
(setq smtpmail-smtp-service 587)
(setq smtpmail-warn-about-unknown-extensions t)
(setq starttls-extra-arguments nil)
(setq starttls-use-gnutls t)
(setq user-full-name me/fullname)
(setq mail-default-headers
(concat
"CC:\n"
"BCC:\n"
"X-RefLink: http://tinyurl.com/bprfeg\n"
"User-Agent: " (mapconcat 'identity (cl-subseq (split-string (emacs-version) " ") 0 3) " ") "\n"
))
(setq mail-signature me/mail/signature)
Oh and before I forget - when I flub my password, use the following to drop all credentials.
M-x auth-source-forget-all-cached
A hook to set things up nicely for mutt.
For keybindings, see Mail keybindings.
(defun me/mutt-mode-hook ()
(visual-line-mode)
(orgstruct-mode)
)
(add-hook 'message-mode-hook 'me/mutt-mode-hook)
For reading asciidoc files.
(add-hook 'adoc-mode-hook
(lambda()
(auto-fill-mode -1)
(visual-line-mode)))
(add-hook 'python-mode-hook
(lambda()
(cond ((eq buffer-file-number nil)
(progn (interactive)
(goto-line 1)
(insert "#!/usr/bin/env python\n")
(insert "# -*- tab-width: 4 -*-\n")
)))))
(add-hook 'python-mode-hook 'blacken-mode)
A decade or so ago, I manually edited dns zone files a lot, and I made frequent use of the $INCLUDE directive - meaning most dns zone files didn’t have a SOA post to increment. This resulted in an error when saving.
I wrote this piece of advice to avoid this problem.
(defadvice dns-mode-soa-maybe-increment-serial (before maybe-set-increment)
"if there is a dns soa post, increment it. Otherwise, just save"
(save-excursion
(beginning-of-buffer)
(message "dns-mode-soa-auto-increment-serial %s"
(setq dns-mode-soa-auto-increment-serial
(and (search-forward-regexp "IN[ ''\t'']+SOA" nil t)
(not (search-forward-regexp "@SERIAL@" nil t)))
)
)
)
)
(ad-activate 'dns-mode-soa-maybe-increment-serial)
I love org-mode, even if I only use a fraction of its capabilities.
(org-insert-structure-template)
-
Insert structure block with shortcut C-c C-, (like code, comment, etc). If region is selected, the structure will wrap around it.
(org-edit-special)
-
If in a structure block, C-c C-’ spawns a separate window for editing the contents of the block. If code structure block, will set the right major mode. Exit using the same C-c C-’ key sequence.
;; Basic config of Org
(require 'org)
(setq org-startup-indented t)
(setq org-log-done 'time)
(add-hook 'org-mode-hook
(lambda ()
(visual-line-mode)
(flyspell-mode)
(org-bullets-mode)
(auto-fill-mode -1)))
;; My notes
(setq org-directory (concat sys/home "/notes/"))
(make-directory org-directory 1)
(setq org-default-notes-file (concat org-directory "/notes.org"))
;; Editing code in Org
(setq org-edit-src-content-indentation 2)
(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)
;; (setq org-fontify-whole-heading-line t)
;; (defun org-font-lock-ensure () ; This is apparently a bugfix. (?)
;; (font-lock-fontify-buffer))
Change how Org shows bullets - nice UTF-8 bullets instead of stars.
(setq org-hide-leading-stars t) ; remove leading stars in org-mode
(setq org-bullets t) ; activate said pretty bullets
Make the org-blocks (the code where the emacs-lisp is) a darker colour than the default.
(set-face-attribute 'org-block-begin-line nil :background "grey10")
(set-face-attribute 'org-block nil :background "grey16")
(set-face-attribute 'org-block-end-line nil :background "grey10")
(org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))
With the above configuration, I can run shell commands in my org-mode
files. All examples below prefixed with a .
to force the interpreter
of this doc to not evaluate it…
.#+BEGIN_SRC shell :results output :exports results . echo hello .#+END_SRC
… or even this:
.#+BEGIN_SRC shell :results output :exports results . ping -c 1 ping.sunet.se .#+END_SRC
To execute the code, move the cursor into the block and hit C-c
. It
will ask you if you really want to do this, then the output (marked
with #RESULTS:
) will be placed immediately below the shell block.
.#+BEGIN_SRC shell :results output :exports results . ping -c 1 ping.sunet.se .#+END_SRC . .#+RESULTS: .: PING ping.sunet.se (192.36.125.18): 56 data bytes .: 64 bytes from 192.36.125.18: icmp_seq=0 ttl=251 time=16.815 ms .: .: --- ping.sunet.se ping statistics --- .: 1 packets transmitted, 1 packets received, 0.0% packet loss .: round-trip min/avg/max/stddev = 16.815/16.815/16.815/0.000 ms
See Tide-mode for Javascript or Tide-mode for JSX for more configuration stuff.
(add-hook 'js-mode-hook 'js2-minor-mode)
(defun me/java-mode-hook ()
(c-add-style
"my-java"
'("java"
(c-basic-offset . 2)))
(c-set-style "my-java"))
(add-hook 'java-mode-hook 'me/java-mode-hook)
(defun insert-c-function-doc-comment ()
"Insert a C function-style doc comment and position the cursor."
(interactive)
(insert "/**\n")
(insert " * \n")
(insert " */")
(forward-line -1)
(move-end-of-line nil))
For keybindings, see Perl keybindings.
Generally, I like how indentation is done in cperl, but one thing which drives me nuts is when cperl decides that my one-line code like:
if ( $a == 1 ) { print "hello"; }
Should be spaced out in the traditional way:
if ( $a == 1 ) { print "hello"; }
Of course I understand that one-liners can be jarring, but it’s useful at times. And I really don’t want my editor to join the invisible throngs of people who have opinions about my coding style.
So let’s kill this behaviour before it gets out of hand.
(setq cperl-break-one-line-blocks-when-indent nil)
It’s useful to auto-indent when I press semicolon.
(setq cperl-autoindent-on-semi t)
Cperl-mode has more useful features than plain Perl-mode. Since Perl-mode is autoloaded when opening files with perl suffixes, we begin below by replacing perl-mode with cperl-mode.
I mentioned lots of useful features right? To turn most of them on,
set cperl-hairy
to t
. But this turns all of the bells and
whistles on, so instead I activate only the stuff I want.
cperl-electric-parens
-
Setting this to
t
, I get auto-complete of the following paired symbols:({[]})
and in special cases, like in the following code, the<>
too. cperl-electric-keywords
-
If set to
t
some keywords get auto-expanded. E.g.if
,while
,for
,unless
,until
. cperl-electric-linefeed
-
If set to
t
, hittingC-j
inside of, say, the inner conditional parens will place the cursor inside the curly brackets with the right indentation.(defalias 'perl-mode 'cperl-mode) ;;(setq cperl-hairy t) (setq cperl-electric-parens nil) (setq cperl-electric-keywords nil) (setq cperl-electric-linefeed t)
Next, a cperl hook to set some stuff up.
First, load =flymake-mode= when cperl is started.I’m giving flycheck a chance for a while…- Next,
cperl-mode
has quite aggressive syntax-highlighting, and its face for arrays and hashes are kind of ugly. Here I change it so it’s slanted, unbolded and coloured.
(add-hook 'cperl-mode-hook
(lambda () (progn
;;(flymake-mode)
(flycheck-mode)
(set-face-italic 'cperl-array-face t)
(set-face-bold 'cperl-array-face nil)
(set-face-foreground 'cperl-array-face "yellow")
(set-face-background 'cperl-array-face nil)
(set-face-italic 'cperl-hash-face t)
(set-face-bold 'cperl-hash-face nil)
(set-face-foreground 'cperl-hash-face "red")
(set-face-background 'cperl-hash-face nil)
)
)
)
I’ll be re-using this function to initialise tide-mode with the configuration I want in the following sections.
(defun setup-tide-mode ()
(interactive)
(tide-setup)
(flycheck-mode 1) ; enable flycheck
(setq flycheck-check-syntax-automatically '(save mode-enabled))
(eldoc-mode 1) ; show language item at point in the echo area
(tide-hl-identifier-mode 1) ; highlight identical strings
(company-mode 1) ; turn on company mode
)
;; Associate typescript-mode with .ts files
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode))
;; When starting up typescript-mode:
(add-hook 'typescript-mode-hook #'setup-tide-mode)
(add-hook 'typescript-mode-hook
(lambda ()
(setq company-idle-delay 0.5)
(local-set-key (kbd "C-M-,") 'me/insert-fat-comma)
;; I noticed that if I don't add editorconfig-apply after
;; setup-tide-mode, the editorconfig rules of the project
;; are overruled by what tide-mode thinks is good-looking
;; code. Very annoying.
(editorconfig-apply)
))
;; formats the buffer before saving
(add-hook 'before-save-hook 'tide-format-before-save)
(add-hook 'js-mode-hook #'setup-tide-mode)
;; configure javascript-tide checker to run after your default javascript checker
;(flycheck-add-next-checker 'javascript-eslint 'javascript-tide 'append)
(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.jsx\\'" . web-mode))
(add-hook 'web-mode-hook
(lambda ()
;; configure jsx-tide checker to run after your default jsx checker
(flycheck-add-mode 'javascript-eslint 'web-mode)
(flycheck-add-next-checker 'javascript-eslint 'jsx-tide 'append)
(when (string-equal "jsx" (file-name-extension buffer-file-name))
(setup-tide-mode))))
(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . web-mode))
(add-hook 'web-mode-hook
(lambda ()
;; enable typescript-tslint checker
(flycheck-add-mode 'typescript-tslint 'web-mode)
(when (string-equal "tsx" (file-name-extension buffer-file-name))
(setup-tide-mode))))
Finally, a bunch of small hooks for various modes.
(add-hook 'css-mode-hook 'hexcolour-add-to-font-lock)
(add-hook 'html-helper-mode-hook 'hexcolour-add-to-font-lock)
(add-hook 'html-mode-hook 'hexcolour-add-to-font-lock)
(add-hook 'text-mode-hook 'visual-line-mode)
Here’s a bunch of functions, some of them written by me, most by other people.
Create a reasonable titlebar for emacs, which works on both windows
and unix. Note: assumes HOSTNAME
is exported.
(defun me/create-title-format (user host)
"Creates a window title string which works for both win and unix"
(interactive)
(list
"<Emacs> " (getenv user) "@" (getenv host) ":"
'(:eval
(if buffer-file-name
(replace-regexp-in-string
sys/home
"~"
(buffer-file-name))
(buffer-name))))
)
;; Set window and icon title.
(if (eq system-type 'windows-nt)
(setq frame-title-format (me/create-title-format "USERNAME" "COMPUTERNAME"))
(setq frame-title-format (me/create-title-format "USER" "HOSTNAME")))
This function has been really useful for me, since I often find myself wanting to jot something down in some trash buffer.
(defun me/switch-to-scratch ()
"Switch to scratch buffer. Create one in `org-mode' if not exists."
(interactive)
(let ((previous (get-buffer "*scratch*")))
(switch-to-buffer "*scratch*")
;; don't change current mode
(unless previous (org-mode))))
Until lately, my emacs configuration was in
~/.emacs-stuff/dot.emacs.el
which I symlinked to from
~/.emacs.el
. Up until then (1992-2018), this function pointed at
this file, which was opened upon invocation. Since switching to
literal emacs configuration using org-babel
, I’ve modified it a
bit so that it opens ~/.emacs.d
and moves the pointer to
setup.org
, which I open most often.
The function name isn’t really correct anymore since it actually doesn’t open the file, but call me melodramatic - this name reminds me of those other times. :)
(defun me/open-dot-emacs ()
"Opens my main emacs configuration file."
(interactive)
(find-file sys/emacs-root)
(end-of-buffer)
(search-backward (concat (file-name-base this-file-org)
(file-name-extension this-file-org t)))
)
Ansi-term, when invoked, normally starts by asking which shell I
want. Since I go with /bin/bash
, and I can have multiple
ansi-term sessions running simultaneously on different machines or
for different purposes, I replaced the query for what shell I want
with a name for the ansi-term buffer.
(defun me/ansi-term()
"Starts an ansi-term with optional buffer name"
(interactive)
(let (string)
(setq string
(read-from-minibuffer
"Enter terminal buffer name: "
"ansi-term"))
(ansi-term "/bin/bash" string)
)
)
I found this function at emacswiki.org, helping me jump to important symbols in my buffer.
(defun ido-goto-symbol (&optional symbol-list)
"Refresh imenu and jump to a place in the buffer using Ido."
(interactive)
(unless (featurep 'imenu)
(require 'imenu nil t))
(cond
((not symbol-list)
(let ((ido-mode ido-mode)
(ido-enable-flex-matching
(if (boundp 'ido-enable-flex-matching)
ido-enable-flex-matching t))
name-and-pos symbol-names position)
(unless ido-mode
(ido-mode 1)
(setq ido-enable-flex-matching t))
(while (progn
(imenu--cleanup)
(setq imenu--index-alist nil)
(ido-goto-symbol (imenu--make-index-alist))
(setq selected-symbol
(ido-completing-read "Symbol? " symbol-names))
(string= (car imenu--rescan-item) selected-symbol)))
(unless (and (boundp 'mark-active) mark-active)
(push-mark nil t nil))
(setq position (cdr (assoc selected-symbol name-and-pos)))
(cond
((overlayp position)
(goto-char (overlay-start position)))
(t
(goto-char position)))))
((listp symbol-list)
(dolist (symbol symbol-list)
(let (name position)
(cond
((and (listp symbol) (imenu--subalist-p symbol))
(ido-goto-symbol symbol))
((listp symbol)
(setq name (car symbol))
(setq position (cdr symbol)))
((stringp symbol)
(setq name symbol)
(setq position
(get-text-property 1 'org-imenu-marker symbol))))
(unless (or (null position) (null name)
(string= (car imenu--rescan-item) name))
(add-to-list 'symbol-names name)
(add-to-list 'name-and-pos (cons name position))))))))
The functions me/generate-ptr-records and me/sort-A-records were really useful for me back when I managed Spotify’s DNS manually in the bad-old-days (which were in fact really good old days despite having to deal with our chaos that was DNS :))
(defun me/generate-ptr-records (start-pos end-pos)
"Finds DNS A-records in region, and for each one, creates a PTR
record in a temporary buffer.
The PTR posts are sorted into sections by domainname.
If no region was set, finds all A-records from point to end of
buffer."
(interactive "r")
(let (origin ; to make the hostname a fqdn
rgx ; ugly regex matching an A-record
hostname ; one hostname
ip ; one IPv4 address
oct-list ; each IPv4 octet in a list
first-octets ; 'aaa.bbb.ccc'
last-octet ; 'ddd'
comment ; optional comment, if any
ptr-rec ; one generated PTR record
list-of-ptr-recs ; PTR records with first 3 octets in common
ptr-hash ; key first 3 octets, value list-of-ptr-recs
)
;; if no region was set, work from point to end-of-buffer.
(setq end-pos (if (= (point) (mark)) (end-of-buffer)))
;; Bring point to beginning of region if selection was made from
;; upper part of the buffer to the end.
(if (> (point) (mark)) (exchange-point-and-mark))
;; Pads string to three chars
(defun pad-octet (octet)
(if (= (length octet) 3)
octet
(pad-octet (concat octet " "))))
;; Read Origin from minibuffer
(setq origin
(read-from-minibuffer
"Enter $ORIGIN: "
(chomp (shell-command-to-string (concat "hostname -d")))))
(setq origin (if (string= (substring origin -1) ".") ; make fqdn
origin ; if not fqdn
(concat origin ".")))
;; Regexp matching an A-record with optional comment
(setq rgx
(concat
;; hostname part
"^\\([[:alnum:]\.-]+\\)"
".*?"
;; followed by A
"[ ''\t'']A[ ''\t'']+"
".*?"
;; followed by (very) loose definition of an ip address
"\\([[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\\)"
;; followed by an optional comment
".*?\\(;.*?\\)?$"))
;; Walk through region, picking up all A-records and putting them
;; into a hash, using first three octets as key
(setq ptr-hash (make-hash-table :test 'equal))
(while (search-forward-regexp rgx end-pos 1)
(setq hostname (match-string 1))
(setq ip (match-string 2))
(setq comment (if (null (match-string 3)) "" (match-string 3)))
(setq oct-list (split-string ip "\\."))
(setq first-octets (mapconcat
(lambda (x) x)
(nreverse (cons "IN-ADDR.ARPA." (butlast oct-list 1)))
"."))
(setq last-octet (nth 3 oct-list))
;; create a PTR record
(setq ptr-rec (concat (pad-octet last-octet)
" IN PTR "
hostname "." origin
" " comment))
;; put the PTR record into the correct list
(setq list-of-ptr-recs (gethash first-octets ptr-hash))
(setq list-of-ptr-recs
(if (null list-of-ptr-recs)
(list ptr-rec)
(cons ptr-rec list-of-ptr-recs)))
;; put the list
(puthash first-octets list-of-ptr-recs ptr-hash)
)
(with-output-to-temp-buffer "ptr-records"
(maphash
(lambda (k v)
(princ (format "\n$ORIGIN %s\n" k))
(setq v (sort v (lambda (a b)
(< (string-to-number (car (split-string a " ")))
(string-to-number (car (split-string b " ")))))))
(while (not (null v))
(princ (format "%s\n" (pop v)))
)
)
ptr-hash)
)
)
)
(defun me/sort-A-records (start-pos end-pos)
"Given a DNS buffer containing a bunch of A-records, this
function finds all records inside a region and sorts them by ip
address. The output is placed in a temporary buffer called
'sorted-ips'.
Todo someday: support the GENERATE directive"
(interactive "r")
;; --------------------------------------------------
;; Helper functions
(defun eq-octet (a b index)
(= (string-to-number (nth index a))
(string-to-number (nth index b))))
(defun lt-octet (a b index)
(< (string-to-number (nth index a))
(string-to-number (nth index b))))
(defun sort-hash-by-ip (hashtable)
(let (mylist)
(setq mylist ;; Create a list of ip-hostname pairs
(let (mylist)
(maphash
(lambda (kk vv)
(setq mylist (cons (list kk vv) mylist))) hashtable)
mylist
))
(sort mylist ;; sort them by ip
(lambda (y z)
(setq y (split-string (car y) "\\."))
(setq z (split-string (car z) "\\."))
(if (eq-octet y z 0)
(if (eq-octet y z 1)
(if (eq-octet y z 2)
(lt-octet y z 3)
(lt-octet y z 2))
(lt-octet y z 1))
(lt-octet y z 0))
)
)
)
)
;; --------------------------------------------------
;; Main body starts here
(let (iphash)
;; create hash
(setq iphash (make-hash-table :test 'equal))
;; if no region selected, just grab all A-records from point.
(setq end-pos (if (= (point) (mark)) (end-of-buffer)))
(if (> (point) (mark)) (exchange-point-and-mark))
(while (search-forward-regexp
"^\\([[:alnum:]\.-]+\\).*?[ ''\t'']A[ ''\t'']+.*?\\([[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\\)" end-pos 1)
(puthash (match-string 2) (match-string 1) iphash)
)
(with-output-to-temp-buffer "sorted-ips"
(let (item mylist)
(setq mylist (sort-hash-by-ip iphash))
(while (setq item (pop mylist))
(princ (format "%s\t%s\n" (car item) (cadr item)))
)
)
)
)
)
Gmail messed everything up.
Prior to 2009, I had my own mail server, synced all my mail to my local machine using offline. I read email using mutt-ng and composed email in emacs. Often, I also sent email directly from emacs.
This worked flawlessly for me - I configured everything just the way I wanted it, and it was sweeeet. Mass mailing a long list of people with payloads which were all slightly different? No problem. Using GPG for people who understood what it was, but not others? Simple. Emailing someone a IRC transcript or code with just a few keystrokes? Wonderfully quick.
Then came Gmail. With lots of storage. And a powerful search engine. And how they almost email threads to work quite well (but not as well as in mutt). And how they used some vim and emacs navigation keybindings. And all of this without having to worry about maintaining my mail server…
Ultimately, I couldn’t resist the change. I moved everything to Google, and though I’m still concerned about my privacy, convenience is.. well, convenient.
So.
These functions are from before 2009, and I’m not 100% sure that bitrot hasn’t set in.
(defun me/random-quote ()
"Gets a random quote"
(load "fimblo-quotes" nil t)
(aref fimblo-quotes
(random (- (length fimblo-quotes) 1)))
)
(defun me/generate-sig ()
(with-temp-buffer
(insert (me/random-quote))
(goto-char (point-min))
(fill-paragraph)
(insert (concat
mail-signature
"\n\n"))
(goto-char (point-min))
;; (while (re-search-forward "^" nil t) (replace-match " "))
;; (goto-char (point-min))
;; (insert "\n-- \n")
(buffer-string)
)
)
(defun me/kill-signature ()
"Delete current sig"
(interactive)
(end-of-buffer)
(if (search-backward-regexp "^-- $" nil t )
(progn
(beginning-of-line)
(setq start (point))
(end-of-buffer)
(delete-region start (point))))
)
(defun me/message-replace-sig ()
"Replaces signature with new sig"
(interactive)
(me/kill-signature)
(end-of-buffer)
(delete-char -1)
(insert (me/generate-sig))
)
(defun me/kill-to-signature ()
"Delete all text between text and signature."
(interactive)
(setq start (point))
(end-of-buffer)
(search-backward-regexp "^-- $" nil 1)
(previous-line)
(setq end (point))
(delete-region start end)
(recenter-top-bottom)
(insert "\n\n\n")
(previous-line 2)
)
(defun me/mail-snip (b e summ)
"remove selected lines, and replace it with [snip:summary (n lines)]"
(interactive "r\nsSummary: ")
(let ((n (count-lines b e)))
(delete-region b e)
(insert (format "\n[snip%s (%d line%s)]\n\n"
(if (= 0 (length summ)) "" (concat ": " summ))
n
(if (= 1 n) "" "s")))))
Here’s a bunch of small functions which help me modify text in different ways.
First off, some interactive functions to convert strings into
camelCase. I bound it to M-s-c
over in Buffer manipulation.
(defun me/upper-camelcase-region (beg end)
"Convert region to upper camelCase."
(interactive "r")
(let ((str (buffer-substring-no-properties beg end)))
(delete-region beg end)
(insert (mapconcat 'capitalize (split-string str) ""))))
(defun me/lower-camelcase-region (beg end)
"Convert region to lower camelCase."
(interactive "r")
(let* ((str (buffer-substring-no-properties beg end))
(words (split-string str))
(first-word (car words))
(rest-words (cdr words)))
(delete-region beg end)
(insert (concat (downcase first-word)
(mapconcat 'capitalize rest-words "")))))
(defun me/camelcase-region (beg end &optional upperCamelCase)
"Transform words in region to camelCase.
If the universal argument is given, transform words instead
to upperCamelCase."
(interactive "rP")
(if (equal current-prefix-arg '(4))
(me/upper-camelcase-region beg end)
(me/lower-camelcase-region beg end)))
Then the rest which I’ve written some time in the past.
(defun me/insert-fat-comma ()
"Inserts a ' => ' at point.
Used in Perl and Javascript."
(interactive)
(insert " => ")
)
(defun me/merge-lines ()
"Make paragraph I am in right now into one line."
(interactive)
(let (p)
(forward-paragraph)
(setq p (point))
(backward-paragraph)
(next-line)
(while (re-search-forward "\n +" p t)
(replace-match " ")
)
)
)
;; inserts a context-aware commented separator
(fset 'add-separator
[?\C-a return up ?\C-5 ?\C-0 ?- ?\C- ?\C-a ?\M-x ?c ?o ?m ?m ?e ?n ?t ? ?r ?e ?g ?i ?o ?n return down])
(defun me/insert-time ()
"Insert time at point in format %H:%M:%S. If universal-argument
is set, use format %H%M%S instead."
(interactive)
(if current-prefix-arg
(insert (format-time-string "%H%M%S"))
(insert (format-time-string "%H:%M:%S"))))
(defun me/insert-date ()
"Insert date at point in format %Y/%m/%d. If universal-argument
is set, use format %Y%m%d instead."
(interactive)
(if current-prefix-arg
(insert (format-time-string "%Y%m%d"))
(insert (format-time-string "%Y/%m/%d"))))
(defun me/insert-datetime ()
"Insert datetime at point in format %Y/%m/%d-%H:%M:%S. If
universal-argument is set, use format %Y%m%d-%H%M%S instead."
(interactive)
(if current-prefix-arg
(insert (format-time-string "%Y%m%d-%H%M%S"))
(insert (format-time-string "%Y/%m/%d-%H:%M:%S"))))
;; skipping the 'me/' prefix since this needs to be short
(defun iwb ()
"indent and untabify whole buffer"
(interactive)
(delete-trailing-whitespace)
(indent-region (point-min) (point-max) nil)
(untabify (point-min) (point-max)))
(defun me/wrap-text (start end)
"Asks for two strings, which will be placed before and after a
selected region"
(interactive "r")
(let (prefix suffix)
(setq prefix (read-from-minibuffer "Prefix: "))
(setq suffix (read-from-minibuffer "Suffix: "))
(save-restriction
(narrow-to-region start end)
(goto-char (point-min))
(insert prefix)
(goto-char (point-max))
(insert suffix)
)))
(defun me/wrap-region (start end)
"Given a prefix and a suffix, this function will wrap each line
in the region such that they are prefixed with the prefix and
suffixed with the suffix.
If no region is selected, it will do the above for all lines from
point to the end of the buffer."
(interactive "r")
(let (prefix suffix linecount str-len end-pos)
(setq prefix (read-from-minibuffer "Prefix: "))
(setq suffix (read-from-minibuffer "Suffix: "))
;; if no region was set, work from point to end-of-buffer.
(setq end-pos (if (= (point) (mark)) (end-of-buffer) end))
;; Bring point to beginning of region if selection was made from
;; upper part of the buffer to the end.
(if (> (point) (mark)) (exchange-point-and-mark))
(setq linecount (count-lines (point) end-pos))
(setq linecount (if (= start (point))
linecount
(progn
(forward-line)
(- linecount 1))))
(setq str-len (+ end-pos (* linecount (+ (length (concat prefix suffix))))))
(message "Start: %s, End-Pos: %s, Point: %s" start end-pos (point))
(message "Linecount: %s" linecount)
(while (re-search-forward "^\\(.*\\)$" str-len nil)
(replace-match (concat prefix "\\1" suffix) nil nil)
)
)
)
In html-mode
and css-mode
, make all instances of strings
matching #xxyyzz where x, y, and z are two-char hex chars get
syntax highlighting corresponding to the colour specified.
(defun hexcolour-luminance (color)
"Calculate the luminance of a color string (e.g. \"#ffaa00\", \"blue\").
This is 0.3 red + 0.59 green + 0.11 blue and always between 0 and 255."
(let* ((values (x-color-values color))
(r (car values))
(g (cadr values))
(b (caddr values)))
(floor (+ (* .3 r) (* .59 g) (* .11 b)) 256)))
(defun hexcolour-add-to-font-lock ()
(interactive)
(font-lock-add-keywords
nil
`((,(concat "#[0-9a-fA-F]\\{3\\}[0-9a-fA-F]\\{3\\}?\\|"
(regexp-opt (x-defined-colors) 'words))
(0 (let ((colour (match-string-no-properties 0)))
(put-text-property
(match-beginning 0) (match-end 0)
'face `((:foreground ,(if (> 128.0 (hexcolour-luminance colour))
"white" "black"))
(:background ,colour)))))))))
For a couple of years I put all my todos into an org-file called ~/todo.org. These functions helped me with this.
(defun me/switch-to-todo ()
"Switch to todo buffer. Open file if necessary"
(interactive)
(find-file-other-window (concat sys/home "/todo.org"))
(goto-char (point-min)))
(defun me/add-todo ()
"Add a todo to the todo buffer."
(interactive)
(me/add-todo-helper (read-from-minibuffer "Todo: "))
)
(defun me/add-todo-helper (msg)
(save-current-buffer
(set-buffer (find-file-noselect (concat sys/home "/todo.org")))
(goto-char (point-min))
(re-search-forward "^\* Todo$" nil t)
(insert "\n** TODO " msg)
(org-schedule nil (current-time))
(save-buffer)
)
)
I use this following function when I use plain org-mode for presentations.
;; http://stackoverflow.com/questions/12915528/easier-outline-navigation-in-emacs
(defun org-show-next-heading-tidily ()
"Show next entry, keeping other entries closed."
(interactive)
(if (save-excursion (end-of-line) (outline-invisible-p))
(progn (org-show-entry) (show-children))
(outline-next-heading)
(unless (and (bolp) (org-on-heading-p))
(org-up-heading-safe)
(hide-subtree)
(error "Boundary reached"))
(org-overview)
(org-reveal t)
(org-show-entry)
(show-children)
)
)
These functions allow me to move single lines or entire regions up and down. For keybindings, see: Buffer manipulation.
;; http://www.emacswiki.org/emacs/MoveLineRegion
(defun me/move-line (&optional n)
"Move current line N (1) lines up/down leaving point in place."
(interactive "p")
(when (null n)
(setq n 1))
(let ((col (current-column)))
(beginning-of-line)
(forward-line)
(transpose-lines n)
(forward-line -1)
(forward-char col))
(indent-according-to-mode))
(defun me/move-line-up (n)
"Moves current line N (1) lines up leaving point in place."
(interactive "p")
(me/move-line (if (null n) -1 (- n))))
(defun me/move-line-down (n)
"Moves current line N (1) lines down leaving point in place."
(interactive "p")
(me/move-line (if (null n) 1 n)))
(defun me/move-region (start end n)
"Move the current region up or down by N lines."
(interactive "r\np")
(let ((line-text (delete-and-extract-region start end)))
(forward-line n)
(let ((start (point)))
(insert line-text)
(setq deactivate-mark nil)
(set-mark start))))
(defun me/move-region-up (start end n)
"Move the current region up by N lines."
(interactive "r\np")
(me/move-region start end (if (null n) -1 (- n))))
(defun me/move-region-down (start end n)
"Move the current region down by N lines."
(interactive "r\np")
(me/move-region start end (if (null n) 1 n)))
(defun me/move-line-region-up (start end n)
(interactive "r\np")
(if (region-active-p) (me/move-region-up start end n) (me/move-line-up n)))
(defun me/move-line-region-down (start end n)
(interactive "r\np")
(if (region-active-p) (me/move-region-down start end n) (me/move-line-down n)))
This function is useful to toggle selective-display, which is often (but not always) used to show all lines which don’t start with indentation - that is, function/method/class names in a buffer.
(defun me/toggle-selective-display ()
"Run this to show only lines in buffer with a non-whitespace
character on column 0. run again to go back."
(interactive)
(set-selective-display (if selective-display nil 1)))
When I want to do simple arithmetic in the buffer, I write (for
example): (+ 3 8)
then place my cursor after the close paren and
run eval-and-replace
which replaces the expression with its
output.
(defun me/eval-and-replace ()
"Replace the preceding sexp with its value."
(interactive)
(backward-kill-sexp)
(condition-case nil
(prin1 (eval (read (current-kill 0)))
(current-buffer))
(error (message "Invalid expression")
(insert (current-kill 0)))))
These two functions help me do operations on both a file and its corresponding buffer.
;; Ripped from Steve Yegges .emacs
(defun rename-file-and-buffer (new-name)
"Renames both current buffer and file it's visiting to NEW-NAME."
(interactive "sNew name: ")
(let ((name (buffer-name))
(filename (buffer-file-name)))
(if (not filename)
(message "Buffer '%s' is not visiting a file!" name)
(if (get-buffer new-name)
(message "A buffer named '%s' already exists!" new-name)
(progn
(rename-file name new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
(set-buffer-modified-p nil))))))
;; copied from http://blog.tuxicity.se/
(defun delete-file-and-buffer ()
"Deletes file connected to current buffer and kills buffer."
(interactive)
(let ((filename (buffer-file-name))
(buffer (current-buffer))
(name (buffer-name)))
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(when (yes-or-no-p "Are you sure you want to remove this file? ")
(delete-file filename)
(kill-buffer buffer)
(message "File '%s' successfully removed" filename)))))
I used this function before I found out about forward-sexp
and
backward-sexp
, bound by default to C-M-f
and C-M-b
. I’m
keeping it mostly as an example of how to use prefix arguments in
(interactive "p")
.
(defun me/match-paren (arg)
"Go to the matching paren if on a paren; otherwise insert %."
(interactive "p")
(cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
((looking-at "\\s\)") (forward-char 1) (backward-list 1))
(t (self-insert-command (or arg 1)))))
My oldest remaining emacs configuration, copied in ‘93 from someone who in turn copied it from someone called “phille” at KTH. He was considered an emacs-god at the time.
I don’t really use these anymore, since there are simpler ways of removing ^M or removing whitespaces at the end of all lines in a buffer.
But I keep them here to remind me of those early days when I had to turn off my modem to exit emacs.
(defun philles-takM-formatterare ()
"Tar bort dessa irriterande ^M."
(interactive)
(save-excursion
(goto-char (point-min))
(while (search-forward "
" nil t)
(replace-match "" nil t)))
)
(defun philles-whitespace-formatterare ()
"Ta bort allt whitespace (space + tabbar) i slutet av varje rad i bufferten"
(interactive)
(message "Function disabled. Use delete-trailing-whitespace instead.")
)
These functions are called by others.
(defun file-string (file)
"Read the contents of a file and return as a string."
(with-temp-buffer
(insert-file-contents file)
(buffer-string)))
(defun chomp (str)
"Chomp tailing newlines from string"
(let ((s (if (symbolp str) (symbol-name str) str)))
(replace-regexp-in-string "[''\n'']*$" "" s)))
(defun get-ipv4-regex ()
(let (p1 p2 p3 octet-re)
(setq p1 "[01]?[[:digit:]]?[[:digit:]]")
(setq p2 "2[01234][[:digit:]]")
(setq p3 "25[012345]")
(setq octet-re (concat "\\(" p1 "\\|" p2 "\\|" p3 "\\)"))
(concat "^" (mapconcat (lambda (x) x)
(list octet-re octet-re octet-re octet-re)
"\\.") "$")
)
)
Keybindings!
I split the bindings into global keybindings which work everywhere, and local keybindings which work only in buffers with specific buffers.
Of all these global keybindings, I think I just use a handful. Some of them should be local too.
(global-set-key "\C-c\C-d" 'me/insert-date)
(global-set-key "\C-x\C-y" 'toggle-truncate-lines)
(global-set-key [ f7 ] 'me/ansi-term)
(global-set-key (kbd "M-%") 'query-replace-regexp)
(global-set-key (kbd "C-x SPC") 'whitespace-mode)
Change focus to window in the direction of the arrow.
(global-set-key (kbd "C-x <down>") 'windmove-down)
(global-set-key (kbd "C-x <up>") 'windmove-up)
(global-set-key (kbd "C-x <right>") 'windmove-right)
(global-set-key (kbd "C-x <left>") 'windmove-left)
Swap contents of this window the the window in the direction of the arrow.
(global-set-key (kbd "C-M-x <down>") 'windmove-swap-states-down)
(global-set-key (kbd "C-M-x <up>") 'windmove-swap-states-up)
(global-set-key (kbd "C-M-x <right>") 'windmove-swap-states-right)
(global-set-key (kbd "C-M-x <left>") 'windmove-swap-states-left)
Enlarge/shrink this window vertically or horizontally.
;; Vertical expand/shrink
(global-set-key [ f11 ] #'(lambda () (interactive) (enlarge-window 4 )))
(global-set-key [ M-f11 ] #'(lambda () (interactive) (enlarge-window -4)))
;; Horizontal expand/shrink
(global-set-key [ f12 ] #'(lambda () (interactive) (enlarge-window 4 1)))
(global-set-key [ M-f12 ] #'(lambda () (interactive) (enlarge-window -4 1)))
(global-set-key (kbd "C-:") 'iedit-mode)
(global-set-key (kbd "C-S-e") 'me/merge-lines)
(global-set-key [(shift meta ?c)] 'me/camelcase-region)
;; Move lines and regions up and down
(global-set-key [(meta up)] 'me/move-line-up)
(global-set-key [(meta down)] 'me/move-line-down)
(global-set-key [(shift meta up)] 'me/move-line-region-up)
(global-set-key [(shift meta down)] 'me/move-line-region-down)
(global-set-key "\C-c\C-g" 'goto-line)
(global-set-key (kbd "M-i") 'ido-goto-symbol)
;; move the buffer contents up and down without moving the cursor
(global-set-key [(meta ?n)] #'(lambda () (interactive) (scroll-up 3)))
(global-set-key [(meta ?p)] #'(lambda () (interactive) (scroll-down 3)))
(global-set-key [(shift meta ?n)] #'(lambda () (interactive) (scroll-other-window 3)))
(global-set-key [(shift meta ?p)] #'(lambda () (interactive) (scroll-other-window -3)))
;; Jump to top and bottom of a buffer or the other one
;; The last two are default keybindings. But I added them here to remind me of them.
(global-set-key [ home ] 'beginning-of-buffer)
(global-set-key [ end ] 'end-of-buffer )
(global-set-key [(meta home) ] 'beginning-of-buffer-other-window)
(global-set-key [(meta end) ] 'end-of-buffer-other-window )
(global-set-key [ f5 ] 'me/switch-to-scratch)
(global-set-key [ M-f5 ] 'me/open-dot-emacs)
(global-set-key (kbd "C-x C-b") 'ibuffer)
(global-set-key "\C-x\C-g" 'find-file-at-point)
(global-set-key (kbd "C-h C-s") 'find-function-at-point)
(global-set-key (kbd "C-S-s") 'swiper)
(global-set-key (kbd "C-c C-r") 'ivy-resume)
;; (global-set-key (kbd "<f6>") 'ivy-resume)
(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key (kbd "<f1> f") 'counsel-describe-function)
(global-set-key (kbd "<f1> v") 'counsel-describe-variable)
(global-set-key (kbd "<f1> l") 'counsel-find-library)
(global-set-key (kbd "<f2> i") 'counsel-info-lookup-symbol)
(global-set-key (kbd "<f2> u") 'counsel-unicode-char)
(global-set-key (kbd "C-c g") 'counsel-git)
(global-set-key (kbd "C-c j") 'counsel-git-grep)
(define-key minibuffer-local-map (kbd "C-r") 'counsel-minibuffer-history)
;(global-set-key (kbd "C-x l") 'counsel-locate)
;(global-set-key (kbd "C-c k") 'counsel-ag)
(global-set-key (kbd "<f1> 1") 'me/toggle-treemacs)
(global-set-key "\C-x\C-m" 'execute-extended-command) (global-set-key "\C-c\C-m" 'execute-extended-command) (global-set-key "\C-c\C-k" 'kill-buffer) (global-set-key "\C-co" 'org-capture) (global-set-key "\C-xm" 'mail) (global-set-key [ f6 ] 'me/switch-to-todo) (global-set-key [ S-f6 ] 'me/add-todo) (global-set-key [ f10 ] 'me/org-show-next-heading-tidily)
I use two different ways of assigning mode-local keybindings.
- The first tells emacs to add a key-function mapping to a specific mode-map after it loads the module (e.g. Mail keybindings).
- The second adds a lambda where a key is mapped to a function to a
mode’s hook. (e.g. Javascript keybindings).
I think I like the second method more.
Available in all prog-modes.
(defun me/revert-buffer-with-fine-grain ()
(interactive)
(revert-buffer-with-fine-grain nil t))
(add-hook 'prog-mode-hook
(lambda ()
(progn
(local-set-key "\C-cc" 'compile)
(local-set-key "\C-cd" 'gdb)
(local-set-key "\C-cn" 'next-error)
(local-set-key (kbd "M-0") 'add-separator)
(local-set-key [ \C-tab ] 'hippie-expand)
(local-set-key [ f6 ] 'me/toggle-selective-display)
(local-set-key [ f8 ] 'hl-line-mode)
(local-set-key [ M-f8 ] 'linum-mode)
(local-set-key (kbd "C-x x g") 'me/revert-buffer-with-fine-grain)
)
)
)
(add-hook 'js-mode-hook
(lambda ()
(local-set-key (kbd "C-M-,") 'me/insert-fat-comma)))
(eval-after-load 'message
'(define-key message-mode-map [ f9 ] 'me/message-replace-sig))
(eval-after-load 'message
'(define-key message-mode-map [?\C-c ?\C-k] 'me/kill-to-signature))
(add-hook 'mail-mode-hook
#'(lambda ()
(define-key mail-mode-map "\C-c\C-w" 'me/message-replace-sig)
))
(add-hook 'cperl-mode-hook
#'(lambda () (local-set-key (kbd "C-M-,") 'me/insert-fat-comma)))
When your cursor is on a directory and you press i
,
dired-maybe-insert-subdir
is called. It adds the subdirectory at
the bottom of the buffer. Though this is useful, Dired-subtree is
better - it adds the subdir directly under the dir you opened,
indented a bit.
Use <tab>
to expand a dir, and <tab>
again to close it. If
you’ve moved your cursor into the contents of the dir, then
shift-tab
will close it for you.
(eval-after-load "dired"
'(progn
(define-key dired-mode-map (kbd "<tab>") 'dired-subtree-toggle)
(define-key dired-mode-map (kbd "<backtab>") 'dired-subtree-remove)
(define-key dired-mode-map (kbd ".") 'me/dired-dotfiles-toggle)
)
)
For some reason, I haven’t used etags (or any other tag functionality) over all these years. Kind of strange. Anyway, here’s how to set up etags for a project.
First, run this command to create the etags table. Stand in the root
of the project in question, since the etags file (called TAGS
) will
be created there. The pattern you specify depends on what language
you’re creating the tags for.
find . -name '<pattern>' -exec etags -a {} \;
For perl, one could have the pattern: *.p[lm]
to capture both .pl
and .pm
files. For emacs-lisp it would be *.el
. Etc.
Next, open one of the files in that directory, and load it with M-x
visit-tags-table
. After this, you can find the definition of a
function or variable using M-.
and jump back to where you were with
meta-comma.
Here some pocket lint which I don’t use but might want to at some point in the future.
;; Never compile .emacs by hand again
;;(add-hook 'after-save-hook 'autocompile)
;; (defun autocompile ()
;; "compile itself if dot.emacs.el"
;; (interactive)
;; (if (string= (buffer-file-name) (concat default-directory "dot.emacs.el"))
;; (byte-compile-file (buffer-file-name))))
;;(defmacro help/on-gui (statement &rest statements)
;; "Evaluate the enclosed body only when run on GUI."
;; `(when (display-graphic-p)
;; ,statement
;; ,@statements))
;; or
;;
;;(when (display-graphic-p)
;; (set-frame-font "...")
;; (require '...)
;; (...-mode))
;;
;; (defun html-mode-end-paragraph ()
;; "End the paragraph nicely"
;; (interactive)
;; (insert "</p>\n"))