Minimal Setup
This layer holds some same standards which should be enabled across most machines.
Emacs Server
Start the server first, but only if emacs is not currently running.
(load "server") (unless (server-running-p) (server-start))
When connected to server using emacsclient, we want to kill the client using
C-x k
, the way it seems natural.
(add-hook 'server-switch-hook (lambda () (when (current-local-map) (use-local-map (copy-keymap (current-local-map)))) (local-set-key (kbd "C-x k") 'server-edit)))
Theme
Load and/or enable my theme of choice
(cond ;; "dark" theme is based on wombat ((eq do.theme/enabled-theme 'dark) (load-theme 'wombat)) ;; "light theme is based on plan9-theme ((eq do.theme/enabled-theme 'light) (use-package plan9-theme :ensure t :demand)))
Popup menu colors
Check Completion and Snippets. Those two require some colors for the popup tooltips. We set them here and refer to them in those files.
(cond ((eq do.theme/enabled-theme 'dark) ;; main face (setq do.theme.popup/popup-face-attrs `(:foreground ,(face-attribute 'font-lock-comment-face :foreground) :background "#2d2d2d")) ;; selected face (setq do.theme.popup/selected-face-attrs `(:foreground ,(face-attribute 'font-lock-builtin-face :foreground) :background "gray30" :slant italic :weight semibold)) ;; summary (setq do.theme.popup/summary-face-attrs `(:foreground ,(face-attribute 'font-lock-builtin-face :foreground))) ;; summary (selected) (setq do.theme.popup/summary-selected-face-attrs `(:foreground ,(face-attribute 'font-lock-builtin-face :foreground) :slant italic :weight semibold)) ;; scroll bar (setq do.theme.popup/scrollbar-fg-attrs ; foreground `(:background ,(face-attribute 'font-lock-comment-face :foreground))) (setq do.theme.popup/scrollbar-bg-attrs ; background `(:background "#2d2d2d")) ;; annotation (setq do.theme.popup/annotation-attrs `(:background ,(face-attribute 'font-lock-comment-face :foreground))) ;; preview (setq do.theme.popup/preview-attrs `(:foreground ,(face-attribute 'font-lock-builtin-face :foreground) :background "SlateBlue4")) (setq do.theme.popup/preview-search-attrs '(:background "SlateBlue1"))) ((eq do.theme/enabled-theme 'light) (plan9/with-color-variables (setq do.theme.popup/popup-face-attrs `(:foreground ,fg :background ,bg-dark)) ;; selected face (setq do.theme.popup/selected-face-attrs `(:foreground ,fg :background ,cyan :slant italic :weight semibold)) ;; summary (setq do.theme.popup/summary-face-attrs `(:foreground ,red :background ,bg-dark)) ;; summary (selected) (setq do.theme.popup/summary-selected-face-attrs `(:foreground ,red :background ,cyan :slant italic :weight semibold)) ;; scroll bar (setq do.theme.popup/scrollbar-fg-attrs ; foreground `(:background ,cyan-light)) (setq do.theme.popup/scrollbar-bg-attrs ; background `(:background ,bg-dark)) ;; annotation (setq do.theme.popup/annotation-attrs `(:foreground ,green :background ,bg-dark)) ;; preview (setq do.theme.popup/preview-attrs `(:foreground ,fg :background ,green)) (setq do.theme.popup/preview-search-attrs `(:background ,blue)))) (t (setq do.theme.popup/popup-face-attrs '()) (setq do.theme.popup/selected-face-attrs '()) (setq do.theme.popup/summary-face-attrs '()) (setq do.theme.popup/summary-selected-face-attrs '()) (setq do.theme.popup/scrollbar-fg-attrs '()) (setq do.theme.popup/scrollbar-bg-attrs '()) (setq do.theme.popup/preview-attrs '()) (setq do.theme.popup/preview-search-attrs '())))
We define a small convenience function to set these attributes.
(defun do.theme/set-face-attr-from-list (face attrs) "Apply the attributes in ATTRS to the face FACE." (when (and face attrs) (set-face-attribute face nil (car attrs) (cadr attrs)) (do.theme/set-face-attr-from-list face (cddr attrs))))
UI Elements
To reduce some visual noise, I get rid of most UI elements. The toolbar can be useful however, so I show it on the press of a button.
(customize-set-variable 'scroll-bar-mode nil)
(customize-set-variable 'horizontal-scroll-bar-mode nil)
(customize-set-variable 'tool-bar-mode nil)
(customize-set-variable 'menu-bar-mode nil)
(when (display-graphic-p)
(global-set-key [f9] 'toggle-menu-bar-mode-from-frame))
Fringe
Set the width of the fringe
(defvar do.minimal.ui/fringe-width 6 "The width of both fringes.") (when (display-graphic-p) (fringe-mode (cons do.minimal.ui/fringe-width do.minimal.ui/fringe-width)))
For the dark
theme, I use the fringe as divider between buffers, so I have to
make sure that its backround differs from the default background. For the
light
theme, I want the fringe to be the same color as the background.
changed this a little bit.
(cond ((eq do.theme/enabled-theme 'dark) ;; (set-face-attribute 'fringe nil :background "#2d2d2d") (set-face-attribute 'fringe nil :background (face-attribute 'default :background))) ((eq do.theme/enabled-theme 'light) (set-face-attribute 'fringe nil :background (face-attribute 'default :background))))
For the dark theme, I want to remove the vertical border.
(cond ((eq do.theme/enabled-theme 'dark) ;; (set-face-attribute 'vertical-border nil :foreground (face-attribute 'fringe :background)) (set-face-attribute 'vertical-border nil :foreground "#2d2d2d")) ((eq do.theme/enabled-theme 'light)))
Welcome Screen
We also want to get rid of the splash screen and start viewing my notes file, if it exists.
(defvar do.minimal/notes-file "" "Path to my notes file.") (if (file-exists-p do.minimal/notes-file) (progn (setq initial-buffer-choice do.minimal/notes-file) ;; pop to the notes file with =C-c n= (global-set-key (kbd "C-c n") (lambda () (interactive) (find-file do.minimal/notes-file)))) (setq initial-buffer-choice "~/")) (setq inhibit-startup-message t) (setq inhibit-splash-screen t) (setq initial-scratch-message nil)
Window Geometry
(defvar do.minimal.ui/initial-height 80 "Initial height of a frame") (defvar do.minimal.ui/initial-width 120 "Initial width of a frame") (when (display-graphic-p) (add-to-list 'default-frame-alist (cons 'height do.minimal.ui/initial-height)) (add-to-list 'default-frame-alist (cons 'width do.minimal.ui/initial-width)))
Scrolling
More natural scrolling. I'm not a fan of the default paged scrolling.
(setq scroll-step 1
scroll-conservatively 10000)
Autosave and Backup files
It's annoying when autosave files are in the same directory. Too much
clutter. Instead, let's save them somewhere in ~/.emacs.d/
.
;; save backups and autosaves in .emacs.d/ (defvar do.minimal/backup-dir nil) (defvar do.minimal/autosave-dir nil) (let ((backup-dir (or do.minimal/backup-dir (expand-file-name (concat initel-directory "emacs_backup/")))) (autosave-dir (or do.minimal/autosave-dir (expand-file-name (concat initel-directory "autosave/"))))) ;; emacs internals (setq backup-directory-alist (list (cons ".*" backup-dir))) (setq auto-save-list-file-prefix autosave-dir) (setq auto-save-file-name-transforms (list (list ".*" autosave-dir t))) ;; TRAMP (setq tramp-backup-directory-alist backup-directory-alist) (setq tramp-auto-save-directory autosave-dir))
Window movement
Navigate through windows and frames using Shift-<Arrow>
(use-package framemove :ensure t :demand :init (require 'cl-lib) (windmove-default-keybindings) :config (setq framemove-hook-into-windmove t))
Trailing whitespace
Show, identify, and nuke the devil which calls itself "trailing whitespace".
(use-package whitespace :demand :init ;; delete trailing whitespace before saving a file (add-hook 'before-save-hook 'delete-trailing-whitespace) :config (set-face-attribute 'trailing-whitespace nil :background "indian red") (setq-default show-trailing-whitespace t))
Don't show trailing whitespace in some modes
…but sometimes (especially in read-only buffers that I don't control), this gets annoying. Which is why we can add this small function to any hook that we want:
(defun no-trailing-whitespace () (setq show-trailing-whitespace nil))
We already know two places to add it: the minibuffer and eww.
(add-hook 'minibuffer-setup-hook 'no-trailing-whitespace) (add-hook 'eww-mode-hook 'no-trailing-whitespace) (add-hook 'ielm-mode-hook 'no-trailing-whitespace) (add-hook 'help-mode-hook 'no-trailing-whitespace)
Cursor
Highlight current line
Globally enable this, turn off when not needed.
(use-package hl-line :config (global-hl-line-mode 1) (make-variable-buffer-local 'global-hl-line-mode))
Blink forever
(setq blink-cursor-blinks 0)
Change cursor based on reading or writing
Always reset the cursor color and the faces of the highlight line. Also, use vertical bar if we're in edit mode, and use box if in read-only mode, (almost like in gvim…)
But first, define the colors for the different themes.
(cond ;; dark theme colors ((eq do.theme/enabled-theme 'dark) (setq do.minimal.cursor/color "gold") (setq do.minimal.cursor/region-attrs '(:background "gold" :foreground "black")) (setq do.minimal.cursor/hl-line-color "gray30") (setq do.minimal.cursor/overwrite-color "red")) ;; light theme colors ((eq do.theme/enabled-theme 'light) (plan9/with-color-variables (setq do.minimal.cursor/color "black") (setq do.minimal.cursor/region-attrs `(:background ,cyan-light :foreground nil)) (setq do.minimal.cursor/hl-line-color purple-light) (setq do.minimal.cursor/overwrite-color red))) (t (setq do.minimal.cursor/color "green") (setq do.minimal.cursor/region-attrs '(:background "red" :foreground nil)) (setq do.minimal.cursor/hl-line-color "SlateBlue1") (setq do.minimal.cursor/overwrite-color "red")))
Now, define our cursor-update function
(defun do.minimal.cursor/update () ;; update the colors (set-cursor-color do.minimal.cursor/color) (apply #'set-face-attribute (append '(region nil) do.minimal.cursor/region-attrs)) (set-face-background 'hl-line do.minimal.cursor/hl-line-color) ;; make sure the hl-line does not get screwed up (set-face-foreground 'highlight nil) (set-face-underline 'hl-line nil) ;; decide bar/box (cond (buffer-read-only (setq cursor-type 'box)) (t (setq cursor-type 'bar))) ;; red cursor for overwrite mode (when overwrite-mode (set-cursor-color do.minimal.cursor/overwrite-color)))
This needs to run after every command, since some modes screw with the cursor.
(when (bound-and-true-p do.theme/enabled-theme) (do.minimal.cursor/update) (add-hook 'post-command-hook 'do.minimal.cursor/update))
Line numbers
A long time ago I used linum-mode
for this. Since Emacs 26 there is a nicer built-in way of doing this.
Configure display-line-numbers-mode
;; in narrowed buffers we still want the "true" line number' (setq display-line-numbers-widen t) (cond ;; dark theme colors ((eq do.theme/enabled-theme 'dark) (set-face-attribute 'line-number nil :background (face-attribute 'default :background) :foreground (face-attribute 'font-lock-comment-face :foreground)) (set-face-attribute 'line-number-current-line nil :foreground (face-attribute 'default :foreground) :background (face-attribute 'hl-line :background))) ;; light theme colors ((eq do.theme/enabled-theme 'light) (plan9/with-color-variables (set-face-attribute 'line-number nil :background (face-attribute 'default :background) :foreground fg) ;; also change the current-line-face (set-face-attribute 'line-number-current-line nil :background purple-light :foreground red))))
Turn line numbers on and off
Somehow, calling display-line-numbers-mode
by itself does not quite work for me…
(defun do.minimal.line-numbers/toggle-line-numbers (&optional relative) "Toggle the display of line numbers in the buffer. I am not sure why I need this." (interactive) (cond (display-line-numbers (display-line-numbers-mode nil) (setq display-line-numbers nil)) (t (display-line-numbers-mode t) (setq display-line-numbers (or relative t))))) (defun do.minimal.line-numbers/toggle-relative-line-numbers () "Toggle the display of relative line numbers." (interactive) (do.minimal.line-numbers/toggle-line-numbers 'relative)) (defalias 'num #'do.minimal.line-numbers/toggle-line-numbers) (defalias 'rnum #'do.minimal.line-numbers/toggle-relative-line-numbers)
Show line numbers while using goto-line
Shamelessly stolen from here.
(defun do.minimal.line-numbers/goto-line-with-feedback () "Show line numbers temporarily, while prompting for the line number input" (interactive) (let ((line-numbers-off-p (not display-line-numbers))) (unwind-protect (progn (when line-numbers-off-p (do.minimal.line-numbers/toggle-line-numbers)) (call-interactively 'goto-line)) (when line-numbers-off-p (do.minimal.line-numbers/toggle-line-numbers))))) (global-set-key [remap goto-line] #'do.minimal.line-numbers/goto-line-with-feedback)
Keys
Change a key for current buffer only
(defun do.minimal.keys/buffer-local-set-key (key func) (interactive "KSet key on this buffer: \naCommand: ") (let ((name (format "%s-magic" (buffer-name)))) (eval `(define-minor-mode ,(intern name) "Automagically built minor mode to define buffer-local keys.")) (let* ((mapname (format "%s-map" name)) (map (intern mapname))) (unless (boundp (intern mapname)) (set map (make-sparse-keymap))) (eval `(define-key ,map ,key func))) (funcall (intern name) t)))
To undo, use
(buffer-local-set-key key nil)
Spaces and tabs
Spaces instead of tabs
Tabs are evil! I want spaces instead of tabs, and want exactly 2 spaces instead of a tab. In Python modes, we revert back to 4 spaces, but we will do that there.
(setq-default indent-tabs-mode nil) (setq-default tab-width 2) (setq-default tab-stop-list (number-sequence 2 120 2)) (setq sh-basic-offset 2)
Exceptions
Makefiles
Makefiles are not so friendly when it comes to spaces.
(defun do.minimal.spaces/makefile-tabs-exception () (setq indent-tabs-mode t)) (add-hook 'makefile-mode-hook 'do.minimal.spaces/makefile-tabs-exception)
Parentheses
Show parens
This mode highlights the mathing parenthesis on point.
(use-package paren :config (show-paren-mode 1) (setq show-paren-delay 0))
Highlight Parens
This mode highlights the parentheses surrounding the point in shades of red. It works quite well for c-based languages and bash and other things, which is why I enable it globally.
(use-package highlight-parentheses :ensure t :demand :diminish highlight-parentheses-mode :config ;; define a global minor mode for this (define-globalized-minor-mode global-highlight-parentheses-mode highlight-parentheses-mode (lambda () (highlight-parentheses-mode t))) ;; enable it (global-highlight-parentheses-mode t))
Rainbow delimiters for LISP
For lispy languages, I want to witness the full power of colorful rainbow-delimiters!
(use-package rainbow-delimiters :ensure t :init <<do.minimal.parens.rainbow/hooks>> :config <<do.minimal.parens.rainbow/theme-1>> <<do.minimal.parens.rainbow/theme-2>>)
Color theme
I stole some pastel rainbow colors from this wallpaper.
(set-face-attribute 'rainbow-delimiters-depth-1-face nil :foreground "#78c5d6") (set-face-attribute 'rainbow-delimiters-depth-2-face nil :foreground "#bf62a6") (set-face-attribute 'rainbow-delimiters-depth-3-face nil :foreground "#459ba8") (set-face-attribute 'rainbow-delimiters-depth-4-face nil :foreground "#e868a2") (set-face-attribute 'rainbow-delimiters-depth-5-face nil :foreground "#79c267") (set-face-attribute 'rainbow-delimiters-depth-6-face nil :foreground "#f28c33") (set-face-attribute 'rainbow-delimiters-depth-7-face nil :foreground "#c5d647") (set-face-attribute 'rainbow-delimiters-depth-8-face nil :foreground "#f5d63d") (set-face-attribute 'rainbow-delimiters-depth-9-face nil :foreground "#78c5d6")
We also want to make unmatched parens stand out more with a strikethrough.
(set-face-attribute 'rainbow-delimiters-unmatched-face nil :foreground 'unspecified :inherit 'show-paren-mismatch :strike-through t)
Hooks
Now we just need to adjust the hook for lispy languages.
(add-hook 'emacs-lisp-mode-hook 'rainbow-delimiters-mode) (add-hook 'lisp-mode-hook 'rainbow-delimiters-mode) (add-hook 'scheme-mode-hook 'rainbow-delimiters-mode) (add-hook 'c-mode-common-hook 'rainbow-delimiters-mode)
Insert closing parens automagically
(use-package elec-pair :demand :config (electric-pair-mode 1))
Paredit
No editing lispy languages without paredit!
(use-package paredit :ensure t :bind <<do.minimal.parens.paredit/keys>> :init <<do.minimal.parens.paredit/hooks>>)
Enable this for all lispy modes.
(add-hook 'lisp-interaction-mode-hook #'paredit-mode) (add-hook 'emacs-lisp-mode-hook #'paredit-mode) (add-hook 'ielm-mode-hook #'paredit-mode) (add-hook 'eval-expression-minibuffer-setup-hook #'paredit-mode) (add-hook 'lisp-mode-hook #'paredit-mode)
Paredit overwrites the C-j
binding to eval-last-sexp
. This needs to be
undone.
(:map paredit-mode-map ("C-j" . nil))
Ibuffer
Combining some Ibuffer tips from here and here. Ibuffer lets me filter the list of all currently open buffers.
(use-package ibuffer :demand :config <<do.minimal.ibuffer/filters>> <<do.minimal.ibuffer/filters-adv>> <<do.minimal.ibuffer/misc-1>> <<do.minimal.ibuffer/misc-2>> <<do.minimal.ibuffer/misc-3>> <<do.minimal.ibuffer/format>> <<do.minimal.ibuffer/hooks>> :bind <<do.minimal.ibuffer/keys>>)
Filter groups
My filter groups are defined below.
(setq ibuffer-saved-filter-groups (list (nreverse '(("Directories" (mode . dired-mode)) ("Magit" (name . "^\\*magit.*$")) ("Org" (mode . org-mode)) ("IRC" (mode . erc-mode)) ("Interactive" (or (mode . matlab-shell-mode) (mode . inferior-julia-shell-mode) (mode . inferior-python-mode) (mode . inferior-octave-mode) (mode . inferior-lisp-mode) (mode . inferior-scheme-mode) (mode . ielm-mode))) ("Global" (name . "^\\*.*\\*$")) ("Shell" (or (mode . term-mode) (mode . eshell-mode) (mode . shell-mode) (name . "^\\*ansi-term.*$"))) ("Jabber" (name . "^\\*-jabber-.*$")) "do.minimal.ibuffer.filters/default")) ))
Reverse the order of the filter groups. Kind of confusing: Since I'm reversing the order of the groups above, this snippet ensures that the groups are ordered in the way they are written above, with the "Default" group on top. This advice might need to be ported to the new advice system soon.
(defadvice do.minimal.ibuffer/generate-filter-groups (after reverse-ibuffer-groups () activate) (setq ad-return-value (nreverse ad-return-value)))
Other settings
Only show groups that have active buffers
(setq ibuffer-show-empty-filter-groups nil)
Don't show the summary or headline
(setq ibuffer-display-summary nil)
Enable instead of list-buffers
(defalias 'list-buffers 'ibuffer)
Format
Display more characters in the buffer name column, also convert sizes to a human-readable format. Stolen from the wiki.
;; Use human readable Size column instead of original one (define-ibuffer-column size-h (:name "Size" :inline t) (cond ((> (buffer-size) 1000000) (format "%7.1fM" (/ (buffer-size) 1000000.0))) ((> (buffer-size) 100000) (format "%7.0fk" (/ (buffer-size) 1000.0))) ((> (buffer-size) 1000) (format "%7.1fk" (/ (buffer-size) 1000.0))) (t (format "%8d" (buffer-size))))) ;; Modify the default ibuffer-formats (setq ibuffer-formats '((mark modified read-only " " (name 40 60 :left :elide) " " (size-h 9 -1 :right) " " (mode 16 16 :left :elide) " " filename-and-process)))
Hooks
Automagically keep the buffer list up to date, enable the filter groups defined above, disable trailing whitespace
(defun do.minimal.ibuffer/hooks () (ibuffer-auto-mode 1) (ibuffer-switch-to-saved-filter-groups "do.minimal.ibuffer.filters/default") (no-trailing-whitespace)) (add-hook 'ibuffer-mode-hook #'do.minimal.ibuffer/hooks)
Keys
Bind to the key that normally is bound to list-buffers
(("C-x C-b" . ibuffer))
Dired
General Settings
The first two lines of this tell dired to stop asking me whether I want to recursively delete or copy, since I never respond to that question with "No".
The last line enables "Do What I Mean" mode for dired: If I'm in a split frame with two dired buffers, the default target to copy (and rename) will be the other window.
(use-package dired :config (setq dired-recursive-copies 'always) (setq dired-recursive-deletes 'always) (setq dired-dwim-target t) (setq dired-listing-switches "-alh") ;; enable ALL THE FEATURES (require 'dired-x) (require 'dired-aux))
Automatically revert dired buffers
I also want dired to automatically revert, but to be quiet about it. The first line actually enables auto-revert for any buffers.
(use-package autorevert :config (global-auto-revert-mode 1) (setq global-auto-revert-non-file-buffers t) (setq auto-revert-verbose nil))
Less verbosity
We can show file details using the (
and )
keys.
(use-package dired-details :ensure t :config (setq dired-details-hidden-string "") (dired-details-install))
We want to omit the .
and ..
files, which are shown in dired
by default.
(setq-default dired-omit-mode t) ;; in dired-x (setq-default dired-omit-verbose nil) (setq-default dired-omit-files "^.dropbox$\\|^.dropbox.cache$\\|^\\.$\\|^\\.\\.$") (defadvice dired-omit-startup (after diminish-dired-omit activate) "Make sure to remove \"Omit\" from the modeline." (diminish 'dired-omit-mode) dired-mode-map)
Some files we need to show regardless
(setq dired-omit-extensions (remove ".bin" dired-omit-extensions))
Opening files
This is mostly stolen from here. Uses nohup
to spawn child processes without
annoying new buffers. First, we define a list of default programs.
;; some reasonable defaults. but use site file for this. (defaults to nil) (unless dired-guess-shell-alist-user (setq dired-guess-shell-alist-user '(("\\.\\(?:djvu\\|eps\\|pdf\\)\\'" "evince") ("\\.\\(?:ipe\\)\\'" "ipe") ("\\.\\(?:jpg\\|jpeg\\|png\\|gif\\|xpm\\)\\'" "eog") ("\\.\\(?:xcf\\)\\'" "gimp") ("\\.\\(?:csv\\|odt\\|ods\\)\\'" "libreoffice") ("\\.\\(?:mp4\\|mp3\\|mkv\\|avi\\|flv\\|ogv\\)\\(?:\\.part\\)?\\'" "vlc") ("\\.html?\\'" "firefox"))))
Now, define a new function to start a process in the background.
(defvar do.minimal.dired/filelist-cmd '(("vlc" "-L"))) (defun do.minimal.dired/start-process (cmd &optional file-list) (interactive (let ((files (dired-get-marked-files t current-prefix-arg))) (list (dired-read-shell-command "Open with: " current-prefix-arg files) files))) (let (list-switch) (start-process cmd nil shell-file-name shell-command-switch (format "nohup 1>/dev/null 2>/dev/null %s \"%s\"" (if (and (> (length file-list) 1) (setq list-switch (cadr (assoc cmd do.minimal.dired/filelist-cmd)))) (format "%s %s" cmd list-switch) cmd) (mapconcat #'expand-file-name file-list "\" \"")))))
At last, we remap two standard keys: We want !
, which defaults to
dired-do-shell-command
, to run the old dired-do-aync-shell-command
and use
it for things like a quick unzip
or unrar x
or something like that. The old
&
shall be remapped to dired-start-process
.
(define-key dired-mode-map "!" 'dired-do-async-shell-command) (define-key dired-mode-map "&" 'do.minimal.dired/start-process)
Async
We can use async to copy files asynchronously.
(use-package async :ensure t :demand)
TRAMP
(setq tramp-default-method "scp")
Keybindings in tmux
Update
: I don't know when I used this last. This might not be necessary anymore.
When launching terminal emacs in tmux, some keys do not work. This hint from
the archwiki is supposed to help. To make this smarter, we add some logic to
toggle the key-translation-map
on a frame-by-frame basis.
First, we define the translation map.
(setq do.minimal.tmux/tmux-translation-map (let ((x 2) (tkey "") (km (make-sparse-keymap))) (while (<= x 8) (when (= x 2) (setq tkey "S-")) ;; shift (when (= x 3) (setq tkey "M-")) ;; alt (when (= x 4) (setq tkey "M-S-")) ;; alt + shift (when (= x 5) (setq tkey "C-")) ;; ctrl (when (= x 6) (setq tkey "C-S-")) ;; ctrl + shift (when (= x 7) (setq tkey "C-M-")) ;; ctrl + alt (when (= x 8) (setq tkey "C-M-S-")) ;; ctrl + alt + shift ;; arrows (define-key km (kbd (format "M-[ 1 ; %d A" x)) (kbd (format "%s<up>" tkey))) ;; uparrow (define-key km (kbd (format "M-[ 1 ; %d B" x)) (kbd (format "%s<down>" tkey))) ;; downarrow (define-key km (kbd (format "M-[ 1 ; %d C" x)) (kbd (format "%s<right>" tkey))) ;; rightarrow (define-key km (kbd (format "M-[ 1 ; %d D" x)) (kbd (format "%s<left>" tkey))) ;; leftarrow ;; special keys (define-key km (kbd (format "M-[ 1 ; %d H" x)) (kbd (format "%s<home>" tkey))) ;; home (define-key km (kbd (format "M-[ 1 ; %d F" x)) (kbd (format "%s<end>" tkey))) ;; end (define-key km (kbd (format "M-[ 5 ; %d ~" x)) (kbd (format "%s<prior>" tkey))) ;; pgup (define-key km (kbd (format "M-[ 6 ; %d ~" x)) (kbd (format "%s<next>" tkey))) ;; pgdown (define-key km (kbd (format "M-[ 2 ; %d ~" x)) (kbd (format "%s<delete>" tkey))) ;; insert (define-key km (kbd (format "M-[ 3 ; %d ~" x)) (kbd (format "%s<delete>" tkey))) ;; delete ;; function keys (define-key km (kbd (format "M-[ 1 ; %d P" x)) (kbd (format "%s<f1>" tkey))) (define-key km (kbd (format "M-[ 1 ; %d Q" x)) (kbd (format "%s<f2>" tkey))) (define-key km (kbd (format "M-[ 1 ; %d R" x)) (kbd (format "%s<f3>" tkey))) (define-key km (kbd (format "M-[ 1 ; %d S" x)) (kbd (format "%s<f4>" tkey))) (define-key km (kbd (format "M-[ 15 ; %d ~" x)) (kbd (format "%s<f5>" tkey))) (define-key km (kbd (format "M-[ 17 ; %d ~" x)) (kbd (format "%s<f6>" tkey))) (define-key km (kbd (format "M-[ 18 ; %d ~" x)) (kbd (format "%s<f7>" tkey))) (define-key km (kbd (format "M-[ 19 ; %d ~" x)) (kbd (format "%s<f8>" tkey))) (define-key km (kbd (format "M-[ 20 ; %d ~" x)) (kbd (format "%s<f9>" tkey))) (define-key km (kbd (format "M-[ 21 ; %d ~" x)) (kbd (format "%s<f10>" tkey))) (define-key km (kbd (format "M-[ 23 ; %d ~" x)) (kbd (format "%s<f11>" tkey))) (define-key km (kbd (format "M-[ 24 ; %d ~" x)) (kbd (format "%s<f12>" tkey))) (define-key km (kbd (format "M-[ 25 ; %d ~" x)) (kbd (format "%s<f13>" tkey))) (define-key km (kbd (format "M-[ 26 ; %d ~" x)) (kbd (format "%s<f14>" tkey))) (define-key km (kbd (format "M-[ 28 ; %d ~" x)) (kbd (format "%s<f15>" tkey))) (define-key km (kbd (format "M-[ 29 ; %d ~" x)) (kbd (format "%s<f16>" tkey))) (define-key km (kbd (format "M-[ 31 ; %d ~" x)) (kbd (format "%s<f17>" tkey))) (define-key km (kbd (format "M-[ 32 ; %d ~" x)) (kbd (format "%s<f18>" tkey))) (define-key km (kbd (format "M-[ 33 ; %d ~" x)) (kbd (format "%s<f19>" tkey))) (define-key km (kbd (format "M-[ 34 ; %d ~" x)) (kbd (format "%s<f20>" tkey))) ;; continue loop (setq x (+ x 1))) ;; return the keymap km))
Save the default translation map in a variable
(setq do.minimal.tmux/default-translation-map (copy-keymap key-translation-map))
Define a function to toggle between both keymaps. TODO: figure out a hook that
we can attach this to. It seems like focus-in-hook
does not work for this.
(defun do.minimal.tmux/toggle-maps () "Toggle the `key-translation-map' on frame focus." (interactive) (if (getenv "TMUX" (selected-frame)) ;; check env of current frame! (setq key-translation-map do.minimal.tmux/tmux-translation-map) (setq key-translation-map do.minimal.tmux/default-translation-map)))
Note that in order for this to work, we need to have the following snippet in .tmux.conf
:
setw -g xterm-keys on
Eshell
Update
: Another one of these things that I haven't touched in years.Eshell is interesting. It could be a great replacement for bash if I'm ever forced to use a windows machine. Also, it's just kind of cool to have sort of a lisp/shell hybrid thingy.
(use-package eshell :config <<do.minimal.eshell/general>> <<do.minimal.eshell/visual-commands>> <<do.minimal.eshell/clear>> <<do.minimal.eshell/prompt-1>> <<do.minimal.eshell/prompt-2>> <<do.minimal.eshell/prompt-3>> <<do.minimal.eshell/hooks>>)
General settings
Disable the banner message.
(setq eshell-banner-message "")
Visual commands
Inspired by Howard's eshell config, this next block defines some programs for
which eshell
launches a comint buffer. These are mostly curses
programs.
(defvar do.minimal.eshell/visual-commands '("ssh" "htop" "ncmpcpp" "tail") "List of eshell commands for which to launch a seperate comint buffer.")
Clear the screen
Clear the screen using the (regular?) clear
command. Stolen from this old
list post.
(defun eshell/clear () "Deletes the contents of eshell buffer, except the last prompt" (save-excursion (goto-char eshell-last-output-end) (let ((lines (count-lines 1 (point))) (inhibit-read-only t)) (beginning-of-line) (let ((pos (point))) (if (bobp) (if (interactive-p) (error "Buffer too short to truncate")) (delete-region (point-min) (point)))))))
Prompt
This is mimicking my zsh prompt. Without the git indicator for now, but that should be easy to code up.
First, we want to make sure that the home directory is always replaced
(defun do.minimal.eshell.prompt/pwd-replace-home (pwd) "Replace home in PWD with tilde (~) character." (interactive) (let* ((home (expand-file-name (getenv "HOME"))) (home-len (length home))) (if (and (>= (length pwd) home-len) (equal home (substring pwd 0 home-len))) (concat "~" (substring pwd home-len)) pwd)))
Now the prompt function itself. Note that this setup should be modified somehow
to play nice with tramp
. I should decide based on (file-remote-p)
whether
I'm editing a remote file. If that's the case, take the default-directory
string and either split it into "real" hostname and directory or truncate the
/ssh:<hostname>
part out of it if I can get the "real" hostname from
somewhere else. Emacs will always have a small project for you…
;; a macro to propertize, stolen from emacswiki. (defmacro do.minimal.eshell.prompt/with-face (str &rest properties) `(propertize ,str 'face (list ,@properties))) ;; the prompt function (defun do.minimal.eshell.prompt/prompt () (let* ;; need some string lengths to fill the screen with spaces ((spc-cnt (length hostname)) (dir (do.minimal.eshell.prompt/pwd-replace-home (eshell/pwd))) (offset (if on-windows 3 2)) ;; need extra offset in terminal (fill-size (- (window-body-width) (+ spc-cnt (length dir) offset)))) ;; put together the prompt (concat "\n" (do.minimal.eshell.prompt/with-face hostname :foreground "cornflower blue" :weight "bold") (do.minimal.eshell.prompt/with-face "|" :foreground "red" :weight "bold") (do.minimal.eshell.prompt/with-face dir :foreground "gold" :weight "extra-bold") (make-string fill-size 32) ;; go to the right side ;; the return status indicator (if (equal eshell-last-command-status 0) (do.minimal.eshell.prompt/with-face (char-to-string #x2713) :foreground "green" :weight "bold") (do.minimal.eshell.prompt/with-face (char-to-string #x2717) :foreground "red" :weight "bold")) "\n" (make-string spc-cnt 32) (do.minimal.eshell.prompt/with-face "|" :foreground "red" :weight "bold") (do.minimal.eshell.prompt/with-face (char-to-string #x25b8) :foreground (face-attribute 'default :foreground)) " ")))
Enable the prompt.
(setq eshell-prompt-function #'do.minimal.eshell.prompt/prompt eshell-prompt-regexp (concat "\s*|" (char-to-string #x25b8) " ")) ;; \s* captures whitespace
Hooks
(defun do.minimal.eshell/hooks () (eshell-read-aliases-list) (setq global-hl-line-mode nil) (setq show-trailing-whitespace nil) (setq-local ml-interactive? t) ;; for mode line (eshell/addpath "~/bin") (dolist (command do.minimal.eshell/visual-commands) add-to-list 'eshell-visual-commands command)) (add-hook 'eshell-mode-hook #'do.minimal.eshell/hooks)
Magit
(use-package magit :ensure t :bind (("C-x g" . magit-status)) :config (setq magit-push-always-verify nil) ;; fix color issue (set-face-attribute 'magit-section-highlight nil :background "#3a3a3a") (set-face-attribute 'magit-branch-current nil :foreground "black" :background "gold"))
ledger
Started doing my personal accouting in plain text. This works very well.
(use-package ledger-mode :ensure t :hook ((ledger-mode . outline-minor-mode) (ledger-mode . (lambda () (outline-hide-sublevels 1)))) :init (defvar do.minimal.ledger/current-ledger "" "Path to this year's ledger file") (defun do.minimal.ledger/find-current-ledger () "Visit the current ledger" (interactive) (find-file do.minimal.ledger/current-ledger)) :config (font-lock-add-keywords 'ledger-mode outline-font-lock-keywords) (setq ledger-default-date-format ledger-iso-date-format) :bind (("C-c L" . #'do.minimal.ledger/find-current-ledger) :map ledger-mode-map ("TAB" . org-cycle)))
Normally, I wouldn't use something like this, but for ledger
files, automatically comitting every change to git seems like a good idea. I enable this by setting a file-local variable like this: ; -*- mode: Ledger; eval: (git-auto-commit-mode 1) -*-
(use-package git-auto-commit-mode :ensure t :after ledger-mode :diminish git-auto-commit-mode)
Projectile
(use-package projectile :ensure t :diminish projectile-mode :config (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map) (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) (setq projectile-completion-system 'ivy) (projectile-mode +1))
Highlighting symbols
Use highlight-symbol with a custom face and a low timeout value.
(use-package highlight-symbol :ensure t :diminish highlight-symbol-mode :config (setq highlight-symbol-idle-delay 0.5) ;; use the same background as hl-line (set-face-attribute 'highlight-symbol-face nil :background (face-attribute 'hl-line :background)) ;; add to prog-mode-hooks (add-hook 'prog-mode-hook 'highlight-symbol-mode))
Misc. settings
Some one-liners and other settings go in here. Update
: There is some ancient stuff in here.Only y/n questions, not yes/no
(defalias 'yes-or-no-p 'y-or-n-p )
Disable ad-handle-definition
warnings
(setq ad-redefinition-action 'accept)
Unique buffer names
(setq uniquify-buffer-name-style 'forward)
Stop pasting at the mouse click point
Emacs' default setting first moves point to the click location and then yanks. This is annoying, and we rather want it to yank at the current position of point.
(setq mouse-yank-at-point t)
Open file as root
Inspired by this, but using the regular find-file
instead of ido-find-file
.
(defun do.minimal.misc/sudo-open () "Like `find-file', but with root rights using TRAMP" (interactive) (let ((file (read-file-name "Open as root: "))) (unless (file-writable-p file) (find-file (concat "/sudo:root@localhost:" file))))) ;; bind to a key (global-set-key (kbd "C-x F") #'do.minimal.misc/sudo-open)
Auto-fill-mode
(setq-default fill-column 79) (setq default-fill-column 79)
DocView
Make DocView suck a little less. I still prefer evince.
(use-package doc-view :config (defun doc-view-buffer-message () "rendering pdf...") (setq doc-view-continuous t) (add-hook 'doc-view-mode-hook 'auto-revert-mode) )
Moving to beginning of line
Spotted here. Using C-a
to toggle between going to the true beginning of the
line and the beginning of the indented line. Goes to the true beginning of line
first.
(defun do.minimal.misc/beginning-of-line-or-indentation () "Move to beginning of line, or indentation" (interactive) (if (bolp) (back-to-indentation) (beginning-of-line)))
Bind this to the correct key.
(global-set-key [remap move-beginning-of-line] #'do.minimal.misc/beginning-of-line-or-indentation)
Shell-mode
Use bash
when invoking M-x shell
.
(setq explicit-shell-file-name "/bin/bash") (add-hook 'shell-mode-hook 'no-trailing-whitespace) (add-hook 'shell-mode-hook '(lambda () (setq-local ml-interactive? t)))
Google this!
Take the current region and send it as search query to google.
(defun google () "Googles a query or region if any." (interactive) (browse-url (concat "http://www.google.com/search?ie=utf-8&oe=utf-8&q=" (if mark-active (buffer-substring (region-beginning) (region-end)) (read-string "Google: ")))))
Fill line from point to end with some character
(defvar do.minimal.misc/fill-to-end-with-char-col fill-column "The value that `fill-to-end' fills the column until.") (defun fill-to-end-with-char (char) "Fill the current line with CHAR from point until the column including `do.minimal.misc/fill-to-end-with-char-col'. When called with a prefix argument, show a prompt asking for the character to fill with. The default character to fill is '-'." (interactive (if current-prefix-arg (list (let ((input)) (while (not (= (length input) 1)) (setq input (read-string "Fill with character: "))) input)) (list "-"))) (save-excursion (let* ((cur-point (point)) (cur-point-on-line (- cur-point (point-at-bol))) (str-len (- do.minimal.misc/fill-to-end-with-char-col cur-point-on-line)) (str (make-string str-len (string-to-char char)))) (goto-char cur-point) (insert str))))
Reuse frames when popping up
(add-to-list 'display-buffer-alist
'("." nil (reusable-frames . t)))
Cycle when popping mark ring
(setq set-mark-command-repeat-pop t)
Recursive grep by default
(setq grep-command "grep --color -nH --null -r -e ")
Copy strings to OS clipboard
(defun do.minimal/string-to-clipboard (str) "Insert the string STR into the OS clipboard by way of the kill ring." (with-temp-buffer (insert str) (kill-region (point-min) (point-max))))
PDF reader
(defvar do.minimal/pdf-reader "xdg-open" "The default PDF reader")
Flash evaluated regions
(use-package eval-sexp-fu :ensure t :commands (eval-sexp-fu-flash-mode) :init (eval-sexp-fu-flash-mode))
Insert ISO 8601 date
(defun do.misc/insert-date () "Insert an ISO 8601 formatted date string at point." (interactive) (insert (format-time-string "%Y-%m-%d")))
FlyCheck
This is only in this file because I'm only using one line of configuration.
(unless on-windows (use-package flycheck :ensure t :init (global-flycheck-mode) (setq flycheck-checker-error-threshold 2000)))
Which-key
(unless on-windows (use-package which-key :ensure t :diminish which-key-mode :init (which-key-setup-side-window-bottom) (setq which-key-side-window-max-height 0.25) (setq which-key-idle-delay 0.4) (which-key-mode)))
ivy/counsel
(use-package ivy :ensure t :demand :diminish ivy-mode :bind (("C-c C-r" . ivy-resume) ("C-x B" . ivy-switch-buffer-other-window) ("C-x b" . ivy-switch-buffer)) :custom (ivy-count-format "(%d/%d) ") (ivy-use-virtual-buffers t) (ivy-use-selectable-prompt t) ;; :hook ;; (ivy-mode . ivy-posframe-mode) ;; see the posframe block below :config (ivy-mode) (setq enable-recursive-minibuffers t)) (use-package counsel :after ivy :ensure t :demand :diminish counsel-mode :config (counsel-mode 1) ;; configure ripgrep for fast and easy searching. <<do.minimal/counsel-ripgrep>> ;; change default regexes (setq ivy-initial-inputs-alist '((counsel-minor . "^+") (counsel-package . "^+") (counsel-org-capture . "") (counsel-M-x . "") (counsel-describe-function . "") (counsel-describe-variable . "") (org-refile . "^") (org-agenda-refile . "^") (org-capture-refile . "^") (Man-completion-table . "^") (woman . "^")))) (use-package ivy-rich :after ivy :ensure t :demand :custom (ivy-virtual-abbreviate 'full ivy-rich-switch-buffer-align-virtual-buffer t ivy-rich-path-style 'abbrev) :config (setq ivy-rich-parse-remote-buffer nil) (setq ivy-rich-parse-remote-file-path nil) (ivy-set-display-transformer 'ivy-switch-buffer 'ivy-rich-switch-buffer-transformer) (ivy-rich-mode 1))
Ripgrep checks a lot of boxes: fast, compatible with ivy, and it is distributed as a small static binary that I can include in my Emacs "distribution". So let's configure it
;; this needs to go in the config (setq counsel-rg-base-command (cons (concat initel-directory "random/" (car counsel-rg-base-command)) (cdr counsel-rg-base-command))) ;; we want the search results to be displayed at the bottom of the window with a half split (defun do.minimal.rg/get-window-height (caller) "Return the height of the current window divided by 2" (/ (window-total-height) 2)) (add-to-list 'ivy-height-alist '(counsel-rg . do.minimal.rg/get-window-height)) ;; always default to the current directory and display a smaller prompt (defun do.minimal.rg/call-counsel-rg () (interactive) (let ((ivy-fixed-height-minibuffer t) (ivy-count-format "")) (counsel-rg nil default-directory nil "rg: "))) ;; set an alias for easier search (defalias 'rg #'do.minimal.rg/call-counsel-rg)
I dabbled with using ivy-posframe, but I found that it is not responsive enough for my tastes. So I'll disable it for now and get back to it later.
(use-package ivy-posframe :ensure t :after ivy :diminish ivy-posframe-mode :custom-face (ivy-posframe ((t (list :background (face-attribute 'default :background))))) (ivy-posframe-border ((t (:background "gold")))) (ivy-posframe-cursor ((t (:background "gold")))) :config ;; custom define height of post frame per function (setq ivy-posframe-height-alist '((find-file . 13) (t . 13))) ;; display at `ivy-posframe-style' (setq ivy-posframe-display-functions-alist '((complete-symbol . ivy-posframe-display-at-point) (counsel-M-x . ivy-posframe-display-at-point) (counsel-find-file . ivy-posframe-display-at-window-bottom-left) (ivy-switch-buffer . ivy-posframe-display-at-window-bottom-left) (t . ivy-posframe-display-at-window-bottom-left))) ;; other customizations (setq ivy-posframe-hide-minibuffer t) ;; (setq ivy-posframe-min-width 120) ;; (setq ivy-posframe-width 120) (setq ivy-posframe-border-width 1) (ivy-posframe-mode 1))
Windows
(when on-windows (setq visible-bell t))