Put the following into the header for it to work
#+BABEL: :cache yes #+PROPERTY: header-args :tangle yes #+STARTUP: overview
Initially inspired by larstvei’s setup. Check out EmacsCast, my podcast about Emacs. I talk about my config in Episode 2, and then provide updates in consequent episodes.
This config works best with this https://emacsformacosx.com/. Alternative build I’m currently using is this port:
brew tap railwaycat/emacsmacport
brew cask install emacs-mac
It works well and provides some things the official distribution doesn’t:
- per-pixel scrolling
- nicer-looking native transparent topbar
- emoji support
Few utils needed.
brew install ripgrep
brew cask install mactex
brew install aspell
brew cask install xquartz
brew install wordnet
brew install pandoc
I think it’ll be better not to clone and use this config as is, but rather build your own config using mine as a starting point. But if you really want to try it, then follow these steps:
Clone the repo:
git clone https://github.com/freetonik/emacs-dotfiles
Make a backup of your old .emacs.d
:
mv ~/.emacs.d ~/.emacs.d-bak
Rename cloned directory:
mv emacs-dotfiles ~/.emacs.d
On the first run Emacs will install some packages. It’s best to restart Emacs after that process is done for the first time.
There is no reason to track the init.el
that is generated; by running the following command git
will not bother tracking it:
git update-index --assume-unchanged init.el
If one wishes to make changes to the repo-version of init.el
start tracking again with:
git update-index --no-assume-unchanged init.el
(add-hook 'emacs-startup-hook
(lambda ()
(message "Emacs ready in %s with %d garbage collections."
(format "%.2f seconds"
(float-time
(time-subtract after-init-time before-init-time)))
gcs-done)))
When this configuration is loaded for the first time, the init.el
is the file that is loaded. It looks like this:
;; This file replaces itself with the actual configuration at first run.
;; We can't tangle without org!
(require 'org)
;; Open the configuration
(find-file (concat user-emacs-directory "init.org"))
;; tangle it
(org-babel-tangle)
;; load it
(load-file (concat user-emacs-directory "init.el"))
;; finally byte-compile it
(byte-compile-file (concat user-emacs-directory "init.el"))
Lexical scoping for the init-file is needed, it can be specified in the header. Make startup faster by reducing the frequency of garbage collection. The default is 800 kilobytes. Measured in bytes. These are the first lines of the actual configuration.
;;; -*- lexical-binding: t -*-
(setq gc-cons-threshold (* 50 1000 1000))
Tangle and compile this file on save automatically:
(defun tangle-init ()
"If the current buffer is 'init.org' the code-blocks are
tangled, and the tangled file is compiled."
(when (equal (buffer-file-name)
(expand-file-name (concat user-emacs-directory "init.org")))
;; Avoid running hooks when tangling.
(let ((prog-mode-hook nil))
(org-babel-tangle)
(byte-compile-file (concat user-emacs-directory "init.el")))))
(add-hook 'after-save-hook 'tangle-init)
This helps get rid of functions might not be defined at runtime
warnings. See jwiegley/use-package#590
;; (eval-when-compile
;; (setq use-package-expand-minimally byte-compile-current-file))
Initialize package and add Melpa source.
(require 'package)
(let* ((no-ssl (and (memq system-type '(windows-nt ms-dos))
(not (gnutls-available-p))))
(proto (if no-ssl "http" "https")))
(add-to-list 'package-archives (cons "melpa" (concat proto "://melpa.org/packages/")) t)
;;(add-to-list 'package-archives (cons "melpa-stable" (concat proto "://stable.melpa.org/packages/")) t)
(when (< emacs-major-version 24)
;; For important compatibility libraries like cl-lib
(add-to-list 'package-archives '("gnu" . (concat proto "://elpa.gnu.org/packages/")))))
(package-initialize)
Install use-package.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(eval-when-compile (require 'use-package))
(setq use-package-always-ensure t)
;; this package is useful for overriding major mode keybindings
(use-package bind-key)
Emacs control is Ctrl. Emacs Super is Command. Emacs Meta is Alt. Right Alt (option) can be used to enter symbols like em dashes —
.
(setq mac-right-command-modifier 'super)
(setq mac-command-modifier 'super)
(setq mac-option-modifier 'meta)
(setq mac-left-option-modifier 'meta)
(setq mac-right-option-modifier 'meta)
(setq mac-right-option-modifier 'nil)
I’ve tried many 3rd party themes, but keep coming back to the default light theme.
(load-theme 'tsdh-light)
(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist '(ns-appearance . light))
Inconsolata font, remove the cruft and make the initial size bigger.
(set-face-attribute 'default nil :font "SF Mono 16")
(setq-default line-spacing 0)
(setq initial-frame-alist '((width . 135) (height . 55)))
(tool-bar-mode -1)
Matching parenthesis appearance.
(set-face-background 'show-paren-match "wheat")
(set-face-attribute 'show-paren-match nil :weight 'extra-bold)
(show-paren-mode)
Nice and simple mode line.
(setq column-number-mode t) ;; show columns in addition to rows in mode line
Show full path in the title bar.
(setq-default frame-title-format "%b (%f)")
Never use tabs, use spaces instead.
(setq-default indent-tabs-mode nil)
(setq-default c-basic-indent 2)
(setq-default c-basic-offset 2)
(setq-default tab-width 2)
(setq tab-width 2)
(setq js-indent-level 2)
(setq css-indent-offset 2)
(setq c-basic-offset 2)
Disable blinking cursor.
(blink-cursor-mode 0)
Visual lines.
(global-visual-line-mode t)
I don’t care about auto save and backup files.
(setq make-backup-files nil) ; stop creating backup~ files
(setq auto-save-default nil) ; stop creating #autosave# files
(setq create-lockfiles nil) ; stop creating .# files
Revert (update) buffers automatically when underlying files are changed externally.
(global-auto-revert-mode t)
Some basic things.
(setq
inhibit-startup-message t ; Don't show the startup message
inhibit-startup-screen t ; or screen
cursor-in-non-selected-windows t ; Hide the cursor in inactive windows
echo-keystrokes 0.1 ; Show keystrokes right away, don't show the message in the scratch buffer
initial-scratch-message nil ; Empty scratch buffer
sentence-end-double-space nil ; Sentences should end in one space, come on!
confirm-kill-emacs 'y-or-n-p ; y and n instead of yes and no when quitting
)
(fset 'yes-or-no-p 'y-or-n-p) ; y and n instead of yes and no everywhere else
(scroll-bar-mode -1)
(delete-selection-mode 1)
(global-unset-key (kbd "s-p"))
I want Emacs kill ring and system clipboard to be independent. Simpleclip is the solution to that.
(use-package simpleclip
:init
(simpleclip-mode 1))
Quickly switch to scratch buffer with Cmd+0
:
(global-set-key (kbd "s-0") (lambda ()
(interactive)
(if (string= (buffer-name) "*scratch*")
(previous-buffer)
(switch-to-buffer "*scratch*"))))
This is great for learning Emacs, it shows a nice table of possible commands.
(use-package which-key
:config
(which-key-mode)
(setq which-key-idle-delay 1.5))
Pass system shell environment to Emacs. This is important primarily for shell inside Emacs, but also things like Org mode export to Tex PDF don’t work, since it relies on running external command pdflatex
, which is loaded from PATH
.
(use-package exec-path-from-shell
:config
(when (memq window-system '(mac ns))
(exec-path-from-shell-initialize)))
Use Cmd+i
to open the current folder in a new tab of iTerm:
(defun iterm-goto-filedir-or-home ()
"Go to present working dir and focus iterm"
(interactive)
(do-applescript
(concat
" tell application \"iTerm2\"\n"
" tell current window\n"
" create tab with profile \"Default\"\n"
" end tell\n"
" tell the current session of current window\n"
(format " write text \"cd %s\" \n"
;; string escaping madness for applescript
(replace-regexp-in-string "\\\\" "\\\\\\\\"
(shell-quote-argument (or default-directory "~"))))
" end tell\n"
" end tell\n"
" do shell script \"open -a iTerm\"\n"
))
)
(global-set-key (kbd "s-i") 'iterm-goto-filedir-or-home)
Kill line with s-Backspace
, which is Cmd-Backspace
. Note that thanks to Simpleclip, killing doesn’t rewrite the system clipboard. Kill one word by Alt-Backspace
. Also, kill forward word with Alt-Shift-Backspace
, since Alt-Backspace
is kill word backwards.
(global-set-key (kbd "s-<backspace>") 'kill-whole-line)
(global-set-key (kbd "s-<delete>") 'kill-whole-line)
(global-set-key (kbd "M-S-<backspace>") 'kill-word)
(global-set-key (kbd "M-<delete>") 'kill-word)
(bind-key* "S-<delete>" 'kill-word)
Use super
(which is Cmd
) for movement and selection just like in macOS.
(global-set-key (kbd "s-<right>") 'end-of-visual-line)
(global-set-key (kbd "s-<left>") 'beginning-of-visual-line)
(global-set-key (kbd "s-<up>") 'beginning-of-buffer)
(global-set-key (kbd "s-<down>") 'end-of-buffer)
(global-set-key (kbd "s-l") 'goto-line)
Basic things you should expect from macOS.
(global-set-key (kbd "s-a") 'mark-whole-buffer) ;; select all
(global-set-key (kbd "s-s") 'save-buffer) ;; save
(global-set-key (kbd "s-S") 'write-file) ;; save as
(global-set-key (kbd "s-q") 'save-buffers-kill-emacs) ;; quit
Regular undo-redo.
(use-package undo-fu)
(global-unset-key (kbd "C-z"))
(global-set-key (kbd "C-z") 'undo-fu-only-undo)
(global-set-key (kbd "C-S-z") 'undo-fu-only-redo)
(global-set-key (kbd "s-z") 'undo-fu-only-undo)
(global-set-key (kbd "s-r") 'undo-fu-only-redo)
Avy for fast navigation.
(use-package avy
:config
(global-set-key (kbd "s-;") 'avy-goto-char-timer))
Go back to previous mark (position) within buffer and go back (forward?).
(defun my-pop-local-mark-ring ()
(interactive)
(set-mark-command t))
(defun unpop-to-mark-command ()
"Unpop off mark ring. Does nothing if mark ring is empty."
(interactive)
(when mark-ring
(setq mark-ring (cons (copy-marker (mark-marker)) mark-ring))
(set-marker (mark-marker) (car (last mark-ring)) (current-buffer))
(when (null (mark t)) (ding))
(setq mark-ring (nbutlast mark-ring))
(goto-char (marker-position (car (last mark-ring))))))
(global-set-key (kbd "s-,") 'my-pop-local-mark-ring)
(global-set-key (kbd "s-.") 'unpop-to-mark-command)
Since Cmd+,
and Cmd+.
move you back in forward in the current buffer, the same keys with Shift
move you back and forward between open buffers.
(global-set-key (kbd "s-<") 'previous-buffer)
(global-set-key (kbd "s->") 'next-buffer)
Go to other windows easily with one keystroke s-something
instead of C-x something
.
(defun vsplit-last-buffer ()
(interactive)
(split-window-vertically)
(other-window 1 nil)
(switch-to-next-buffer))
(defun hsplit-last-buffer ()
(interactive)
(split-window-horizontally)
(other-window 1 nil)
(switch-to-next-buffer))
(global-set-key (kbd "s-w") (kbd "C-x 0")) ;; just like close tab in a web browser
(global-set-key (kbd "s-W") (kbd "C-x 1")) ;; close others with shift
(global-set-key (kbd "s-T") 'vsplit-last-buffer)
(global-set-key (kbd "s-t") 'hsplit-last-buffer)
(global-set-key (kbd "s-k") 'kill-this-buffer)
Expand-region allows to gradually expand selection inside words, sentences, etc. C-'
is bound to Org’s cycle through agenda files
, which I don’t really use, so I unbind it here before assigning global shortcut for expansion.
(use-package expand-region
:config
(global-set-key (kbd "s-'") 'er/expand-region)
(global-set-key (kbd "s-\"") 'er/contract-region))
Move-text
allows moving lines around with meta-up/down.
(use-package move-text
:config
(move-text-default-bindings))
Smarter open-line by bbatsov. Once again, I’m taking advantage of CMD and using it to quickly insert new lines above or below the current line, with correct indentation and stuff.
(defun smart-open-line ()
"Insert an empty line after the current line. Position the cursor at its beginning, according to the current mode."
(interactive)
(move-end-of-line nil)
(newline-and-indent))
(defun smart-open-line-above ()
"Insert an empty line above the current line. Position the cursor at it's beginning, according to the current mode."
(interactive)
(move-beginning-of-line nil)
(newline-and-indent)
(forward-line -1)
(indent-according-to-mode))
(global-set-key (kbd "s-<return>") 'smart-open-line)
(global-set-key (kbd "s-S-<return>") 'smart-open-line-above)
Join lines whether you’re in a region or not.
(defun smart-join-line (beg end)
"If in a region, join all the lines in it. If not, join the current line with the next line."
(interactive "r")
(if mark-active
(join-region beg end)
(top-join-line)))
(defun top-join-line ()
"Join the current line with the next line."
(interactive)
(delete-indentation 1))
(defun join-region (beg end)
"Join all the lines in the region."
(interactive "r")
(if mark-active
(let ((beg (region-beginning))
(end (copy-marker (region-end))))
(goto-char beg)
(while (< (point) end)
(join-line 1)))))
(global-set-key (kbd "s-j") 'smart-join-line)
Upcase word and region using the same keys.
(global-set-key (kbd "M-u") 'upcase-dwim)
(global-set-key (kbd "M-l") 'downcase-dwim)
Delete trailing spaces and add new line in the end of a file on save.
(add-hook 'before-save-hook 'delete-trailing-whitespace)
(setq require-final-newline t)
Multiple cusors are a must. Make <return> insert a newline; multiple-cursors-mode can still be disabled with C-g.
(use-package multiple-cursors
:config
(setq mc/always-run-for-all 1)
(global-set-key (kbd "s-d") 'mc/mark-next-like-this)
(global-set-key (kbd "s-D") 'mc/mark-all-dwim)
(define-key mc/keymap (kbd "<return>") nil))
Comment lines.
(global-set-key (kbd "s-/") 'comment-line)
I’m still not happy with the way new windows are spawned. For now, at least, let’s make it so that new automatic windows are always created on the bottom, not on the side.
(setq split-height-threshold 0)
(setq split-width-threshold nil)
Move between windows with Control-Command-Arrow and with Cmd
just like in iTerm.
(global-set-key (kbd "s-o") (kbd "C-x o"))
(use-package windmove
:config
(global-set-key (kbd "s-[") 'windmove-left) ;; Cmd+[ go to left window
(global-set-key (kbd "s-]") 'windmove-right) ;; Cmd+] go to right window
(global-set-key (kbd "s-{") 'windmove-up) ;; Cmd+Shift+ go to upper window
(global-set-key (kbd "<s-}>") 'windmove-down)) ;; Ctrl+Shift+ go to down window
Enable winner mode to quickly restore window configurations
(winner-mode 1)
Shackle to make sure all windows are nicely positioned.
(use-package shackle
:init
(setq shackle-default-alignment 'below
shackle-default-size 0.4
shackle-rules '((help-mode :align below :select t)
(helpful-mode :align below)
(compilation-mode :select t :size 0.25)
("*compilation*" :select nil :size 0.25)
("*ag search*" :select nil :size 0.25)
("*Flycheck errors*" :select nil :size 0.25)
("*Warnings*" :select nil :size 0.25)
("*Error*" :select nil :size 0.25)
("*Org Links*" :select nil :size 0.1)
(magit-status-mode :align bottom :size 0.5 :inhibit-window-quit t)
(magit-log-mode :same t :inhibit-window-quit t)
(magit-commit-mode :ignore t)
(magit-diff-mode :select nil :align left :size 0.5)
(git-commit-mode :same t)
(vc-annotate-mode :same t)
))
:config
(shackle-mode 1))
Select any region and edit it in another buffer.
TODO: if region selected, invoke edit indirect with same keystroke as narrow
TODO: Since M-h is org-mark-element
, make M-s-h org-babel-mark-block
(use-package edit-indirect)
(use-package ivy
:config
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format "(%d/%d) ")
(setq enable-recursive-minibuffers t)
(setq ivy-initial-inputs-alist nil)
(setq ivy-re-builders-alist
'((swiper . ivy--regex-plus)
(swiper-isearch . regexp-quote)
;; (counsel-git . ivy--regex-plus)
;; (counsel-ag . ivy--regex-plus)
(counsel-rg . ivy--regex-plus)
(t . ivy--regex-fuzzy))) ;; enable fuzzy searching everywhere except for Swiper and ag
(global-set-key (kbd "s-b") 'ivy-switch-buffer))
(use-package swiper
:config
(global-set-key (kbd "s-f") 'swiper-isearch))
(use-package counsel
:config
(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "s-y") 'counsel-yank-pop)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key (kbd "s-F") 'counsel-rg)
(global-set-key (kbd "s-p") 'counsel-git))
;; When using git ls (via counsel-git), include unstaged files
(setq counsel-git-cmd "git ls-files -z --full-name --exclude-standard --others --cached --")
(use-package smex)
(use-package flx)
(use-package ivy-rich
:config
(ivy-rich-mode 1)
(setq ivy-rich-path-style 'abbrev)) ;; To abbreviate paths using abbreviate-file-name (e.g. replace “/home/username” with “~”
It’s time for Magit!
(use-package magit
:config
(global-set-key (kbd "s-g") 'magit-status))
Navigate to projects with Cmd+Shift+P
(thanks to reddit user and emacscast listener fritzgrabo):
(setq magit-repository-directories '(("\~/code" . 4) ("\~/Dropbox/projects/" . 4) ("~/gdrive/Codexpanse" . 3)))
(defun magit-status-with-prefix-arg ()
"Call `magit-status` with a prefix."
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively #'magit-status)))
(global-set-key (kbd "s-P") 'magit-status-with-prefix-arg)
Spell checking requires an external command to be available. Install aspell
on your Mac, then make it the default checker for Emacs’ ispell
. Note that personal dictionary is located at ~/.aspell.LANG.pws
by default.
(setq ispell-program-name "aspell")
Enable spellcheck on the fly for all text modes. This includes org, latex and LaTeX. Spellcheck current word.
(add-hook 'text-mode-hook 'flyspell-mode)
(global-set-key (kbd "s-\\") 'ispell-word)
Spellcheck was Cmd+\
, synonym search is Cmd+Shift+\
. It requires wordnet
to be installed locally.
;; (use-package powerthesaurus
;; :config
;; (global-set-key (kbd "s-|") 'powerthesaurus-lookup-word-dwim)
;; )
;; Alternative, local thesaurus
(use-package synosaurus
:config
(global-set-key (kbd "s-|") 'synosaurus-choose-and-replace)
)
Word definition search.
(use-package define-word
:config
(global-set-key (kbd "M-\\") 'define-word-at-point))
(read-abbrev-file abbrev-file-name t)
(setq-default abbrev-mode t)
YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.
(use-package yasnippet
:defer t
:config
(setq yas-snippet-dirs
'("~/.emacs.d/snippets"))
(yas-global-mode 1))
Utility functions for export.
(defun markdown-export-html-to-clipboard (lines-to-skip)
"Export Markdown to HTML while skipping first lines-to-skip, copy to cliboard"
(interactive)
(markdown-kill-ring-save)
(let ((oldbuf (current-buffer)))
(save-current-buffer
(set-buffer "*markdown-output*")
(if lines-to-skip
(progn
(goto-char (point-min))
(kill-whole-line)))
(with-no-warnings (mark-whole-buffer))
(simpleclip-copy (point-min) (point-max)))))
(defun markdown-export-html-to-clipboard-full ()
(interactive) (markdown-export-html-to-clipboard nil))
(defun markdown-export-html-to-clipboard-no-1st-line ()
(interactive) (markdown-export-html-to-clipboard 1))
Cmd-O
to copy Markdown as raw HTML to clipboard. Widen and narrow like in Org.
(use-package markdown-mode
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:bind (:map markdown-mode-map
("s-k" . 'markdown-insert-link)
("C-s-<down>" . 'markdown-narrow-to-subtree)
("C-s-<up>" . 'widen)
("s-O" . 'markdown-export-html-to-clipboard-full)
("M-s-O" . 'markdown-export-html-to-clipboard-no-1st-line))
:init (setq markdown-command '("pandoc" "--no-highlight")))
** Packages for programming
Here are all the packages needed for programming languages and formats.
(use-package yaml-mode
:defer t
:mode (("\\.yml\\'" . yaml-mode)
("\\.yaml\\'" . yaml-mode)))
Clojure.
(use-package clojure-mode)
(use-package cider)
Web mode.
(use-package web-mode
:defer t
:mode ("\\.html\\'")
:config
(setq web-mode-markup-indent-offset 2))
Emmet.
(use-package emmet-mode
:defer t
:init
(setq emmet-indentation 2)
(setq emmet-move-cursor-between-quotes t)
:config
(add-hook 'sgml-mode-hook 'emmet-mode) ;; Auto-start on any markup modes
(add-hook 'web-mode-hook 'emmet-mode)
(add-hook 'html-mode-hook 'emmet-mode)
(add-hook 'css-mode-hook 'emmet-mode)) ;; enable Emmet's css abbreviation.
Code formatting.
(use-package format-all)
Always open files in the same frame, even when double-clicked from Finder.
(setq ns-pop-up-frames nil)
Handy killall command.
(defun kill-all-buffers ()
(interactive)
(mapc 'kill-buffer (buffer-list)))
Visually indent sections. This looks better for smaller files. Also, disallow editing invisible areas and show any number of empty lines in collapsed state. And use ⤵
instead of ...
for wrapped sections.
(setq org-startup-indented t)
(setq org-catch-invisible-edits 'error)
(setq org-cycle-separator-lines -1)
(setq calendar-week-start-day 1)
(setq org-ellipsis "⤵")
Store all my org files in Dropbox (/Users/rakhim/Dropbox
). And all of those files should be in included agenda.
(setq org-directory "/Users/rakhim/Dropbox/Org")
(setq org-agenda-files '("/Users/rakhim/Dropbox/Org"))
Refile targets should include files and down to 9 levels into them.
(setq org-refile-targets (quote ((nil :maxlevel . 9)
(org-agenda-files :maxlevel . 9))))
Allow shift selection with arrows. This will not interfere with some built-in shift+arrow functionality in Org.
(setq org-support-shift-select t)
Custom source block templates (e.g. <el
for emacs lisp).
(with-eval-after-load 'org
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
;; (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(define-key org-mode-map (kbd "C-'") nil))
And inside those code blocks indentation should be correct depending on the source language used and have code highlighting.
(setq org-src-tab-acts-natively t)
(setq org-src-preserve-indentation t)
(setq org-src-fontify-natively t)
Quickly open the config file.
(global-set-key (kbd "\e\ec") (lambda () (interactive) (find-file "~/.emacs.d/init.org")))
Not sure about this… I want to retain Shift-Alt movement and selection everywhere, but in Org mode these bindings are important built ins, and I don’t know if there is a viable alternative.
Consider switching meta-left/right to C-c C-,
and C-c C-.
. These are used to promote and demote subtrees.
(with-eval-after-load 'org
;; no shift or alt with arrows
(define-key org-mode-map (kbd "<S-left>") nil)
(define-key org-mode-map (kbd "<S-right>") nil)
(define-key org-mode-map (kbd "<M-left>") nil)
(define-key org-mode-map (kbd "<M-right>") nil)
;; no shift-alt with arrows
(define-key org-mode-map (kbd "<M-S-left>") nil)
(define-key org-mode-map (kbd "<M-S-right>") nil)
(define-key org-mode-map (kbd "C-s-<left>") 'org-metaleft)
(define-key org-mode-map (kbd "C-s-<right>") 'org-metaright))
Enable speed keys to manage headings without arrows.
(setq org-use-speed-commands t)
Narrow and widen with Cmd+Ctrl+Up/Down
.
(with-eval-after-load 'org
(define-key org-mode-map (kbd "C-s-<down>") 'org-narrow-to-subtree)
(define-key org-mode-map (kbd "C-s-<up>") 'widen))
(require 'ox-latex)
(setq org-format-latex-options (plist-put org-format-latex-options :scale 2.0))
(setq org-highlight-latex-and-related '(latex))
(with-eval-after-load 'ox-latex
(add-to-list
'org-latex-classes
'("tufte-book"
"\\documentclass{tufte-book}
\\input{/users/rakhim/.emacs.d/latex/tufte.tex}"
("\\part{%s}" . "\\part*{%s}")
("\\chapter{%s}" . "\\chapter*{%s}")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))))
(use-package ox-pandoc)
slimhtml is an emacs org mode export backend. It is a set of transcoders for common org elements which outputs minimal HTML.
(use-package htmlize)
(use-package ox-slimhtml)
(defun org-html-export-as-slimhtml
(&optional async subtreep visible-only body-only ext-plist)
(interactive)
(org-export-to-buffer 'slimhtml "*slimhtml*"
async subtreep visible-only body-only ext-plist (lambda () (html-mode))))
;; Cmd+Shift+Alt+o to export whole buffer to HTML without first two lines and one last line
;; This is done to remove container div and header title
(eval-after-load 'org
`(define-key org-mode-map (kbd "M-s-o") (lambda ()
(interactive)
(org-html-export-as-html nil nil t t)
(goto-char (point-min))
(kill-whole-line)
(kill-whole-line)
(goto-char (point-max))
(forward-line -1)
(kill-whole-line)
(with-no-warnings (mark-whole-buffer))
(simpleclip-copy (point-min) (point-max))
(delete-window))))
;; Cmd+Shift+o to export whole buffer to HTML
(eval-after-load 'org
`(define-key org-mode-map (kbd "s-O") (lambda ()
(interactive)
(org-html-export-as-html nil nil t t)
(with-no-warnings (mark-whole-buffer))
(simpleclip-copy (point-min) (point-max))
(delete-window))))
Drag and drop images to org files.
(use-package org-download
:config
;; add support to dired
(add-hook 'dired-mode-hook 'org-download-enable))
(add-to-list 'org-capture-templates
'("t"
"TODO"
entry
(file+datetree "main.org")
"* %U\n** TODO %?"))
(add-to-list 'org-capture-templates
'("T"
"Scheduled TODO"
entry
(file+datetree "main.org")
"* %U\n** TODO %?\nDEADLINE: %^t\n"))
(add-to-list 'org-capture-templates
'("j"
"Journal"
entry
(file+datetree "main.org")
"* %U\n%?"))
(global-set-key (kbd "\e\ef") (lambda () (interactive) (counsel-rg nil org-directory)))
Store custom-file separately, don’t freak out when it’s not found.
(setq custom-file "~/.emacs.d/custom.el")
(load custom-file 'noerror)
Start an Emacs server. This way, I can use emacsclient from the terminal.
(unless (server-running-p) (server-start))
To enable easy emacsclient, create ~/bin/ec
:
#!/bin/sh
# this assumes that my regular emacs app runs the server as part of startup
emacsclient -n "$@" || (open -a emacs "$@")
Here are some snippets of elisp and config that aren’t currently used, but I don’t want to lose them in git history. None of these are tangled.
(require 'paren)
(setq show-paren-delay 0)
(show-paren-mode 1)
(set-face-foreground 'show-paren-match nil)
(use-package smartparens
:config
;; (require 'smartparens-config)
(smartparens-global-mode t)
(show-smartparens-global-mode t)
(setq sp-show-pair-delay 0)
;; no '' pair in emacs-lisp-mode
;; (sp-local-pair 'emacs-lisp-mode "'" nil :actions nil)
;; (sp-local-pair 'markdown-mode "`" nil :actions '(wrap insert)) ;; only use ` for wrap and auto insertion in markdown-mode
;; (sp-local-tag 'markdown-mode "s" "```scheme" "```")
(define-key smartparens-mode-map (kbd "C-s-<right>") 'sp-forward-slurp-sexp)
(define-key smartparens-mode-map (kbd "C-s-<left>") 'sp-forward-barf-sexp))
(global-set-key (kbd "s-i") 'previous-line)
(global-set-key (kbd "s-k") 'next-line)
(global-set-key (kbd "s-j") 'left-char)
(global-set-key (kbd "s-l") 'right-char)
(use-package git-gutter
:config
(global-git-gutter-mode 't)
(set-face-background 'git-gutter:modified 'nil) ;; background color
(set-face-foreground 'git-gutter:added "green4")
(set-face-foreground 'git-gutter:deleted "red"))
Install Projectile.
(use-package projectile
:config
(setq projectile-enable-caching t)
(setq projectile-completion-system 'ivy)
;; (define-key projectile-mode-map (kbd "s-P") 'projectile-command-map)
(projectile-mode +1))
(use-package helm-swoop)
(use-package helm
:config
(require 'helm-config)
(helm-mode 1)
(helm-autoresize-mode 1)
(setq helm-follow-mode-persistent t)
(global-set-key (kbd "M-x") 'helm-M-x)
(setq helm-M-x-fuzzy-match t)
(setq helm-buffers-fuzzy-matching t)
(setq helm-recentf-fuzzy-match t)
(setq helm-apropos-fuzzy-match t)
(setq helm-split-window-inside-p t)
;; (global-set-key (kbd "M-y") 'helm-show-kill-ring)
;; (global-set-key (kbd "s-b") 'helm-mini)
;; (global-set-key (kbd "C-x C-f") 'helm-find-files)
;; (global-set-key (kbd "s-f") 'helm-swoop)
)
(setq helm-swoop-pre-input-function
(lambda () ""))
(use-package helm-projectile
:config
(helm-projectile-on))
(use-package helm-ag
:config
(global-set-key (kbd "s-F") 'helm-projectile-ag))
(global-set-key (kbd "s-p") 'helm-projectile-find-file)
(use-package counsel-projectile
:config
(counsel-projectile-mode 1)
(global-set-key (kbd "s-F") 'counsel-projectile-ag)
(global-set-key (kbd "s-p") 'counsel-projectile))
(setq projectile-completion-system 'ivy)
(set-frame-name "EDIT")
(make-frame '((name . "ORG")))
(progn
(make-frame '((name . "TERM")))
(select-frame-by-name "EDIT")
(multi-term))
(make-frame '((name . "ORG")))
(global-set-key (kbd "s-1") (lambda () (interactive) (select-frame-by-name "EDIT")))
(global-set-key (kbd "s-2") (lambda () (interactive) (select-frame-by-name "TERM")))
(global-set-key (kbd "s-3") (lambda () (interactive) (select-frame-by-name "ORG")))
(defface my-custom-curly-face-2
'((t (:foreground "lightgray")))
"Face for fringe curly bitmaps."
:group 'basic-faces)
(set-fringe-bitmap-face 'right-arrow 'my-custom-curly-face-2)
(setq-default fringe-indicator-alist '((truncation left-arrow right-arrow)
(continuation nil right-arrow)
(overlay-arrow . right-triangle)
(up . up-arrow)
(down . down-arrow)
(top top-left-angle top-right-angle)
(bottom bottom-left-angle bottom-right-angle top-right-angle top-left-angle)
(top-bottom left-bracket right-bracket top-right-angle top-left-angle)
(empty-line . empty-line)
(unknown . question-mark)))
(set-face-attribute 'mode-line nil :background "NavajoWhite")
(set-face-attribute 'mode-line-inactive nil :background "#FAFAFA")
(put 'paredit-backward-delete 'delete-selection 'supersede)
(electric-pair-mode 1)
(defvar markdown-electric-pairs '((?* . ?*)) "Electric pairs for markdown-mode.")
(defun markdown-add-electric-pairs ()
(setq-local electric-pair-pairs (append electric-pair-pairs markdown-electric-pairs))
(setq-local electric-pair-text-pairs electric-pair-pairs))
(add-hook 'markdown-mode-hook 'markdown-add-electric-pairs)
(use-package neotree
:config
(setq neo-window-width 32
neo-create-file-auto-open t
neo-banner-message nil
neo-mode-line-type 'neotree
neo-smart-open t
neo-show-hidden-files t
neo-mode-line-type 'none
neo-auto-indent-point t)
(setq neo-theme (if (display-graphic-p) 'nerd 'arrow))
(global-set-key (kbd "s-B") 'neotree-toggle))
I keep my links in links.org
, export them to HTML and access them via browser. This makes the HTML file automatically on every save.
(defun org-mode-export-links ()
"Export links document to HTML automatically when 'links.org' is changed"
(when (equal (buffer-file-name) "PATH_TO/org/links.org")
(progn
(org-html-export-to-html)
(message "HTML exported"))))
(global-set-key (kbd "\e\el") (lambda () (interactive) (find-file "PATH_TO/org/links.org")))
(add-hook 'after-save-hook 'org-mode-export-links)
A nice little real terminal in a popup.
(use-package shell-pop)
(use-package visual-regexp
:config
(define-key global-map (kbd "s-r") 'vr/replace))
Temporarily disabled to see if I really need this.
(use-package company
:config
(setq company-idle-delay 0.1)
(setq company-global-modes '(not org-mode markdown-mode))
(setq company-minimum-prefix-length 1)
(add-hook 'after-init-hook 'global-company-mode))
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu/mu4e")
(require 'mu4e)
(setq
mue4e-headers-skip-duplicates t
mu4e-view-show-images t
mu4e-view-show-addresses 't
mu4e-compose-format-flowed nil
mu4e-date-format "%y/%m/%d"
mu4e-headers-date-format "%Y/%m/%d"
mu4e-change-filenames-when-moving t
mu4e-attachments-dir "~/Downloads"
mu4e-maildir "~/Maildir" ;; top-level Maildir
;; note that these folders below must start with /
;; the paths are relative to maildir root
mu4e-refile-folder "/Archive" ;; saved messages
mu4e-sent-folder "/Sent" ;; folder for sent messages
mu4e-drafts-folder "/Drafts" ;; unfinished messages
mu4e-trash-folder "/Trash") ;; trashed messages
(fset 'my-move-to-trash "mTrash")
(define-key mu4e-headers-mode-map (kbd "d") 'my-move-to-trash)
(define-key mu4e-view-mode-map (kbd "d") 'my-move-to-trash)
(setq mu4e-get-mail-command "mbsync -a")
Install ox-hugo
and enable auto export.
(use-package ox-hugo
:after ox)
Org Capture template to quickly create posts and generate slugs.
;; Populates only the EXPORT_FILE_NAME property in the inserted headline.
(defvar org-capture-templates nil)
(with-eval-after-load 'org-capture
(defun org-hugo-new-subtree-post-capture-template ()
"Returns `org-capture' template string for new blog post.
See `org-capture-templates' for more information."
(let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
(fname (org-hugo-slug title)))
(mapconcat #'identity
`(
,(concat "* TODO " title)
":PROPERTIES:"
,(concat ":EXPORT_FILE_NAME: " fname)
":END:"
"%?\n") ;Place the cursor here finally
"\n")))
(defun org-hugo-new-subtree-post-capture-template-comic ()
"Returns `org-capture' template string for new comic post.
See `org-capture-templates' for more information."
(let* ((title (read-from-minibuffer "Comic Title: ")) ;Prompt to enter the post title
(fname (completing-read "Image File:" (directory-files "/Users/rakhim/code/rakhim.org/static/images/honestly-undefined/" nil "\\.jpg$")))
(cnumber (number-to-string (length (org-map-entries nil nil '("/Users/rakhim/code/rakhim.org/content-org/honestly-undefined.org"))))))
(mapconcat #'identity
`(
,(concat "* TODO " title)
":PROPERTIES:"
,(concat ":EXPORT_FILE_NAME: " fname)
,(concat ":EXPORT_HUGO_SLUG: " cnumber)
":END:"
"%?\n") ;Place the cursor here finally
"\n")))
(add-to-list 'org-capture-templates
'("b"
"Blog post at rakhim.org"
entry
(file+olp "/Users/rakhim/code/rakhim.org/content-org/blog.org" "Blog")
(function org-hugo-new-subtree-post-capture-template)))
(add-to-list 'org-capture-templates
'("c"
"Comic at rakhim.org"
entry
(file+olp "/Users/rakhim/code/rakhim.org/content-org/honestly-undefined.org" "Comics")
(function org-hugo-new-subtree-post-capture-template-comic))))
Org-download creates links, but I need to change the path for my blog. This simple hook runs on every save.
(defun org-mode-blog-fix-org-downloaded-image-paths ()
(when (equal (buffer-file-name) "/Users/rakhim/code/rakhim.org/content-org/blog.org")
(progn
(while (re-search-forward "file:../static" nil t)
(replace-match "file:"))
(while (re-search-backward "file:../static" nil t)
(replace-match "file:"))
)))
(add-hook 'after-save-hook 'org-mode-blog-fix-org-downloaded-image-paths)
Make gc pauses faster by decreasing the threshold.
(setq gc-cons-threshold (* 2 1000 1000))