|
|
#+title: emacs init file |
|
|
#+author: Jacopo De Simoi |
|
|
#+email: jacopods@math.utoronto.ca |
|
|
#+options: *:t ::t |
|
|
|
|
|
* COMMENT About indentation in this file |
|
|
This file will be indented with ~org-adapt-indentation~ set to t |
|
|
|
|
|
* TODO Look at tangling at |
|
|
[http://thewanderingcoder.com/2015/02/literate-emacs-configuration/] |
|
|
|
|
|
although I'd rather do something like a local variable evaluation such as |
|
|
# -*- eval: (add-hook 'before-save-hook icalendar-export-to-ics nil t) -*- |
|
|
Or makefile approach |
|
|
[https://emacs.stackexchange.com/questions/27126/is-it-possible-to-org-bable-tangle-an-org-file-from-the-command-line] |
|
|
|
|
|
See also [http://orgmode.org/manual/Extracting-source-code.html#Extracting-source-code] |
|
|
|
|
|
|
|
|
* Introduction |
|
|
** ~setq~ vs ~setq-default~ |
|
|
Some variables are buffer-local, others are not; for non buffer-local |
|
|
variables the two commands are equivalent; for the buffer-local variables, |
|
|
setq acts on the current buffer, whereas setq-default sets the default |
|
|
value of the variable for buffers that do not have a local value. |
|
|
|
|
|
:source: [https://stackoverflow.com/a/18173666] |
|
|
|
|
|
** Why not ~package.el~ |
|
|
it is convenient, but it is not git-based; ~borg.el~ is promising, but it is |
|
|
way complicated; I can do everything by hand as soon as it is neatly |
|
|
~org~-anized. In the end we use a few, but I plan to phase them out and |
|
|
replace them with git submodules |
|
|
|
|
|
* Files and dirs |
|
|
Move ~Customize~ to a separate file |
|
|
#+begin_src emacs-lisp |
|
|
(setq custom-file "~/.emacs.d/custom.el") |
|
|
(load custom-file 'noerror) |
|
|
#+end_src |
|
|
|
|
|
Add relevant dirs to load path [This is somewhat undesirable; I should |
|
|
uniformize the path-loading business at some point…] |
|
|
#+begin_src emacs-lisp |
|
|
(let ((default-directory "~/.emacs.d/")) |
|
|
(normal-top-level-add-subdirs-to-load-path) |
|
|
(normal-top-level-add-to-load-path '("biblio.el" |
|
|
"fringe-helper.el" |
|
|
"multiple-cursors.el" |
|
|
"expand-region.el" |
|
|
"dash.el" |
|
|
"webpaste.el" |
|
|
"highlight-parentheses.el"))) |
|
|
|
|
|
#+end_src |
|
|
|
|
|
Put autosave files (ie #foo#) and backup files (ie foo~) in ~/.emacs.d/. |
|
|
create the autosave dir if necessary, since emacs won't. |
|
|
#+begin_src emacs-lisp |
|
|
(make-directory "~/.emacs.d/autosaves/" t) |
|
|
(make-directory "~/.emacs.d/backup/" t) |
|
|
#+end_src |
|
|
|
|
|
* Themes |
|
|
Use (my clone of) the solarized theme |
|
|
#+begin_src emacs-lisp |
|
|
(add-to-list 'custom-theme-load-path "/home/jacopods/.emacs.d/emacs-color-theme-solarized") |
|
|
(load-theme 'lunarized t) |
|
|
|
|
|
#+end_src |
|
|
|
|
|
Use patched terminus font (this overrides the settings in Customize, |
|
|
but I never could it work otherwise). See the special treatment |
|
|
later on for dealing with bold weight. To avoid flickering one |
|
|
should also set the font in .Xresources |
|
|
#+begin_src emacs-lisp |
|
|
(set-face-font 'default "-xos4-hackminus-medium-r-normal--20-200-72-72-c-100-ISO10646-1") |
|
|
(set-face-font 'fixed-pitch "-xos4-hackminus-medium-r-normal--20-200-72-72-c-100-ISO10646-1") |
|
|
(set-face-font 'fixed-pitch-serif "-xos4-hackminus-medium-r-normal--20-200-72-72-c-100-ISO10646-1") |
|
|
(set-face-font 'variable-pitch "-xos4-hackminus-medium-r-normal--20-200-72-72-c-100-ISO10646-1") |
|
|
#+end_src |
|
|
|
|
|
* Global settings |
|
|
** Global helper functions |
|
|
This is a helper function used to set several global keys given in |
|
|
provided as a parameter in ~binding-alist~ |
|
|
#+begin_src emacs-lisp |
|
|
(defun global-set-key-alist (binding-alist) |
|
|
"This function iterates over the binding-alist, which should be an alist of key and binding and sets each binding in the global-key-map" |
|
|
(dolist (binding binding-alist) |
|
|
(global-set-key (kbd (car binding)) (cdr binding)))) |
|
|
|
|
|
(defun define-key-alist (keymap binding-alist) |
|
|
"This function iterates over the binding-alist, which should be an alist of key and binding and sets each binding in the global-key-map" |
|
|
(dolist (binding binding-alist) |
|
|
(define-key keymap (kbd (car binding)) (cdr binding)))) |
|
|
#+end_src |
|
|
This is a hack to remove slant and bold from all faces |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/fixup-faces () |
|
|
(interactive) |
|
|
(mapc (lambda (face) |
|
|
(set-face-attribute face nil :weight 'normal) |
|
|
(set-face-attribute face nil :slant 'normal)) |
|
|
(face-list)) |
|
|
(set-face-font 'default "-xos4-hackminus-medium-r-normal--20-200-72-72-c-100-ISO10646-1")) |
|
|
#+end_src |
|
|
** Cosmetics |
|
|
Prefer a minimal appearance: no menu, toolbar or scroll-bars; no |
|
|
splash screens or messages |
|
|
#+begin_src emacs-lisp |
|
|
(menu-bar-mode -1) |
|
|
(tool-bar-mode 0) |
|
|
(scroll-bar-mode -1) |
|
|
(setq inhibit-startup-screen t |
|
|
inhibit-startup-message t) |
|
|
#+end_src |
|
|
a blinking cursor keeps the gpu awake; add global hl-line mode to more |
|
|
easily spot the cursor |
|
|
#+begin_src emacs-lisp |
|
|
(blink-cursor-mode 0) |
|
|
(set-default 'cursor-type 'box) |
|
|
#+end_src |
|
|
Set frame title |
|
|
#+begin_src emacs-lisp |
|
|
(setq frame-title-format |
|
|
'((buffer-file-name "%f" |
|
|
(dired-directory dired-directory "%b")) " · emacs")) |
|
|
#+end_src |
|
|
Themed tooltips |
|
|
#+begin_src emacs-lisp |
|
|
(setq x-gtk-use-system-tooltips nil) |
|
|
#+end_src |
|
|
Set =fill-column= to 72, which happens to be good for third-tiled frames |
|
|
and it also is a reasonable standard |
|
|
#+begin_src emacs-lisp |
|
|
(setq default-fill-column 72) |
|
|
#+end_src |
|
|
Highlight sexp (apparently I am not even using this) |
|
|
#+begin_src emacs-lisp |
|
|
;(require 'highlight-sexps) |
|
|
#+end_src |
|
|
Try to get along with large margins |
|
|
#+begin_src emacs-lisp |
|
|
(setq-default left-margin-width 3 |
|
|
right-margin-width 3 |
|
|
helm-left-margin-width left-margin-width |
|
|
helm-buffers-left-margin-width left-margin-width) |
|
|
(fringe-mode 10) |
|
|
;(set-window-buffer nil (current-buffer)) |
|
|
#+end_src |
|
|
Setup [[https://github.com/purcell/page-break-lines][page-break-lines]] |
|
|
#+begin_src emacs-lisp |
|
|
(require 'page-break-lines) |
|
|
(setq page-break-lines-char ?· |
|
|
page-break-lines-lighter "") |
|
|
(global-page-break-lines-mode) |
|
|
#+end_src |
|
|
** Mouseless |
|
|
Disable mouse interaction with emacs |
|
|
#+begin_src emacs-lisp |
|
|
(mouse-wheel-mode -1) |
|
|
(setq mouse-autoselect-window nil |
|
|
mouse-yank-at-point nil |
|
|
focus-follows-mouse nil |
|
|
mouse-highlight nil) |
|
|
#+end_src |
|
|
*** TODO Find out what of the above is necessary given the disable-mouse mode below |
|
|
Use the ~disable-mouse~ package by Steve Purcell available at |
|
|
[https://github.com/purcell/disable-mouse] |
|
|
#+begin_src emacs-lisp |
|
|
(require 'disable-mouse) |
|
|
(global-disable-mouse-mode) |
|
|
#+end_src |
|
|
** Window behavior |
|
|
Prevent compilation window eating up other windows |
|
|
#+begin_src emacs-lisp |
|
|
(setq compilation-window-height 12 |
|
|
compilation-scroll-output nil) |
|
|
#+end_src |
|
|
Prevent any automatic splitting to split vertically |
|
|
#+begin_src emacs-lisp |
|
|
(setq split-height-threshold 0) |
|
|
#+end_src |
|
|
*** Display-buffer rules |
|
|
First, treat ~mu4e-headers~ properly |
|
|
#+begin_src emacs-lisp |
|
|
(add-to-list 'display-buffer-alist |
|
|
'("\\*mu4e-headers\\*" |
|
|
(display-buffer-in-side-window) |
|
|
(dedicated . t) |
|
|
(side . top ) |
|
|
(slot . 0) |
|
|
(window-height . 40))) |
|
|
#+end_src |
|
|
Then, treat the ~calendar~ |
|
|
#+begin_src emacs-lisp |
|
|
(add-to-list 'display-buffer-alist |
|
|
'("\\*Calendar\\*" |
|
|
(display-buffer-in-side-window) |
|
|
(side . bottom ) |
|
|
(slot . 0) |
|
|
(window-height . 10))) |
|
|
#+end_src |
|
|
** Scrolling |
|
|
Scrolling setup |
|
|
#+begin_src emacs-lisp |
|
|
(setq redisplay-dont-pause t |
|
|
scroll-margin 3 |
|
|
scroll-step 1 |
|
|
scroll-conservatively 10000 |
|
|
scroll-preserve-screen-position 1) |
|
|
#+end_src |
|
|
|
|
|
*** TODO find out what this was actually meant to do |
|
|
#+begin_src emacs-lisp |
|
|
(setq-default display-buffer-reuse-frames t) |
|
|
#+end_src |
|
|
|
|
|
** Mode-line |
|
|
#+begin_src emacs-lisp |
|
|
(setq-default mode-line-modified '(:eval (if (buffer-modified-p) "●" "·")) |
|
|
;Still needs some improvements, does not report Readonly state |
|
|
mode-line-remote '(:eval (let ((s (format-mode-line "%@"))) |
|
|
(cond |
|
|
((equal s "-") " ") ((equal s "@") "@") (t s))))) |
|
|
|
|
|
(defface mode-line-indicator |
|
|
'((t nil)) |
|
|
"mode-line active indicator" |
|
|
:group 'mode-line-faces) |
|
|
|
|
|
(defface mode-line-inactive-indicator |
|
|
'((t nil)) |
|
|
"Inactive variant of indicator" |
|
|
:group 'mode-line-faces) |
|
|
|
|
|
|
|
|
;; This has some issues when two buffers are shown in different windows |
|
|
;; There are some solutions but let us see if this works first |
|
|
(defun mode-line-update-face (window) |
|
|
"Update the `mode-line' face in WINDOW to indicate whether the window is selected." |
|
|
(with-current-buffer (window-buffer window) |
|
|
(if (eq (current-buffer) (window-buffer (selected-window))) |
|
|
(face-remap-reset-base 'mode-line-indicator) |
|
|
(face-remap-set-base 'mode-line-indicator (face-all-attributes 'mode-line-inactive-indicator))))) |
|
|
|
|
|
(add-hook 'buffer-list-update-hook (lambda () (walk-windows #'mode-line-update-face nil t))) |
|
|
|
|
|
(defvar wilder-buffer-vc-mode-line |
|
|
'("%b" (vc-mode (:propertize |
|
|
;; Strip the backend name from the VC status information |
|
|
(:eval (let* ((backend (downcase (symbol-name (vc-backend (buffer-file-name))))) |
|
|
(branch (substring vc-mode (+ (length backend) 2))) |
|
|
(s (substring vc-mode (+ (length backend) 1) (+ (length backend) 2))) |
|
|
(status (cond ((equal s "-") "") ((equal s ":") "!") (t s)))) |
|
|
(concat "·" branch status))) |
|
|
face magit-branch-local)))) |
|
|
|
|
|
(defvar wilder-position |
|
|
'("%p · %I" )) |
|
|
|
|
|
(put 'wilder-buffer-vc-mode-line 'risky-local-variable t) |
|
|
(put 'wilder-position 'risky-local-variable t) |
|
|
|
|
|
(defvar wilder/mode-line-modes |
|
|
(let ((recursive-edit-help-echo "Recursive edit, type C-M-c to get out")) |
|
|
(list (propertize "%[" 'help-echo recursive-edit-help-echo) |
|
|
'("" mode-name) |
|
|
'("" mode-line-process) |
|
|
" " |
|
|
'("" minor-mode-alist) |
|
|
(propertize "%n" 'help-echo "mouse-2: Remove narrowing from buffer" |
|
|
'mouse-face 'mode-line-highlight |
|
|
'local-map (make-mode-line-mouse-map |
|
|
'mouse-2 #'mode-line-widen)) |
|
|
(propertize "%]" 'help-echo recursive-edit-help-echo) |
|
|
" ")) |
|
|
"Mode line construct for displaying major and minor modes.") |
|
|
|
|
|
(put 'wilder/mode-line-modes 'risky-local-variable t) |
|
|
|
|
|
(setq-default header-line-format nil);'("%e" ));wilder-buffer-vc-mode-line)) |
|
|
|
|
|
(setq mode-line-separator |
|
|
'(:eval (let* ((len-left (length (format-mode-line mode-line-format-left))) |
|
|
(len-right (length (format-mode-line mode-line-format-right))) |
|
|
(len-separator (- (+ (window-width) 2 2 2) (+ len-left len-right)))) |
|
|
(format (format "%%%ds" len-separator) "")))) |
|
|
|
|
|
(setq mode-line-format-left |
|
|
'("%e" |
|
|
(:propertize "╺═╸" face mode-line-indicator) |
|
|
mode-line-front-space |
|
|
mode-line-modified |
|
|
mode-line-remote |
|
|
mode-line-frame-identification |
|
|
wilder-buffer-vc-mode-line |
|
|
" " |
|
|
wilder-position)) |
|
|
|
|
|
(setq mode-line-format-right |
|
|
'(" " |
|
|
wilder/mode-line-modes |
|
|
mode-line-misc-info |
|
|
" ")) |
|
|
|
|
|
(put 'mode-line-format-left 'risky-local-variable t) |
|
|
(put 'mode-line-format-right 'risky-local-variable t) |
|
|
(put 'mode-line-separator 'risky-local-variable t) |
|
|
|
|
|
(setq-default mode-line-format |
|
|
'("%e" mode-line-format-left |
|
|
mode-line-separator |
|
|
mode-line-format-right)) |
|
|
#+end_src |
|
|
The following has been found in [[https://www.masteringemacs.org/article/hiding-replacing-modeline-strings][here]] to clean up the modeline |
|
|
#+begin_src emacs-lisp |
|
|
(defvar mode-line-cleaner-alist |
|
|
'() |
|
|
"Alist for `clean-mode-line.' |
|
|
|
|
|
When you add a new element to the alist, keep in mind that you |
|
|
must pass the correct minor/major mode symbol and a string you |
|
|
want to use in the modeline *in lieu of* the original.") |
|
|
|
|
|
(setq mode-line-cleaner-alist |
|
|
`((auto-complete-mode . " α") |
|
|
(yas/minor-mode . " υ") |
|
|
(paredit-mode . " π") |
|
|
(eldoc-mode . "") |
|
|
(abbrev-mode . "") |
|
|
(auto-fill-function . "") |
|
|
(disable-mouse-global-mode . "") |
|
|
(auto-revert-mode . "") |
|
|
(helm-mode . " η" ) |
|
|
(smart-tab-mode . "") |
|
|
(subword-mode . "") |
|
|
(outshine-mode . " o") |
|
|
(outline-minor-mode . "") |
|
|
(reftex-mode . "") |
|
|
(flyspell-mode . "") |
|
|
(geiser-mode . " γ") |
|
|
(geiser-autodoc-mode . "·α") |
|
|
(org-src-mode . " ωσ") |
|
|
;; Major modes |
|
|
(lisp-interaction-mode . "λ") |
|
|
(hi-lock-mode . "") |
|
|
(python-mode . "Py") |
|
|
(emacs-lisp-mode . "ελ") |
|
|
(help-mode . "+") |
|
|
(scheme-mode . "λ") |
|
|
(tex-mode . "χ") |
|
|
(latex-mode . "χ") |
|
|
(TeX-latex-mode . "χ") |
|
|
(org-mode . "Ω") |
|
|
(org-agenda-mode . "Ω:Agenda"))) |
|
|
|
|
|
(defun clean-mode-line () |
|
|
(interactive) |
|
|
(loop for cleaner in mode-line-cleaner-alist |
|
|
do (let* ((mode (car cleaner)) |
|
|
(mode-str (cdr cleaner)) |
|
|
(old-mode-str (cdr (assq mode minor-mode-alist)))) |
|
|
(when old-mode-str |
|
|
(setcar old-mode-str mode-str)) |
|
|
;; major mode |
|
|
(when (eq mode major-mode) |
|
|
(setq mode-name mode-str))))) |
|
|
|
|
|
|
|
|
(add-hook 'after-change-major-mode-hook 'clean-mode-line) |
|
|
|
|
|
#+end_src |
|
|
** Coding system |
|
|
Prefer the utf-8 coding system globally. This helps with the issue of |
|
|
magit not correctly staging hunks containing utf-8 characters. |
|
|
See [https://github.com/magit/magit/issues/32] |
|
|
#+begin_src emacs-lisp |
|
|
(prefer-coding-system 'utf-8) |
|
|
#+end_src |
|
|
** Mark handling |
|
|
No transient mark is more flexible |
|
|
#+begin_src emacs-lisp |
|
|
(transient-mark-mode 0) |
|
|
#+end_src |
|
|
But of course we need to /see/ the mark |
|
|
#+begin_src emacs-lisp |
|
|
(require 'visible-mark) |
|
|
(visible-mark-mode t) |
|
|
(global-visible-mark-mode t) |
|
|
#+end_src |
|
|
|
|
|
The following are some convenient bindings; notice that on my layout F13 and |
|
|
F14 are obtained by tapping the left and right shift respectively |
|
|
#+begin_src emacs-lisp |
|
|
(defun jump-to-mark () |
|
|
"Jumps to the local mark, respecting the `mark-ring' order. |
|
|
This is the same as using \\[set-mark-command] with the prefix argument." |
|
|
(interactive) |
|
|
(set-mark-command 1)) |
|
|
|
|
|
(defun just-activate-mark () |
|
|
(interactive) |
|
|
(activate-mark)) |
|
|
|
|
|
(global-set-key-alist |
|
|
'(("<f13>" . set-mark-command) |
|
|
("<f14>" . set-mark-command) |
|
|
("<M-f13>" . jump-to-mark) |
|
|
("<M-f14>" . jump-to-mark) |
|
|
("<S-f13>" . just-activate-mark) |
|
|
("<S-f14>" . just-activate-mark))) |
|
|
#+end_src |
|
|
** Global bindings |
|
|
Remove some bindings that I find supremely annoying. |
|
|
Additionally, unbind the ~C-x o~ binding to force me using ~ace-window~ |
|
|
Finally, unbind the ~C-SPC~ to mark the point, as I am using LR-shift |
|
|
Note that I will use ~C-SPC~ as a hydra below to deal with parenthesis |
|
|
#+begin_src emacs-lisp |
|
|
(dolist (ch '("C-c C-x" "C-x C-r" "C-z" "C-x f" "C-x C-f" "<M-f4>" "C-M-u" "C-M-d" "C-x o" "C-SPC")) |
|
|
(global-unset-key (kbd ch))) |
|
|
#+end_src |
|
|
|
|
|
Change {up,down}-list |
|
|
#+begin_src emacs-lisp |
|
|
(global-set-key (kbd "C-M-i") 'down-list) ;; -i stands for /in/ |
|
|
(global-set-key (kbd "C-M-o") 'up-list) ;; -o stands for /out/ |
|
|
#+end_src |
|
|
Add some opinionated bindings |
|
|
#+begin_src emacs-lisp |
|
|
(global-set-key-alist |
|
|
'(("C-M-d" . kill-sexp) |
|
|
("C-M-<backspace>" . backward-kill-sexp) |
|
|
("C-x k" . kill-this-buffer) |
|
|
("C-S-v" . scroll-down-command) |
|
|
("C-M-u" . universal-argument))) |
|
|
#+end_src |
|
|
** Disable commands |
|
|
Enable narrow commands |
|
|
#+begin_src emacs-lisp |
|
|
(put 'narrow-to-region 'disabled nil) |
|
|
(put 'LaTeX-narrow-to-environment 'disabled nil) |
|
|
(put 'narrow-to-page 'disabled nil) |
|
|
#+end_src |
|
|
** Smart beginning-of-line |
|
|
This is a relatively smart way to go back to the beginning of the line |
|
|
#+begin_src emacs-lisp |
|
|
(defun smart-line-beginning () |
|
|
"Move point to the beginning of text on the current line; if that |
|
|
is already the current position of point, then move it to the |
|
|
beginning of the line." |
|
|
(interactive) |
|
|
(let ((pt (point))) |
|
|
(beginning-of-line-text) |
|
|
(when (eq pt (point)) |
|
|
(beginning-of-line)))) |
|
|
(global-set-key (kbd "C-a") 'smart-line-beginning) |
|
|
#+end_src |
|
|
** Whitespace |
|
|
Trailing whitespace is evil; get rid of it unless we are in special |
|
|
modes, namely calendar (and later mu4e) |
|
|
#+begin_src emacs-lisp |
|
|
(setq-default show-trailing-whitespace t) |
|
|
(add-hook 'calendar-mode-hook |
|
|
#'(lambda () (setq-local show-trailing-whitespace nil))) |
|
|
(add-hook 'before-save-hook 'delete-trailing-whitespace) |
|
|
#+end_src |
|
|
Define what is whitespace during a search |
|
|
#+begin_src emacs-lisp |
|
|
(setq search-whitespace-regexp "[ \t\r\n]+") |
|
|
#+end_src |
|
|
Tabs are evil; get rid of them |
|
|
#+begin_src emacs-lisp |
|
|
(setq c-basic-offset 4) |
|
|
(setq-default tab-width 4 |
|
|
indent-tabs-mode nil) |
|
|
#+end_src |
|
|
*** TODO Have a look at ws-trim.el |
|
|
It is conf'ble to avoid removing whitespace from lines that were not |
|
|
modified; sounds like a good idea for git |
|
|
[ftp://ftp.lysator.liu.se/pub/emacs/ws-trim.el] |
|
|
|
|
|
** Show matching parens |
|
|
Use ~show-paren~ for paren-highlighting |
|
|
#+begin_src emacs-lisp |
|
|
(show-paren-mode 1) |
|
|
#+end_src |
|
|
*** TODO find out if there are better options out there |
|
|
** Load hydra |
|
|
#+begin_src emacs-lisp |
|
|
(require 'hydra) |
|
|
#+end_src |
|
|
|
|
|
** Abbrevs |
|
|
Set up abbrevs |
|
|
#+begin_src emacs-lisp |
|
|
(setq-default abbrev-mode t) |
|
|
(read-abbrev-file "~/.abbrev_defs") |
|
|
(setq save-abbrevs t) |
|
|
#+end_src |
|
|
** Spellcheck |
|
|
Use flyspell |
|
|
|
|
|
#+begin_src emacs-lisp |
|
|
(require 'flyspell-lazy) |
|
|
(flyspell-lazy-mode 1) |
|
|
(add-hook 'text-mode-hook 'flyspell-mode) |
|
|
(setq flyspell-use-meta-tab nil) |
|
|
(defface flyspell-margin-incorrect |
|
|
'((t nil)) |
|
|
"flyspell-margin-incorrect" |
|
|
:group 'flyspell) |
|
|
(setq flyspell-before-incorrect-word-string |
|
|
(propertize "." 'display `((margin left-margin) |
|
|
,(propertize "×" 'face 'flyspell-margin-incorrect)))) |
|
|
#+end_src |
|
|
** Fringe treatment |
|
|
*** TODO try linum-relative |
|
|
#+begin_src emacs-lisp |
|
|
; (require 'linum-relative) |
|
|
; (linum-relative-mode 1) |
|
|
#+end_src |
|
|
Add line numbers globally |
|
|
#+begin_src emacs-lisp |
|
|
; (global-linum-mode 1) |
|
|
#+end_src |
|
|
Highlight also the linum in the fringe |
|
|
#+begin_src emacs-lisp |
|
|
; (require 'hlinum) |
|
|
; (hlinum-activate) |
|
|
#+end_src |
|
|
Add marker for current line in the margin (see |
|
|
[https://github.com/kyanagi/fringe-current-line] and |
|
|
[https://github.com/nschum/fringe-helper.el] for inspiration) |
|
|
#+begin_src emacs-lisp |
|
|
(require 'margin-current-line) |
|
|
(global-margin-current-line-mode 1) |
|
|
#+end_src |
|
|
|
|
|
** Turn on echo mode |
|
|
Display unfinished commands in the echo area after the specified delay. |
|
|
#+begin_src emacs-lisp |
|
|
(setq echo-keystrokes 0.10) |
|
|
#+end_src |
|
|
** Disable completion buffer |
|
|
Remove the completion buffer as soon as we get out of the minibuffer |
|
|
#+begin_src emacs-lisp |
|
|
(add-hook 'minibuffer-exit-hook |
|
|
'(lambda () |
|
|
(let ((buffer "*Completions*")) |
|
|
(and (get-buffer buffer) |
|
|
(kill-buffer buffer))) )) |
|
|
#+end_src |
|
|
** Auto save on compile |
|
|
#+begin_src emacs-lisp |
|
|
(setq compilation-ask-about-save nil) |
|
|
#+end_src |
|
|
** Idle-kill |
|
|
#+begin_src emacs-lisp |
|
|
(defun kill-buffers-on-idle () |
|
|
"Kill buffers that have the appropriate property set." |
|
|
(save-excursion |
|
|
(save-window-excursion |
|
|
(dolist (b (buffer-list)) |
|
|
(when (and (with-current-buffer b (boundp 'buffer-close-on-idle)) |
|
|
(with-current-buffer b buffer-close-on-idle)) |
|
|
(with-current-buffer b (message "Killed %s" buffer-file-name)) |
|
|
(pop-to-buffer-same-window b) |
|
|
(kill-buffer)))))) |
|
|
(run-with-idle-timer 180 t 'kill-buffers-on-idle) |
|
|
#+end_src |
|
|
** Calendar |
|
|
#+begin_src emacs-lisp |
|
|
(add-hook 'calendar-load-hook |
|
|
(lambda () |
|
|
(calendar-set-date-style 'european))) |
|
|
;; first day of the week is monday instead of sunday: |
|
|
(setq calendar-week-start-day 1 |
|
|
calendar-location-name "TO" |
|
|
calendar-latitude 43.7 |
|
|
calendar-longitude -79.4 |
|
|
calendar-today-marker "·") |
|
|
(add-hook 'today-visible-calendar-hook 'calendar-mark-today) |
|
|
#+end_src |
|
|
** Use local proxy |
|
|
I need this to route bibretrieve requests through my local proxy, |
|
|
that in turn routes requests to ~mathscinet~ through the University network |
|
|
#+begin_src emacs-lisp |
|
|
;; Use system proxy |
|
|
(setq url-proxy-services '(("http" . "127.0.0.1:8118") |
|
|
("https" . "127.0.0.1:8118"))) |
|
|
#+end_src |
|
|
** Indicator |
|
|
Change cursor color to reflect different layers on the keyboard |
|
|
This is the handler |
|
|
#+begin_src emacs-lisp |
|
|
(defun xkbvleds-indicator-signal-handler (indicator on) |
|
|
(if on |
|
|
(cond ((string-equal indicator "Greek") |
|
|
(set-face-background 'cursor "#2aa198")) |
|
|
((string-equal indicator "Math") |
|
|
(set-face-background 'cursor "#cb4b16"))) |
|
|
(set-face-background 'cursor "#919191"))) |
|
|
#+end_src |
|
|
Connect the signal from xkbvleds to the handler |
|
|
#+begin_src emacs-lisp |
|
|
(require 'dbus) |
|
|
;; Register for the signal indicatorChanged coming from our service. It is |
|
|
;; important that the =service= argument is =nil=, otherwise the registration |
|
|
;; would stop once the process is respawned. |
|
|
(ignore-errors (dbus-register-signal |
|
|
:session nil "/org/xkbvleds" "org.xkbvleds" "indicatorChanged" |
|
|
'xkbvleds-indicator-signal-handler)) |
|
|
#+end_src |
|
|
** COMMENT Tarmak-ready |
|
|
Set up all possible combinations of modifiers |
|
|
#+begin_src emacs-lisp |
|
|
(defun concat-recursive (b &optional a) |
|
|
(if b |
|
|
(append (concat-recursive (cdr b) (concat a (car b))) |
|
|
(concat-recursive (cdr b) a)) |
|
|
(when a (list a)))) |
|
|
|
|
|
(setq modifier-combo (concat-recursive '("C-" "M-" "s-" "H-"))) |
|
|
#+end_src |
|
|
Then list the swapped keys |
|
|
#+begin_src emacs-lisp |
|
|
(setq tarmak1-swaps '(("j" . "e") |
|
|
("e" . "k") |
|
|
("k" . "n") |
|
|
("n" . "j"))) |
|
|
#+end_src |
|
|
** Transmission |
|
|
#+begin_src emacs-lisp |
|
|
(require 'transmission) |
|
|
(setq transmission-host "192.168.0.40") |
|
|
#+end_src |
|
|
|
|
|
* Main major modes |
|
|
** mu4e |
|
|
#+begin_src emacs-lisp |
|
|
(require 'smtpmail) |
|
|
(require 'mu4e) |
|
|
(require 'helm-mu) |
|
|
(global-set-key (kbd "C-x μ") 'mu4e) |
|
|
|
|
|
(setq mu4e-maildir "~/.mail" |
|
|
mu4e-attachment-dir "~/attachments" |
|
|
mu4e-html2text-command "w3m -dump -T text/html -cols 72 -o display_link_number=true -o auto_image=false -o display_image=false -o ignore_null_img_alt=true" |
|
|
mu4e-headers-show-threads nil |
|
|
mu4e-headers-sort-direction "descending" |
|
|
mail-user-agent 'mu4e-user-agent |
|
|
read-mail-command 'mu4e |
|
|
mu4e-update-interval 120 |
|
|
mu4e-view-use-gnus t |
|
|
mu4e-compose-format-flowed t |
|
|
fill-flowed-encode-column 998 |
|
|
mm-text-html-renderer 'gnus-w3m |
|
|
mu4e-completing-read-function 'completing-read) |
|
|
|
|
|
(let (p1 p2 myLine) |
|
|
(setq p1 (line-beginning-position) ) |
|
|
(setq p2 (line-end-position) ) |
|
|
(setq myLine (buffer-substring-no-properties p1 p2))) |
|
|
|
|
|
(defun get-buffer-current-line () |
|
|
(let ((p (line-beginning-position)) |
|
|
(q (line-end-position))) |
|
|
(buffer-substring-no-properties p q))) |
|
|
|
|
|
(defun get-buffer-to-eol () |
|
|
(let ((p (point)) |
|
|
(q (line-end-position))) |
|
|
(buffer-substring-no-properties p q))) |
|
|
|
|
|
(defun format-zoom-meeting () |
|
|
(if (boundp 'zoom-link) |
|
|
(concat zoom-summary |
|
|
" [[" zoom-link "][link]] (" zoom-passcode "\n SCHEDULED: " zoom-scheduled-time) |
|
|
(concat zoom-summary "\n SCHEDULED: " zoom-scheduled-time))) |
|
|
|
|
|
(defun create-item-for-meeting (msg) |
|
|
"Search for messages sent by the sender of the message at point." |
|
|
(makunbound 'zoom-link) |
|
|
(makunbound 'zoom-passcode) |
|
|
(save-excursion |
|
|
(beginning-of-buffer) |
|
|
(cond ((search-forward "Join Zoom Meeting" nil t) |
|
|
(message "found zoom meeting") |
|
|
(right-char) |
|
|
(setq zoom-link (get-buffer-current-line)) |
|
|
(when (search-forward "Passcode:") |
|
|
(setq zoom-passcode (get-buffer-current-line))) |
|
|
(search-backward "Time:") |
|
|
(search-forward "<") |
|
|
(left-char) |
|
|
(setq zoom-scheduled-time (get-buffer-to-eol)) |
|
|
(search-backward "Summary:") |
|
|
(right-word) |
|
|
(right-word) |
|
|
(left-word) |
|
|
(setq zoom-summary (get-buffer-to-eol)) |
|
|
(org-capture nil "z")) |
|
|
((search-forward "[ Accept ]") |
|
|
(message "found generic meeting") |
|
|
(search-forward "Summary:") |
|
|
(right-word) |
|
|
(right-word) |
|
|
(left-word) |
|
|
(setq zoom-summary (get-buffer-to-eol)) |
|
|
(search-forward "Location:") |
|
|
(right-word) |
|
|
(left-word) |
|
|
(setq zoom-summary (concat zoom-summary " @" (get-buffer-to-eol))) |
|
|
(search-forward "Time:") |
|
|
(search-forward "<") |
|
|
(left-char) |
|
|
(setq zoom-scheduled-time (get-buffer-to-eol)) |
|
|
(org-capture nil "z"))))) |
|
|
|
|
|
|
|
|
;; define 'z' as the shortcut |
|
|
(add-to-list 'mu4e-view-actions |
|
|
'("zCreate agenda item for meeting" . create-item-for-meeting) t) |
|
|
|
|
|
(advice-add 'mu4e~header-line-format :around |
|
|
(lambda (orig-fun &rest args) |
|
|
(let ((mu4e-use-fancy-chars t) |
|
|
(mu4e--mark-fringe-len 5)) |
|
|
(apply orig-fun args)))) |
|
|
|
|
|
#+end_src |
|
|
|
|
|
Customize ~mu4e~ bookmarks |
|
|
#+begin_src emacs-lisp |
|
|
|
|
|
(add-to-list 'mu4e-bookmarks |
|
|
'( :name "Starred" |
|
|
:query "flag:flagged" |
|
|
:key ?s)) |
|
|
(add-to-list 'mu4e-bookmarks |
|
|
'( :name "Archived, last two weeks" |
|
|
:query "date:14d..now maildir:/math/Archive" |
|
|
:key ?a)) |
|
|
#+end_src |
|
|
|
|
|
Customize ~mu4e~ context |
|
|
#+begin_src emacs-lisp |
|
|
|
|
|
(setq mu4e-contexts |
|
|
`( ,(make-mu4e-context |
|
|
:name "Work" |
|
|
:enter-func (lambda () (mu4e-message "Switch to the Work context")) |
|
|
;; no leave-func |
|
|
;; we match based on the maildir of the message |
|
|
:match-func (lambda (msg) |
|
|
(when msg |
|
|
(string-match-p "^/math" (mu4e-message-field |
|
|
msg :maildir)))) |
|
|
:vars '( ( user-mail-address . "jacopods@math.utoronto.ca" ) |
|
|
( user-full-name . "Jacopo De Simoi" ) |
|
|
( mu4e-sent-folder . "/math/Sent") |
|
|
( mu4e-drafts-folder . "/math/Drafts") |
|
|
( mu4e-trash-folder . "/math/Trash") |
|
|
( mu4e-refile-folder . "/math/Archive") |
|
|
( smtpmail-stream-type . starttls ) |
|
|
( smtpmail-smtp-service . 9587 ) |
|
|
( mu4e-compose-signature . (concat |
|
|
"Jacopo De Simoi (he · him)\n" |
|
|
"Associate Professor · U Toronto\n")))))) |
|
|
|
|
|
;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should |
|
|
;; guess or ask the correct context, e.g. |
|
|
|
|
|
;; start with the first (default) context; |
|
|
;; default is to ask-if-none (ask when there's no context yet, and none match) |
|
|
(setq mu4e-context-policy 'pick-first) |
|
|
|
|
|
;; compose with the current context is no context matches; |
|
|
;; default is to ask |
|
|
(setq mu4e-compose-context-policy 'pick-first) |
|
|
|
|
|
(add-hook 'mu4e-headers-mode-hook |
|
|
#'(lambda () (setq-local show-trailing-whitespace nil))) |
|
|
(add-hook 'mu4e-view-mode-hook |
|
|
#'(lambda () (setq-local show-trailing-whitespace nil))) |
|
|
(add-hook 'mu4e-compose-mode-hook |
|
|
#'(lambda () |
|
|
(auto-fill-mode -1) |
|
|
(visual-line-mode) |
|
|
(flyspell-mode))) |
|
|
|
|
|
(define-key-alist message-mode-map |
|
|
'(("M-\"" . (lambda (r-begin r-end) |
|
|
(interactive "r") |
|
|
(add-delimiter "“" "”" r-begin r-end))))) |
|
|
|
|
|
#+end_src |
|
|
|
|
|
Use ~mbsync~ te fetch emails from server |
|
|
#+begin_src emacs-lisp |
|
|
|
|
|
|
|
|
;; Get mail |
|
|
(setq mu4e-get-mail-command "mbsync math-fast" |
|
|
mu4e-change-filenames-when-moving t ; needed for mbsync |
|
|
mu4e-headers-include-related nil ;do not include threaded |
|
|
;sent messages in inbox queries |
|
|
) |
|
|
|
|
|
(define-key mu4e-headers-mode-map (kbd "h") 'mu4e-headers-mark-for-refile) |
|
|
(define-key mu4e-headers-mode-map (kbd "h") 'mu4e-headers-mark-for-refile) |
|
|
|
|
|
(define-key mu4e-headers-mode-map (kbd "h") 'mu4e-headers-mark-for-refile) |
|
|
(define-key mu4e-view-mode-map (kbd "h") 'mu4e-view-mark-for-refile) |
|
|
|
|
|
(define-key mu4e-headers-mode-map (kbd "r") 'mu4e-compose-reply) |
|
|
(define-key mu4e-view-mode-map (kbd "r") 'mu4e-compose-reply) |
|
|
|
|
|
(define-key mu4e-headers-mode-map (kbd "f") 'mu4e-compose-forward) |
|
|
(define-key mu4e-headers-mode-map (kbd "C-k") 'mu4e-headers-mark-for-trash) |
|
|
|
|
|
|
|
|
;; Send mail |
|
|
(setq message-send-mail-function 'smtpmail-send-it |
|
|
smtpmail-servers-requiring-authorization ".*" |
|
|
smtpmail-smtp-server "127.0.0.1") |
|
|
|
|
|
(require 'org-mu4e) |
|
|
;;store link to message if in header view, not to header query |
|
|
(setq mu4e-org-link-query-in-headers-mode nil) |
|
|
#+end_src |
|
|
|
|
|
This is supposed to make the calendar invitation buttons work. |
|
|
#+begin_src emacs-lisp |
|
|
(require 'mu4e-icalendar) |
|
|
(mu4e-icalendar-setup) |
|
|
#+end_src |
|
|
|
|
|
This snippet filters stale addresses from address completion; I |
|
|
found this method [[https://emacs.stackexchange.com/questions/47789/how-to-remove-email-address-from-local-database-in-mu4e][here]]. The code loads the definition of ~filter-addresses~ from an external file and checks if ~addr~ is |
|
|
one of the addresses in the list. |
|
|
|
|
|
#+begin_src emacs-lisp |
|
|
(load-file "~/.emacs.d/filtered-emails.el") |
|
|
|
|
|
(defun my-mu4e-contact-filter-function (addr) |
|
|
(let ((ignore nil)) |
|
|
(if (dolist (address filter-addresses ignore) |
|
|
(setq ignore (or ignore (string-equal addr address)))) |
|
|
nil addr))) |
|
|
|
|
|
(setq mu4e-contact-process-function 'my-mu4e-contact-filter-function) |
|
|
#+end_src |
|
|
** org-mode |
|
|
*** Require |
|
|
Require the ~org~ package; I also occasionally use org-pomodoro |
|
|
#+begin_src emacs-lisp |
|
|
(with-eval-after-load "org" |
|
|
(define-key org-src-mode-map (kbd "C-c C-c") 'org-edit-src-exit) |
|
|
(define-key org-src-mode-map (kbd "C-c C-k") nil) ;this conflicts with my LaTeX bindings |
|
|
(define-key org-src-mode-map (kbd "C-c C-`") 'org-edit-src-abort)) |
|
|
|
|
|
(require 'org) |
|
|
(require 'org-pomodoro) |
|
|
(require 'org-habit) |
|
|
|
|
|
;(require 'helm-org) |
|
|
;(add-to-list 'helm-completing-read-handlers-alist '(org-capture . helm-org-completing-read-tags)) |
|
|
; (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags)) |
|
|
; (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags |
|
|
; . helm-org-completing-read-tags)) |
|
|
#+end_src |
|
|
*** Hooks |
|
|
Enable ~auto-fill-mode~ (see [[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Auto-Fill.html][the manual]]]: honestly I do not see |
|
|
where I would not want to use this feature). |
|
|
#+begin_src emacs-lisp |
|
|
(add-hook 'org-mode-hook 'turn-on-auto-fill) |
|
|
#+end_src |
|
|
It sometimes happen (e.g. when opening files from magit) that |
|
|
the point is in an invisible region; then the usual ~smart-line-beginning~ would get upset. This variation takes care |
|
|
of the problem. |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-smart-line-beginning () |
|
|
(interactive) |
|
|
(when (invisible-p (point)) |
|
|
;; assume that we are in a folded headline; move back to heading |
|
|
;; otherwise we will be trapped in the invisible region |
|
|
(org-back-to-heading)) |
|
|
(smart-line-beginning)) |
|
|
(define-key org-mode-map (kbd "C-a") 'org-smart-line-beginning) |
|
|
#+end_src |
|
|
Disable ~truncate-lines~ |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-starting-truncated nil |
|
|
org-startup-folded t) |
|
|
#+end_src |
|
|
*** Cosmetics |
|
|
Change the default ellipsis ~...~ to the unicode ellipsis ~…~ and |
|
|
set my preferred indentation |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-ellipsis "…" |
|
|
org-adapt-indentation t) |
|
|
#+end_src |
|
|
*** Ligatures [not in working order] |
|
|
These require a patched ~hackminus~ font. Too bad it does not work |
|
|
for some reason |
|
|
#+begin_src emacs-lisp |
|
|
;; (defun delayed-org-prettify () |
|
|
;; (prettify-symbols-mode 0) |
|
|
;; (run-with-idle-timer 0 nil |
|
|
;; (lambda () |
|
|
;; (prettify-symbols-mode 1) |
|
|
;; (add-to-list 'prettify-symbols-alist '("**" . (? (Br . Bl) ?))) |
|
|
;; (add-to-list 'prettify-symbols-alist '("***" . (? (Br . Bl) ? (Br . Bl) ?))) |
|
|
;; (add-to-list 'prettify-symbols-alist '("****" . (? (Br . Bl) ? (Br . Bl) ? (Br . Bl) ?)))))) |
|
|
;;(add-hook 'org-mode-hook 'delayed-org-prettify) |
|
|
|
|
|
#+end_src |
|
|
*** Links |
|
|
Open links with external stuff |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-file-apps |
|
|
'((auto-mode . emacs) |
|
|
("\\.x?html?\\'" . "xdg-open %s") |
|
|
("\\.djvu\\'" . "xdg-open \"%s\"") |
|
|
("\\.pdf\\'" . "xdg-open \"%s\""))) |
|
|
#+end_src |
|
|
*** Agenda |
|
|
Set the canonical binding for the agenda |
|
|
#+begin_src emacs-lisp |
|
|
(global-set-key (kbd "C-c a") 'org-agenda) |
|
|
#+end_src |
|
|
Define agenda files: the main one is ~master.org~, but then I have |
|
|
a bunch of them on orgzly |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-files |
|
|
'("~/org/master.org" |
|
|
"~/org/orgzly/work.org" |
|
|
"~/org/orgzly/scheduled.org" |
|
|
"~/org/orgzly/hack.org" |
|
|
"~/org/orgzly/library.org" |
|
|
"~/org/kaizen.org" |
|
|
"~/org/orgzly/refile.org")) |
|
|
#+end_src |
|
|
Use less verbose reminders: |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-deadline-leaders |
|
|
'("⇒" "@ %3d d. " "! %2d d. ago ") |
|
|
org-agenda-scheduled-leaders |
|
|
'("→" "↻ %2d×") |
|
|
org-agenda-habit-leader "·") |
|
|
#+end_src |
|
|
Default to daily agenda |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-span 1) |
|
|
#+end_src |
|
|
Prevent headlines to be marked as DONE if some sub-headings are |
|
|
still TODO. |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-enforce-todo-dependencies t) |
|
|
#+end_src |
|
|
Do not show scheduled or deadline'd stuff in the Global TODO list |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-todo-ignore-deadlines 'all |
|
|
org-agenda-todo-ignore-scheduled 'all) |
|
|
#+end_src |
|
|
Do not show stuff marked with DONE even if they have a deadline |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-skip-deadline-if-done t) |
|
|
#+end_src |
|
|
Custom separator |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-block-separator ?─) |
|
|
#+end_src |
|
|
Turn on speed keys |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-use-speed-commands t) |
|
|
#+end_src |
|
|
Customize the agenda interface a bit |
|
|
- Sleeker time-grid |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-time-grid '((daily today require-timed) |
|
|
(800 1000 1200 1400 1600 1800 2000) |
|
|
" ······· " "───────────────") |
|
|
org-agenda-current-time-string "····· now ·····" |
|
|
org-agenda-time-leading-zero t) |
|
|
#+end_src |
|
|
- Use figlet-type fonts to display the date |
|
|
#+begin_src emacs-lisp |
|
|
(setq figlet |
|
|
[["███" "██ " "███" "███" "█ █" "███" "███" "███" "███" "███"] |
|
|
["█ █" " █ " " █" " █" "█ █" "█ " "█ " " █" "█ █" "█ █"] |
|
|
["█ █" " █ " "███" "███" "███" "███" "███" " █" "███" "███"] |
|
|
["█ █" " █ " "█ " " █" " █" " █" "█ █" " █" "█ █" " █"] |
|
|
["███" "███" "███" "███" " █" "███" "███" " █" "███" "███"]]) |
|
|
|
|
|
(setq figlet-lean |
|
|
[["┌──┐" "╶┐ " "╶──┐" "╶──┐" "╷ ╷" "┌──╴" "┌──╴" "╶──┐" "┌──┐" "┌──┐"] |
|
|
["│ │" " │ " " │" " │" "│ │" "│ " "│ " " │" "│ │" "│ │"] |
|
|
["│ │" " │ " "┌──┘" "╶──┤" "└──┤" "└──┐" "├──┐" " │" "├──┤" "└──┤"] |
|
|
["│ │" " │ " "│ " " │" " │" " │" "│ │" " │" "│ │" " │"] |
|
|
["└──┘" "╶┴╴" "└──╴" "╶──┘" " ╵" "╶──┘" "└──┘" " ╵" "└──┘" "╶──┘"]]) |
|
|
|
|
|
(defun figlet-digit (digit row) |
|
|
(aref (aref figlet-lean row) digit)) |
|
|
|
|
|
(defun figlet-num (number row) |
|
|
(let ((n number) |
|
|
(d (list))) |
|
|
(while (> n 0) |
|
|
(setq d (append (list (mod n 10)) d)) |
|
|
(setq n (/ n 10))) |
|
|
(string-join (mapcar |
|
|
(lambda (digit) (concat (figlet-digit digit row) " ")) |
|
|
d)))) |
|
|
(defun org-agenda-format-date-figlet (date) |
|
|
"Format a DATE string for display in the daily/weekly agenda. |
|
|
This function makes sure that dates are aligned for easy reading." |
|
|
;(require 'cal-iso) |
|
|
(let* ((dayname (calendar-day-name date)) |
|
|
(day (cadr date)) |
|
|
(day-of-week (calendar-day-of-week date)) |
|
|
(month (car date)) |
|
|
(monthname (calendar-month-name month)) |
|
|
(year (nth 2 date))nf |
|
|
(iso-week (org-days-to-iso-week |
|
|
(calendar-absolute-from-gregorian date))) |
|
|
(weekyear (cond ((and (= month 1) (>= iso-week 52)) |
|
|
(1- year)) |
|
|
((and (= month 12) (<= iso-week 1)) |
|
|
(1+ year)) |
|
|
(t year))) |
|
|
(weekstring (if (= day-of-week 1) |
|
|
(format " W%02d" iso-week) |
|
|
""))) |
|
|
|
|
|
(format (concat "\n" |
|
|
"%9s %s %4d%s\n" |
|
|
"%9s \n" |
|
|
"%9s %-10s\n" |
|
|
"%9s \n" |
|
|
"%9s %s\n") |
|
|
(figlet-num day 0) monthname year weekstring |
|
|
(figlet-num day 1) |
|
|
(figlet-num day 2) dayname |
|
|
(figlet-num day 3) |
|
|
(figlet-num day 4) (sunrise-sunset)))) |
|
|
|
|
|
(setq org-agenda-format-date #'org-agenda-format-date-figlet) |
|
|
(setq org-agenda-prefix-format |
|
|
'((agenda . " %i %6c %s %?-12t") |
|
|
(todo . " %i %6c · ") |
|
|
(tags . " %i %6c · ") |
|
|
(search . " %i %6c · "))) |
|
|
#+end_src |
|
|
- Add a whiteline after the header Now, this is a horrible hack: |
|
|
the string that forms the header is obtained by constructing |
|
|
some pretty-text in a temporary buffer and then retrieving the |
|
|
string with #'buffer-string Hence we add (and remove) an advice |
|
|
to the function buffer-string that concat's a newline around |
|
|
each function that produces the headers |
|
|
#+begin_src emacs-lisp |
|
|
(defun concat-newline (s) |
|
|
(concat s "\n")) |
|
|
|
|
|
(dolist (fun '(org-tags-view org-todo-list)) |
|
|
(advice-add fun :around |
|
|
(lambda (orig-fun &rest args) |
|
|
(advice-add #'buffer-string :filter-return #'concat-newline) |
|
|
(apply orig-fun args) |
|
|
(advice-remove #'buffer-string #'concat-newline)))) |
|
|
#+end_src |
|
|
#+begin_src emacs-lisp |
|
|
(advice-add #'org-agenda--insert-overriding-header :filter-args |
|
|
(lambda (params) |
|
|
(list `(concat ,(car params) "\n")))) |
|
|
#+end_src |
|
|
Improve the org-habit graph thing |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-habit-regular-glyph ?□ |
|
|
org-habit-today-glyph ? |
|
|
org-habit-completed-glyph ?▣ |
|
|
org-habit-completed-today-glyph ?) |
|
|
|
|
|
(defun wilder/org-habit-build-graph (habit starting current ending) |
|
|
"Build a graph for the given HABIT, from STARTING to ENDING. |
|
|
CURRENT gives the current time between STARTING and ENDING, for |
|
|
the purpose of drawing the graph. It need not be the actual |
|
|
current time." |
|
|
(let* ((all-done-dates (sort (org-habit-done-dates habit) #'<)) |
|
|
(done-dates all-done-dates) |
|
|
(scheduled (org-habit-scheduled habit)) |
|
|
(s-repeat (org-habit-scheduled-repeat habit)) |
|
|
(start (time-to-days starting)) |
|
|
(now (time-to-days current)) |
|
|
(end (time-to-days ending)) |
|
|
(graph (make-string (- end start) org-habit-regular-glyph)) |
|
|
(index 0) |
|
|
last-done-date) |
|
|
(while (and done-dates (< (car done-dates) start)) |
|
|
(setq last-done-date (car done-dates) |
|
|
done-dates (cdr done-dates))) |
|
|
(while (< start end) |
|
|
(let* ((in-the-past-p (< start now)) |
|
|
(todayp (= start now)) |
|
|
(donep (and done-dates (= start (car done-dates)))) |
|
|
(faces |
|
|
(if (and in-the-past-p |
|
|
(not last-done-date) |
|
|
(not (< scheduled now))) |
|
|
(if (and all-done-dates (= (car all-done-dates) start)) |
|
|
;; This is the very first done of this habit. |
|
|
'(org-habit-ready-face . org-habit-ready-future-face) |
|
|
'(org-habit-clear-face . org-habit-clear-future-face)) |
|
|
(org-habit-get-faces |
|
|
habit start |
|
|
(and in-the-past-p |
|
|
last-done-date |
|
|
;; Compute scheduled time for habit at the time |
|
|
;; START was current. |
|
|
(let ((type (org-habit-repeat-type habit))) |
|
|
(cond |
|
|
;; At the last done date, use current |
|
|
;; scheduling in all cases. |
|
|
((null done-dates) scheduled) |
|
|
((equal type ".+") (+ last-done-date s-repeat)) |
|
|
((equal type "+") |
|
|
;; Since LAST-DONE-DATE, each done mark |
|
|
;; shifted scheduled date by S-REPEAT. |
|
|
(- scheduled (* (length done-dates) s-repeat))) |
|
|
(t |
|
|
;; Compute the scheduled time after the |
|
|
;; first repeat. This is the closest time |
|
|
;; past FIRST-DONE which can reach SCHEDULED |
|
|
;; by a number of S-REPEAT hops. |
|
|
;; |
|
|
;; Then, play TODO state change history from |
|
|
;; the beginning in order to find current |
|
|
;; scheduled time. |
|
|
(let* ((first-done (car all-done-dates)) |
|
|
(s (let ((shift (mod (- scheduled first-done) |
|
|
s-repeat))) |
|
|
(+ (if (= shift 0) s-repeat shift) |
|
|
first-done)))) |
|
|
(if (= first-done last-done-date) s |
|
|
(catch :exit |
|
|
(dolist (done (cdr all-done-dates) s) |
|
|
;; Each repeat shifts S by any |
|
|
;; number of S-REPEAT hops it takes |
|
|
;; to get past DONE, with a minimum |
|
|
;; of one hop. |
|
|
(cl-incf s (* (1+ (/ (max (- done s) 0) |
|
|
s-repeat)) |
|
|
s-repeat)) |
|
|
(when (= done last-done-date) |
|
|
(throw :exit s)))))))))) |
|
|
donep))) |
|
|
markedp face) |
|
|
(cond |
|
|
(donep |
|
|
(aset graph index (if todayp org-habit-completed-today-glyph org-habit-completed-glyph)) |
|
|
(setq markedp t) |
|
|
(while (and done-dates (= start (car done-dates))) |
|
|
(setq last-done-date (car done-dates)) |
|
|
(setq done-dates (cdr done-dates)))) |
|
|
(todayp |
|
|
(aset graph index org-habit-today-glyph))) |
|
|
(setq face (if (or in-the-past-p todayp) |
|
|
(car faces) |
|
|
(cdr faces))) |
|
|
(when (and in-the-past-p |
|
|
(not (eq face 'org-habit-overdue-face)) |
|
|
(not markedp)) |
|
|
(setq face (cdr faces))) |
|
|
(put-text-property index (1+ index) 'face face graph) |
|
|
(put-text-property index (1+ index) |
|
|
'help-echo |
|
|
(concat (format-time-string |
|
|
(org-time-stamp-format) |
|
|
(time-add starting (days-to-time (- start (time-to-days starting))))) |
|
|
(if donep " DONE" "")) |
|
|
graph)) |
|
|
(setq start (1+ start) |
|
|
index (1+ index))) |
|
|
graph)) |
|
|
|
|
|
(advice-add 'org-habit-build-graph :override #'wilder/org-habit-build-graph) |
|
|
#+end_src |
|
|
Ideally I should tag some tasks as “break” tasks, which are |
|
|
suitable to be taken care of during a pomodoro break. Such tasks |
|
|
should be marked with tags ~:5m:~ and ~:20m:~ according to the |
|
|
estimate on the time it would take to take care of them |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-agenda-custom-commands |
|
|
'( |
|
|
("a" "Main agenda" |
|
|
((agenda "") |
|
|
(todo "NEXT|ONGOING") |
|
|
(tags-todo "hack") |
|
|
(tags-todo "5m") |
|
|
(todo "TODO"))) |
|
|
("h" "Agenda and Android tasks" |
|
|
((agenda "") |
|
|
(tags-todo "Android"))) |
|
|
("5" "Agenda and Break tasks" |
|
|
((agenda "") |
|
|
(tags-todo "5m") |
|
|
(tags-todo "20m"))))) |
|
|
#+end_src |
|
|
|
|
|
Custom agenda view |
|
|
- cleanup stuff (TODO, what is this doing, exactly? ) |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-agenda-prepare (&optional name) |
|
|
(let ((filter-alist (if org-agenda-persistent-filter |
|
|
(with-current-buffer |
|
|
(get-buffer-create org-agenda-buffer-name) |
|
|
(list `(tag . ,org-agenda-tag-filter) |
|
|
`(re . ,org-agenda-regexp-filter) |
|
|
`(effort . ,org-agenda-effort-filter) |
|
|
`(cat . ,org-agenda-category-filter)))))) |
|
|
(if (org-agenda-use-sticky-p) |
|
|
(progn |
|
|
(put 'org-agenda-tag-filter :preset-filter nil) |
|
|
(put 'org-agenda-category-filter :preset-filter nil) |
|
|
(put 'org-agenda-regexp-filter :preset-filter nil) |
|
|
;; Popup existing buffer |
|
|
(org-agenda-prepare-window (get-buffer org-agenda-buffer-name) |
|
|
filter-alist) |
|
|
(message "Sticky Agenda buffer, use `r' to refresh") |
|
|
(or org-agenda-multi (org-agenda-fit-window-to-buffer)) |
|
|
(throw 'exit "Sticky Agenda buffer, use `r' to refresh")) |
|
|
(setq org-todo-keywords-for-agenda nil) |
|
|
(put 'org-agenda-tag-filter :preset-filter |
|
|
org-agenda-tag-filter-preset) |
|
|
(put 'org-agenda-category-filter :preset-filter |
|
|
org-agenda-category-filter-preset) |
|
|
(put 'org-agenda-regexp-filter :preset-filter |
|
|
org-agenda-regexp-filter-preset) |
|
|
(put 'org-agenda-effort-filter :preset-filter |
|
|
org-agenda-effort-filter-preset) |
|
|
(if org-agenda-multi |
|
|
(progn |
|
|
(setq buffer-read-only nil) |
|
|
(goto-char (point-max)) |
|
|
(unless (or (bobp) org-agenda-compact-blocks |
|
|
(not org-agenda-block-separator)) |
|
|
(insert "\n" |
|
|
(if (stringp org-agenda-block-separator) |
|
|
org-agenda-block-separator |
|
|
(make-string (window-width) org-agenda-block-separator)) |
|
|
"\n\n")) |
|
|
(narrow-to-region (point) (point-max))) |
|
|
(setq org-done-keywords-for-agenda nil) |
|
|
;; Setting any org variables that are in org-agenda-local-vars |
|
|
;; list need to be done after the prepare call |
|
|
(org-agenda-prepare-window |
|
|
(get-buffer-create org-agenda-buffer-name) filter-alist) |
|
|
(setq buffer-read-only nil) |
|
|
(org-agenda-reset-markers) |
|
|
(let ((inhibit-read-only t)) (erase-buffer)) |
|
|
(org-agenda-mode) |
|
|
(setq org-refile-targets '((org-agenda-files :level . 1))) |
|
|
(setq org-refile-use-outline-path 'file) |
|
|
(setq org-agenda-buffer (current-buffer)) |
|
|
(setq org-agenda-contributing-files nil) |
|
|
(setq org-agenda-columns-active nil) |
|
|
(org-agenda-prepare-buffers (org-agenda-files nil 'ifmode)) |
|
|
(setq org-todo-keywords-for-agenda |
|
|
(org-uniquify org-todo-keywords-for-agenda)) |
|
|
(setq org-done-keywords-for-agenda |
|
|
(org-uniquify org-done-keywords-for-agenda)) |
|
|
(setq org-agenda-last-prefix-arg current-prefix-arg) |
|
|
(setq org-agenda-this-buffer-name org-agenda-buffer-name) |
|
|
(and name (not org-agenda-name) |
|
|
(setq-local org-agenda-name name))) |
|
|
(setq buffer-read-only nil)))) |
|
|
|
|
|
(add-hook 'org-agenda-mode-hook |
|
|
(lambda () (setq truncate-lines t |
|
|
show-trailing-whitespace nil))) |
|
|
|
|
|
#+end_src |
|
|
*** Automate saving |
|
|
Save all org buffers with the auto-save-hook. Avoid saving the |
|
|
buffer that is currently being edited; doing so removes trailing |
|
|
whitespace, which is undesirable while writing. |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-save-all-org-buffers-except-current () |
|
|
"Save all Org buffers without user confirmation." |
|
|
(interactive) |
|
|
(message "Saving all Org buffers...") |
|
|
(let ((buffer (current-buffer))) |
|
|
(save-some-buffers t (lambda () (and (not (eq (current-buffer) buffer)) (derived-mode-p 'org-mode))))) |
|
|
(when (featurep 'org-id) (org-id-locations-save)) |
|
|
(message "Saving all Org buffers... done")) |
|
|
|
|
|
(add-hook 'auto-save-hook 'org-save-all-org-buffers-except-current) |
|
|
#+end_src |
|
|
*** Reverting stuff from orgzly |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-revert-all-orgzly-buffers (&optional ALL) |
|
|
"Revert all Org buffers that are sitting in orgzly |
|
|
Prompt for confirmation when there are unsaved changes. Be sure |
|
|
you know what you are doing before letting this function |
|
|
overwrite your changes." |
|
|
(interactive) |
|
|
(message "Reverting Orgzly buffers…") |
|
|
(save-excursion |
|
|
(save-window-excursion |
|
|
(dolist (b (buffer-list)) |
|
|
(when (and (with-current-buffer b (derived-mode-p 'org-mode)) |
|
|
(with-current-buffer b buffer-file-name) |
|
|
(with-current-buffer b (string-match-p "orgzly" default-directory))) |
|
|
(pop-to-buffer-same-window b) |
|
|
(revert-buffer t 'no-confirm))))) |
|
|
(org-agenda-redo-all)) |
|
|
(define-key org-agenda-mode-map (kbd "Z") 'org-revert-all-orgzly-buffers) |
|
|
#+end_src |
|
|
*** Hydras for timestamps |
|
|
#+begin_src emacs-lisp |
|
|
(defun hydra-timestamp/hl-paren-force-fix () |
|
|
;;; This is needed b/c hl-parent caches the point position and |
|
|
;;; refuses to update if the point did not move |
|
|
(let ((hl-paren-last-point -1)) |
|
|
(hl-paren-highlight))) |
|
|
(defun hydra-timestamp/pre () |
|
|
(unless (boundp 'hydra-timestamp/hpm) |
|
|
(highlight-parentheses-mode 1)) |
|
|
(setq hydra-timestamp/hpm t)) |
|
|
(defun hydra-timestamp/post () |
|
|
(highlight-parentheses-mode -1) |
|
|
(makunbound 'hydra-timestamp/hpm)) |
|
|
(global-set-key |
|
|
(kbd "C-α") |
|
|
(defhydra hydra-timestamp |
|
|
(:pre hydra-timestamp/pre :post hydra-timestamp/post) |
|
|
"paren slurp and barf" |
|
|
("l" (progn |
|
|
(org-timestamp-up-day) |
|
|
(hydra-timestamp/hl-paren-force-fix)) "+1D") |
|
|
("n" (progn |
|
|
(org-timestamp-down-day) |
|
|
(hydra-timestamp/hl-paren-force-fix)) "-1D") |
|
|
("t" (progn |
|
|
(org-timestamp-up-day 7) |
|
|
(hydra-timestamp/hl-paren-force-fix)) "+1W") |
|
|
("s" (progn |
|
|
(org-timestamp-down-day 7) |
|
|
(hydra-timestamp/hl-paren-force-fix)) "-1W"))) |
|
|
#+end_src |
|
|
*** Hydras for refiling |
|
|
This bit evolved from [[https://www.mollermara.com/blog/Fast-refiling-in-org-mode-with-hydras/][this post]]; see more at [[https://gist.github.com/mm--/60e0790bcbf8447160cc87a66dc949ab ][this gist]]. |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/refile (file headline &optional arg) |
|
|
(let ((pos (save-excursion |
|
|
(find-file file) |
|
|
(org-find-exact-headline-in-buffer headline)))) |
|
|
(org-refile arg nil (list headline file nil pos))) |
|
|
(switch-to-buffer (current-buffer))) |
|
|
#+end_src |
|
|
#+begin_src emacs-lisp |
|
|
(defhydra wilder/hydra-org-refile (:foreign-keys run) |
|
|
"Refile" |
|
|
("g" (wilder/refile "hack.org" "emacs") "Refile to Hack-emacs") |
|
|
("q" nil "cancel")) |
|
|
(define-key org-mode-map (kbd "C-x \\") 'wilder/hydra-org-refile/body) |
|
|
#+end_src |
|
|
*** Hydras for moving headlines around |
|
|
#+begin_src emacs-lisp |
|
|
(defhydra wilder/hydra-org-move () |
|
|
"Move subtree" |
|
|
("f" org-backward-heading-same-level "move up" :column "Movement") |
|
|
("s" org-forward-heading-same-level "move down") |
|
|
("r" outline-up-heading "level down") |
|
|
("n" org-promote-subtree "promote" :column "Bubbling") |
|
|
("l" org-demote-subtree "demote") |
|
|
("i" org-move-subtree-up "bubble up") |
|
|
("e" org-move-subtree-down "bubble down") |
|
|
("q" nil "cancel")) |
|
|
(define-key org-mode-map (kbd "C-x SPC") 'wilder/hydra-org-move/body) |
|
|
#+end_src |
|
|
|
|
|
*** Capture |
|
|
Set default keybinding |
|
|
#+begin_src emacs-lisp |
|
|
(global-set-key (kbd "C-c c") 'org-capture) |
|
|
#+end_src |
|
|
This is my capture template: it needs to be revised as I really do not use |
|
|
The Idea, journal and break entry |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-default-notes-file "~/org/master.org") |
|
|
(setq org-capture-templates |
|
|
'(("t" "TODO today" entry (file+headline "~/org/orgzly/scheduled.org" "Tasks") |
|
|
"* TODO %?\n SCHEDULED: %t" :clock-in t :clock-resume t) |
|
|
("n" "TODO next" entry (file+headline "~/org/orgzly/scheduled.org" "Tasks") |
|
|
"* NEXT %?\n " :clock-in t :clock-resume t) |
|
|
("T" "TODO" entry (file+headline "~/org/orgzly/scheduled.org" "Tasks") |
|
|
"* TODO %?\n %a" :clock-in t :clock-resume t) |
|
|
("i" "Idea" entry (file+headline "~/org/master.org" "Ideas") |
|
|
"* IDEA %?\n %u" :clock-in t :clock-resume t ) |
|
|
("j" "Journal" entry (file+datetree "~/org/master.org") |
|
|
"* %?\n%U\n" :clock-in t :clock-resume t) |
|
|
("b" "Break" entry (file+datetree "~/org/master.org") |
|
|
"* break %?\n" :clock-in t :clock-resume t) |
|
|
("e" "Mail To" entry (file+headline "~/org/orgzly/scheduled.org" "E-mails") |
|
|
"* DONE mailto:%?") |
|
|
("R" "Reply" entry (file+headline "~/org/orgzly/scheduled.org" "E-Mails") |
|
|
"* TODO Reply to %:from \nSCHEDULED: %(org-insert-time-stamp |
|
|
(org-read-date nil t \"+0d\"))\n%a\n%?\n") |
|
|
("z" "Zoom meeting" entry (file+headline "~/org/orgzly/scheduled.org" "Tasks") |
|
|
"* TODO Attend %(format-zoom-meeting)\n%a" :immediate-finish t))) |
|
|
#+end_src |
|
|
*** LaTeX export |
|
|
#+begin_src emacs-lisp |
|
|
; (add-to-list 'org-latex-classes |
|
|
; '("amsart" "\\documentclass[11pt]{amsart}" |
|
|
; ("\\section{%s}" . "\\section*{%s}") |
|
|
; ("\\subsection{%s}" . "\\subsection*{%s}") |
|
|
; ("\\subsubsection{%s}" . "\\subsubsection*{%s}") |
|
|
; ("\\paragraph{%s}" . "\\paragraph*{%s}") |
|
|
; ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))) |
|
|
#+end_src |
|
|
*** Source blocks |
|
|
Add template for a source block in a few selected languages; for |
|
|
now I have |
|
|
- emacs-lisp |
|
|
- shell |
|
|
. This is useful for writing the emacs init file in literate form, |
|
|
or dotfiles for other code. Since Org 9.2, the behavior that I |
|
|
grew accustomed to (e.g. ~<el TAB~) )is implemented by ~org-tempo~, so I need to require that as well. |
|
|
#+begin_src emacs-lisp |
|
|
(dolist (structure-template '(("el" . "src emacs-lisp") |
|
|
("sh" . "src sh") |
|
|
("ss" . "src scheme") |
|
|
("py" . "src python"))) |
|
|
(add-to-list 'org-structure-template-alist structure-template)) |
|
|
|
|
|
(require 'org-tempo) |
|
|
#+end_src |
|
|
Fontify src blocks |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-src-fontify-natively t) |
|
|
#+end_src |
|
|
This makes scheme src blocks work |
|
|
#+begin_src emacs-lisp |
|
|
(org-babel-do-load-languages |
|
|
'org-babel-load-languages |
|
|
'((python . t) |
|
|
(scheme . t) |
|
|
(emacs-lisp . t))) |
|
|
#+end_src |
|
|
Patch ~ob-scheme.el~ while waiting for upstream |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-babel-scheme-execute-with-geiser (code output impl repl) |
|
|
"Execute code in specified REPL. |
|
|
If the REPL doesn't exist, create it using the given scheme |
|
|
implementation. |
|
|
|
|
|
Returns the output of executing the code if the OUTPUT parameter |
|
|
is true; otherwise returns the last value." |
|
|
|
|
|
(let ((result nil)) |
|
|
(with-temp-buffer |
|
|
(insert (format ";; -*- geiser-scheme-implementation: %s -*-" impl)) |
|
|
(newline) |
|
|
(insert code) |
|
|
(geiser-mode) |
|
|
(let ((geiser-repl-window-allow-split nil) |
|
|
(geiser-repl-use-other-window nil)) |
|
|
(let ((repl-buffer (save-current-buffer |
|
|
(org-babel-scheme-get-repl impl repl)))) |
|
|
(when (not (eq impl (org-babel-scheme-get-buffer-impl |
|
|
(current-buffer)))) |
|
|
(message "Implementation mismatch: %s (%s) %s (%s)" impl (symbolp impl) |
|
|
(org-babel-scheme-get-buffer-impl (current-buffer)) |
|
|
(symbolp (org-babel-scheme-get-buffer-impl |
|
|
(current-buffer))))) |
|
|
(setq geiser-repl--repl repl-buffer) |
|
|
(setq geiser-impl--implementation nil) |
|
|
(let ((geiser-debug-jump-to-debug-p nil) |
|
|
(geiser-debug-show-debug-p nil)) |
|
|
(let ((ret (funcall |
|
|
;; use `geiser-eval-region/wait' only when available |
|
|
;; in newer versions of `geiser' |
|
|
(if (fboundp 'geiser-eval-region/wait) |
|
|
'geiser-eval-region/wait |
|
|
'geiser-eval-region) |
|
|
(point-min) |
|
|
(point-max)))) |
|
|
(setq result (if output |
|
|
(or (geiser-eval--retort-output ret) |
|
|
"Geiser Interpreter produced no output") |
|
|
(geiser-eval--retort-result-str ret ""))))) |
|
|
(when (not repl) |
|
|
(save-current-buffer (set-buffer repl-buffer) |
|
|
(geiser-repl-exit)) |
|
|
(set-process-query-on-exit-flag (get-buffer-process repl-buffer) nil) |
|
|
(kill-buffer repl-buffer))))) |
|
|
result)) |
|
|
#+end_src |
|
|
*** Lowercase org blocks |
|
|
There is a new trend to use lowercase in the org block |
|
|
definitions; this gist (taken from the [[https://scripter.co/org-keywords-lower-case/][Scripter blog]]) lowercases |
|
|
all instances in the current buffer |
|
|
#+begin_src emacs-lisp |
|
|
(defun modi/lower-case-org-keywords () |
|
|
"Lower case Org keywords and block identifiers. |
|
|
|
|
|
Example: \"#+TITLE\" → \"#+title\" |
|
|
\"#+BEGIN_EXAMPLE\" → \"#+begin_example\" |
|
|
|
|
|
Inspiration: |
|
|
https://code.orgmode.org/bzg/org-mode/commit/13424336a6f30c50952d291e7a82906c1210daf0." |
|
|
(interactive) |
|
|
(save-excursion |
|
|
(goto-char (point-min)) |
|
|
(let ((case-fold-search nil) |
|
|
(count 0)) |
|
|
;; Match examples: "#+foo bar", "#+foo:", "=#+foo=", "~#+foo~", |
|
|
;; "‘#+foo’", "“#+foo”", ",#+foo bar", |
|
|
;; "#+FOO_bar<eol>", "#+FOO<eol>". |
|
|
(while (re-search-forward "\\(?1:#\\+[A-Z_]+\\(?:_[[:alpha:]]+\\)*\\)\\(?:[ :=~’”]\\|$\\)" nil :noerror) |
|
|
(setq count (1+ count)) |
|
|
(replace-match (downcase (match-string-no-properties 1)) :fixedcase nil nil 1)) |
|
|
(message "Lower-cased %d matches" count)))) |
|
|
#+end_src |
|
|
*** Clocking |
|
|
#+begin_src emacs-lisp |
|
|
;; Separate drawers for clocking and logs |
|
|
(setq org-drawers (quote ("PROPERTIES" "CLOCKBOOK"))) |
|
|
;; Save clock data and state changes and notes in the CLOCK drawer |
|
|
(setq org-clock-into-drawer "CLOCKBOOK") |
|
|
;; Sometimes I change tasks I'm clocking quickly - this removes clocked tasks with 0:00 duration |
|
|
(setq org-clock-out-remove-zero-time-clocks t) |
|
|
;; Clock out when moving task to a done state |
|
|
(setq org-clock-out-when-done t) |
|
|
|
|
|
(setq org-clock-persist 'history) |
|
|
|
|
|
(setq org-duration-format 'h:mm) |
|
|
(setq org-clock-clocktable-default-properties '(:maxlevel 2 :scope subtree)) |
|
|
(org-clock-persistence-insinuate) |
|
|
#+end_src |
|
|
*** COMMENT Tangle to different files |
|
|
This has been broken by commit 3ebee033103ccd3c3e8c354bad01c15332b9d901 |
|
|
and a2cb9b853d30fc301f4553d1556dba4ee6bc1ead. |
|
|
Try some workarounds to pinpoint the issue: |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-babel-tangle-use-relative-file-links nil) |
|
|
#+end_src |
|
|
---- |
|
|
|
|
|
This is some some super-clever stuff. See |
|
|
[https://emacs.stackexchange.com/questions/39032/tangle-the-same-src-block-to-different-files] |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-babel-tangle-collect-blocks-handle-tangle-list (&optional language tangle-file) |
|
|
"Can be used as :override advice for `org-babel-tangle-collect-blocks'. |
|
|
Handles lists of :tangle files." |
|
|
(let ((counter 0) last-heading-pos blocks) |
|
|
(org-babel-map-src-blocks (buffer-file-name) |
|
|
(let ((current-heading-pos |
|
|
(org-with-wide-buffer |
|
|
(org-with-limited-levels (outline-previous-heading))))) |
|
|
(if (eq last-heading-pos current-heading-pos) (cl-incf counter) |
|
|
(setq counter 1) |
|
|
(setq last-heading-pos current-heading-pos))) |
|
|
(unless (org-in-commented-heading-p) |
|
|
(let* ((info (org-babel-get-src-block-info)) |
|
|
(src-lang (nth 0 info)) |
|
|
(src-tfiles (cdr (assq :tangle (nth 2 info))))) ; Tobias: accept list for :tangle |
|
|
(unless (consp src-tfiles) ; Tobias: unify handling of strings and lists for :tangle |
|
|
(setq src-tfiles (list src-tfiles))) ; Tobias: unify handling |
|
|
(dolist (src-tfile src-tfiles) ; Tobias: iterate over list |
|
|
(unless (or (string= src-tfile "no") |
|
|
(and tangle-file (not (equal tangle-file src-tfile))) |
|
|
(and language (not (string= language src-lang)))) |
|
|
;; Add the spec for this block to blocks under its |
|
|
;; language. |
|
|
(let ((by-lang (assoc src-lang blocks)) |
|
|
(block (org-babel-tangle-single-block counter))) |
|
|
(setcdr (assoc :tangle (nth 4 block)) src-tfile) ; Tobias: |
|
|
(if by-lang (setcdr by-lang (cons block (cdr by-lang))) |
|
|
(push (cons src-lang (list block)) blocks)))))))) ; Tobias: just () |
|
|
;; Ensure blocks are in the correct order. |
|
|
(mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) blocks))) |
|
|
|
|
|
(defun org-babel-tangle-single-block-handle-tangle-list (oldfun block-counter &optional only-this-block) |
|
|
"Can be used as :around advice for `org-babel-tangle-single-block'. |
|
|
If the :tangle header arg is a list of files. Handle all files" |
|
|
(let* ((info (org-babel-get-src-block-info)) |
|
|
(params (nth 2 info)) |
|
|
(tfiles (cdr (assoc :tangle params)))) |
|
|
(message nil) |
|
|
(if (null (and only-this-block (consp tfiles))) |
|
|
(funcall oldfun block-counter only-this-block) |
|
|
(cl-assert (listp tfiles) nil |
|
|
":tangle only allows a tangle file name or a list of tangle file names") |
|
|
(let ((ret (mapcar |
|
|
(lambda (tfile) |
|
|
(message tfile) |
|
|
(sleep-for 1) |
|
|
(let (old-get-info) |
|
|
(cl-letf* (((symbol-function 'old-get-info) (symbol-function 'org-babel-get-src-block-info)) |
|
|
((symbol-function 'org-babel-get-src-block-info) |
|
|
`(lambda (&rest get-info-args) |
|
|
(let* ((info (apply 'old-get-info get-info-args)) |
|
|
(params (nth 2 info)) |
|
|
(tfile-cons (assoc :tangle params))) |
|
|
(setcdr tfile-cons ,tfile) |
|
|
info)))) |
|
|
(funcall oldfun block-counter only-this-block)))) |
|
|
tfiles))) |
|
|
(if only-this-block |
|
|
(list (cons (cl-caaar ret) (mapcar #'cadar ret))) |
|
|
ret))))) |
|
|
|
|
|
|
|
|
(advice-add 'org-babel-tangle-collect-blocks :override |
|
|
#'org-babel-tangle-collect-blocks-handle-tangle-list) |
|
|
(advice-add 'org-babel-tangle-single-block :around |
|
|
#'org-babel-tangle-single-block-handle-tangle-list) |
|
|
#+end_src |
|
|
*** Append tangle |
|
|
#+begin_src emacs-lisp |
|
|
(defun org-babel-tangle-append (filename) |
|
|
"Append source code block at point to its tangle file. |
|
|
The command works like `org-babel-tangle' with prefix arg |
|
|
but `delete-file' is ignored." |
|
|
(interactive) |
|
|
(cl-letf (((symbol-function 'delete-file) #'ignore)) |
|
|
(org-babel-tangle '(4) filename))) |
|
|
#+end_src |
|
|
*** Tangle file |
|
|
This can be used to tangle one or more files to their output files [[https://gitlab.com/to1ne/literate-dotfiles/blob/master/elisp/tangle.el][ |
|
|
Source on gitlab]] |
|
|
#+begin_src emacs-lisp |
|
|
(defun tangle-file(&rest files nokill) |
|
|
"Tangle FILES or all files in the project." |
|
|
(when (null files) |
|
|
(setq files command-line-args-left)) |
|
|
(dolist (file files) |
|
|
(with-current-buffer (find-file-noselect file) |
|
|
(org-babel-tangle) |
|
|
; (unless nokill (kill-buffer)) |
|
|
))) |
|
|
#+end_src |
|
|
*** Export file |
|
|
This can be used to tangle one or more files to their output files [[https://gitlab.com/to1ne/literate-dotfiles/blob/master/elisp/tangle.el][ |
|
|
Source on gitlab]] |
|
|
#+begin_src emacs-lisp |
|
|
(defun export-org-file-pdf(&rest files nokill) |
|
|
"Export FILES or all files in the project." |
|
|
(when (null files) |
|
|
(setq files command-line-args-left)) |
|
|
(dolist (file files) |
|
|
(with-current-buffer (find-file-noselect file) |
|
|
(org-latex-export-to-pdf) |
|
|
(kill-buffer)))) |
|
|
#+end_src |
|
|
*** Crypto stuff |
|
|
#+begin_src emacs-lisp |
|
|
(require 'org-crypt) |
|
|
(org-crypt-use-before-save-magic) |
|
|
(setq org-tags-exclude-from-inheritance (quote ("crypt"))) |
|
|
|
|
|
(setq org-crypt-key "C86A9E7675295C62") |
|
|
#+end_src |
|
|
|
|
|
*** ox-export |
|
|
Load ox-hugo |
|
|
#+begin_src emacs-lisp |
|
|
(with-eval-after-load 'ox |
|
|
(require 'ox-hugo)) |
|
|
#+end_src |
|
|
*** ~org-roam~ setup |
|
|
#+begin_src emacs-lisp |
|
|
(setq org-roam-directory (file-truename "~/org-roam")) |
|
|
(org-roam-db-autosync-mode) |
|
|
(global-set-key (kbd "C-c n f") 'org-roam-node-find) |
|
|
#+end_src |
|
|
** elisp |
|
|
*** Paredit |
|
|
#+begin_src emacs-lisp |
|
|
(autoload 'enable-paredit-mode "paredit" "Turn on |
|
|
pseudo-structural editing of Lisp code." t) |
|
|
(add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode) |
|
|
(add-hook 'scheme-mode-hook #'enable-paredit-mode) |
|
|
(require 'highlight-parentheses) |
|
|
|
|
|
(defun hydra-paren/hl-paren-force-fix () |
|
|
;;; This is needed b/c hl-parent caches the point position and |
|
|
;;; refuses to update if the point did not move |
|
|
(let ((hl-paren-last-point -1)) |
|
|
(hl-paren-highlight))) |
|
|
(defun hydra-paren/pre () |
|
|
(unless (boundp 'hydra-paren/hpm) |
|
|
(highlight-parentheses-mode 1)) |
|
|
(setq hydra-paren/hpm t)) |
|
|
(defun hydra-paren/post () |
|
|
(highlight-parentheses-mode -1) |
|
|
(makunbound 'hydra-paren/hpm)) |
|
|
(global-set-key |
|
|
(kbd "C-SPC") |
|
|
(defhydra hydra-paren |
|
|
(:pre hydra-paren/pre :post hydra-paren/post) |
|
|
"paren slurp and barf" |
|
|
("l" (progn |
|
|
(paredit-forward-slurp-sexp) |
|
|
(hydra-paren/hl-paren-force-fix)) "slurp forward") |
|
|
("n" (progn |
|
|
(paredit-forward-barf-sexp) |
|
|
(hydra-paren/hl-paren-force-fix)) "barf forward") |
|
|
("r" (progn |
|
|
(paredit-backward-slurp-sexp) |
|
|
(hydra-paren/hl-paren-force-fix)) "slurp backward") |
|
|
("t" (progn |
|
|
(paredit-backward-barf-sexp) |
|
|
(hydra-paren/hl-paren-force-fix)) "barf backward"))) |
|
|
#+end_src |
|
|
*** Replace last sexp |
|
|
I use this a lot to evaluate (e.g.) quick computations in files |
|
|
#+begin_src emacs-lisp |
|
|
(defun replace-last-sexp () |
|
|
(interactive) |
|
|
(let ((value (eval (preceding-sexp)))) |
|
|
(kill-sexp -1) |
|
|
(insert (format "%S" value)))) |
|
|
|
|
|
(global-set-key (kbd "C-c C-x C-e") #'replace-last-sexp) |
|
|
#+end_src |
|
|
** qml |
|
|
Load ~qml-mode~ |
|
|
#+begin_src emacs-lisp |
|
|
(autoload 'qml-mode "qml-mode.el" t) |
|
|
(add-to-list 'auto-mode-alist '("\\.qml\\'" . qml-mode)) |
|
|
#+end_src |
|
|
** Python |
|
|
Silent offset warning |
|
|
#+begin_src emacs-lisp |
|
|
(setq python-indent-guess-indent-offset-verbose nil) |
|
|
#+end_src |
|
|
** C and C++ |
|
|
*** Hooks |
|
|
#+begin_src emacs-lisp |
|
|
;; (require 'doxymacs) |
|
|
|
|
|
(add-hook 'c-mode-hook |
|
|
(lambda () |
|
|
(subword-mode) |
|
|
(doxymacs-mode) |
|
|
(define-key c-mode-map (kbd "C-c C-c") 'make))) |
|
|
(add-hook 'c++-mode-hook |
|
|
(lambda () |
|
|
(subword-mode) |
|
|
(doxymacs-mode) |
|
|
(define-key c++-mode-map (kbd "C-c C-c") 'make))) |
|
|
#+end_src |
|
|
** TODO split --- LaTeX |
|
|
*** TODO Setup ~reftex~ |
|
|
#+begin_src emacs-lisp |
|
|
(with-eval-after-load "reftex" |
|
|
(setq reftex-cite-format "~\\cite{%l}" |
|
|
reftex-plug-into-AUCTeX '(t t nil t t) |
|
|
reftex-insert-label-flags '(t t) |
|
|
reftex-label-alist |
|
|
'(("thm" ?t "thm:" "~\\ref{%s}" thm (regexp "theorems?")) |
|
|
("lem" ?l "lem:" "~\\ref{%s}" lem (regexp "lemma{ta}?")) |
|
|
("prop" ?p "prp:" "~\\ref{%s}" prp (regexp "propositions?")) |
|
|
("cor" ?c "cor:" "~\\ref{%s}" cor (regexp "corollary")) |
|
|
("def" ?d "def:" "~\\ref{%s}" cor (regexp "defintions?"))))) |
|
|
;;; advice the auctex function to replace the |
|
|
;;; lighter with somthing softer |
|
|
(with-eval-after-load "auctex" |
|
|
(defun cleanup-TeX-mode (&optional mode) |
|
|
"Advice for the base auctex MODE naming" |
|
|
(setq mode-name (replace-regexp-in-string "LaTeX" "χ" mode-name t)) |
|
|
(setq mode-name (replace-regexp-in-string "/" "·" mode-name))) |
|
|
(advice-add 'TeX-set-mode-name :after 'cleanup-TeX-mode)) |
|
|
|
|
|
#+end_src |
|
|
*** Setup ~latex-mode~ |
|
|
#+begin_src emacs-lisp |
|
|
(eval-after-load 'latex |
|
|
'(progn |
|
|
(font-lock-add-keywords 'latex-mode |
|
|
`((,(rx "$") 0 'font-latex-sedate-face t)) t) |
|
|
(defface font-latex-special-comment-face '((t (:foreground "#2aa198"))) "Cyan") |
|
|
|
|
|
|
|
|
(font-lock-add-keywords 'latex-mode '(("^% \\([^*].*\\)" 1 'font-latex-special-comment-face t))) |
|
|
|
|
|
(define-key-alist LaTeX-mode-map |
|
|
'(("M-S-SPC" . TeX-insert-braces) |
|
|
("C-c C-v" . wilder/TeX-insert-reference) |
|
|
("C-M-<return>" . wilder/TeX-insert-align-dwim) |
|
|
;; unbind return - NOTE it is important to unbind |
|
|
;; <return> and not RET. If we unbind RET then C-m won't work |
|
|
;; either. |
|
|
("<return>" . (lambda() (interactive) (insert "\\"))) |
|
|
("S-<return>" . (lambda() (interactive) (insert "|"))) |
|
|
("C-c C-." . LaTeX-mark-environment) |
|
|
("C-c <return>" . LaTeX-environment) |
|
|
("C-c C-e" . (lambda (r-begin r-end) |
|
|
(interactive "r") |
|
|
(add-delimiter "_{" "}" r-begin r-end))) |
|
|
("C-c C-i" . (lambda (r-begin r-end) |
|
|
(interactive "r") |
|
|
(add-delimiter "^{" "}" r-begin r-end))) |
|
|
("M-|" . (lambda (r-begin r-end) |
|
|
(interactive "r") |
|
|
(add-delimiter "|" "|" r-begin r-end))) |
|
|
("M-," . (lambda (r-begin r-end) |
|
|
(interactive "r") |
|
|
(add-delimiter ", " ", " r-begin r-end))) |
|
|
("M-\"" . (lambda (r-begin r-end) |
|
|
(interactive "r") |
|
|
(add-delimiter "“" "”" r-begin r-end))) |
|
|
|
|
|
;; This is the rationale: C-M-SPC starts inline math C-M-RET starts display math |
|
|
|
|
|
("C-M-SPC" . (lambda (r-begin r-end) (interactive "r") (add-delimiter "$" "$" r-begin r-end))) |
|
|
("C-c C-d" . wilder/TeX-insert-todonote) |
|
|
("C-M-d" . kill-sexp) |
|
|
("C-M-i" . down-list) ;; -i stands for /in/ |
|
|
("C-M-o" . up-list) ;; -o stands for /out/ |
|
|
("<f4>" . ( lambda() (interactive) (message "Use C-c C-c"))) |
|
|
("C-c C-c" . ( lambda() (interactive) (compile "/home/jacopods/scripts/latex-mk")) ) |
|
|
("~" . wilder/TeX-replace-tilde) |
|
|
("C-o" . wilder/open-line-and-indent) |
|
|
;; Force moves around to be more “semantic” |
|
|
("C-v" . goto-next-comment-line) |
|
|
("M-v" . goto-previous-comment-line) |
|
|
("C-x n c" . narrow-between-comments) |
|
|
("C-S-v" . backward-paragraph))) |
|
|
|
|
|
(dolist (ch '("=" "≠" ">" "<" "≥" "≤" "⇒" "∩" "∪" "∨" "∧" "×" "⊂" "⊃")) |
|
|
(define-key LaTeX-mode-map (kbd ch) 'insert-char-with-padding)) |
|
|
|
|
|
;; Move around commands in the Right Way™ |
|
|
(modify-syntax-entry ?\\ "w" LaTeX-mode-syntax-table) |
|
|
|
|
|
;; add fancy quotes to the syntax table |
|
|
(modify-syntax-entry ?“ "(”" LaTeX-mode-syntax-table) |
|
|
(modify-syntax-entry ?” ")“" LaTeX-mode-syntax-table) |
|
|
|
|
|
(setq subword-forward-regexp "\\W*\\(\\([\\\\[:upper:]]*\\W?\\)[[:lower:][:digit:]]*\\)") |
|
|
(setq subword-backward-regexp "\\(\\(\\W\\|[[:lower:][:digit:]]\\)\\([\\\\[:upper:]]+\\W*\\)\\|\\W\\w+\\)"))) |
|
|
#+end_src |
|
|
|
|
|
*** Require |
|
|
Load auctex, reftex and set up related hooks |
|
|
#+begin_src emacs-lisp |
|
|
(require 'tex-site) |
|
|
(require 'reftex) |
|
|
#+end_src |
|
|
*** Set default TeX options |
|
|
#+begin_src emacs-lisp |
|
|
(setq-default TeX-master nil) |
|
|
(setq TeX-auto-save t |
|
|
TeX-parse-self t |
|
|
TeX-insert-braces nil) |
|
|
#+end_src |
|
|
*** Appearance |
|
|
No fontification for sub and superscripts |
|
|
#+begin_src emacs-lisp |
|
|
(setq font-latex-fontify-script nil) |
|
|
#+end_src |
|
|
Add unicode quotes to the quote-list |
|
|
#+begin_src emacs-lisp |
|
|
; (defvar font-latex-quote-list '(("``" "''") ("“" "”") ("<<" ">>" french) ("«" "»" french)) ; |
|
|
#+end_src |
|
|
Redefine fold ellipsis |
|
|
#+begin_src emacs-lisp |
|
|
(setq TeX-fold-ellipsis " …") |
|
|
#+end_src |
|
|
*** Default labels |
|
|
#+begin_src emacs-lisp |
|
|
(setq LaTeX-equation-label "e_" |
|
|
LaTeX-section-label "s_" |
|
|
LaTeX-figure-label "f_") |
|
|
#+end_src |
|
|
*** Spell checking help |
|
|
Do not spell-check inside the following commands. See |
|
|
[https://tex.stackexchange.com/questions/117204/skip-spelling-in-emacs-for-the-content-of-a-user-macro] |
|
|
#+begin_src emacs-lisp |
|
|
(setq ispell-tex-skip-alists |
|
|
(list |
|
|
(append |
|
|
(car ispell-tex-skip-alists) ;tell ispell to ignore content of this: |
|
|
'(("\\\\eqref" ispell-tex-arg-end) |
|
|
("\\\\cite" ispell-tex-arg-end) |
|
|
("\\\\author" ispell-tex-arg-end) |
|
|
("\\\\address" ispell-tex-arg-end) |
|
|
("\\\\include" ispell-tex-arg-end) |
|
|
)) |
|
|
(cadr ispell-tex-skip-alists))) |
|
|
#+end_src |
|
|
*** Specialty functions |
|
|
This function adds a pair of delimiters or surrounds the active region |
|
|
with the given delimiters. |
|
|
TODO it breaks when there is no mark |
|
|
#+begin_src emacs-lisp |
|
|
(defun add-delimiter (delim-begin delim-end r-begin r-end) |
|
|
"Add the pair of delimiters given in delim at the ends of the |
|
|
region if it is activated" |
|
|
(interactive "cBegin delimiter: \ncEnd delimiter: \nr") |
|
|
(if (region-active-p) |
|
|
(progn |
|
|
(save-excursion |
|
|
(goto-char r-end) |
|
|
(insert delim-end) |
|
|
(goto-char r-begin) |
|
|
(insert delim-begin))) |
|
|
(progn |
|
|
(save-excursion |
|
|
(insert delim-end)) |
|
|
(insert delim-begin)))) |
|
|
#+end_src |
|
|
The following re-implements =TeX-insert-braces= to work with |
|
|
negative argument |
|
|
#+begin_src emacs-lisp |
|
|
(defun TeX-insert-braces (arg) |
|
|
"Make a pair of braces around next ARG sexps and leave point inside. |
|
|
No argument is equivalent to zero: just insert braces and leave point |
|
|
between. |
|
|
|
|
|
If there is an active region, ARG will be ignored, braces will be |
|
|
inserted around the region, and point will be left after the |
|
|
closing brace." |
|
|
(interactive "P") |
|
|
(if (TeX-active-mark) |
|
|
(progn |
|
|
(if (< (point) (mark)) |
|
|
(exchange-point-and-mark)) |
|
|
(insert TeX-grcl) |
|
|
(save-excursion |
|
|
(goto-char (mark)) |
|
|
(insert TeX-grop))) |
|
|
(if (and arg (< arg 0)) |
|
|
(progn |
|
|
(save-excursion |
|
|
(backward-sexp (prefix-numeric-value (- 0 arg))) |
|
|
(insert TeX-grop)) |
|
|
(insert TeX-grcl)) |
|
|
(insert TeX-grop) |
|
|
(save-excursion |
|
|
(if arg (forward-sexp (prefix-numeric-value arg))) |
|
|
(insert TeX-grcl))))) |
|
|
|
|
|
(defun TeX-back-insert-braces (arg) |
|
|
(interactive "P") |
|
|
(if arg (TeX-insert-braces (- 0 arg)) |
|
|
(insert TeX-grcl))) |
|
|
#+end_src |
|
|
This is a helper for [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Dynamic-Abbrevs.html][Dynamic abbrev expansion]] |
|
|
#+begin_src emacs-lisp |
|
|
(defun dabbrev-expand-helper () |
|
|
(interactive) |
|
|
(call-interactively 'dabbrev-expand)) |
|
|
#+end_src |
|
|
This inserts a char adding some whitespace padding whenever necessary |
|
|
#+begin_src emacs-lisp |
|
|
(defun insert-char-with-padding (arg) |
|
|
(interactive "*P") |
|
|
(unless (string-match (string (preceding-char)) " \&") |
|
|
(insert " ")) |
|
|
(self-insert-command (prefix-numeric-value arg)) |
|
|
(unless (char-equal (following-char) ?\s) |
|
|
(insert " "))) ;; decide what to do with the point |
|
|
#+end_src |
|
|
This function opens a line and indents it |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/open-line-and-indent (arg) |
|
|
"This function opens a line using (open-line) and indents it" |
|
|
(interactive "p") |
|
|
(open-line arg) |
|
|
(save-excursion (forward-char arg) (unless (eolp) (indent-according-to-mode)))) |
|
|
#+end_src |
|
|
This inserts a plain reference (or a reference to an equation with |
|
|
prefix arg) |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/TeX-insert-reference (arg) |
|
|
(interactive "P") |
|
|
(insert "~") |
|
|
(if arg (TeX-insert-macro "eqref") |
|
|
(TeX-insert-macro "ref"))) |
|
|
#+end_src |
|
|
This inserts a citation |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/TeX-insert-cite (arg) |
|
|
(interactive "P") |
|
|
(insert "~") |
|
|
(TeX-insert-macro "cite")) |
|
|
#+end_src |
|
|
This inserts an ~align~ environment (or an ~align*~ with prefix arg) |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/TeX-insert-align (arg) |
|
|
(interactive "P") |
|
|
(if arg |
|
|
(LaTeX-insert-environment "align") |
|
|
(LaTeX-insert-environment "align*")) |
|
|
(indent-according-to-mode)) |
|
|
|
|
|
(defun wilder/TeX-insert-align-dwim (arg) |
|
|
(interactive "P") |
|
|
(if (texmathp) |
|
|
(let ((why (car texmathp-why))) |
|
|
(cond ((string-prefix-p "$" why) (wilder/TeX-promote-inline-math arg)) |
|
|
((string-equal "equation*" why) (LaTeX-modify-environment "align*")) |
|
|
((string-equal "equation" why) (LaTeX-modify-environment "align")) |
|
|
((string-equal "align*" why) (when arg (LaTeX-modify-environment "align"))))) |
|
|
(wilder/TeX-insert-align arg))) |
|
|
#+end_src |
|
|
This replaces horizontal space with a tilde |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/TeX-replace-tilde (arg) |
|
|
(interactive "P") |
|
|
(delete-horizontal-space) |
|
|
(insert "~")) |
|
|
#+end_src |
|
|
Insert a todonote at point or wrap the region in a todonote. |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/TeX-insert-todonote (arg r-begin r-end) |
|
|
(interactive "P\nr") |
|
|
(if arg |
|
|
(add-delimiter "{\\todo[inline]{" "}}" r-begin r-end) |
|
|
(add-delimiter "{\\todo{" "}}" r-begin r-end))) |
|
|
#+end_src |
|
|
Promote inline math to an align |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/char-punctuation-p (ch) |
|
|
"returns t if the char is a punctuation" |
|
|
(interactive) |
|
|
(string-match (string ch) ".,:;")) |
|
|
(defun wilder/TeX-promote-inline-math (arg) |
|
|
(interactive "P") |
|
|
(when (and (texmathp) (string-prefix-p "$" (car texmathp-why))) |
|
|
(let* ((delim (car texmathp-why)) |
|
|
(pos (cdr texmathp-why)) |
|
|
(n (length delim))) |
|
|
(goto-char pos) |
|
|
(push-mark) |
|
|
(delete-char n) |
|
|
(search-forward delim) |
|
|
(delete-backward-char n) |
|
|
;; include punctuations in the align |
|
|
(when (wilder/char-punctuation-p (char-after)) (forward-char)) |
|
|
(activate-mark) |
|
|
(wilder/TeX-insert-align arg) |
|
|
(pop-mark) |
|
|
(pop-mark)))) |
|
|
#+end_src |
|
|
Shadow the AucTeX version of this function |
|
|
#+begin_src emacs-lisp |
|
|
(defun LaTeX-insert-environment (environment &optional extra) |
|
|
"Insert LaTeX ENVIRONMENT with optional argument EXTRA." |
|
|
(let ((active-mark (and (TeX-active-mark) (not (eq (mark) (point))))) |
|
|
prefix content-start env-start env-end) |
|
|
(when (and active-mark (< (mark) (point))) (exchange-point-and-mark)) |
|
|
;; Compute the prefix. |
|
|
(when (and LaTeX-insert-into-comments (TeX-in-commented-line)) |
|
|
(save-excursion |
|
|
(beginning-of-line) |
|
|
(looking-at |
|
|
(concat "^\\([ \t]*" TeX-comment-start-regexp "+\\)+[ \t]*")) |
|
|
(setq prefix (match-string 0)))) |
|
|
;; What to do with the line containing point. |
|
|
(cond (;; if the line contains only whitespace, delete them |
|
|
(save-excursion (beginning-of-line) |
|
|
(looking-at (concat prefix "[ \t]*$"))) |
|
|
(delete-region (match-beginning 0) (match-end 0))) |
|
|
;; otherwise, something is written on the line. We can be |
|
|
;; at the beginning of the text |
|
|
((TeX-looking-at-backward (concat "^" prefix "[ \t]*") |
|
|
(line-beginning-position)) |
|
|
;;this morally opens a new line |
|
|
(beginning-of-line) |
|
|
(newline) |
|
|
(beginning-of-line 0)) |
|
|
((bolp) |
|
|
(delete-horizontal-space) |
|
|
(newline) |
|
|
(beginning-of-line 0)) |
|
|
((looking-at "[ \t]*$") |
|
|
(delete-horizontal-space) |
|
|
(newline) |
|
|
(when prefix (insert prefix)) |
|
|
(beginning-of-line)) |
|
|
(t |
|
|
(delete-horizontal-space) |
|
|
(newline 2) |
|
|
(when prefix (insert prefix)) |
|
|
(beginning-of-line 0))) |
|
|
;; What to do with the line containing mark. |
|
|
(when active-mark |
|
|
(save-excursion |
|
|
(goto-char (mark)) |
|
|
(cond ((save-excursion (beginning-of-line) |
|
|
(or (looking-at (concat prefix "[ \t]*$")) |
|
|
(looking-at "[ \t]*$"))) |
|
|
(delete-region (match-beginning 0) (match-end 0))) |
|
|
((TeX-looking-at-backward (concat "^" prefix "[ \t]*") |
|
|
(line-beginning-position)) |
|
|
(beginning-of-line) |
|
|
(newline) |
|
|
(beginning-of-line 0)) |
|
|
((looking-at "[ \t]*$") |
|
|
(delete-horizontal-space) |
|
|
(insert-before-markers "\n") |
|
|
;(newline) |
|
|
(when prefix (insert prefix))) |
|
|
(t |
|
|
(delete-horizontal-space) |
|
|
(insert-before-markers "\n") |
|
|
(newline) |
|
|
(when prefix (insert prefix)))))) |
|
|
;; Now insert the environment. |
|
|
(when prefix (insert prefix)) |
|
|
(setq env-start (point)) |
|
|
(insert TeX-esc "begin" TeX-grop environment TeX-grcl) |
|
|
(indent-according-to-mode) |
|
|
(when extra (insert extra)) |
|
|
(setq content-start (line-beginning-position 2)) |
|
|
(unless active-mark |
|
|
(newline) |
|
|
(when prefix (insert prefix)) |
|
|
(newline)) |
|
|
(when active-mark (goto-char (mark))) |
|
|
(when prefix (insert prefix)) |
|
|
(insert TeX-esc "end" TeX-grop environment TeX-grcl) |
|
|
(end-of-line 0) |
|
|
(if active-mark |
|
|
(progn |
|
|
(or (assoc environment LaTeX-indent-environment-list) |
|
|
(if auto-fill-function |
|
|
;; Fill the region only when `auto-fill-mode' is active. |
|
|
(LaTeX-fill-region content-start (line-beginning-position 2)))) |
|
|
(set-mark content-start)) |
|
|
(indent-according-to-mode)) |
|
|
(save-excursion (beginning-of-line 2) (indent-according-to-mode)) |
|
|
(TeX-math-input-method-off) |
|
|
(setq env-end (save-excursion |
|
|
(search-forward |
|
|
(concat TeX-esc "end" TeX-grop |
|
|
environment TeX-grcl)) |
|
|
(match-beginning 0))) |
|
|
(run-hook-with-args 'LaTeX-after-insert-env-hooks |
|
|
environment env-start env-end))) |
|
|
#+end_src |
|
|
*** Patch auctex |
|
|
I don't particularly like the way that the prefix arguments works |
|
|
with font selection. The function below patches the annoying |
|
|
behavior, by defaulting to the default behavior if the prefix |
|
|
argument is 0 |
|
|
#+begin_src emacs-lisp |
|
|
(defun wilder/TeX-font (replace what) |
|
|
"Insert template for font change command. |
|
|
If REPLACE is not nil, replace current font. WHAT determines the font |
|
|
to use, as specified by `TeX-font-list'." |
|
|
(interactive "*P\nc") |
|
|
(TeX-update-style) |
|
|
(let* ((entry (assoc what TeX-font-list)) |
|
|
(in-math (texmathp)) |
|
|
(before (nth 1 entry)) |
|
|
(after (nth 2 entry))) |
|
|
(setq replace (or replace (eq t (nth 3 entry)) (eq t (nth 5 entry)))) |
|
|
(if (and in-math (stringp (nth 3 entry))) |
|
|
(setq before (nth 3 entry) |
|
|
after (nth 4 entry))) |
|
|
(setq arg (prefix-numeric-value replace)) |
|
|
(cond |
|
|
((null entry) |
|
|
(let ((help (concat |
|
|
"Font list: " |
|
|
"KEY TEXTFONT MATHFONT\n\n" |
|
|
(mapconcat 'TeX-describe-font-entry |
|
|
TeX-font-list "\n")))) |
|
|
(with-output-to-temp-buffer "*Help*" |
|
|
(set-buffer "*Help*") |
|
|
(insert help)))) |
|
|
((and replace (eq 0 arg)) |
|
|
(funcall TeX-font-replace-function before after)) |
|
|
((and replace (< arg 0) ) |
|
|
(progn |
|
|
(save-excursion |
|
|
(backward-sexp (- 0 arg)) |
|
|
(insert before)) |
|
|
(insert after))) |
|
|
((and replace (> arg 0)) |
|
|
(insert before) |
|
|
(save-excursion |
|
|
(forward-sexp arg) |
|
|
(insert after))) |
|
|
((TeX-active-mark) |
|
|
(save-excursion |
|
|
(cond ((> (mark) (point)) |
|
|
(insert before) |
|
|
(goto-char (mark)) |
|
|
(insert after)) |
|
|
(t |
|
|
(insert after) |
|
|
(goto-char (mark)) |
|
|
(insert before))))) |
|
|
(t |
|
|
(insert before) |
|
|
(save-excursion |
|
|
(insert after)))))) |
|
|
(advice-add 'TeX-font :override #'wilder/TeX-font) |
|
|
#+end_src |
|
|
*** The hook |
|
|
#+begin_src emacs-lisp |
|
|
(add-hook 'LaTeX-mode-hook |
|
|
(lambda () |
|
|
(turn-on-reftex) |
|
|
|
|
|
(setq comment-column 0) |
|
|
(setq prettify-symbols-alist nil) |
|
|
(add-to-list 'prettify-symbols-alist '(" ⊂ " . (? (Br . Bl) ? (Br . Bl) ?))) |
|
|
|
|
|
(turn-on-auto-fill) |
|
|
(subword-mode) |
|
|
(TeX-fold-mode 1) |
|
|
(outshine-mode))) |
|
|
#+end_src |
|
|
*** Compilation fixes |
|
|
Load appropriate compliation filters for warnings |
|
|
#+begin_src emacs-lisp |
|
|
(load "latex-compile-filters.el") |
|
|
#+end_src |
|
|
Fix erroneous parsing of the LuaLaTeχ output "avail lists" as an |
|
|
error (works on ≥ emacs-27.1) |
|
|
#+begin_src emacs-lisp |
|
|
(add-to-list 'compilation-transform-file-match-alist '(" avail lists" nil)) |
|
|
#+end_src |
|
|
*** Load ~bibretrieve~ |
|
|
#+begin_src emacs-lisp |
|
|
(byte-recompile-directory "~/.emacs.d/bibretrieve" 0) |
|
|
(load "bibretrieve") |
|
|
(setq bibretrieve-backends '(("msn" . 10))) |
|
|
(require 'biblio) |
|
|
#+end_src |
|
|
** TODO KDE integration |
|
|
*** TODO cleanup and split |
|
|
These functions connects to dbus to find out the current activity |
|
|
id and name |
|
|
#+begin_src emacs-lisp |
|
|
(defun kde-current-activity () |
|
|
"Returns the current KDE activity" |
|
|
(substring (shell-command-to-string "qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity") 0 -1) |
|
|
) |
|
|
(defun kde-current-activity-name () |
|
|
"Returns the name of the current KDE activity" |
|
|
(substring (shell-command-to-string (concat "qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.ActivityName " (kde-current-activity))) 0 -1)) |
|
|
#+end_src |
|
|
|
|
|
#+begin_src emacs-lisp |
|
|
(defun X-window-id-belongs-to-activity (window-id activity) |
|
|
(= (shell-command (concat "xprop -id " window-id " | grep _KDE_NET_WM_ACTIVITIES | grep " activity ">/dev/null")) 0) |
|
|
) |
|
|
|
|
|
(defun select-frame-on-activity (activity) |
|
|
(setq framelist (frame-list)) |
|
|
(setq done nil) |
|
|
(while (and framelist (not done)) |
|
|
(setq cur (car framelist)) |
|
|
(setq cur-id (cdr (assq 'window-id (frame-parameters cur)))) |
|
|
(if cur-id (if (X-window-id-belongs-to-activity cur-id activity) (progn; |
|
|
(setq done 't) (select-frame cur)) )) |
|
|
(setq framelist (cdr framelist))) |
|
|
done |
|
|
) |
|
|
|
|
|
(defun select-X-frame () |
|
|
(setq framelist (frame-list)) |
|
|
(setq done nil) |
|
|
(while (and framelist (not done)) |
|
|
(setq cur (car framelist)) |
|
|
(setq cur-id (cdr (assq 'window-id (frame-parameters cur)))) |
|
|
(if cur-id (progn; |
|
|
(setq done 't) (raise-frame cur))) |
|
|
(setq framelist (cdr framelist))) |
|
|
done |
|
|
) |
|
|
#+end_src |
|
|
This function is used to raise the frame associated to the current |
|
|
activity |
|
|
*** TODO These entries should be added to the subtree once it is split |
|
|
#+begin_src emacs-lisp |
|
|
(defun select-frame-on-current-activity () |
|
|
(select-frame-on-activity (kde-current-activity))) |
|
|
#+end_src |
|
|
I prefer to keep one server for each KDE activity, so |
|
|
set the server name to be the name of the current activity |
|
|
#+begin_src emacs-lisp |
|
|
; (setq server-name (kde-current-activity-name)) |
|
|
#+end_src |
|
|
*** Journal |
|
|
Setup ~org-journal~ |
|
|
#+begin_src emacs-lisp |
|
|
(require 'org-journal) |
|
|
(setq org-journal-dir "~/org/journal" |
|
|
org-journal-file-type 'yearly |
|
|
org-journal-file-format "%Y" |
|
|
org-journal-encrypt-journal t) |
|
|
|
|
|
|
|
|
#+end_src |
|
|
* Specialties |
|
|
** beacon =beacon= is a package that helps finding the point when switching |
|
|
buffers. I like the idea but the aesthetics is a bit baroque. I |
|
|
should do the same with =flash-hline= |
|
|
** flash-hline |
|
|
This function is defined to help in training with new keybindings. It |
|
|
acts as a visual bell which flashes the current line. It is (arbitrarily) |
|
|
bound to F15 which is supposed to be triggered by some “illegal” key hit |
|
|
#+begin_src emacs-lisp |
|
|
(defun flash-hline () |
|
|
"Flash the current line to emph some mistake" |
|
|
(interactive) |
|
|
(let ((fg (face-foreground 'default)) |
|
|
(bg (face-background 'hl-line))) |
|
|
(set-face-background 'hl-line fg) |
|
|
(run-with-timer |
|
|
0.1 nil (lambda () |
|
|
(set-face-background 'hl-line "#303030") )))) |
|
|
|
|
|
(global-set-key (kbd "<f15>") 'flash-hline) |
|
|
#+end_src |
|
|
** unfill-paragraph |
|
|
This is authored by Stefan Monnier <foo at acm.org>. It is the opposite of |
|
|
fill-paragraph |
|
|
#+begin_src emacs-lisp |
|
|
(defun unfill-paragraph (&optional region) |
|
|
"Takes a multi-line paragraph and makes it into a single line of text." |
|
|
(interactive (progn (barf-if-buffer-read-only) '(t))) |
|
|
(let ((fill-column (point-max)) |
|
|
;; This would override `fill-column' if it's an integer. |
|
|
(emacs-lisp-docstring-fill-column t)) |
|
|
(fill-paragraph nil region))) |
|
|
#+end_src |
|
|
** kill-word |
|
|
dwim manage space after kill-word. This has been morally pasted from |
|
|
[[https://www.reddit.com/r/emacs/comments/3nlws0/automanage_spaces_post_word_kills/][this reddit post]]. The original version does not allow for double |
|
|
spaces after a period. |
|
|
#+begin_src emacs-lisp |
|
|
(defun modi/just-one-space-post-kill-word (&rest _) |
|
|
"Function to manage white space after `kill-word' operations. |
|
|
|
|
|
1. If point is at the beginning of the line after possibly some white space, |
|
|
remove that white space and re-indent that line. |
|
|
2. If there is space before or after the point, ensure that there is only |
|
|
one white space around the point. |
|
|
3. Otherwise, do nothing. |
|
|
|
|
|
During the whole operation do not change the point position with respect to the |
|
|
surrounding white space. |
|
|
|
|
|
abc| def ghi <-- point on the left of white space after 'abc' |
|
|
abc| ghi <-- point still before white space after calling this function |
|
|
abc |def ghi <-- point on the right of white space before 'def' |
|
|
abc |ghi <-- point still after white space after calling this function." |
|
|
(save-excursion ; maintain the initial position of the pt with respect to space |
|
|
(cond ((looking-back "^ *") ; remove extra space at beginning of line |
|
|
(just-one-space 0) |
|
|
(indent-according-to-mode)) |
|
|
((looking-back "\\. *") |
|
|
(just-one-space 2)) |
|
|
((or (looking-at " ") |
|
|
(looking-back " ")) ; adjust space only if it exists |
|
|
(just-one-space 1)) |
|
|
(t ; do nothing otherwise, includes case where the point is at EOL |
|
|
)))) |
|
|
;; Delete extra horizontal white space after `kill-word' and `backward-kill-word' |
|
|
(advice-add 'kill-word :after #'modi/just-one-space-post-kill-word) |
|
|
#+end_src |
|
|
|
|
|
#+results: |
|
|
|
|
|
** word-count |
|
|
Add word count in modeline; useful to prepare those pesky grant |
|
|
applications |
|
|
#+begin_src emacs-lisp |
|
|
(require 'word-count) |
|
|
#+end_src |
|
|
** COMMENT gist |
|
|
Use [[https://github.com/defunkt/gist.el][gist.el]] |
|
|
#+begin_src emacs-lisp |
|
|
(require 'gist) |
|
|
#+end_src |
|
|
* Main packages |
|
|
** Magit |
|
|
Load ~magit~ and introduce the keybinding |
|
|
#+begin_src emacs-lisp |
|
|
(require 'magit) |
|
|
(global-set-key (kbd "C-x g") 'magit-status) |
|
|
#+end_src |
|
|
Customize the sections in ~magit-status~ by adding modules overview |
|
|
and untracked files. |
|
|
#+begin_src emacs-lisp |
|
|
(magit-add-section-hook 'magit-status-sections-hook |
|
|
#'magit-insert-modules-overview |
|
|
#'magit-insert-unpushed-to-pushremote |
|
|
:append) |
|
|
(magit-add-section-hook 'magit-status-sections-hook |
|
|
#'magit-insert-untracked-files |
|
|
#'magit-insert-modules-overview |
|
|
:append) |
|
|
#+end_src |
|
|
Enable [[https://github.com/alphapapa/magit-todos ][magit-todos]] in magit status buffer |
|
|
#+begin_src emacs-lisp |
|
|
(require 'magit-todos) |
|
|
(magit-todos-mode) |
|
|
#+end_src |
|
|
** smart-tab |
|
|
This package is a gem: it allows to make tab work dwim |
|
|
#+begin_src emacs-lisp |
|
|
(require 'smart-tab) |
|
|
(global-smart-tab-mode 1) |
|
|
#+end_src |
|
|
** Outshine |
|
|
#+begin_src emacs-lisp |
|
|
(defvar outline-minor-mode-prefix "\M-#") |
|
|
(setq outshine-use-speed-commands t) |
|
|
(with-eval-after-load 'outshine |
|
|
(define-key outshine-mode-map (kbd "C-M-i") nil)) |
|
|
(add-hook 'sh-mode-hook 'outshine-mode) |
|
|
(require 'outshine) |
|
|
(require 'outorg) |
|
|
#+end_src |
|
|
Cook up some extra narrowing function |
|
|
#+begin_src emacs-lisp |
|
|
(defun goto-next-comment-line () |
|
|
(interactive) |
|
|
(re-search-forward "^\\S<" nil 1) |
|
|
(re-search-forward "^\\s<" nil 1) |
|
|
(beginning-of-line) |
|
|
) |
|
|
|
|
|
(defun goto-previous-comment-line () |
|
|
(interactive) |
|
|
(re-search-backward "^\\s<" nil 1 ) |
|
|
(re-search-backward "^\\S<" nil 1 ) |
|
|
(beginning-of-line) |
|
|
(unless (bobp) (next-line)) |
|
|
) |
|
|
|
|
|
(defun narrow-between-comments () |
|
|
(interactive) |
|
|
(save-excursion |
|
|
(next-line) |
|
|
(goto-previous-comment-line) |
|
|
(setq beginning (point))) |
|
|
(save-excursion |
|
|
(goto-next-comment-line) |
|
|
(unless (bobp) (previous-line) (end-of-line)) |
|
|
(setq ending (point))) |
|
|
(narrow-to-region beginning ending)) |
|
|
|
|
|
#+end_src |
|
|
|
|
|
** helm |
|
|
#+begin_src emacs-lisp |
|
|
(require 'helm-config) |
|
|
(helm-mode 1) |
|
|
|
|
|
(defun fix-helm-margins () |
|
|
(setq helm-left-margin-width left-margin-width) |
|
|
(overlay-put helm-selection-overlay 'before-string |
|
|
(propertize "." 'display |
|
|
`((margin left-margin) ,(propertize "▶ " 'face 'default)))) |
|
|
) |
|
|
|
|
|
(global-set-key-alist |
|
|
'(("M-x" . helm-M-x) |
|
|
("C-x f" . helm-find-files) |
|
|
("C-x b" . helm-mini) |
|
|
("M-y" . helm-show-kill-ring) |
|
|
("C-c s" . helm-occur))) |
|
|
|
|
|
(define-key helm-map (kbd "C-h") nil) |
|
|
(define-key helm-find-files-map (kbd "C-h") nil) |
|
|
(define-key helm-find-files-map (kbd "C-<backspace>") nil) |
|
|
(define-key helm-read-file-map (kbd "C-<backspace>") nil) |
|
|
|
|
|
(helm-define-key-with-subkeys helm-find-files-map (kbd "DEL") ?\d 'helm-ff-delete-char-backward |
|
|
'(([C-c DEL] . helm-ff-run-toggle-auto-update)) |
|
|
nil 'helm-ff-delete-char-backward--exit-fn) |
|
|
(add-hook 'helm-after-initialize-hook 'fix-helm-margins) |
|
|
|
|
|
#+end_src |
|
|
Customize ~helm-mini~ adding bookmarks for files and dirs. This |
|
|
is particularly useful to quickly open commonly accessed files |
|
|
that I keep in the bookmarks. As an exercise, I wonder if we |
|
|
could have per-server bookmarks. |
|
|
#+begin_src emacs-lisp |
|
|
(setq helm-mini-default-sources |
|
|
'(helm-source-buffers-list |
|
|
helm-source-recentf |
|
|
helm-source-bookmark-files&dirs |
|
|
helm-source-buffer-not-found)) |
|
|
#+end_src |
|
|
Fix helm margins the bad way |
|
|
#+begin_src emacs-lisp |
|
|
(defun helm-display-mode-line (source &optional force) |
|
|
"Set up mode-line and header-line for `helm-buffer'. |
|
|
|
|
|
SOURCE is a Helm source object. |
|
|
|
|
|
Optional argument FORCE forces redisplay of the Helm buffer's |
|
|
mode and header lines." |
|
|
(set (make-local-variable 'helm-mode-line-string) |
|
|
(helm-interpret-value (or (and (listp source) ; Check if source is empty. |
|
|
(assoc-default 'mode-line source)) |
|
|
(default-value 'helm-mode-line-string)) |
|
|
source)) |
|
|
(let ((follow (and (or (helm-follow-mode-p source) |
|
|
(and helm-follow-mode-persistent |
|
|
(member (assoc-default 'name source) |
|
|
helm-source-names-using-follow))) |
|
|
" (HF)")) |
|
|
(marked (and helm-marked-candidates |
|
|
(cl-loop with cur-name = (assoc-default 'name source) |
|
|
for c in helm-marked-candidates |
|
|
for name = (assoc-default 'name (car c)) |
|
|
when (string= name cur-name) |
|
|
collect c)))) |
|
|
;; Setup mode-line. |
|
|
(if helm-mode-line-string |
|
|
(setq mode-line-format |
|
|
`(:propertize |
|
|
("╺═╸ " mode-line-buffer-identification " " |
|
|
(:eval (format "L%-3d" (helm-candidate-number-at-point))) |
|
|
,follow |
|
|
" " |
|
|
(:eval ,(and marked |
|
|
(propertize |
|
|
(format "M%d" (length marked)) |
|
|
'face 'helm-visible-mark))) |
|
|
(:eval (when ,helm--mode-line-display-prefarg |
|
|
(let ((arg (prefix-numeric-value |
|
|
(or prefix-arg current-prefix-arg)))) |
|
|
(unless (= arg 1) |
|
|
(propertize (format " [prefarg:%s]" arg) |
|
|
'face 'helm-prefarg))))) |
|
|
" " |
|
|
(:eval (with-helm-buffer |
|
|
(helm-show-candidate-number |
|
|
(car-safe helm-mode-line-string)))) |
|
|
" " helm--mode-line-string-real " " |
|
|
(:eval (make-string (window-width) ? ))) |
|
|
keymap (keymap (mode-line keymap |
|
|
(mouse-1 . ignore) |
|
|
(down-mouse-1 . ignore) |
|
|
(drag-mouse-1 . ignore) |
|
|
(mouse-2 . ignore) |
|
|
(down-mouse-2 . ignore) |
|
|
(drag-mouse-2 . ignore) |
|
|
(mouse-3 . ignore) |
|
|
(down-mouse-3 . ignore) |
|
|
(drag-mouse-3 . ignore)))) |
|
|
helm--mode-line-string-real |
|
|
(substitute-command-keys (if (listp helm-mode-line-string) |
|
|
(cadr helm-mode-line-string) |
|
|
helm-mode-line-string))) |
|
|
(setq mode-line-format (default-value 'mode-line-format))) |
|
|
;; Setup header-line. |
|
|
(cond (helm-echo-input-in-header-line |
|
|
(setq force t) |
|
|
(helm--set-header-line)) |
|
|
(helm-display-header-line |
|
|
(let ((hlstr (helm-interpret-value |
|
|
(and (listp source) |
|
|
(assoc-default 'header-line source)) |
|
|
source)) |
|
|
(endstr (make-string (window-width) ? ))) |
|
|
(setq header-line-format |
|
|
(propertize (concat "··· " hlstr endstr) |
|
|
'face 'helm-header)))))) |
|
|
(when force (force-mode-line-update))) |
|
|
#+end_src |
|
|
** multiple-cursors |
|
|
#+begin_src emacs-lisp |
|
|
(require 'multiple-cursors) |
|
|
(define-key mc/keymap (kbd "<return>") nil) |
|
|
(global-set-key-alist |
|
|
'(("C->" . mc/mark-next-like-this) |
|
|
("C-<" . mc/mark-previous-like-this) |
|
|
("C-c C-<" . mc/mark-all-like-this) |
|
|
("C-c C-#" . mc/insert-numbers) |
|
|
("C-c C-\\" . mc/mark-all-dwim) |
|
|
("C-c <f13>" . mc/mark-pop) |
|
|
("C-c <f14>" . mc/mark-pop))) |
|
|
#+end_src |
|
|
** =avy= |
|
|
This packages allows fast navigation and action-ing |
|
|
It is probably a good idea to explore some smarter key-bindings |
|
|
#+begin_src emacs-lisp |
|
|
(require 'avy) |
|
|
(avy-setup-default) |
|
|
(global-set-key-alist |
|
|
'(("C-c C-j" . avy-resume) |
|
|
("C-c SPC" . avy-goto-word-or-subword-1) |
|
|
("M-s" . avy-goto-word-or-subword-1) |
|
|
("M-g M-g" . avy-goto-line) |
|
|
("M-g g" . avy-goto-line))) |
|
|
(require 'ace-window) |
|
|
(global-set-key (kbd "M-o") 'ace-window) |
|
|
(setq aw-keys '(?f ?j ?d ?k ?s ?l ?a ?g ?h) |
|
|
aw-scope 'frame |
|
|
aw-ignore-current t) |
|
|
(require 'ace-link) |
|
|
(ace-link-setup-default) |
|
|
(setq avy-styles-alist nil |
|
|
avy-background t) |
|
|
(define-key org-mode-map (kbd "C-c M-o") 'ace-link-org) |
|
|
(define-key org-agenda-mode-map (kbd "C-c M-o") 'ace-link-org) |
|
|
#+end_src |
|
|
** expand-region |
|
|
This is an excellent package, although I do not use it that much |
|
|
I should find a better binding |
|
|
#+begin_src emacs-lisp |
|
|
(require 'expand-region) |
|
|
(global-set-key (kbd "C-=") 'er/expand-region) |
|
|
#+end_src |
|
|
** ERC |
|
|
Enable ~erc dcc~ files transfer |
|
|
#+begin_src emacs-lisp |
|
|
(require 'erc-dcc) |
|
|
#+end_src |
|
|
** vterm |
|
|
#+begin_src emacs-lisp |
|
|
(require 'vterm) |
|
|
#+end_src |
|
|
** TODO Phase out Package.el |
|
|
Load package.el |
|
|
#+begin_src emacs-lisp |
|
|
(require 'package) |
|
|
(add-to-list 'package-archives |
|
|
'("melpa-stable" . "http://stable.melpa.org/packages/") t) |
|
|
#+end_src |
|
|
|
|
|
* Tidy-up |
|
|
** Save emacs-session files in appropriate directory |
|
|
Save session files to the ~sessions~ directory so that they do not litter |
|
|
the ~.emacs.d~ base directory. |
|
|
#+begin_src emacs-lisp |
|
|
(defun emacs-session-filename (session-id) |
|
|
"Construct a filename to save the session in based on SESSION-ID. |
|
|
If the directory ~/.emacs.d exists, we make a filename in there, otherwise |
|
|
a file in the home directory." |
|
|
(let ((basename (concat "sessions/session." session-id)) |
|
|
(emacs-dir user-emacs-directory)) |
|
|
(expand-file-name (if (file-directory-p emacs-dir) |
|
|
(concat emacs-dir basename) |
|
|
(concat "~/.emacs-" basename))))) |
|
|
#+end_src |
|
|
|
|
|
* Moving to colemak |
|
|
Set up all possible combinations of modifiers |
|
|
#+begin_src emacs-lisp |
|
|
(defun concat-recursive (b &optional a) |
|
|
(if b |
|
|
(append (concat-recursive (cdr b) (concat a (car b))) |
|
|
(concat-recursive (cdr b) a)) |
|
|
(when a (list a)))) |
|
|
|
|
|
(setq modifier-combo (concat-recursive '("C-" "M-" "s-" "H-"))) |
|
|
#+end_src |
|
|
Then list the swapped keys |
|
|
#+begin_src emacs-lisp |
|
|
(setq tarmak1-swap-alist '((?j . ?e) |
|
|
(?e . ?k) |
|
|
(?k . ?n) |
|
|
(?n . ?j))) |
|
|
(setq tarmak2-swap-alist '((?j . ?g) |
|
|
(?g . ?t) |
|
|
(?t . ?f) |
|
|
(?f . ?e) |
|
|
(?e . ?k) |
|
|
(?k . ?n) |
|
|
(?n . ?j))) |
|
|
(setq tarmak3-swap-alist '((?j . ?r) |
|
|
(?r . ?s) |
|
|
(?s . ?d) |
|
|
(?d . ?g) |
|
|
(?g . ?t) |
|
|
(?t . ?f) |
|
|
(?f . ?e) |
|
|
(?e . ?k) |
|
|
(?k . ?n) |
|
|
(?n . ?j))) |
|
|
|
|
|
(setq tarmak-current-swap-alist tarmak3-swap-alist) |
|
|
#+end_src |
|
|
|
|
|
#+begin_src emacs-lisp |
|
|
(defun set-key-translation-map (pairs) |
|
|
(dolist (p pairs) |
|
|
(define-key key-translation-map (kbd (downcase (car p))) (kbd (downcase (cdr p)))) |
|
|
(define-key key-translation-map (kbd (upcase (car p))) (kbd (upcase (cdr p)))))) |
|
|
|
|
|
(defun set-key-translation-map-caseless (pairs) |
|
|
(dolist (p pairs) |
|
|
(define-key key-translation-map (kbd (car p)) (kbd (cdr p))))) |
|
|
|
|
|
(defun reset-key-translation-map () |
|
|
(dolist (c (mapcar 'cdr tarmak-current-swap-alist)) |
|
|
(define-key key-translation-map (kbd (downcase c)) nil) |
|
|
(define-key key-translation-map (kbd (upcase c)) nil))) |
|
|
|
|
|
(defun untarmak-modifiers () |
|
|
(interactive) |
|
|
(set-key-translation-map-caseless |
|
|
(mapcan (lambda (swap-pair) |
|
|
(mapcar (lambda (mod-combo) |
|
|
(cons (concat mod-combo |
|
|
(string (car swap-pair))) |
|
|
(concat mod-combo |
|
|
(string (cdr swap-pair))))) |
|
|
modifier-combo)) |
|
|
tarmak-current-swap-alist))) |
|
|
|
|
|
(defun untarmak-key (char) |
|
|
(let ((pair (rassoc char tarmak-current-swap-alist))) |
|
|
(if pair (car pair) char))) |
|
|
|
|
|
(defun tarmak-avy () |
|
|
(interactive) |
|
|
(setq aw-keys (mapcar 'untarmak-key |
|
|
'(?j ?f ?g ?h ?k ?d ?l ?s ?a)) |
|
|
avy-keys (mapcar 'untarmak-key |
|
|
'(?j ?f ?g ?h ?k ?d ?l ?s ?a)))) |
|
|
|
|
|
(defun qwerty() |
|
|
(interactive) |
|
|
|
|
|
(reset-key-translation-map)) |
|
|
|
|
|
;; (untarmak-modifiers) |
|
|
(tarmak-avy) |
|
|
#+end_src |
|
|
* Finale |
|
|
** Fixup faces |
|
|
This is really a workaround as I do not like either bold or italic. |
|
|
It needs to be at the end of the file since it sets the face for |
|
|
packages that have loaded in the meantime; yet it does not work |
|
|
perfectly as some packages are still to be loaded (most notably ~magit~) |
|
|
#+begin_src emacs-lisp |
|
|
(set-face-bold 'bold nil) |
|
|
(wilder/fixup-faces) |
|
|
(with-eval-after-load "info" (wilder/fixup-faces) nil) |
|
|
(with-eval-after-load "mu4e" (wilder/fixup-faces) nil) |
|
|
(with-eval-after-load "mm-view" (wilder/fixup-faces) nil) |
|
|
(with-eval-after-load 'gnus-cite (wilder/fixup-faces) nil) |
|
|
(add-hook 'gnus-art-load-hook #'wilder/fixup-faces) |
|
|
#+end_src |
|
|
|
|
|
|