Emacs does not source this file automatically, so I need to instruct it to. Check org-babel documentation for more info. The following snippet is an adaptation of that idea and goes to my .emacs.d/init.el
.
;;; init.el --- The start of my configuration
;;; Commentary:
;;; Code:
(require 'package)
(setq package-enable-at-startup nil)
(package-initialize)
(defvar rasen/dotfiles-directory
(file-name-as-directory
(expand-file-name ".." (file-name-directory (file-truename user-init-file))))
"The path to the dotfiles directory.")
(require 'org-install)
(require 'ob-tangle)
(require 'el-patch)
;; org-babel fixes to tangle ALL matching sections
(defun rasen/map-regex (regex fn)
"Map the REGEX over the BUFFER executing FN.
FN is called with the match-data of the regex.
Returns the results of the FN as a list."
(save-excursion
(goto-char (point-min))
(let (res)
(save-match-data
(while (re-search-forward regex nil t)
(let ((f (match-data)))
(setq res
(append res
(list
(save-match-data
(funcall fn f))))))))
res)))
(el-patch-feature ob-core)
(el-patch-defun org-babel-expand-noweb-references (&optional info parent-buffer)
"Expand Noweb references in the body of the current source code block.
For example the following reference would be replaced with the
body of the source-code block named `example-block'.
<<example-block>>
Note that any text preceding the <<foo>> construct on a line will
be interposed between the lines of the replacement text. So for
example if <<foo>> is placed behind a comment, then the entire
replacement text will also be commented.
This function must be called from inside of the buffer containing
the source-code block which holds BODY.
In addition the following syntax can be used to insert the
results of evaluating the source-code block named `example-block'.
<<example-block()>>
Any optional arguments can be passed to example-block by placing
the arguments inside the parenthesis following the convention
defined by `org-babel-lob'. For example
<<example-block(a=9)>>
would set the value of argument \"a\" equal to \"9\". Note that
these arguments are not evaluated in the current source-code
block but are passed literally to the \"example-block\"."
(let* ((parent-buffer (or parent-buffer (current-buffer)))
(info (or info (org-babel-get-src-block-info 'light)))
(lang (nth 0 info))
(body (nth 1 info))
(ob-nww-start org-babel-noweb-wrap-start)
(ob-nww-end org-babel-noweb-wrap-end)
(new-body "")
(nb-add (lambda (text) (setq new-body (concat new-body text))))
index source-name evaluate prefix)
(with-temp-buffer
(setq-local org-babel-noweb-wrap-start ob-nww-start)
(setq-local org-babel-noweb-wrap-end ob-nww-end)
(insert body) (goto-char (point-min))
(setq index (point))
(while (and (re-search-forward (org-babel-noweb-wrap) nil t))
(save-match-data (setf source-name (match-string 1)))
(save-match-data (setq evaluate (string-match "(.*)" source-name)))
(save-match-data
(setq prefix
(buffer-substring (match-beginning 0)
(save-excursion
(beginning-of-line 1) (point)))))
;; add interval to new-body (removing noweb reference)
(goto-char (match-beginning 0))
(funcall nb-add (buffer-substring index (point)))
(goto-char (match-end 0))
(setq index (point))
(funcall
nb-add
(with-current-buffer parent-buffer
(save-restriction
(widen)
(mapconcat ;; Interpose PREFIX between every line.
#'identity
(split-string
(if evaluate
(let ((raw (org-babel-ref-resolve source-name)))
(if (stringp raw) raw (format "%S" raw)))
(or
;; Retrieve from the Library of Babel.
(nth 2 (assoc-string source-name org-babel-library-of-babel))
;; Return the contents of headlines literally.
(save-excursion
(when (org-babel-ref-goto-headline-id source-name)
(org-babel-ref-headline-body)))
;; Find the expansion of reference in this buffer.
(save-excursion
(goto-char (point-min))
(let* ((name-regexp
(org-babel-named-src-block-regexp-for-name
source-name))
(comment
(string= "noweb"
(cdr (assq :comments (nth 2 info)))))
(c-wrap
(lambda (s)
;; Comment, according to LANG mode,
;; string S. Return new string.
(with-temp-buffer
(funcall (intern (concat lang "-mode")))
(comment-region (point)
(progn (insert s) (point)))
(org-trim (buffer-string)))))
(expand-body
(lambda (i)
;; Expand body of code blocked
;; represented by block info I.
(let ((b (if (org-babel-noweb-p (nth 2 i) :eval)
(org-babel-expand-noweb-references i)
(nth 1 i))))
(if (not comment) b
(let ((cs (org-babel-tangle-comment-links i)))
(concat (funcall c-wrap (car cs)) "\n"
b "\n"
(funcall c-wrap (cadr cs)))))))))
(if (re-search-forward name-regexp nil t)
(el-patch-swap
(funcall expand-body
(org-babel-get-src-block-info 'light))
;; Found a source block named SOURCE-NAME.
;; Assume it is unique; do not look after
;; `:noweb-ref' header argument.
(mapconcat
#'identity
(rasen/map-regex name-regexp
(lambda (md)
(funcall expand-body
(org-babel-get-src-block-info 'light))))
"\n"))
;; Though luck. We go into the long process
;; of checking each source block and expand
;; those with a matching Noweb reference.
(let ((expansion nil))
(org-babel-map-src-blocks nil
(let* ((info (org-babel-get-src-block-info 'light))
(parameters (nth 2 info)))
(when (equal source-name
(cdr (assq :noweb-ref parameters)))
(push (funcall expand-body info) expansion)
(push (or (cdr (assq :noweb-sep parameters))
"\n")
expansion))))
(when expansion
(mapconcat #'identity
(nreverse (cdr expansion))
""))))))
;; Possibly raise an error if named block doesn't exist.
(if (or org-babel-noweb-error-all-langs
(member lang org-babel-noweb-error-langs))
(error "%s could not be resolved (see \
`org-babel-noweb-error-langs')"
(org-babel-noweb-wrap source-name))
"")))
"[\n\r]")
(concat "\n" prefix))))))
(funcall nb-add (buffer-substring index (point-max))))
new-body))
(org-babel-load-file (expand-file-name "emacs.org" rasen/dotfiles-directory))
;;; init.el ends here
You might notice that I don’t change load-path—that’s because my setup relies on org-plus-contrib
to be installed by NixOS.
All emacs packages are installed with Nix. Disable usage of emacs internal archives.
(require 'package)
(setq package-archives nil)
(setq package-enable-at-startup nil)
(package-initialize)
use-package is a cool emacs library that helps managing emacs configuration making it simpler and more structured. It is the core of my configuration infrastructure and is required
; Do not ensure packages---they are installed with Nix
(setq use-package-always-ensure nil)
; (setq use-package-verbose t)
(eval-when-compile
(require 'use-package))
(require 'bind-key)
Quickly open configuration file.
(global-set-key
(kbd "<f12>")
(lambda ()
(interactive)
(find-file (expand-file-name "emacs.org" rasen/dotfiles-directory))))
Quickly open global system configuration file.
(global-set-key
(kbd "<C-f12>")
(lambda ()
(interactive)
(find-file (expand-file-name "README.org" rasen/dotfiles-directory))))
I use string interpolation in the main README.org
.
This macro copied from here.
(defmacro rasen/interpolate-string (text)
"Expand text like \"Hello <<name>>\" to (format \"Hello %s\" name)."
(let ((pattern "<<\\(.*?\\)>>"))
;; The regexp matches anything between delimiters, non-greedily
(with-temp-buffer
(save-excursion (insert text))
(let ((matches '()))
(while (re-search-forward pattern nil t)
(push (match-string 1) matches)
(replace-match "%s" t t))
`(format ,(buffer-string) ,@(reverse (mapcar 'read matches)))))))
(use-package evil
:init
(setq evil-want-integration nil)
(setq evil-want-keybinding nil)
:config
<<evil-config>>
(evil-mode 1))
A couple of functions to make configuration a little bit easier.
(defun nmap (key action)
(define-key evil-normal-state-map (kbd key) action))
(defun vmap (key action)
(define-key evil-visual-state-map (kbd key) action))
(defun imap (key action)
(define-key evil-insert-state-map (kbd key) action))
(defun mmap (key action)
(define-key evil-motion-state-map (kbd key) action))
(defun omap (key action)
(define-key evil-operator-state-map (kbd key) action))
Use SPC
as one of leaders.
(nmap "SPC" nil)
(vmap "SPC" nil)
(mmap "SPC" nil)
Hard way: prohibit usage of keybinding I have more efficient binding for.
(defmacro rasen/hard-way (key)
`(lambda () (interactive) (error "Don't use this key! Use %s instead" ,key)))
Prohibit usage of arrows.
(mmap "<left>" (rasen/hard-way "h"))
(mmap "<up>" (rasen/hard-way "j"))
(mmap "<down>" (rasen/hard-way "k"))
(mmap "<right>" (rasen/hard-way "l"))
Swap .
and ;
.
(nmap ";" 'evil-repeat)
(nmap "C-;" 'evil-repeat-pop)
(mmap "." 'evil-repeat-find-char)
(nmap "." nil)
(mmap "g." 'goto-last-change)
(mmap "SPC ;" 'eval-expression)
Close other window.
(defun rasen/quit-other ()
(interactive)
(other-window 1)
(quit-window))
(mmap "SPC q" 'rasen/quit-other)
Move to beginning/end of line with H
and L
respectively.
(defun rasen/smart-move-beginning-of-line (arg)
"Move point back to indentation of beginning of line.
Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.
If ARG is not nil or 1, move forward ARG - 1 lines first. If
point reaches the beginning or end of the buffer, stop there."
(interactive "^p")
(setq arg (or arg 1))
;; Move lines first
(when (/= arg 1)
(let ((line-move-visual nil))
(forward-line (1- arg))))
(let ((orig-point (point)))
(back-to-indentation)
(when (= orig-point (point))
(move-beginning-of-line 1))))
(mmap "H" 'rasen/smart-move-beginning-of-line)
(mmap "L" 'evil-end-of-line)
Save buffer with SPC SPC
.
(nmap "SPC SPC" 'save-buffer)
With workman layout, j
is located on qwerty y
and k
—on qwerty n
; thus j
is higher than k
, and it is not convenient to press lower key for going up. Just swap them.
(mmap "k" 'evil-next-visual-line)
(mmap "j" 'evil-previous-visual-line)
(mmap "gk" 'evil-next-line)
(mmap "gj" 'evil-previous-line)
(omap "k" 'evil-next-line)
(omap "j" 'evil-previous-line)
(omap "gk" 'evil-next-visual-line)
(omap "gj" 'evil-previous-visual-line)
(mmap "C-h" 'windmove-left)
(mmap "C-k" 'windmove-down)
(mmap "C-j" 'windmove-up)
(mmap "C-l" 'windmove-right)
I use Vim’s C-a
and C-x
(increment/decrement number at point) a lot.
evil-numbers
provides that functionality for evil.
(use-package evil-numbers
:after evil
:bind (:map evil-normal-state-map
("C-a" . evil-numbers/inc-at-pt)
("C-x" . evil-numbers/dec-at-pt)))
Now, remap C-x
to RET
. (Because C-x
is used for decrementing numbers.)
(mmap "RET" (lookup-key (current-global-map) (kbd "C-x")))
(use-package evil-swap-keys
:config
(global-evil-swap-keys-mode)
(add-hook 'prog-mode-hook #'evil-swap-keys-swap-number-row))
evil-collection is a collection of evil bindings for different modes.
(require 'warnings)
(add-to-list 'warning-suppress-types '(evil-collection))
(use-package evil-collection
:after (evil evil-magit)
:config
(defun rasen/rotate-keys (_mode mode-keymaps &rest _rest)
(evil-collection-translate-key 'normal mode-keymaps
"k" "j"
"j" "k"
"gk" "gj"
"gj" "gk"
(kbd "M-j") (kbd "M-k")
(kbd "M-k") (kbd "M-j")
(kbd "C-j") nil ; used for window-management
(kbd "C-k") nil ; used for window-management
"." ";"
";" "."))
(add-hook 'evil-collection-setup-hook #'rasen/rotate-keys)
(setq evil-collection-mode-list
'(compile
flycheck
help
js2-mode
magit
;; notmuch bindings aren't that cool and are less efficient than native
;; keymap
; notmuch
python
racer
restclient
tide
typescript-mode
which-key))
(evil-collection-init))
(use-package compile
:config
(setq compilation-scroll-output t))
And evil commands to go to navigate errors.
(mmap "SPC ," 'previous-error)
(mmap "SPC ." 'next-error)
(mmap "M-," 'previous-error)
(mmap "M-." 'next-error)
Use single-key y/n
instead of a more verbose yes/no
.
(fset 'yes-or-no-p 'y-or-n-p)
Do not use tabs for indentation.
(setq-default indent-tabs-mode nil)
Make ‘_’ a part of words, so commands like evil-forward-word-begin
work properly.
(add-hook 'prog-mode-hook
(lambda () (modify-syntax-entry ?_ "w")))
Save custom configuration in the ~/.emacs.d/custom.el
file so emacs does not clutter init.el
.
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file t)
Don’t clutter the current directory with backups. Save them in a separate directory.
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
Don’t clutter the current directory with auto-save files.
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/backups/" t)))
Do not create lockfiles either. (I am the only user in the system and only use emacs through daemon, so that should be ok.)
(setq create-lockfiles nil)
(load "server")
(unless (server-running-p)
(server-start))
(use-package ivy
:demand
:bind (:map evil-motion-state-map
("SPC b" . ivy-switch-buffer))
:diminish ivy-mode
:config
Do not start input with ^
and ignore the case.
(setq-default ivy-initial-inputs-alist nil)
(setq-default ivy-re-builders-alist '((t . ivy--regex-ignore-order)))
The normal C-j
is not placed conveniently on Workman layout, so move its function to C-e
(which is qwerty k
).
(define-key ivy-minibuffer-map (kbd "C-e") 'ivy-alt-done)
(define-key ivy-minibuffer-map (kbd "C-M-e") 'ivy-immediate-done)
Remap k-j in ivy-occur
, make it default to emacs state.
(dolist (map (list ivy-occur-mode-map ivy-occur-grep-mode-map))
(evil-define-key 'normal map
(kbd "k") 'ivy-occur-next-line
(kbd "j") 'ivy-occur-previous-line
(kbd "C-n") 'ivy-occur-next-line
(kbd "C-p") 'ivy-occur-previous-line
(kbd "RET") 'ivy-occur-press-and-switch
(kbd "C-e") 'ivy-occur-press-and-switch
(kbd "g r") 'ivy-occur-revert-buffer
(kbd "d") 'ivy-occur-delete-candidate
(kbd "r") 'read-only-mode
(kbd "a") 'ivy-occur-read-action
(kbd "c") 'ivy-occur-toggle-calling
(kbd "f") 'ivy-occur-press
(kbd "o") 'ivy-occur-dispatch
(kbd "q") 'quit-window))
(evil-define-key 'normal ivy-occur-grep-mode-map
(kbd "w") 'ivy-wgrep-change-to-wgrep-mode)
(ivy-mode 1))
I use smex for improved counsel-M-x
(show most frequently used commands first).
(use-package smex
:config
(smex-initialize))
(use-package counsel
:demand
:diminish counsel-mode
:bind (:map evil-motion-state-map
("SPC x" . counsel-M-x)
("SPC f" . counsel-find-file)
("g r" . counsel-git-grep)
("g /" . counsel-rg)
:map read-expression-map
("C-r" . counsel-expression-history))
:config
(counsel-mode 1))
Jump anywhere with a few keystrokes in tree-like way.
(use-package avy
:bind
(:map evil-motion-state-map
("K" . avy-goto-char))
:custom
;; easy workman keys (excluding pinky)
(avy-keys '(?s ?h ?t ?n ?e ?o ?d ?r ?u ?p)))
Edit grep buffers and apply changes to the files.
(use-package wgrep)
A good mode to highlight whitespace issues (leading/trainiling spaces/newlines) and too long lines.
(use-package whitespace
:diminish (global-whitespace-mode
whitespace-mode
whitespace-newline-mode)
:config
(setq-default whitespace-line-column 120
whitespace-style '(face
tab-mark
empty
trailing
lines-tail))
Original face overrides foreground, so you don’t see syntax highlight. Use underlines to show characters past limit.
(set-face-attribute 'whitespace-line nil
:foreground nil
:background nil
:underline (list :color "yellow4" :style 'wave))
Activate the mode in all programming modes.
(add-hook 'prog-mode-hook 'whitespace-mode))
Fix whitespaces on file save.
(use-package whitespace-cleanup-mode
:diminish whitespace-cleanup-mode
:config
(global-whitespace-cleanup-mode 1))
It’s enable by default. Just diminish it.
(use-package undo-tree
:diminish (undo-tree-mode global-undo-tree-mode))
which-key is a minor mode for Emacs that displays the key bindings following your currently entered incomplete command (a prefix) in a popup.
(use-package which-key
:defer 2
:diminish which-key-mode
:config
(which-key-mode))
(use-package nix-sandbox
:disabled
:commands (nix-shell-command
nix-shell
nix-compile
nix-find-sandbox
nix-current-sandbox
nix-executable-find))
(use-package projectile
:bind (:map evil-motion-state-map
("SPC p p" . projectile-switch-project)
("SPC p &" . projectile-run-async-shell-command-in-root)
("SPC p !" . projectile-run-shell-command-in-root)
;; That works much better than the default
("g f" . projectile-find-file-dwim)
("U" . projectile-find-file)
("<f3>" . projectile-test-project)
("<f4>" . projectile-compile-project)
("<f5>" . projectile-run-project))
:commands (projectile-project-name)
:diminish projectile-mode
:config
;; Use the prefix arg if you want to change the compilation command
(setq-default compilation-read-command nil)
(setq-default projectile-use-git-grep t)
(setq-default projectile-completion-system 'ivy)
(projectile-mode))
(use-package counsel-projectile
:after projectile
:config
(counsel-projectile-mode))
Install noccur to multi-occur project-wide.
(use-package noccur)
(el-patch-feature projectile)
(with-eval-after-load 'projectile
(el-patch-defun projectile-default-generic-command (project-type command-type)
"Generic retrieval of COMMAND-TYPEs default cmd-value for PROJECT-TYPE.
If found, checks if value is symbol or string. In case of symbol
resolves to function `funcall's. Return value of function MUST
be string to be executed as command."
(let ((command (plist-get (alist-get project-type projectile-project-types) command-type)))
(cond
((stringp command) command)
((functionp command)
(if (fboundp command)
(funcall (symbol-function command))))
((and (not command) (eq command-type 'compilation-dir))
;; `compilation-dir' is special in that it is used as a fallback for the root
nil)
(el-patch-remove
(t
(user-error "The value for: %s in project-type: %s was neither a function nor a string." command-type project-type)))))))
(use-package magit
:bind (:map evil-motion-state-map
("g m" . magit-status))
:diminish auto-revert-mode
; :defer 6
:init
(global-set-key (kbd "C-c m") (rasen/hard-way "g m"))
:config
<<magit-config>>
)
Do not put files into trash can. Delete them for real.
(setq-default magit-delete-by-moving-to-trash nil)
(setq-default magit-completing-read-function 'ivy-completing-read)
Evilify magit-mode.
(use-package evil-magit
:config
<<evil-magit-config>>
)
(setq evil-magit-use-y-for-yank t)
Evilify magit-blame.
(evil-define-key 'normal magit-blame-read-only-mode-map
(kbd "k") 'evil-next-visual-line
(kbd "j") 'evil-previous-visual-line
(kbd "C-k") 'magit-blame-next-chunk
(kbd "C-j") 'magit-blame-previous-chunk
(kbd "gk") 'magit-blame-next-chunk-same-commit
(kbd "gj") 'magit-blame-previous-chunk-same-commit)
(evil-define-key 'motion magit-blame-mode-map
(kbd "SPC") (lookup-key evil-motion-state-map (kbd "SPC")))
(evil-define-key `(,evil-magit-state visual) magit-mode-map
(kbd "j") 'evil-previous-visual-line
(kbd "k") 'evil-next-visual-line
(kbd "C-j") 'magit-section-backward
(kbd "C-k") 'magit-section-forward
(kbd "gj") 'magit-section-backward-sibling
(kbd "gk") 'magit-section-forward-sibling)
Add a magit command to push HEAD
into a specified ref.
(defun rasen/magit-push-head (target args)
"Push HEAD to a branch read in the minibuffer."
(interactive
(list (magit-read-remote-branch "Push HEAD to"
nil nil nil 'confirm)
(magit-push-arguments)))
(magit-git-push "HEAD" target args))
(if (fboundp 'transient-insert-suffix)
(transient-insert-suffix 'magit-push 'magit-push-other
'(1 "h" "HEAD" rasen/magit-push-head))
(magit-define-popup-action 'magit-push-popup
?h "HEAD" 'rasen/magit-push-head))
(evil-magit)
(defun rasen/magit-fco-master ()
"Fetch origin/master and checkout it."
(interactive)
(magit-git-fetch "origin" "master")
(magit-checkout "origin/master"))
(evil-magit-define-key evil-magit-state 'magit-mode-map
"g m" 'rasen/magit-fco-master)
Sign commits by default.
(setq magit-commit-arguments '("--gpg-sign=DCEF7BCCEB3066C3"))
Show commit signatures in log.
(setq magit-log-arguments '("--graph" "--decorate" "--show-signature" "-n256"))
diff-hl is an emacs package to highlight uncommitted changes.
(use-package diff-hl
:after magit
:config
; (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
; (unless (display-graphic-p)
; (diff-hl-margin-mode t))
; (diff-hl-flydiff-mode t)
(global-diff-hl-mode t))
(use-package yasnippet
:defer 5
:diminish yas-minor-mode
:config
(yas-global-mode 1)
(setq rasen/snippets-directory
(file-name-as-directory
(expand-file-name ".emacs.d/snippets" rasen/dotfiles-directory)))
(make-directory rasen/snippets-directory t)
(yas-load-directory rasen/snippets-directory)
(add-hook 'term-mode-hook (lambda ()
(setq-local yas-dont-activate-functions t))))
Company mode provides autocomplete features.
(use-package company
:defer 2
:bind (:map evil-insert-state-map
("C-n" . company-complete-common-or-cycle)
("C-p" . company-select-previous)
(:map company-active-map
("C-n" . company-complete-common-or-cycle)
("C-p" . company-select-previous-or-abort)
("C-e" . company-complete)
("TAB" . company-complete-common-or-cycle)))
:diminish company-mode
:config
(setq-default company-dabbrev-downcase nil)
(global-company-mode))
(use-package flycheck
:config
;; not sure I actually use nix-sandbox
; (setq flycheck-command-wrapper-function
; (lambda (cmd) (apply 'nix-shell-command (nix-current-sandbox) cmd))
; flycheck-executable-find
; (lambda (cmd) (nix-executable-find (nix-current-sandbox) cmd)))
;; Do not check for elisp header/footer
(setq-default flycheck-disabled-checkers
(append flycheck-disabled-checkers
'(emacs-lisp-checkdoc)))
(global-flycheck-mode))
Auto-close pairs.
(electric-pair-mode)
(use-package hippie-exp
:bind (:map evil-insert-state-map
("C-/" . hippie-expand))
:config
(setq hippie-expand-try-functions-list
'(try-expand-dabbrev-visible
try-expand-dabbrev
try-expand-dabbrev-all-buffers
try-complete-file-name-partially
try-complete-file-name
try-expand-line
try-expand-list)))
(use-package color-identifiers-mode
:commands (color-identifiers-mode
global-color-identifiers-mode)
:diminish (color-identifiers-mode
global-color-identifiers-mode))
Shamelessly stealed from https://github.com/purcell/emacs.d.
(defun rename-this-file-and-buffer (new-name)
"Renames both current buffer and file it's visiting to NEW-NAME."
(interactive "FNew name: ")
(let ((name (buffer-name))
(filename (buffer-file-name)))
(unless filename
(error "Buffer '%s' is not visiting file!" name))
(if (get-buffer new-name)
(message "A buffer named '%s' already exists!" new-name)
(progn
(when (file-exists-p filename)
(rename-file filename new-name 1))
(rename-buffer new-name)
(set-visited-file-name new-name)))))
(defun delete-this-file-and-buffer ()
"Delete the current file, and kill the buffer."
(interactive)
(or (buffer-file-name) (error "No file is currently being edited"))
(when (yes-or-no-p (format "Really delete '%s'?"
(file-name-nondirectory buffer-file-name)))
(delete-file (buffer-file-name))
(kill-buffer)))
(defun add-to-path (str)
"Add an STR to the PATH environment variable."
(setenv "PATH" (concat str ":" (getenv "PATH"))))
(use-package google-translate
:bind (:map evil-motion-state-map
("g t" . rasen/google-translate-at-point)
("g T" . google-translate-smooth-translate))
:config
(defun rasen/google-translate-at-point (arg)
"Translate word at point. If prefix is provided, do reverse translation"
(interactive "P")
(if arg
(google-translate-at-point-reverse)
(google-translate-at-point)))
(require 'google-translate-default-ui)
(require 'google-translate-smooth-ui)
(setq google-translate-show-phonetic t)
(setq google-translate-default-source-language "en"
google-translate-default-target-language "ru")
(setq google-translate-translation-directions-alist '(("en" . "ru") ("ru" . "en")))
; auto-toggle input method
(setq google-translate-input-method-auto-toggling t
google-translate-preferable-input-methods-alist '((nil . ("en"))
(russian-computer . ("ru")))))
(use-package org
:mode ("\\.org$" . org-mode)
:bind (("C-c l" . org-store-link)
:map evil-motion-state-map
("SPC o" . org-clock-out)
("SPC l" . org-clock-in-last)
("SPC j" . org-clock-goto)
("SPC c" . org-capture)
("SPC a" . org-agenda)
:map org-mode-map
("C-c ," . org-time-stamp-inactive))
:ensure org-plus-contrib
:init
<<org-init>>
:config
<<org-config>>
)
Do not indent inside tasks
(setq org-adapt-indentation nil)
Do not indent org-babel blocks.
(setq org-edit-src-content-indentation 0)
Do not indent tags.
(setq org-tags-column 0)
Set tags faces, so they are more distinguishable.
(set-face-attribute 'org-tag nil :inverse-video t)
(set-face-attribute 'org-archived nil :inverse-video nil)
(set-face-attribute 'org-ellipsis nil :inverse-video nil)
(setq org-tag-faces
'(("ARCHIVE" . (:inverse-video nil))))
Open pdfs in external viewer:
(add-to-list 'org-file-apps '("\\.pdf\\'" . "zathura %s"))
Use whitespace-mode
in Org (but don’t show too long lines).
(add-hook 'org-mode-hook (lambda ()
(setq-local whitespace-style '(face
tab-mark
empty
trailing))
(whitespace-mode t)))
My directory for org files.
(setq rasen/org-directory "~/org")
My helper to find all org files in a directory.
(defun rasen/org-files-in-dir (dir)
(f-files dir
(lambda (file) (f-ext? file "org"))
nil))
Package for f-files
and f-ext?
functions.
(use-package f
:commands (f-files f-ext?))
For some reason, org-drill fails to load with the following error:
Debugger entered–Lisp error: (void-function copy-list) copy-list((1 (quote org-drill-visible-cloze-face) nil)) org-drill–compute-cloze-keywords() (defvar org-drill-cloze-keywords (org-drill–compute-cloze-keywords) nil) require(org-drill)
I googled it up and copy-list
seems to be defined in cl
, so load it first:
(require 'cl)
(require 'org-drill)
(setq org-drill-scope (rasen/org-files-in-dir "~/org/drill"))
(add-to-list 'org-modules 'org-drill)
https://bitbucket.org/eeeickythump/org-drill/issues/62/org-drill-doesnt-work-with-org-mode-92
(el-patch-feature org-drill)
(el-patch-defun org-drill-hide-subheadings-if (test)
"TEST is a function taking no arguments. TEST will be called for each
of the immediate subheadings of the current drill item, with the point
on the relevant subheading. TEST should return nil if the subheading is
to be revealed, non-nil if it is to be hidden.
Returns a list containing the position of each immediate subheading of
the current topic."
(let ((drill-entry-level (org-current-level))
(drill-sections nil))
(org-show-subtree)
(save-excursion
(org-map-entries
(lambda ()
(when (and (not (org-invisible-p))
(> (org-current-level) drill-entry-level))
(when (or (/= (org-current-level) (1+ drill-entry-level))
(funcall test))
(hide-subtree))
(push (point) drill-sections)))
(el-patch-swap "" t) 'tree))
(reverse drill-sections)))
Use the following states: TODO
NEXT
DONE
CANCELED
WAIT
.
(setq-default org-todo-keywords
'((sequence "TODO(t)" "NEXT(n!)" "|" "DONE(d!)")
(sequence "|" "CANCELED(c@)")
(sequence "WAIT(w@)" "|")))
(setq-default org-todo-keyword-faces
'(("TODO" . (:foreground "dodger blue" :weight bold))
("NEXT" . (:box t :foreground "red" :weight bold))
("WAIT" . (:box t :foreground "magenta" :weight bold))
("DONE" . (:foreground "grey" :weight bold))
("CANCELED" . (:foreground "gray" :weight bold))))
(setq-default org-use-fast-todo-selection t)
Switch task state with SPC t
.
(evil-define-key 'normal org-mode-map (kbd "SPC t") 'org-todo)
When repeated task is finished, go back to TODO
state.
(setq-default org-todo-repeat-to-state "TODO")
Log state changes to “LOGBOOK” drawer.
(setq-default org-log-into-drawer 't)
Save CLOSED
timestamp when task is done.
(setq org-log-done t)
Schedule task for today and mark it NEXT. I use this a lot during daily planning.
(defun rasen/org-do-today (arg)
"Schedule task for today and mark it NEXT.
If prefix is supplied, select different scheduled time."
(interactive "P")
(org-schedule nil (unless arg "."))
(org-todo "NEXT"))
(evil-define-key 'normal org-mode-map (kbd "SPC n") #'rasen/org-do-today)
(evil-define-key 'normal org-mode-map (kbd "SPC s") 'org-schedule)
(evil-define-key 'normal org-mode-map (kbd "C-c C-s") (rasen/hard-way "SPC s"))
Remove clocks with 0 duration.
(setq-default org-clock-out-remove-zero-time-clocks t)
Save more last clocks.
(setq-default org-clock-history-length 10)
I use an extension that adds page url to the title (used for page tracking). Strip it down here
(defun rasen/strip-url-from-title (title)
(message "stripping: %s" title)
(replace-regexp-in-string
" @ [^ ]*$"
""
(replace-regexp-in-string " \\[[^]]*\\]\\[[^]]*\\]$" "" title)))
My capture templates.
(setq org-capture-templates
`(("u"
"Task: Read this URL"
entry
(file+headline "refile.org" "Articles To Read")
,(concat "* TODO %(rasen/strip-url-from-title \"%:description\")\n:PROPERTIES:\n:CREATED: %U\n:END:\n%:link\n")
:immediate-finish t)
("w"
"Capture web snippet"
entry
(file+headline "refile.org" "Inbox")
,(concat "* Fact: '%(rasen/strip-url-from-title \"%:description\")' :"
(format "%s" org-drill-question-tag)
":\n:PROPERTIES:\n:CREATED: %U\n:SOURCE_URL: %:link\n:END:\n%i\n%?\n")
:immediate-finish t)
("j" "Journal entry" plain
(file+datetree+prompt "~/org/journal.org")
,(concat
; %U does not here work as timestamp is hijacked by
; file+datetime+prompt
"%(format-time-string (org-time-stamp-format t t))"
"\n%?\n"))
("f"
"Capture normal snippet"
entry
(file+headline "my-facts.org" "Inbox")
,(concat "* Fact: '%f' :"
(format "%s" org-drill-question-tag)
":\n:PROPERTIES:\n:CREATED: %U\n:SOURCE_URL: [[%l][%f]]\n:END:\n%i\n%?\n")
:immediate-finish t)
("t" "todo" entry (file "~/org/refile.org")
"* TODO %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n" :clock-in t :clock-resume t)
("m" "meeting" entry (file "~/org/refile.org")
"* %? :Meeting:\n:PROPERTIES:\n:CREATED: %U\n:END:\n" :clock-in t :clock-resume t)
("n" "note" entry (file "~/org/refile.org")
"* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n")))
Enable org-protocol.
(require 'org-protocol)
Instanly go into insert mode on capture.
(add-hook 'org-capture-mode-hook 'evil-insert-state)
%l
in org-capture fails with multiline context, so use only the first line as a context.
(setq org-context-in-file-links 1)
(defun rasen/org-refile-files ()
(rasen/org-files-in-dir rasen/org-directory))
;; non-nil values work bad with ivy
(setq-default org-refile-use-outline-path 'file)
(setq-default org-outline-path-complete-in-steps nil)
(setq org-refile-targets
'(;(nil :maxlevel . 3)
(org-agenda-files :tag . "honeypot")
(org-agenda-files :tag . "PROJECT")
(org-agenda-files :maxlevel . 2)
(rasen/org-refile-files :maxlevel . 1)))
Better bindings.
(evil-define-key 'normal org-mode-map (kbd "SPC w") 'org-refile)
(evil-define-key 'normal org-mode-map (kbd "C-c C-w") (rasen/hard-way "SPC w"))
I like my archive sibling be the last child. The default org-refile ignores that at refiles all entries after archive.
So here is a little patch to refile before archive sibling if it is present.
(defun rasen/org-goto-last-child ()
"Goto the last child, even if it is invisible.
Return t when a child was found. Otherwise don't move point and return nil."
(when (org-goto-first-child)
(while (org-goto-sibling))
t))
(defun rasen/org-goto-last-archive ()
(and (rasen/org-goto-last-child)
(string= org-archive-sibling-heading (org-get-heading t t t t))
(member org-archive-tag (org-get-tags))
(point)))
(require 'org-archive) ; for org-archive-sibling-heading
(el-patch-feature org)
(el-patch-defun org-refile (&optional arg default-buffer rfloc msg)
"Move the entry or entries at point to another heading.
The list of target headings is compiled using the information in
`org-refile-targets', which see.
At the target location, the entry is filed as a subitem of the
target heading. Depending on `org-reverse-note-order', the new
subitem will either be the first or the last subitem.
If there is an active region, all entries in that region will be
refiled. However, the region must fulfill the requirement that
the first heading sets the top-level of the moved text.
With a `\\[universal-argument]' ARG, the command will only visit the target \
location
and not actually move anything.
With a prefix `\\[universal-argument] \\[universal-argument]', go to the \
location where the last
refiling operation has put the subtree.
With a numeric prefix argument of `2', refile to the running clock.
With a numeric prefix argument of `3', emulate `org-refile-keep'
being set to t and copy to the target location, don't move it.
Beware that keeping refiled entries may result in duplicated ID
properties.
RFLOC can be a refile location obtained in a different way.
MSG is a string to replace \"Refile\" in the default prompt with
another verb. E.g. `org-copy' sets this parameter to \"Copy\".
See also `org-refile-use-outline-path'.
If you are using target caching (see `org-refile-use-cache'), you
have to clear the target cache in order to find new targets.
This can be done with a `0' prefix (`C-0 C-c C-w') or a triple
prefix argument (`C-u C-u C-u C-c C-w')."
(interactive "P")
(if (member arg '(0 (64)))
(org-refile-cache-clear)
(let* ((actionmsg (cond (msg msg)
((equal arg 3) "Refile (and keep)")
(t "Refile")))
(regionp (org-region-active-p))
(region-start (and regionp (region-beginning)))
(region-end (and regionp (region-end)))
(org-refile-keep (if (equal arg 3) t org-refile-keep))
pos it nbuf file level reversed)
(setq last-command nil)
(when regionp
(goto-char region-start)
(or (bolp) (goto-char (point-at-bol)))
(setq region-start (point))
(unless (or (org-kill-is-subtree-p
(buffer-substring region-start region-end))
(prog1 org-refile-active-region-within-subtree
(let ((s (point-at-eol)))
(org-toggle-heading)
(setq region-end (+ (- (point-at-eol) s) region-end)))))
(user-error "The region is not a (sequence of) subtree(s)")))
(if (equal arg '(16))
(org-refile-goto-last-stored)
(when (or
(and (equal arg 2)
org-clock-hd-marker (marker-buffer org-clock-hd-marker)
(prog1
(setq it (list (or org-clock-heading "running clock")
(buffer-file-name
(marker-buffer org-clock-hd-marker))
""
(marker-position org-clock-hd-marker)))
(setq arg nil)))
(setq it
(or rfloc
(let (heading-text)
(save-excursion
(unless (and arg (listp arg))
(org-back-to-heading t)
(setq heading-text
(replace-regexp-in-string
org-bracket-link-regexp
"\\3"
(or (nth 4 (org-heading-components))
""))))
(org-refile-get-location
(cond ((and arg (listp arg)) "Goto")
(regionp (concat actionmsg " region to"))
(t (concat actionmsg " subtree \""
heading-text "\" to")))
default-buffer
(and (not (equal '(4) arg))
org-refile-allow-creating-parent-nodes)))))))
(setq file (nth 1 it)
pos (nth 3 it))
(when (and (not arg)
pos
(equal (buffer-file-name) file)
(if regionp
(and (>= pos region-start)
(<= pos region-end))
(and (>= pos (point))
(< pos (save-excursion
(org-end-of-subtree t t))))))
(error "Cannot refile to position inside the tree or region"))
(setq nbuf (or (find-buffer-visiting file)
(find-file-noselect file)))
(if (and arg (not (equal arg 3)))
(progn
(pop-to-buffer-same-window nbuf)
(goto-char (cond (pos)
((org-notes-order-reversed-p) (point-min))
(t (point-max))))
(org-show-context 'org-goto))
(if regionp
(progn
(org-kill-new (buffer-substring region-start region-end))
(org-save-markers-in-region region-start region-end))
(org-copy-subtree 1 nil t))
(with-current-buffer (setq nbuf (or (find-buffer-visiting file)
(find-file-noselect file)))
(setq reversed (org-notes-order-reversed-p))
(org-with-wide-buffer
(if pos
(progn
(goto-char pos)
(setq level (org-get-valid-level (funcall outline-level) 1))
(goto-char
(if reversed
(or (outline-next-heading) (point-max))
(or (el-patch-add (save-excursion (rasen/org-goto-last-archive)))
(save-excursion (org-get-next-sibling))
(org-end-of-subtree t t)
(point-max)))))
(setq level 1)
(if (not reversed)
(goto-char (point-max))
(goto-char (point-min))
(or (outline-next-heading) (goto-char (point-max)))))
(unless (bolp) (newline))
(org-paste-subtree level nil nil t)
(when org-log-refile
(org-add-log-setup 'refile nil nil org-log-refile)
(unless (eq org-log-refile 'note)
(save-excursion (org-add-log-note))))
(and org-auto-align-tags
(let ((org-loop-over-headlines-in-active-region nil))
(org-align-tags)))
(let ((bookmark-name (plist-get org-bookmark-names-plist
:last-refile)))
(when bookmark-name
(with-demoted-errors
(bookmark-set bookmark-name))))
;; If we are refiling for capture, make sure that the
;; last-capture pointers point here
(when (bound-and-true-p org-capture-is-refiling)
(let ((bookmark-name (plist-get org-bookmark-names-plist
:last-capture-marker)))
(when bookmark-name
(with-demoted-errors
(bookmark-set bookmark-name))))
(move-marker org-capture-last-stored-marker (point)))
(when (fboundp 'deactivate-mark) (deactivate-mark))
(run-hooks 'org-after-refile-insert-hook)))
(unless org-refile-keep
(if regionp
(delete-region (point) (+ (point) (- region-end region-start)))
(org-preserve-local-variables
(delete-region
(and (org-back-to-heading t) (point))
(min (1+ (buffer-size)) (org-end-of-subtree t t) (point))))))
(when (featurep 'org-inlinetask)
(org-inlinetask-remove-END-maybe))
(setq org-markers-to-move nil)
(message (concat actionmsg " to \"%s\" in file %s: done") (car it) file)))))))
(setq-default org-archive-default-command 'org-archive-to-archive-sibling)
Bindings.
(evil-define-key 'normal org-mode-map (kbd "SPC r") 'org-archive-subtree-default)
(evil-define-key 'normal org-mode-map (kbd "C-c C-x C-a") (rasen/hard-way "SPC r"))
Set my org files location.
(setq org-directory "~/org"
org-default-notes-file "~/org/refile.org"
org-agenda-files (rasen/org-files-in-dir "~/org"))
Configure my agenda view.
(setq org-agenda-span 6
org-agenda-start-day "-1d")
(setq org-agenda-custom-commands
'(("N" tags "+TODO=\"NEXT\"-PROJECT|+TODO=\"WAIT\"-PROJECT")
("n" todo-tree "NEXT")
("p" tags "+PROJECT/+NEXT") ; active projects
("P" tags "+PROJECT/-DONE-CANCELED") ; all projects
))
Configure stuck projects.
(setq org-tags-exclude-from-inheritance '("PROJECT"))
(setq org-use-tag-inheritance nil)
(setq org-stuck-projects
'("+PROJECT/-TODO-DONE-CANCELED-WAIT" ("NEXT" "WAIT") nil ""))
org-agenda-list-stuck-projects
marks project as unstuck if its header matches any of specified keywords. This makes all NEXT
projects automatically unstuck.
Fix this by skipping the first line in org-agenda-skip-function
.
(el-patch-feature org-agenda)
(el-patch-defun org-agenda-list-stuck-projects (&rest ignore)
"Create agenda view for projects that are stuck.
Stuck projects are project that have no next actions. For the definitions
of what a project is and how to check if it stuck, customize the variable
`org-stuck-projects'."
(interactive)
(let* ((org-agenda-overriding-header
(or org-agenda-overriding-header "List of stuck projects: "))
(matcher (nth 0 org-stuck-projects))
(todo (nth 1 org-stuck-projects))
(tags (nth 2 org-stuck-projects))
(gen-re (org-string-nw-p (nth 3 org-stuck-projects)))
(todo-wds
(if (not (member "*" todo)) todo
(org-agenda-prepare-buffers (org-agenda-files nil 'ifmode))
(org-delete-all org-done-keywords-for-agenda
(copy-sequence org-todo-keywords-for-agenda))))
(todo-re (and todo
(format "^\\*+[ \t]+\\(%s\\)\\>"
(mapconcat #'identity todo-wds "\\|"))))
(tags-re (cond ((null tags) nil)
((member "*" tags) org-tag-line-re)
(tags
(let ((other-tags (format "\\(?:%s:\\)*" org-tag-re)))
(concat org-outline-regexp-bol
".*?[ \t]:"
other-tags
(regexp-opt tags t)
":" other-tags "[ \t]*$")))
(t nil)))
(re-list (delq nil (list todo-re tags-re gen-re)))
(skip-re
(if (null re-list)
(error "Missing information to identify unstuck projects")
(mapconcat #'identity re-list "\\|")))
(org-agenda-skip-function
;; Skip entry if `org-agenda-skip-regexp' matches anywhere
;; in the subtree.
`(lambda ()
(and (save-excursion
(let ((case-fold-search nil)
(el-patch-add (subtree-end (save-excursion (org-end-of-subtree t)))))
(el-patch-add (forward-line))
(re-search-forward
,skip-re
(el-patch-swap
(save-excursion (org-end-of-subtree t))
subtree-end)
t)))
(progn (outline-next-heading) (point))))))
(org-tags-view nil matcher)
(setq org-agenda-buffer-name (buffer-name))
(with-current-buffer org-agenda-buffer-name
(setq org-agenda-redo-command
`(org-agenda-list-stuck-projects ,current-prefix-arg))
(let ((inhibit-read-only t))
(add-text-properties
(point-min) (point-max)
`(org-redo-cmd ,org-agenda-redo-command))))))
Code-hightlight (fontify) org-babel (#+begin_src
) blocks.
(setq org-src-fontify-natively t)
Do not confirm evaluation for emacs-lisp.
(defun rasen/org-confirm-babel-evaluate (lang body)
(not (member lang '("emacs-lisp"))))
(setq org-confirm-babel-evaluate 'rasen/org-confirm-babel-evaluate)
Fix exporting for confluence.
ox-confluence
has an issue with verbatim—it doesn’t redefine verbatim translation, so org-ascii-verbatim
is used. The following makes org-ascii-verbatim
produce proper confluence fixed-width block.
(add-to-list 'org-modules 'ox-confluence)
(setq org-ascii-verbatim-format "\{\{%s\}\}")
Allow encrypted entries in org files.
(require 'org-crypt)
(org-crypt-use-before-save-magic)
(setq org-tags-exclude-from-inheritance '("crypt"))
(setq org-crypt-key "[email protected]")
(add-hook 'org-babel-pre-tangle-hook 'org-decrypt-entries t)
(use-package org-pomodoro
:commands (org-pomodoro)
:config
(setq org-pomodoro-keep-killed-pomodoro-time t)
)
(require 'org-habit)
(setq org-habit-show-habits-only-for-today t)
(setq org-habit-preceding-days 25)
(setq org-habit-following-days 3)
; (add-to-list 'org-modules 'org-habit)
(use-package evil-org
:after org
:custom
; swap j/k
(evil-org-movement-bindings '((up . "j")
(down . "k")
(left . "h")
(right . "l")))
:config
(add-hook 'org-mode-hook 'evil-org-mode)
(add-hook 'evil-org-mode-hook
(lambda ()
(evil-org-set-key-theme)))
(require 'evil-org-agenda)
(evil-org-agenda-set-keys)
(evil-define-key 'normal org-mode-map
"go" 'org-open-at-point)
(evil-define-key 'motion org-agenda-mode-map
"k" 'org-agenda-next-line
"j" 'org-agenda-previous-line
"gk" 'org-agenda-next-item
"gj" 'org-agenda-previous-item
(kbd "C-k") 'org-agenda-next-item
(kbd "C-j") 'org-agenda-previous-item
"K" 'org-agenda-priority-down
"J" 'org-agenda-priority-up
(kbd "M-k") 'org-agenda-drag-line-forward
(kbd "M-j") 'org-agenda-drag-line-backward)
(evil-define-key 'motion org-agenda-mode-map
; unset prefix
(kbd "SPC") nil
(kbd "SPC s") 'org-agenda-schedule
(kbd "C-c C-s") (rasen/hard-way "SPC s")
(kbd "SPC d") 'org-agenda-deadline
(kbd "C-c C-d") (rasen/hard-way "SPC d")
(kbd "SPC w") 'org-agenda-refile
(kbd "C-c C-w") (rasen/hard-way "SPC w")
"go" 'org-agenda-open-link))
(use-package elisp-mode
:ensure nil ; built-in
:config
<<elisp-mode-config>>
)
Eval last sexp Vim-style.
(evil-define-operator rasen/evil-eval (beg end type)
"Evaluate region."
(if (eq type 'block)
(evil-apply-on-block 'eval-region beg end nil)
(eval-region beg end)))
(evil-define-key 'motion emacs-lisp-mode-map (kbd "SPC e") 'eval-last-sexp)
(evil-define-key 'visual emacs-lisp-mode-map (kbd "SPC e") 'rasen/evil-eval)
Pretty self-explaining.
(use-package nix-mode
:mode "\\.nix$")
(Old un-reviewed stuff.)
(use-package haskell-mode
:mode "\\.hs$"
:init
(setq company-ghc-show-info t)
(setq flycheck-ghc-stack-use-nix t)
:config
(add-hook 'haskell-mode-hook 'interactive-haskell-mode)
(add-hook 'haskell-mode-hook 'haskell-decl-scan-mode)
(setq haskell-compile-cabal-build-command "cd %s && stack build")
(setq haskell-compile-cabal-build-command-alt "cd %s && cabal build --ghc-options=-ferror-spans")
;; Use Nix for stack ghci
(add-to-list 'haskell-process-args-stack-ghci "--nix")
(add-to-list 'haskell-process-args-stack-ghci "--test")
;; Use Nix for default build/test command
(projectile-register-project-type 'haskell-stack
'("stack.yaml")
:compile "stack build --nix"
:test "stack build --nix --test")
(define-key haskell-mode-map [f8] 'haskell-navigate-imports)
(define-key haskell-mode-map (kbd "C-c C-b") 'haskell-compile)
(define-key haskell-mode-map (kbd "C-c v c") 'haskell-cabal-visit-file)
; haskell-interactive-mode
(define-key haskell-mode-map (kbd "C-x C-d") nil)
(define-key haskell-mode-map (kbd "C-c C-z") 'haskell-interactive-switch)
(define-key haskell-mode-map (kbd "C-c C-l") 'haskell-process-load-file)
(define-key haskell-mode-map (kbd "C-c C-t") 'haskell-process-do-type)
(define-key haskell-mode-map (kbd "C-c C-i") 'haskell-process-do-info)
(define-key haskell-mode-map (kbd "C-c M-.") nil)
(define-key haskell-mode-map (kbd "C-c C-d") nil)
;; Disable popups (i.e., report errors in the interactive shell).
(setq haskell-interactive-popup-errors nil)
(setq haskell-process-suggest-remove-import-lines t
haskell-process-auto-import-loaded-modules t)
(with-eval-after-load 'align
(add-to-list 'align-rules-list
'(haskell-types
(regexp . "\\(\\s-+\\)\\(::\\|∷\\)\\s-+")
(modes . '(haskell-mode literate-haskell-mode))))
(add-to-list 'align-rules-list
'(haskell-assignment
(regexp . "\\(\\s-+\\)=\\s-+")
(modes . '(haskell-mode literate-haskell-mode))))
(add-to-list 'align-rules-list
'(haskell-arrows
(regexp . "\\(\\s-+\\)\\(->\\|→\\)\\s-+")
(modes . '(haskell-mode literate-haskell-mode))))
(add-to-list 'align-rules-list
'(haskell-left-arrows
(regexp . "\\(\\s-+\\)\\(<-\\|←\\)\\s-+")
(modes . '(haskell-mode literate-haskell-mode))))))
(use-package eldoc
:commands (eldoc-mode)
:diminish eldoc-mode)
(use-package rust-mode
:mode ("\\.rs$" . rust-mode))
(use-package racer
:after rust-mode
:commands racer-mode
:diminish racer-mode
:config
(add-hook 'rust-mode-hook #'racer-mode)
(add-hook 'racer-mode-hook #'eldoc-mode))
This const is taken from doxymacs and is subject to GPLv2. I’ve copied it my dotfiles as I don’t need all doxymacs features and setup is non-trivial. (It requires compilation, there is no melpa package.)
(defconst doxymacs-doxygen-keywords
(list
(list
;; One shot keywords that take no arguments
(concat "\\([@\\\\]\\(brief\\|li\\|\\(end\\)?code\\|sa"
"\\|note\\|\\(end\\)?verbatim\\|return\\|arg\\|fn"
"\\|hideinitializer\\|showinitializer"
"\\|parblock\\|endparblock"
;; FIXME
;; How do I get & # < > % to work?
;;"\\|\\\\&\\|\\$\\|\\#\\|<\\|>\\|\\%"
"\\|internal\\|nosubgrouping\\|author\\|date\\|endif"
"\\|invariant\\|post\\|pre\\|remarks\\|since\\|test\\|version"
"\\|\\(end\\)?htmlonly\\|\\(end\\)?latexonly\\|f\\$\\|file"
"\\|\\(end\\)?xmlonly\\|\\(end\\)?manonly\\|property"
"\\|mainpage\\|name\\|overload\\|typedef\\|deprecated\\|par"
"\\|addindex\\|line\\|skip\\|skipline\\|until\\|see"
"\\|endlink\\|callgraph\\|endcond\\|else\\)\\)\\>")
'(0 font-lock-keyword-face prepend))
;; attention, warning, etc. given a different font
(list
"\\([@\\\\]\\(attention\\|warning\\|todo\\|bug\\)\\)\\>"
'(0 font-lock-warning-face prepend))
;; keywords that take a variable name as an argument
(list
(concat "\\([@\\\\]\\(param\\(?:\\s-*\\[\\(?:in\\|out\\|in,out\\)\\]\\)?"
"\\|a\\|namespace\\|relates\\(also\\)?"
"\\|var\\|def\\)\\)\\s-+\\(\\sw+\\)")
'(1 font-lock-keyword-face prepend)
'(4 font-lock-variable-name-face prepend))
;; keywords that take a type name as an argument
(list
(concat "\\([@\\\\]\\(class\\|struct\\|union\\|exception\\|enum"
"\\|throw\\|interface\\|protocol\\)\\)\\s-+\\(\\(\\sw\\|:\\)+\\)")
'(1 font-lock-keyword-face prepend)
'(3 font-lock-type-face prepend))
;; keywords that take a function name as an argument
(list
"\\([@\\\\]retval\\)\\s-+\\([^ \t\n]+\\)"
'(1 font-lock-keyword-face prepend)
'(2 font-lock-function-name-face prepend))
;; bold
(list
"\\([@\\\\]b\\)\\s-+\\([^ \t\n]+\\)"
'(1 font-lock-keyword-face prepend)
'(2 (quote bold) prepend))
;; code
(list
"\\([@\\\\][cp]\\)\\s-+\\([^ \t\n]+\\)"
'(1 font-lock-keyword-face prepend)
'(2 (quote underline) prepend))
;; italics/emphasised
(list
"\\([@\\\\]e\\(m\\)?\\)\\s-+\\([^ \t\n]+\\)"
'(1 font-lock-keyword-face prepend)
'(3 (quote italic) prepend))
;; keywords that take a list
(list
"\\([@\\\\]ingroup\\)\\s-+\\(\\(\\sw+\\s-*\\)+\\)\\s-*$"
'(1 font-lock-keyword-face prepend)
'(2 font-lock-string-face prepend))
;; one argument that can contain arbitrary non-whitespace stuff
(list
(concat "\\([@\\\\]\\(link\\|copydoc\\|xrefitem"
"\\|if\\(not\\)?\\|elseif\\)\\)"
"\\s-+\\([^ \t\n]+\\)")
'(1 font-lock-keyword-face prepend)
'(4 font-lock-string-face prepend))
;; one optional argument that can contain arbitrary non-whitespace stuff
(list
"\\([@\\\\]\\(cond\\|dir\\)\\(\\s-+[^ \t\n]+\\)?\\)"
'(1 font-lock-keyword-face prepend)
'(3 font-lock-string-face prepend t))
;; one optional argument with no space between
(list
"\\([@\\\\]\\(~\\)\\([^ \t\n]+\\)?\\)"
'(1 font-lock-keyword-face prepend)
'(3 font-lock-string-face prepend t))
;; one argument that has to be a filename
(list
(concat "\\([@\\\\]\\(example\\|\\(dont\\)?include\\|includelineno"
"\\|htmlinclude\\|verbinclude\\)\\)\\s-+"
"\\(\"?[~:\\/a-zA-Z0-9_. ]+\"?\\)")
'(1 font-lock-keyword-face prepend)
'(4 font-lock-string-face prepend))
;; dotfile <file> ["caption"]
(list
(concat "\\([@\\\\]dotfile\\)\\s-+"
"\\(\"?[~:\\/a-zA-Z0-9_. ]+\"?\\)\\(\\s-+\"[^\"]+\"\\)?")
'(1 font-lock-keyword-face prepend)
'(2 font-lock-string-face prepend)
'(3 font-lock-string-face prepend t))
;; image <format> <file> ["caption"] [<sizeindication>=<size>]
(list
"\\([@\\\\]image\\)\\s-+\\(html\\|latex\\)\\s-+\\(\"?[~:\\/a-zA-Z0-9_. ]+\"?\\)\\(\\s-+\"[^\"]+\"\\)?\\(\\s-+\\sw+=[0-9]+\\sw+\\)?"
'(1 font-lock-keyword-face prepend)
'(2 font-lock-string-face prepend)
'(3 font-lock-string-face prepend)
'(4 font-lock-string-face prepend t)
'(5 font-lock-string-face prepend t))
;; one argument that has to be a word
(list
(concat "\\([@\\\\]\\(addtogroup\\|defgroup\\|weakgroup"
"\\|page\\|anchor\\|ref\\|section\\|subsection\\|subsubsection\\|paragraph"
"\\)\\)\\s-+\\(\\sw+\\)")
'(1 font-lock-keyword-face prepend)
'(3 font-lock-string-face prepend))))
(defconst doxygen-font-lock-keywords
`((,(lambda (limit)
(c-font-lock-doc-comments "/\\(\\*[\\*!]\\|/[/!]\\)<?" limit
doxymacs-doxygen-keywords)))))
(setq c-doc-comment-style '((java-mode . javadoc)
(pike-mode . autodoc)
(c-mode . doxygen)
(c++-mode . doxygen)))
(use-package pip-requirements
:mode "^requirements.txt$")
(use-package js2-mode
:mode "\\.js$"
:init
(add-hook 'js2-mode-hook 'color-identifiers-mode)
:config
(defun rasen/use-eslint-from-node-modules ()
(let* ((root (locate-dominating-file
(or (buffer-file-name) default-directory)
"node_modules"))
(eslint (and root
(expand-file-name "node_modules/eslint/bin/eslint.js"
root))))
(when (and eslint (file-executable-p eslint))
(setq-local flycheck-javascript-eslint-executable eslint))))
(add-hook 'flycheck-mode-hook #'rasen/use-eslint-from-node-modules)
(add-hook 'js2-mode-hook
(lambda ()
(flycheck-select-checker 'javascript-eslint)))
(setq-default flycheck-disabled-checkers
(append flycheck-disabled-checkers
'(javascript-jshint)))
(setq-default flycheck-enabled-checkers
(append flycheck-enabled-checkers
'(javascript-eslint)))
(flycheck-add-mode 'javascript-eslint 'js2-mode)
(setq-default js2-strict-trailing-comma-warning nil))
(el-patch-feature flycheck)
(el-patch-defun flycheck-eslint-config-exists-p ()
"Whether there is a valid eslint config for the current buffer."
(let* ((executable (flycheck-find-checker-executable 'javascript-eslint))
(el-patch-add
(command (funcall flycheck-command-wrapper-function
(cons executable '("--print-config" ".")))))
(exitcode (and executable
(el-patch-swap
(call-process executable nil nil nil "--print-config" ".")
(el-patch-literal command (apply 'call-process (car command) nil nil nil (cdr command)))))))
(eq exitcode 0)))
(use-package rjsx-mode
:mode "\\.js$"
:config
(setq-default js-indent-level 2))
(use-package typescript-mode
:init
(add-hook 'web-mode-hook
(lambda ()
(when (or (string-equal "tsx" (file-name-extension buffer-file-name))
(string-equal "ts" (file-name-extension buffer-file-name)))
(typescript-mode))))
:config
(setq-default typescript-indent-level 2))
(use-package tide
:commands (tide-setup tide-hl-identifier-mode tide-format-before-save rasen/setup-tide-mode)
:hook (typescript-mode . rasen/setup-tide-mode)
:init
; (add-hook 'before-save 'tide-format-before-save)
(defun rasen/setup-tide-mode ()
(interactive)
(tide-setup)
(flycheck-mode +1)
(setq flycheck-check-syntax-automatically '(save mode-enabled))
(eldoc-mode +1)
(tide-hl-identifier-mode +1)
(company-mode +1)))
(use-package flycheck-jest
:after flycheck)
(use-package purescript-mode
:mode "\\.purs$"
:config
(add-hook 'purescript-mode-hook 'turn-on-purescript-indentation))
(use-package psc-ide
:commands (psc-ide-mode)
:init
(add-hook 'purescript-mode-hook 'psc-ide-mode))
(use-package php-mode
:mode "\\.php$")
(use-package web-mode
:commands (web-mode)
:init
(add-to-list 'auto-mode-alist '("\\.blade.php\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.ts\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . web-mode))
:config
(setq web-mode-engines-alist
'(("php" . "\\.phtml\\'")
("blade" . "\\.blade\\."))
)
)
(use-package groovy-mode
:mode "\\.\\(groovy\\|gradle\\)$")
(use-package go-mode
:mode "\\.go$")
(use-package lua-mode
:mode ("\\.lua$" . lua-mode)
:config
(setq lua-indent-level 4))
(use-package ledger-mode
:mode "\\.journal$"
:config
(setq ledger-binary-path "hledger")
(add-hook 'ledger-mode-hook 'orgstruct-mode))
(use-package markdown-mode
:mode ("\\.\\(markdown\\|mdown\\|md\\)$" . markdown-mode)
:init
(add-hook 'markdown-mode-hook 'visual-line-mode)
:config
(setq markdown-fontify-code-blocks-natively t))
Package edit-indirect needed to edit code blocks.
(use-package edit-indirect
:after markdown-mode)
(use-package json-mode
:mode "\\.json$")
(use-package yaml-mode
:mode ("\\.\\(yml\\|yaml\\)$" . yaml-mode))
(use-package jinja2-mode
:mode "\\.j2$")
(use-package gitconfig-mode
:mode "^\\.gitconfig$")
(use-package restclient
:mode "\\.http$")
(use-package terraform-mode
:mode "\\.tf$")
(use-package graphviz-dot-mode
:mode "\\.dot$"
:config
(setq graphviz-dot-view-command "dotty %s"))
(use-package gnus
:config
(setq user-full-name "Alexey Shmalko"
user-mail-address "[email protected]")
(setq gnus-select-method
'(nnimap "Mail"
(nnimap-stream shell)
(nnimap-shell-program "/var/run/current-system/sw/libexec/dovecot/imap")))
(setq gnus-secondary-select-methods nil)
(setq gnus-parameters
'(("Work/?.*"
(posting-style
(name "Alexey Shmalko")
(address "[email protected]")))
("KaaIoT/?.*"
(posting-style
(name "Alexey Shmalko")
(address "[email protected]")))
("Personal/?.*"
(posting-style
(name "Alexey Shmalko")
(address "[email protected]")))))
(setq gnus-fetch-old-headers 'some)
(setq gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]")
(setq message-sendmail-f-is-evil t
message-sendmail-envelope-from nil ; 'header
message-sendmail-extra-arguments '("--read-envelope-from")
mail-specify-envelope-from nil
send-mail-function 'message-send-mail-with-sendmail
message-send-mail-function 'message-send-mail-with-sendmail
sendmail-program "msmtp")
(add-hook 'message-setup-hook 'mml-secure-message-sign-pgpmime)
(setq mm-verify-option 'always)
; (add-to-list 'mm-automatic-display "application/pgp")
; (add-to-list 'mm-automatic-display "application/pgp-signature")
; (add-to-list 'mm-inlined-types "application/pgp")
(setq gnus-buttonized-mime-types '("multipart/encrypted" "multipart/signed"))
(setq gnus-check-new-newsgroups nil ;; NOTE: don't check for new groups
gnus-save-newsrc-file nil ;; NOTE: don't write `.newsrc' file
gnus-read-newsrc-file nil ;; NOTE: don't read it, either
gnus-interactive-exit nil
gnus-save-killed-list nil)
;; TODO uncomment
; (require 'gnus-article-treat-patch)
; (setq ft/gnus-article-patch-conditions
; '( "^@@ -[0-9]+,[0-9]+ \\+[0-9]+,[0-9]+ @@" ))
)
(use-package mbsync
:bind (:map gnus-group-mode-map
("f" . mbsync))
:config
(setq mbsync-executable "mbsync")
(add-hook 'mbsync-exit-hook 'gnus-group-get-new-news))
(use-package notmuch
:config
(setq notmuch-archive-tags '("-unread"))
(setq notmuch-saved-searches
'(
(:name "unread" :query "tag:unread and not tag:nixos" :key "u")
(:name "unread-inbox" :query "tag:inbox and tag:unread" :key "i")
(:name "unread-egoless" :query "tag:egoless and tag:unread" :key "e")
(:name "unread-nixos" :query "tag:unread and tag:nixos and not tag:nixpkgs" :key "n")
(:name "unread-nixpkgs" :query "tag:unread and tag:nixpkgs" :key "p")
(:name "unread-triage" :query "tag:unread and tag:nixpkgs-participating" :key "t")
(:name "unread-doctoright" :query "tag:unread and tag:doctoright" :key "d")
(:name "unread-other" :query "tag:unread and not tag:nixos and not tag:inbox and not tag:doctoright" :key "o")
(:name "later" :query "tag:later" :key "l")
(:name "flagged" :query "tag:flagged" :key "f")
(:name "personal" :query "tag:personal" :key "P")
(:name "doctoright" :query "tag:doctoright" :key "D")
(:name "sent" :query "tag:sent" :key "s")
(:name "drafts" :query "tag:draft" :key "r")
(:name "all mail" :query "*" :key "a")))
(setq notmuch-hello-sections
'(; notmuch-hello-insert-header
notmuch-hello-insert-saved-searches
; notmuch-hello-insert-search
notmuch-hello-insert-alltags
notmuch-hello-insert-recent-searches
; notmuch-hello-insert-footer
))
(setq-default notmuch-show-indent-content nil)
(defun rasen/mbsync ()
(interactive)
(async-shell-command "mbsync sync-gmail & mbsync sync-egoless & mbsync sync-ps & wait; /home/rasen/dotfiles/notmuch.sh" "*mbsync*"))
(define-key 'notmuch-hello-mode-map "f" 'rasen/mbsync)
(define-key 'notmuch-hello-mode-map "g" 'notmuch-refresh-all-buffers)
(define-key 'notmuch-search-mode-map "g" 'notmuch-refresh-all-buffers)
(define-key 'notmuch-show-mode-map "g" 'notmuch-refresh-all-buffers)
(define-key 'notmuch-search-mode-map "k" 'notmuch-search-archive-thread)
(define-key 'notmuch-show-mode-map "k" 'notmuch-show-archive-thread-then-next)
; remap old function
(define-key 'notmuch-search-mode-map "K" 'notmuch-tag-jump)
(define-key 'notmuch-show-mode-map "K" 'notmuch-tag-jump)
(define-key 'notmuch-show-mode-map (kbd "M-u")
(lambda ()
(interactive)
(notmuch-show-tag '("+unread"))))
; notmuch-tag-formats
(setq-default notmuch-tagging-keys
'(("a" notmuch-archive-tags "Archive")
("u" notmuch-show-mark-read-tags "Mark read")
("m" ("+muted") "Mute")
("f" ("+flagged") "Flag")
("s" ("+spam" "-inbox") "Mark as spam")
("d" ("+deleted" "-inbox") "Delete")))
(require 'org-notmuch))
Emacs has built-in capability to change keyboard layout (for insert state only), which is triggered by C-\
. In order to work properly, Emacs needs to know my keyboard layout.
(use-package quail
:ensure nil ; built-in?
:config
(add-to-list 'quail-keyboard-layout-alist
'("workman" . "\
\
1!2@3#4$5%6^7&8*9(0)-_=+`~ \
qQdDrRwWbBjJfFuUpP;:[{]}\\| \
aAsShHtTgGyYnNeEoOiI'\" \
zZxXmMcCvVkKlL,<.>/? \
"))
(quail-set-keyboard-layout "workman"))
Hide menu, toolbar, scrollbar.
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
Do not show startup screen.
(setq inhibit-startup-screen t)
I use airline with molokai color scheme.
(use-package airline-themes
:config
(require 'cl)
(setq-default powerline-display-mbsync-info t)
(load-theme 'airline-molokai t)
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes")
(load-theme 'molokai t))
(defun rasen/font-exists-p (font)
"Check if the FONT exists."
(and (display-graphic-p) (not (null (x-list-fonts font)))))
(defun rasen/set-my-fonts ()
(cond ((rasen/font-exists-p "Terminess Powerline")
(set-face-attribute 'fixed-pitch nil :family "Terminess Powerline" :height 160)
(set-face-attribute 'default nil :family "Terminess Powerline" :height 160))
((rasen/font-exists-p "Terminus")
(set-face-attribute 'fixed-pitch nil :family "Terminus" :height 160)
(set-face-attribute 'default nil :family "Terminus" :height 160))))
(rasen/set-my-fonts)
Apply my font setting when new frame is created (useful when emacs is started in daemon mode).
(defun rasen/font-hook (frame)
(select-frame frame)
(rasen/set-my-fonts))
(add-hook 'after-make-frame-functions 'rasen/font-hook)
Hightlight parentheses, show current column.
(show-paren-mode 1)
(column-number-mode 1)
Highlight current line.
(global-hl-line-mode)
Draw block cursor as wide as the glyph under it. For example, if a block cursor is over a tab, it will be drawn as wide as that tab on the display.
(setq-default x-stretch-cursor t)
scroll-margin
is a number of lines of margin at the top and bottom of a window. Scroll the window whenever point gets within this many lines of the top or bottom of the window. (scroll-conservatively
should be greater than 100 to never recenter point. Value 1 helps, but eventually recenters cursor if you scroll too fast.)
(setq scroll-margin 3
scroll-conservatively 101)
Center all text in the buffer in some modes.
(use-package visual-fill-column
:commands (visual-fill-column-mode)
:init
(add-hook 'org-mode-hook
(lambda ()
(setq-local fill-column 81)
(visual-line-mode t)
(visual-fill-column-mode t)))
:config
(setq-default visual-fill-column-center-text t
visual-fill-column-fringes-outside-margins nil))
Add a little bit of highlighting for the cursor, when buffer scrolls, so I don’t lose it.
(use-package beacon
:diminish beacon-mode
:config
(beacon-mode 1))
Add project name to the title, so I can later analyze my app usage.
(setq-default frame-title-format
'("[%m] " (:eval (projectile-project-name))))