This is is inspired by Sacha’s Emacs configuration.
Setup the necessary things that should happen before anything else.
I prefer to have all Emacs history and temporary files under one directory (~/emacs.d/history/
).
(defvar my/history-dir (expand-file-name "history/" user-emacs-directory))
(defvar my/elisp-dir (expand-file-name "elisp/" user-emacs-directory))
(startup-redirect-eln-cache (concat my/history-dir "eln-cache/"))
(setcar native-comp-eln-load-path (concat my/history-dir "eln-cache/"))
Setup ELPA package sources and initialize package.el
.
(setq package--init-file-ensured t)
(package-initialize)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
(unless package-archive-contents
(package-refresh-contents))
Read about use-package.
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(setq use-package-verbose t
use-package-always-ensure t)
(eval-when-compile
(require 'use-package))
This will help auto compile each library we load.
(setq load-prefer-newer t)
(use-package auto-compile
:init
(setq auto-compile-display-buffer nil
auto-compile-mode-line-counter t)
(auto-compile-on-load-mode))
This whole configuration depends on dash.el and s.el so load them early.
(use-package s)
(use-package dash :config (dash-enable-font-lock))
Avoid breaking packages by pinning those dependencies to their stable version.
(-each '((diminish . "melpa-stable")
(epl . "melpa-stable")
(f . "melpa-stable")
(git-commit . "melpa-stable")
(hydra . "melpa-stable")
(inflections . "melpa-stable")
(logito . "melpa-stable")
(makey . "melpa-stable")
(names . "melpa-stable")
(packed . "melpa-stable")
(pcache . "melpa-stable")
(pkg-info . "melpa-stable")
(popup . "melpa-stable")
(rich-minority . "melpa-stable")
(s . "melpa-stable")
(use-package . "melpa-stable")
(with-editor . "melpa-stable"))
(lambda (package)
(add-to-list 'package-pinned-packages package)))
This function will help us determine if we are on OS X
(defun on-osx-p ()
(eq system-type 'darwin))
Change the default Emacs configuration, from default variables to keybindings.
Let Emacs know who is using it.
(setq user-full-name "Ammar Alammar"
user-mail-address "[email protected]")
Emacs default configuration are awful, lets fix it.
If you want the meaning of these variables move the point to the desired variable
and press C-h v
.
(setq comment-style 'multi-line
create-lockfiles nil
confirm-kill-emacs 'y-or-n-p
delete-by-moving-to-trash t
echo-keystrokes 0.1
font-lock-maximum-decoration t
gc-cons-threshold (* 50 1024 1024)
hscroll-step 1
inhibit-startup-echo-area-message t
inhibit-startup-message t
large-file-warning-threshold nil
mouse-wheel-flip-direction t
mouse-wheel-progressive-speed nil
mouse-wheel-scroll-amount '(0.01)
mouse-wheel-tilt-scroll t
ring-bell-function 'ignore
scroll-conservatively 10
shift-select-mode nil
transient-mark-mode t
truncate-partial-width-windows nil
uniquify-buffer-name-style 'forward
vc-follow-symlinks 't
default-directory "~/"
command-line-default-directory "~/"
kill-ring-max 1000
show-paren-mode nil
;; Double the default undo limits
undo-limit 320000
undo-strong-limit 480000
undo-outer-limit 48000000
)
(setq-default comment-column 0)
I just don’t want to be prompted about disabled commands.
(setq disabled-command-function nil)
UTF-8 everything. Taken from this answer.
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-language-environment 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(setq locale-coding-system 'utf-8)
This is important to set early on so Emacs initializes with the correct language.
(setenv "LANG" "en_US.UTF-8")
(setq initial-major-mode 'org-mode)
And protect it from accidental killing
(with-current-buffer "*scratch*"
(emacs-lock-mode 'kill))
(when window-system (add-hook 'after-init-hook 'server-start t))
Recursivly add every library in my/elisp-dir
to Emacs load path.
(let ((default-directory my/elisp-dir))
(normal-top-level-add-to-load-path '("."))
(normal-top-level-add-subdirs-to-load-path))
Emacs by default saves backup files in the current directory, cluttering your
directory with files ending with ~
. This stashes them away in
my/history-dir
.
(setq backup-directory-alist `(("." . ,(concat my/history-dir "backups"))))
And save lots.
(setq delete-old-versions -1
version-control t
vc-make-backup-files t
auto-save-file-name-transforms `((".*" ,(concat my/history-dir "auto-save-list/") t))
auto-save-list-file-prefix (concat my/history-dir "auto-save-list/saves-"))
Make save-buffer
always creates a backup by passing two C-u
(defun my/save-buffer ()
(interactive)
(let ((current-prefix-arg '(4 4)))
(if (string= (buffer-name (current-buffer)) "*scratch*")
(message "Skipping saving *scratch* buffer")
(call-interactively 'save-buffer))))
(bind-key "C-x C-s" 'my/save-buffer)
This saves our position in files other things between Emacs sessions.
(setq history-length 1000
history-delete-duplicates t
savehist-save-minibuffer-history t
savehist-file (concat my/history-dir "savehist")
save-place-file (concat my/history-dir "saveplace")
savehist-additional-variables '(kill-ring
global-mark-ring
search-ring
regex-search-ring
extended-command-history)
transient-history-file (concat my/history-dir "transient"))
(savehist-mode)
Remembers visited files names.
(use-package recentf
:defer 1
:config
(setq recentf-auto-cleanup 'mode
recentf-max-saved-items 100
recentf-save-file (concat my/history-dir "recentf"))
(recentf-mode))
(setq bookmark-default-file (concat my/history-dir "bookmarks"))
These files show up in my .emacs.d
, so lets stick them in the history file.
(setq image-dired-dir (concat my/history-dir "image-dired/"))
Prevent Emacs from appending Easy Customization to our configuration file.
(setq custom-file (expand-file-name "customization.el" user-emacs-directory))
(load custom-file 'noerror)
When you kill a buffer that has a process attached to it, a repl for example, Emacs will ask fro confirmation if you really want to kill the buffer. This will disable that.
(setq kill-buffer-query-functions
(-remove-item 'process-kill-buffer-query-function kill-buffer-query-functions))
(setq-default indent-tabs-mode nil)
(setq-default tab-width 2)
For wrapping text with M-q
and auto-fill-mode
(setq-default fill-column 90)
In UNIX, a healthy file always ends with a new line.
(setq-default require-final-newline t)
Show the current column position in the mode line.
(column-number-mode)
Subword mode makes commands like forward-word
and backward-words
be aware of
CamelCase words so they stop right after the l
and before the capital C
.
(global-subword-mode)
Sentence end with only one space.
(setq sentence-end-double-space nil)
By default Emacs doesn’t change the content of the selection when you type or yank something. This fixes that.
(delete-selection-mode)
No one likes to type a full yes
, y
is enough as a confirmation.
(setq use-short-answers t)
Allow Emacs to extract compressed files and also compress them back after saving the file.
(auto-compression-mode)
Whenever a file opened by Emacs changed by an external program, this mode automatically reload the file
(use-package autorevert
:defer 1
:config
(global-auto-revert-mode))
Set a better keybinding for revert-buffer
No one likes s-u
(bind-key "C-x t r" 'revert-buffer)
Clean a file on save according to various rules, like trailing whitespaces or empty lines, etc.
(use-package whitespace
:defer 1
:config
(setq whitespace-action '(auto-cleanup)
whitespace-style '(trailing
lines
empty
space-before-tab
indentation
space-after-tab))
(global-whitespace-mode))
I like my cursor to be a thin line.
(setq-default cursor-type 'bar)
Add a one pixel padding to the edges of Emacs window.
(set-fringe-mode 1)
Smart Mode Line makes Emacs mode line beautiful.
(use-package smart-mode-line
:init
(setq sml/name-width 60
sml/no-confirm-load-theme t
sml/shorten-directory t
sml/show-file-name t
sml/theme 'respectful
sml/use-projectile-p 'before-prefixes
rm-whitelist " FlyC*"
rm-blacklist " Fly\\'")
(sml/setup))
Solarized is so good.
(use-package zenburn-theme
:init
(load-theme 'zenburn 't)
:config
(setq zenburn-override-colors-alist
'(("zenburn-green-2" . "#6d926d")))
(set-face-attribute 'region nil
:background "#5c5c5c"
:extend 't)
(set-face-attribute 'font-lock-type-face nil
:weight 'bold
:extend 't))
Rainbow Delimiters help with coloring parentheses and brackets and others. I mainly use it to change all the delimiters colors to one.
(use-package rainbow-delimiters
:defer 1
:config
(setq rainbow-delimiters-max-face-count 1)
(--each '(prog-mode-hook
emacs-lisp-mode-hook
org-mode-hook
markdown-mode-hook
web-mode-hook)
(add-hook it #'rainbow-delimiters-mode))
(--each '(rainbow-delimiters-depth-1-face
rainbow-delimiters-depth-2-face
rainbow-delimiters-depth-3-face
rainbow-delimiters-depth-4-face
rainbow-delimiters-depth-5-face
rainbow-delimiters-depth-6-face
rainbow-delimiters-depth-7-face
rainbow-delimiters-depth-8-face
rainbow-delimiters-depth-9-face)
(set-face-attribute it nil :foreground "#FC5353" :extend 't))
(set-face-attribute 'rainbow-delimiters-unmatched-face nil
:foreground "#dfaf8f"
:background "#FC5353"
:inverse-video nil
:extend 't))
Automatically transform symbols like lambda into the greek letter λ
(--each '(org-mode-hook
ruby-mode-hook)
(add-hook it
(lambda () (add-to-list 'prettify-symbols-alist '("lambda" . ?λ)))))
(--each '(web-mode-hook
js-mode-hook
js2-mode-hook
rjsx-mode-hook)
(add-hook it
(lambda () (setq-local prettify-symbols-alist nil))))
(--each '(python-mode-hook)
(add-hook it
(lambda () (setq-local prettify-symbols-alist '(("lambda" . ?λ))))))
(add-hook 'org-mode-hook
(lambda ()
;; Prettify Org headers
(setq-local prettify-symbols-compose-predicate (lambda (_start _end _match) t))
(add-to-list 'prettify-symbols-alist '("*" . ?●))))
(global-prettify-symbols-mode)
For easily identification of the current line.
(global-hl-line-mode)
I really like the JetBrains Mono font.
(set-face-attribute 'default nil :font "JetBrains Mono NL" :height 120)
Use an Arabic font for Arabic unicode characters
(let ((my-font "Noto Sans Arabic UI"))
(set-fontset-font "fontset-startup" '(#x000600 . #x0006FF) my-font)
(set-fontset-font "fontset-default" '(#x000600 . #x0006FF) my-font)
(set-fontset-font "fontset-standard" '(#x000600 . #x0006FF) my-font))
For regular writing I like to have a proportional font. Sahl Naskh is an improved fork of Droid Arabic Naskh.
(set-face-attribute 'variable-pitch nil
:font "Sahl Naskh"
:height 160
:width 'normal
:weight 'normal)
(bind-keys ("C-x t v" . variable-pitch-mode))
Tramp allows Emacs to edit files over SSH.
(setq tramp-persistency-file-name (concat my/history-dir "tramp")
remote-file-name-inhibit-cache nil
remote-file-name-inhibit-locks 't
vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp))
(use-package eshell
:commands eshell
:config
(setq eshell-history-file-name (concat my/history-dir "eshell/history")
eshell-scroll-to-bottom-on-input 'all
eshell-error-if-no-glob t
eshell-hist-ignoredups t
eshell-save-history-on-exit t
eshell-glob-case-insensitive t
eshell-cmpl-ignore-case t))
A few configurations and styles for Emacs Ediff.
(add-hook 'ediff-mode-hook
(lambda ()
(setq ediff-merge-split-window-function 'split-window-vertically
ediff-split-window-function 'split-window-horizontally
ediff-window-setup-function 'ediff-setup-windows-plain)
(set-face-attribute 'ediff-current-diff-C nil :background "#41421c" :extend 't)
(set-face-attribute 'ediff-fine-diff-A nil :background "#630813" :extend 't)
(set-face-attribute 'ediff-fine-diff-B nil :background "#0a4c1b" :extend 't)
))
Save window layout and restore them after ediff session
(defvar my/ediff-last-windows nil)
(defun my/store-pre-ediff-winconfig ()
(setq my/ediff-last-windows (current-window-configuration)))
(defun my/restore-pre-ediff-winconfig ()
(set-window-configuration my/ediff-last-windows))
(add-hook 'ediff-before-setup-hook #'my/store-pre-ediff-winconfig)
(add-hook 'ediff-quit-hook #'my/restore-pre-ediff-winconfig)
A few configuration for Emacs Dired mode.
(defun my/dired-view-file ()
"Exactly like `dired-view-file' expect it uses `view-file-other-window' instead of `view-file'"
(interactive)
(let ((file (dired-get-file-for-visit)))
(if (file-directory-p file)
(or (and (cdr dired-subdir-alist)
(dired-goto-subdir file))
(dired file))
(view-file-other-window file))))
(use-package dired
:ensure nil
:bind (:map dired-mode-map
("C-l" . dired-up-directory)
("w" . wdired-change-to-wdired-mode)
("v" . my/dired-view-file))
:config
(setq dired-dwim-target 't
insert-directory-program "gls"
dired-listing-switches "-alht --group-directories-first"
dired-recursive-deletes 'always)
(use-package dired-x
:ensure nil))
Winner mode gives you the ability to undo and redo your window configuration, watch this video for better explanation.
(use-package winner
:init (winner-mode))
Useful for defining expandable abbreviations
(setq save-abbrevs t
abbrev-file-name (concat my/history-dir "abbrev_defs"))
(setq-default abbrev-mode t)
Emacs spell checker, and flyspell runs ispell on the fly. Use hunspell because it’s more powerful and supports Arabic.
(setq ispell-program-name "hunspell"
ispell-dictionary "en_US"
ispell-really-hunspell t
ispell-keep-choices-win t
ispell-use-framepop-p nil
ispell-local-dictionary-alist '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)))
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC"))
Use both Ispell and abbrev together. (source)
(defun ispell-word-then-abbrev (p)
"Call `ispell-word'. Then create an abbrev for the correction made.
With prefix P, create local abbrev. Otherwise it will be global."
(interactive "P")
(let ((before (downcase (or (thing-at-point 'word) "")))
after)
(call-interactively 'ispell-word)
(setq after (downcase (or (thing-at-point 'word) "")))
(unless (string= after before)
(message "\"%s\" now expands to \"%s\" %sally" before after (if p "loc" "glob"))
(define-abbrev (if p local-abbrev-table global-abbrev-table)
before after))))
(bind-keys ("C-x t i" . ispell-word-then-abbrev))
Unbind those keys from flyspell-mode
(add-hook 'flyspell-mode-hook
(lambda ()
(unbind-key "C-." flyspell-mode-map)
(unbind-key "C-;" flyspell-mode-map)))
I don’t like line truncation on compilation buffer, it’s nicer to look at.
(add-hook 'compilation-mode-hook (lambda () (setq-local truncate-lines nil)))
It’s so much easier to hit C-x 8 q
than C-x * q
for the quick-calc
command.
(bind-keys ("C-x 8 q" . quick-calc))
I want C-c C-c
to end the editing session.
(add-hook 'server-visit-hook
(lambda ()
(local-set-key (kbd "C-c C-c") 'server-edit)))
The built in view-mode is useful when you just want to read a file. This ease the file navigation
(use-package view
:ensure nil
:bind (("C-x C-q" . view-mode)
:map view-mode-map
("n" . next-line)
("j" . next-line)
("p" . previous-line)
("k" . previous-line)
("/" . swiper))
:config
(add-hook 'view-mode-hook
(lambda ()
(make-variable-buffer-local 'line-move-visual)
(setq-local line-move-visual nil)
(setq-local hl-line-changed-cookie
(face-remap-add-relative 'hl-line '((:background "#734242")))))))
(defun my/vue-imenu-index ()
(when (or (equal "vue" (file-name-extension (buffer-file-name)))
(equal "js" (file-name-extension (buffer-file-name))))
(setq-local imenu-create-index-function
(lambda () (imenu--generic-function
'(("Style" "^\\(<style.*>\\)" 1)
("Function" "^\\s-*\\(?:async \\)?\\(?:function \\)?\\([[:alnum:]]+(.*)\\)\\s-*{" 1)
("Variable" "^\\(?:const\\|let\\|var\\) \\([[:alnum:]]+\\) ?=" 1)
("Localization" "^\\s-*\\(i18n:\\s-*{\\)" 1)
("Methods" "^\\s-*\\(methods:\\s-*{\\)" 1)
("Watch" "^\\s-*\\(watch:\\s-*{\\)" 1)
("Computed" "^\\s-*\\(computed:\\s-*{\\)" 1)
("Props" "^\\s-*\\(props:\\s-*{\\)" 1)
("Components" "^\\s-*\\(components:\\s-*{\\)" 1)
("Script" "^\\(<script.*>\\)" 1)
("Template" "^\\(<template.*>\\)" 1)))))))
(defun my/imenu-default-goto-function (name position &rest args)
(imenu-default-goto-function name position args)
(recenter))
(add-hook 'web-mode-hook #'my/vue-imenu-index)
(add-hook 'js2-mode-hook #'my/vue-imenu-index)
(setq-default imenu-default-goto-function 'my/imenu-default-goto-function)
Hideshow is a builtin minor mode for code folding
(defun my/hs-hide-rest ()
"Hide everything in the buffer and only show the current block"
(interactive)
(hs-hide-all)
(hs-show-block))
(bind-key "C-c @ @" 'my/hs-hide-rest)
(add-hook 'prog-mode-hook #'hs-minor-mode)
Minor mode that buttonizes URLs
(use-package goto-addr
:hook ((compilation-mode . goto-address-mode)
(prog-mode . goto-address-mode))
:bind (:map goto-address-highlight-keymap
("M-<return>" . goto-address-at-point)))
Change the location of these modes config/cache files to the proper path
(setq semanticdb-default-save-directory (concat my/history-dir "semanticdb"))
(setq eshell-directory-name (concat my/history-dir "eshell"))
Unbind these keys because they are used for something else.
(unbind-key "C-;")
(unbind-key "C-x m")
;; I don't like the `upcase-region' command, always use it by mistake
(unbind-key "C-x C-u")
These are my personal preference to the default Emacs keybindings.
(bind-keys ("RET" . reindent-then-newline-and-indent)
("C-w" . backward-kill-word)
("C-x C-k" . kill-region)
("M-/" . hippie-expand)
("C-x t l" . toggle-truncate-lines)
("C-<tab>" . indent-for-tab-command)
("C-x s" . save-buffer)
("C-h a" . apropos)
("C-x C-a" . mark-whole-buffer)
("C-x C-<tab>" . indent-rigidly)
("C-x r w" . copy-rectangle-as-kill))
Use Shift-<arrow key>
to move between windows.
(windmove-default-keybindings)
Make C-x o
switch to next window and C-x C-o
switch to previous window.
(defun my/switch-window-forward ()
(interactive)
(other-window 1))
(defun my/switch-window-backward ()
(interactive)
(other-window -1))
(bind-keys ("C-x o" . my/switch-window-backward)
("C-x C-o" . my/switch-window-forward))
I use M-`
to toggle between two buffers.
(defun my/previous-buffer (args)
(interactive "P")
(if args
(ff-find-other-file)
(switch-to-buffer (other-buffer (current-buffer)))))
(bind-key "M-`" 'my/previous-buffer)
Make windows splitting by default use horizontal split
(setq split-height-threshold nil)
Copied from reddit comment. Makes the newly created window set to the previous buffer.
(defun my/vertical-split-buffer (prefix)
"Split the window vertically and display the previous buffer."
(interactive "p")
(split-window-vertically)
(other-window 1 nil)
(if (= prefix 1) (switch-to-next-buffer)))
(defun my/horizontal-split-buffer (prefix)
"Split the window horizontally and display the previous buffer."
(interactive "p")
(split-window-horizontally)
(other-window 1 nil)
(if (= prefix 1) (switch-to-next-buffer)))
(bind-keys ("C-x 2" . my/vertical-split-buffer)
("C-x 3" . my/horizontal-split-buffer))
These are configuration specific to OSs. Mostly OS X for now.
A feature of Emacs 26.1
(when (and (on-osx-p)
(version<= "26.1" emacs-version))
(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist '(ns-appearance . dark)))
Emacs on OS X can’t access the environment variables set in the shell profile. This help us workaround that.
(use-package exec-path-from-shell
:if (on-osx-p)
:config
(setq exec-path-from-shell-arguments nil
exec-path-from-shell-variables '("PATH" "MANPATH" "BROWSER" "DICPATH" "GOROOT" "GOPATH"))
(exec-path-from-shell-initialize))
(when (on-osx-p)
(setq ns-alternate-modifier 'super
ns-command-modifier 'meta
ns-control-modifier 'control))
Emacs default Arabic keyboard layout doesn’t match with the default OS X layout, this fixes that.
(when (on-osx-p)
(load "arabic-mac")
(setq default-input-method "arabic-mac"))
See my homebrew formula
(when (on-osx-p)
(set-fontset-font t 'symbol (font-spec :family "Apple Color Emoji") nil 'prepend))
Use AppleScript to move files to trash
(defun my/system-move-file-to-trash (file)
"Use the command `trash' to move `file' to the system trash"
(do-applescript (format "tell application \"Finder\" to move the POSIX file \"%s\" to trash" file)))
(when (on-osx-p)
(defalias 'move-file-to-trash 'my/system-move-file-to-trash))
Whenever I’m on a terminal I use emacsclient
to edit a file, this will switch back to
the terminal after editing the file.
(defun my/focus-terminal ()
;; Don't switch if we are committing to git
(unless (or (get-buffer "COMMIT_EDITMSG")
(get-buffer "git-rebase-todo"))
(do-applescript "tell application \"Terminal\" to activate")))
(when (on-osx-p)
(add-hook 'server-done-hook #'my/focus-terminal))
*p Appearance
I rarely, if ever, use the mouse in Emacs. This disable the GUI elements.
(when window-system
(tooltip-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1))
Don’t ever use GUI dialog boxes.
(setq use-dialog-box nil)
Resize Emacs window (called frame in Emacs jargon) as pixels instead of chars resulting in fully sized window.
(setq frame-resize-pixelwise t)
Add a bigger offset to underline property (it makes smart-mode-line looks way nicer).
(setq underline-minimum-offset 4)
Configuration for modes that are always running no matter which buffer we are in.
Smartparens manages pairs for you, so if you insert (
it automatically inserts
the closing pair.
(use-package smartparens
:bind (:map sp-keymap
("M-<backspace>" . sp-unwrap-sexp)
("M-." . sp-forward-slurp-sexp)
("M-," . sp-forward-barf-sexp)
("C-M-." . sp-backward-slurp-sexp)
("C-M-," . sp-backward-barf-sexp))
:init
(smartparens-global-mode)
:config
(setq sp-base-key-bindings 'sp
sp-highlight-pair-overlay nil
sp-highlight-wrap-overlay nil
sp-highlight-wrap-tag-overlay nil
)
(use-package smartparens-config :ensure nil)
(sp-use-smartparens-bindings)
(sp-pair "(" nil :post-handlers '(("| " "SPC")))
(sp-pair "[" nil :post-handlers '(("| " "SPC")))
(sp-pair "{" nil :post-handlers '(("| " "SPC")))
(add-hook 'ruby-mode-hook
(lambda ()
(sp-local-pair 'ruby-mode "{" nil :post-handlers '(("| " "SPC")))))
(add-hook 'nxml-mode-hook
(lambda ()
(sp-local-pair 'nxml-mode "<" ">" :actions :rem)))
(add-hook 'web-mode-hook
(lambda ()
(setq sp-navigate-consider-sgml-tags '(html-mode))
(sp-local-pair 'web-mode "<" nil :actions :rem)
(sp-local-pair 'web-mode "<%" "%>" :post-handlers '(("| " "SPC") (" | " "=")))
(sp-local-pair 'web-mode "{%" "%}" :post-handlers '(("| " "SPC")))
))
(add-hook 'sql-mode-hook
(lambda ()
(sp-local-pair 'sql-mode "{%" "%}" :post-handlers '(("| " "SPC")))))
;; Do not escape closing pair in string interpolation
(add-hook 'swift-mode-hook
(lambda ()
(sp-local-pair 'swift-mode "\\(" nil :actions :rem)
(sp-local-pair 'swift-mode "\\(" ")")))
(set-face-attribute 'sp-show-pair-match-face nil
:foreground 'unspecified
:background "#802A2A"
:extend 't)
(show-smartparens-global-mode))
Change beginning/end of s-expression movement to go outside of the s-expression when the point
is at the beginning/end of the s-expression.
(defun my/sp-beginning-of-sexp ()
"Move to the beginning of sexp, if at beginning then move before it"
(interactive)
(let* ((sexp (or (sp-get-enclosing-sexp) (sp-get-sexp)))
(beginning (sp-get sexp :beg-in)))
(if (= beginning (point))
(goto-char (1- beginning))
(sp-beginning-of-sexp))))
(defun my/sp-end-of-sexp ()
"Move to the end of sexp, if at end then move after it"
(interactive)
(let* ((sexp (or (sp-get-enclosing-sexp) (sp-get-sexp)))
(end (sp-get sexp :end-in)))
(if (= end (point))
(goto-char (1+ end))
(sp-end-of-sexp))))
(defun my/sp-down-sexp (arg)
"Normal `sp-down-sexp' but with prefix it uses `sp-backward-down-sexp'"
(interactive "P")
(if arg
(sp-backward-down-sexp)
(sp-down-sexp)))
(bind-keys :map sp-keymap
("C-M-a" . my/sp-beginning-of-sexp)
("C-M-e" . my/sp-end-of-sexp)
("C-M-d" . my/sp-down-sexp))
Company Mode is a text completion framework for Emacs.
(use-package company
:config
(setq company-global-modes '(not inf-ruby-mode eshell-mode)
company-idle-delay 0.3
company-minimum-prefix-length 3
company-dabbrev-downcase nil)
(add-hook 'company-mode-hook
(lambda ()
(set-face-attribute 'company-template-field nil
:foreground 'unspecified
:background 'unspecified
:inherit 'region
:extend 't)))
(global-company-mode))
When Company suggestions is shown pressing C-w
will be captured by Company and will not execute backward-kill-word
.
(defun my/company-abort ()
"Make company mode not steal C-w and instead pass it down"
(interactive)
(company-abort)
(execute-kbd-macro (kbd "C-w")))
(bind-keys :map company-active-map
("C-w" . my/company-abort))
LSP Mode is emacs’ integration for the Language Server Protocol
;; (use-package lsp-mode
;; :bind (("C-c C-d" . lsp-describe-thing-at-point)
;; ("C-c C-o" . lsp-organize-imports))
;; :hook ((go-mode
;; c-mode
;; ) . lsp-deferred)
;; :commands (lsp lsp-deferred lsp-describe-thing-at-point)
;; :config
;; (setq lsp-session-file (concat my/history-dir "lsp-session")
;; lsp-enable-symbol-highlighting nil
;; lsp-enable-snippet nil
;; lsp-enable-imenu nil
;; lsp-headerline-breadcrumb-enable nil
;; lsp-modeline-diagnostics-enable nil
;; lsp-modeline-code-actions-enable nil
;; lsp-pylsp-plugins-pylint-enabled 't
;; lsp-restart 'ignore
;; lsp-clients-clangd-executable "/usr/local/opt/llvm/bin/clangd"
;; company-lsp-cache-candidates 'auto
;; ;; Disable LSP auto formatting
;; lsp-before-save-edits nil
;; lsp-enable-indentation nil
;; lsp-signature-render-documentation nil
;; )
;; ;; Ignore these directories
;; (--each '("[/\\\\]venv$")
;; (push it lsp-file-watch-ignored))
;; (set-face-attribute 'lsp-flycheck-error-unnecessary-face nil
;; :foreground nil
;; :background nil
;; :underline nil
;; :inherit 'flycheck-warning))
ace-window is a replacment for the other-window
command
(use-package ace-window
:bind (("C-x C-o" . ace-window)
("C-x o" . ace-window))
:config
(setq aw-keys '(?j ?k ?l ?u ?i ?o ?p ?n ?m))
(set-face-attribute 'aw-leading-char-face nil :height 180))
Popwin makes popup window awesome again, every popup window can be closed by C-g
.
(use-package popwin
:bind ("C-h e" . popwin:messages)
:bind-keymap ("C-z" . popwin:keymap)
:init
(autoload 'popwin-mode "popwin.el" nil t)
(popwin-mode)
:config
(--each '(("*rspec-compilation*" :tail nil)
"*Apropos*"
"*Warnings*"
"*projectile-rails-server*"
"*coffee-compiled*"
"*Bundler*"
"*projectile-rails-compilation*"
"*Ack-and-a-half*"
("*ruby*" :height 0.75)
("*rails*" :height 0.75)
"*Compile-Log*"
"*pry*"
"*SQL*"
"*projectile-rails-generate*"
"*Package Commit List*"
"*Compile-Log*"
(" *undo-tree*" :position bottom)
"*compilation*"
("RuboCop.*" :regexp 't)
"*elm*"
"*xcrun swift*"
("*HTTP Response*" :position bottom :height 30)
"*Flycheck errors*"
("*Flycheck error messages*" :noselect t)
"*js*"
"*Python*"
"*cider-error*"
"*cider-doc*")
(push it popwin:special-display-config)))
Focus help popup so we can exit it easily with popwin.
(setq help-window-select t)
Persistent Scratch saves and restores the scratch buffer between Emacs restarts.
(use-package persistent-scratch
:config
(setq persistent-scratch-save-file (concat my/history-dir "persistent-scratch"))
(persistent-scratch-setup-default))
Flycheck is a modern lint runner.
(defun my/current-buffer-is-a (extension)
"Return true if current buffer name ends with `extension'"
(let ((file (buffer-file-name (current-buffer))))
(s-ends-with? extension file)))
(use-package flycheck
:pin melpa-stable
:bind (("C-c ! ," . flycheck-list-errors))
:config
(setq flycheck-indication-mode 'right-fringe
flycheck-navigation-minimum-level 'error
flycheck-idle-change-delay 2
flycheck-check-syntax-automatically '(save idle-change))
(add-hook 'js2-mode-hook
(lambda ()
(direnv-update-environment default-directory)
(when (executable-find "eslint_d")
(setq flycheck-javascript-eslint-executable "eslint_d"))
(setq-local flycheck-checker 'javascript-eslint)))
(flycheck-add-mode 'javascript-eslint 'web-mode)
(add-hook 'web-mode-hook
(lambda ()
(direnv-update-environment default-directory)
(when (executable-find "eslint_d")
(setq flycheck-javascript-eslint-executable "eslint_d"))
(setq-local flycheck-checker 'javascript-eslint)))
(add-hook 'emacs-lisp-mode-hook
(lambda () (add-to-list 'flycheck-disabled-checkers 'emacs-lisp-checkdoc)))
(set-face-attribute 'flycheck-error nil
:background "#885f5f"
:underline '(:style wave :color "#BC8383")
:extend 't)
(global-flycheck-mode))
Show flycheck errors in a popup
(use-package flycheck-popup-tip
:config
(add-hook 'flycheck-mode-hook #'flycheck-popup-tip-mode)
(set-face-attribute 'popup-tip-face nil
:background 'unspecified
:foreground 'unspecified
:inherit 'company-tooltip
:extend 't))
Agggressive Indent Mode automatically indents s-expression. It’s magical.
(use-package aggressive-indent
:commands aggressive-indent-mode
:config
(add-to-list 'aggressive-indent-dont-indent-if
'(and (derived-mode-p 'sgml-mode)
(string-match "^[[:space:]]*{%"
(thing-at-point 'line))))
)
Yasnippet is a snippet expansion framework for Emacs.
(use-package yasnippet
:defer t
:init
(setq yas-snippet-dirs '("~/.emacs.d/snippets/"))
(yas-global-mode))
Use the Yasnippet official snippet collections
(use-package yasnippet-snippets
:defer t)
Undo Tree is a better undo/redo alternative.
(use-package undo-tree
:bind (("C-M-_" . undo-tree-visualize))
:config
(setq undo-tree-auto-save-history nil
undo-tree-history-directory-alist `(("." . ,(concat my/history-dir "undo-tree"))))
(global-undo-tree-mode))
;; (use-package vundo
;; :bind ("C-M-_" . vundo)
;; :config
;; (setq vundo-roll-back-on-quit nil)
;; )
Smex sorts used commands by frequency. Ivy automatically uses it for sorting when it’s present.
(use-package smex
:config
(setq smex-history-length 10
smex-save-file (concat my/history-dir "smex-items")))
Modes and configuration specific to managing and navigating projects.
Ivy is a lightweight completion system
(defun my/sort-files-by-date-directories-first (_name candidates)
"Re-sort CANDIDATES according to file modification date."
(let ((default-directory ivy--directory))
(sort (copy-sequence candidates)
(lambda (file1 file2)
(cond ((and (file-directory-p file1) (not (file-directory-p file2)))
't)
((and (not (file-directory-p file1)) (file-directory-p file2))
nil)
(t
(file-newer-than-file-p file1 file2)))))))
(use-package ivy
:bind (("C-s" . swiper-isearch)
("C-x c b" . ivy-resume)
:map ivy-minibuffer-map
("<return>" . ivy-alt-done))
:config
(setq ivy-use-virtual-buffers t
ivy-height 15
ivy-extra-directories nil
ivy-magic-tilde nil
ivy-switch-buffer-faces-alist '((dired-mode . ivy-subdir)
(org-mode . org-macro))
ivy-ignore-buffers '("\\*HTTP Response\\*" "\\*magit.*"))
(add-to-list 'ivy-sort-matches-functions-alist
'(read-file-name-internal . my/sort-files-by-date-directories-first))
;; Don't wrap lines in ivy-occur
(add-hook 'ivy-occur-grep-mode-hook #'toggle-truncate-lines)
(set-face-attribute 'ivy-minibuffer-match-face-2 nil
:background 'unspecified
:inherit 'isearch
:extend 't)
(set-face-attribute 'ivy-virtual nil
:foreground "#c2d2d3"
:weight 'bold
:inherit nil
:extend 't)
(ivy-mode))
Counsel adds a lot of extra functionality & integraion to ivy-mode
(defun my/counsel-find-file (&optional initial-input initial-directory)
(interactive)
(let ((default-directory (or initial-directory default-directory)))
(if (file-remote-p default-directory)
(call-interactively 'find-file)
(counsel-find-file initial-input initial-directory))))
(defun my/kmacro-end-and-call-macro (arg &optional no-repeat)
"Prompt for macro selection when used with prefix"
(interactive "P")
(if arg
(let ((current-prefix-arg nil))
(counsel-kmacro))
(kmacro-end-and-call-macro no-repeat)))
(use-package counsel
:bind (("C-x C-m" . counsel-M-x)
("C-x m" . counsel-M-x)
("C-x e" . my/kmacro-end-and-call-macro)
("M-y" . counsel-yank-pop)
("C-x C-f" . my/counsel-find-file)
("C-x C-i" . counsel-semantic-or-imenu)
("C-x c p" . counsel-list-processes)
("M-?" . counsel-company)
("C-h f" . counsel-describe-function)
("C-h v" . counsel-describe-variable)
("C-h l" . counsel-find-library)
("C-h i" . counsel-info-lookup-symbol)
("C-h u" . counsel-unicode-char)
:map counsel-find-file-map
("C-l" . ivy-backward-delete-char))
:config
(setq ivy-initial-inputs-alist nil
counsel-preselect-current-file 't
counsel-yank-pop-separator "\n\n"
counsel-find-file-ignore-regexp "\\`\\(\\..*\\|__pycache__\\|.*\\.pyc\\|.*\\.o\\)\\'")
(defalias 'cpkg 'counsel-package)
;; Add copy, move, and delete commands to counsel-find-file
(defun given-file (command prompt)
"Run `command' with `prompt', `source', and `target'. `source' is set from `ivy' and `target' is set from prompting the user"
(apply-partially
'(lambda (command prompt source)
(let ((target (read-file-name (format "%s %s to:" prompt source))))
(funcall command source target 1)))
command prompt))
(defun my/kill-buffer-and-trash-file (file)
(when file
(kill-buffer (get-file-buffer file)))
(move-file-to-trash file))
(ivy-add-actions
'counsel-find-file
`(("c" ,(given-file #'copy-file "Copy") "cp")
("d" my/kill-buffer-and-trash-file "rm")
("m" ,(given-file #'rename-file "Move") "mv"))))
ivy-rich provides extra information to ivy buffers
(use-package ivy-rich
:config
(ivy-rich-mode)
(setq ivy-rich-parse-remote-buffer nil
ivy-rich-parse-remote-file-path nil))
Projectile mode is one the best packages Emacs have, more information is in this blog post.
(use-package projectile
:pin melpa-stable
:bind-keymap (("C-c p" . projectile-command-map)
("C-c C-p" . projectile-command-map))
:init
(setq projectile-known-projects-file (concat my/history-dir "projectile-bookmarks.eld"))
:config
(projectile-global-mode)
(setq projectile-enable-caching t
projectile-indexing-method 'hybrid
projectile-cache-file (concat my/history-dir "projectile.cache")
projectile-completion-system 'ivy
projectile-file-exists-remote-cache-expire nil
)
(setq projectile-ignored-project-function
(lambda (project)
(--any? (s-starts-with? (expand-file-name it) project)
'("~/.zprezto/modules/"
"/usr/loca/"
"~/.rbenv/"))))
(push "vendor" projectile-globally-ignored-directories)
(push "node_modules" projectile-globally-ignored-directories)
(push ".direnv" projectile-globally-ignored-directories)
;; Add prefix to project names in ~/Code/{pb,wf4g}.
(setq projectile-project-name-function
(lambda (path)
(let ((parent (f-filename (f-parent path)))
(project-name (f-filename path)))
(f-join parent project-name))))
(projectile-load-known-projects))
Add even more integration between Projectile and Ivy
(defun my/counsel-projectile-switch-project-action-dwim (project)
"Open magit as the default action when switching to a project"
(let ((projectile-switch-project-action
(lambda ()
(progn
(counsel-projectile-switch-project-action-vc project)
(counsel-projectile-switch-project-action-find-file project)))))
(counsel-projectile-switch-project-by-name project)))
(use-package counsel-projectile
:init
(setq counsel-projectile-remove-current-buffer t
counsel-projectile-remove-current-project t
projectile-switch-project-action #'counsel-projectile-find-file)
(counsel-projectile-mode)
:config
(counsel-projectile-modify-action
'counsel-projectile-switch-project-action
'((add ("O" my/counsel-projectile-switch-project-action-dwim "Open project in magit or find file"))
(default my/counsel-projectile-switch-project-action-dwim))))
Override counsel-rg
and counsel-projectile-rg
with my preference
(defun my/counsel-rg ()
"Search in the current directory"
(interactive)
(counsel-rg "" default-directory))
(defun get-ripgrep-type ()
"Returns the approriate ripgrep type for an extension"
(let ((extension (file-name-extension (buffer-file-name))))
(cond ((string= extension "vue")
"-t js")
(t
(concat "-t " extension)))))
(defun my/counsel-projectile-rg (arg)
"Called with two prefix arguments it prompts for `rg' arguments.
Called with one prefix arugment it searches for files with the same extension as the current buffer
Otherwise it passes its argument to `counsel-projectile-rg'"
(interactive "P")
(cond ((equal arg '(16))
(let ((current-prefix-arg '(4)))
(counsel-projectile-rg)))
((equal arg '(4))
()
(counsel-projectile-rg (get-ripgrep-type)))
(t
(counsel-projectile-rg arg))))
(bind-keys ("C-c s" . my/counsel-rg)
("C-c C-s" . my/counsel-rg)
:map projectile-command-map
("s" . my/counsel-projectile-rg))
Projectile Rails adds Rails integration to projectile.
(use-package projectile-rails
:commands (projectile-rails-on)
:config
(setq projectile-rails-font-lock-face-name 'font-lock-builtin-face
projectile-rails-stylesheet-re "\\.scss\\'")
(set-face-attribute 'projectile-rails-keyword-face nil
:inherit 'font-lock-builtin-face)
(--each '(ruby-mode-hook
web-mode-hook
yaml-mode-hook
scss-mode-hook
js2-mode-hook)
(add-hook it (lambda () (when (projectile-project-p) (projectile-rails-on))))))
Magit is the best interface to Git
(use-package magit
:commands (magit-status magit-file-dispatch)
:bind (("C-c <return>" . magit-status)
("C-c M-g" . magit-file-dispatch))
:config
(setq magit-push-always-verify nil
magit-use-sticky-arguments 'current
magit-bury-buffer-function 'magit-mode-quit-window
magit-section-cache-visibility nil
magit-revert-buffers 'silent
magit-diff-refine-hunk 't
magit-published-branches nil
magit-rebase-arguments '("--autostash")
magit-completing-read-function 'ivy-completing-read
magit-display-buffer-function #'magit-display-buffer-fullcolumn-most-v1
magit-status-sections-hook '(magit-insert-status-headers
magit-insert-merge-log
magit-insert-rebase-sequence
magit-insert-am-sequence
magit-insert-sequencer-sequence
magit-insert-bisect-output
magit-insert-bisect-rest
magit-insert-bisect-log
magit-insert-untracked-files
magit-insert-unstaged-changes
magit-insert-staged-changes
magit-insert-stashes
magit-insert-unpulled-from-upstream
magit-insert-unpulled-from-pushremote
magit-insert-unpushed-to-pushremote
magit-insert-unpushed-to-upstream
magit-insert-recent-commits))
;; Set the initial visibility of magit sections
(setq magit-section-initial-visibility-alist '((stashes . show)
(untracked . show)
(unpushed . show)
(recent . show))
magit-section-visibility-indicator nil)
(use-package magit-popup :pin melpa-stable)
(use-package magit-section :pin melpa-stable)
(use-package git-commit
:config
(add-to-list 'git-commit-setup-hook 'git-commit-turn-on-flyspell))
(defun my/parse-repo-url (url)
"convert a git remote location as a HTTP URL"
(if (string-match "^http" url)
url
(replace-regexp-in-string "\\(.*\\)@\\(.*\\):\\(.*\\)\\(\\.git?\\)"
"https://\\2/\\3"
url)))
(defun my/magit-open-repo ()
"open remote repo URL"
(interactive)
(let ((url (magit-get "remote" "origin" "url")))
(browse-url (my/parse-repo-url url))))
;; (magit-diff-visit-file FILE &optional OTHER-WINDOW FORCE-WORKTREE DISPLAY-FN)
(defun my/magit-visit-file ()
"visit file in other window without leaving magit status"
(interactive)
(let ((file (magit-file-at-point))
(magit-buffer (car (seq-filter (apply-partially #'string-match-p "^magit:")
(mapcar #'buffer-name (buffer-list))))))
(magit-diff-visit-file-other-window file)
(when magit-buffer
(select-window (get-buffer-window magit-buffer)))))
(bind-keys :map magit-status-mode-map
("I" . my/magit-open-repo)
("SPC" . my/magit-visit-file)
:map magit-blob-mode-map
("RET" . magit-show-commit)))
Browse current commit or file at its remote repo.
(use-package browse-at-remote)
Integrating Emacs with direnv allows us to put project specific environment variables in .envrc
(use-package direnv
:defer 1
:config (direnv-mode))
Git Time Machine allows you to step through your changes like a time machine.
(use-package git-timemachine
:commands git-timemachine)
Github Clone allows you to clone or fork a repo on Github without leaving Emacs.
(use-package github-clone
:commands github-clone)
Using gitignore templates
(use-package gitignore-templates
:commands (gitignore-template-insert))
Configuration to modes that are run by a keybinding or from M-x
.
Multiple Cursors, as the name suggest, allows editing over multiple lines
(use-package multiple-cursors
:bind (("C-c SPC" . mc/edit-lines)
("M-]" . mc/mark-next-like-this)
("M-[" . mc/mark-previous-like-this)
("M-}" . mc/unmark-next-like-this)
("M-{" . mc/unmark-previous-like-this))
:config
(unbind-key "<return>" mc/keymap)
(setq mc/list-file (concat my/history-dir "mc-lists.el"))
(set-face-attribute 'mc/cursor-bar-face nil
:foreground nil
:background "#022B35"
:inverse-video nil
:extend 't))
Iedit lets you mark all occurrences of a word to edit them at the same time.
(use-package iedit
:commands iedit-mode
:bind ("C-;" . iedit-mode)
:custom
(iedit-auto-save-occurrence-in-kill-ring nil))
Makes you able to move line or region up or down
(use-package move-text
:init
(move-text-default-bindings))
(use-package expand-region
:bind (("M-2" . er/expand-region)))
Avy lets you jump to things.
(use-package avy
:commands avy-goto-char-timer
:bind ("C-r" . avy-goto-word-1)
:config
;; Displays the full of the match `af' instead of `a' then `f'.
(setq avy-style 'de-bruijn
avy-all-windows nil
avy-all-window-alt 't)
(avy-setup-default))
Visual Regexp is a replacement for query-regexp-replace
(use-package visual-regexp
:commands qrr
:config
(defalias 'qrr 'vr/query-replace))
Embrace mode makes surrounding words with pairs so easy
(use-package embrace
:bind ("C-'" . embrace-change))
Paradox is a better alternative to package-list-packages
(use-package paradox
:commands pkg
:config
(setq paradox-automatically-star t
paradox-execute-asynchronously t
paradox-lines-per-entry 1
paradox-github-token t)
(paradox-enable)
(defalias 'pkg 'list-packages))
REST Client help explore HTTP REST webservices.
(use-package restclient
:mode ("\\.restclient" . restclient-mode)
:commands restclient-mode
:config
(unbind-key "C-c C-p" restclient-mode-map))
Which Key displays available keybindings in a popup
(use-package which-key
:config
(which-key-setup-side-window-bottom)
(which-key-mode))
Evil Numbers makes incrementing and decrementing number easy.
(use-package evil-numbers
:commands (evil-numbers/inc-at-pt evil-numbers/dec-at-pt)
:bind (("M-=" . evil-numbers/inc-at-pt)
("M--" . evil-numbers/dec-at-pt)))
ESUP is an Emacs startup profiler.
(use-package esup
:commands esup)
It’s a better replacement for the just-one-space
command
(use-package shrink-whitespace
:commands shrink-whitespace
:bind ("M-\\" . shrink-whitespace))
wgrep makes refactoring easier
(use-package wgrep-ag)
dumb-jump is a simple jump-to command
(defun my/dumb-jump (arg)
(interactive "P")
(if arg
(dumb-jump-go-other-window)
(dumb-jump-go)))
(use-package dumb-jump
:bind (("M-g j" . my/dumb-jump)
("M-g b" . dumb-jump-back)
("M-g l" . dumb-jump-quick-look))
:config
(setq dumb-jump-selector 'ivy
dumb-jump-force-searcher 'rg))
Allows you edit region in a separate buffer
(use-package edit-indirect
:bind (("C-c C-'" . edit-indirect-region)
("C-c '" . edit-indirect-region)))
Configuration to modes that are associated with file extensions.
(use-package text-mode
:preface (provide 'text-mode)
:ensure nil
:mode ("\\.txt\\'" "\\.text\\'")
:config
(add-hook 'text-mode-hook
(lambda ()
(turn-on-flyspell)
(setq-local word-wrap t))))
General org-mode configuration
(use-package org
:mode ("\\.org\\'" . org-mode)
:bind (:map org-mode-map
("M-p" . org-move-subtree-up)
("M-n" . org-move-subtree-down)
("C-c C-p" . nil))
:config
(setq org-log-done t
org-adapt-indentation nil
org-fontify-whole-heading-line t
org-pretty-entities t
org-use-sub-superscripts nil
org-goto-interface 'outline
org-goto-max-level 10
org-imenu-depth 5
org-src-fontify-natively t
org-src-tab-acts-natively nil
org-src-window-setup 'current-window
org-edit-src-content-indentation 0
org-startup-folded nil)
(add-to-list 'org-structure-template-alist
'("se" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
(add-hook 'org-mode-hook
(lambda ()
(toggle-truncate-lines)))
;; Allow org to run shell commands in source blocks
(org-babel-do-load-languages 'org-babel-load-languages
'((shell . t)
(emacs-lisp . t)))
(set-face-attribute 'org-block nil :background "#3f3f3f" :extend 't)
(--each '(org-document-title
org-level-1
org-level-2
org-level-3
org-level-4
org-level-5
org-level-6
org-level-7
org-level-8)
(set-face-attribute it nil :font "Raleway" :height 180 :weight 'bold :extend 't)))
When I’m editing org documents, sometimes I like to narrow to an org-mode section and use Next Section and Previous Section to move between the sections. (taken from this reddit comment)
(defun my/org-next ()
(interactive)
(when (buffer-narrowed-p)
(beginning-of-buffer)
(widen)
(org-forward-heading-same-level 1))
(org-narrow-to-subtree))
(defun my/org-previous ()
(interactive)
(when (buffer-narrowed-p)
(beginning-of-buffer)
(widen)
(org-backward-heading-same-level 1))
(org-narrow-to-subtree))
(bind-keys :map org-mode-map
("C-x t n" . my/org-next)
("C-x t p" . my/org-previous))
Exit org source edit and save the buffer
(defun my/org-edit-src-exit-and-save ()
(interactive)
(org-edit-src-exit)
(save-buffer))
(bind-keys :map org-src-mode-map
("C-x C-s" . my/org-edit-src-exit-and-save))
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(aggressive-indent-mode)
(turn-on-eldoc-mode)))
Sometimes I use elisp as a calculator, this evaluates the current s-expression and
if universal-argument
is supplied it replaces it s-expression with its result.
(defun eval-and-replace ()
"Replace the preceding sexp with its value."
(interactive)
(backward-kill-sexp)
(condition-case nil
(prin1 (eval (read (current-kill 0)))
(current-buffer))
(error (message "Invalid expression")
(insert (current-kill 0)))))
(defun eval-dwim (args)
"If invoked with C-u then evaluate and replace the current expression, otherwise use regular `eval-last-sexp'"
(interactive "P")
(if args
(eval-and-replace)
(eval-last-sexp nil)))
(bind-keys :map emacs-lisp-mode-map
("C-x C-e" . eval-dwim))
(use-package ruby-mode
:mode "\\.rb\\'"
:interpreter "ruby"
:bind (:map ruby-mode-map
("<return>" . reindent-then-newline-and-indent))
:config
(setq ruby-indent-level 2
ruby-insert-encoding-magic-comment nil)
;; Highlight `&&' and `||' as a builtin ruby keywords
(font-lock-add-keywords 'ruby-mode
'(("\\(&&\\|||\\)" . font-lock-builtin-face)))
(use-package inf-ruby
:commands (ruby-send-block-and-go ruby-send-region-and-go)
:config
(setq inf-ruby-default-implementation "pry")
(add-hook 'ruby-mode-hook #'inf-ruby-minor-mode))
(use-package rake
:commands rake
:config
(setq rake-cache-file (concat my/history-dir "rake.cache")
rake-completion-system 'ivy-read))
(use-package rspec-mode
:bind-keymap ("C-c C-," . rspec-mode-keymap)
:config
(setq rspec-compilation-skip-threshold 2
rspec-snippets-fg-syntax 'concise
rspec-use-spring-when-possible t
rspec-use-bundler-when-possible t
compilation-scroll-output t)
(rspec-install-snippets)
(add-hook 'rspec-compilation-mode-hook (lambda () (setq-local truncate-lines nil))))
(use-package bundler
:commands bundle-install)
(use-package rubocop
:commands (rubocop-check-project rubocop-check-current-file)
:bind (("C-c r <" . my/rubocop-check-project)
("C-c r , " . my/rubocop-check-current-file))))
Override rubocop
functions so they automatically switch to the compilation buffer
(defun my/rubocop-check-current-file ()
(interactive)
(rubocop-check-current-file)
(popwin:select-popup-window))
(defun my/rubocop-check-project ()
(interactive)
(rubocop-check-project)
(popwin:select-popup-window))
rcodetools provide a way to evaulate ruby code inside your buffer. The
way it works is you add # =>
after an expression and then run xmp
command and it will insert the result after the comment.
For more information.
(use-package rcodetools
:ensure nil
:commands xmp
:bind (:map ruby-mode-map ("C-c C-c" . xmp)))
;; (defadvice my/comment-dwim (around rct-hack activate)
;; "If comment-dwim is successively called, add => mark."
;; (if (and (eq major-mode 'ruby-mode)
;; (eq last-command 'my/comment-dwim))
;; (progn (insert "=>")
;; (xmp))
;; ad-do-it))
(use-package js2-mode
:mode ("\\.[m]?js\\'" "\\.cjs\\'")
:config
(setq js2-mode-show-parse-errors nil
js2-mode-show-strict-warnings nil
js2-mode-assume-strict t
js2-basic-offset 2
js2-bounce-indent-p t
js-switch-indent-offset 2)
;; Highlight `require()' as a buildtin javascript keyword
(font-lock-add-keywords 'js2-mode
'(("\\(require\\)(.*)" . (1 font-lock-keyword-face))))
(set-face-attribute 'js2-function-param nil
:foreground nil
:inherit 'font-lock-constant-face
:extend 't)
(use-package js-comint))
Set the built-in js-mode’s indentation
(setq js-indent-level 2)
Add rjsx-mode to handle jsx files
(use-package rjsx-mode
:mode "\\.jsx\\'"
:config
(flycheck-add-mode 'javascript-eslint 'rjsx-mode))
(use-package json-mode
:mode "\\.json$"
:config
(setq json-reformat:indent-width 2)
(unbind-key "C-c C-p" json-mode-map)
(add-hook 'json-mode-hook
(lambda ()
(show-smartparens-mode -1))))
(use-package python
:bind (("RET" . newline-and-indent)
("M-n" . python-nav-forward-block)
("M-p" . python-nav-backward-block))
:config
(unbind-key "C-c C-p" python-mode-map)
;; Run pylint after flake8
(flycheck-add-next-checker 'python-flake8 'python-pylint)
;; Remove `:' from `electric-indent-chars'
(add-hook 'python-mode-hook
(lambda ()
(setq electric-indent-chars (remq ?: electric-indent-chars))
(set (make-local-variable 'comment-inline-offset) 2))))
(use-package web-mode
:pin melpa-stable
:bind (("C-c C-e C-r" . web-mode-element-rename))
:mode ("\\.html$" "\\.xml$" "\\.erb$" "\\.vue$" "\\.svg$" "\\.ts$" "\\.php$")
:config
(setq web-mode-enable-current-element-highlight 't
web-mode-css-indent-offset 2
web-mode-markup-indent-offset 2
web-mode-code-indent-offset 2
web-mode-auto-close-style 2
web-mode-enable-auto-quoting nil
web-mode-enable-auto-pairing nil
web-mode-enable-auto-indentation nil
web-mode-script-padding 0
web-mode-prettify-symbols-alist nil
web-mode-style-padding 0)
(add-hook 'web-mode-hook #'aggressive-indent-mode)
;; Don't let web-mode align JS methods call
(setq web-mode-indentation-params (remove '("lineup-calls" . t) web-mode-indentation-params))
(setq-default web-mode-comment-formats
'(("java" . "/*")
("javascript" . "//")
("php" . "/*")))
;; Set web-mode engin in django projects
(add-hook 'web-mode-hook (lambda ()
(if (and (projectile-project-p)
(file-exists-p (concat (projectile-project-root) "apps/manage.py")))
(web-mode-set-engine "django"))))
(set-face-attribute 'web-mode-block-face nil
:foreground nil
:background nil
:inherit 'hl-line
:extend 't)
(set-face-attribute 'web-mode-current-element-highlight-face nil
:foreground nil
:inherit 'sp-show-pair-match-face
:extend 't)
(set-face-attribute 'web-mode-block-control-face nil
:foreground nil
:inherit 'font-lock-keyword-face
:extend 't)
(set-face-attribute 'web-mode-block-delimiter-face nil
:foreground nil
:inherit 'rainbow-delimiters-depth-1-face
:extend 't)
(set-face-attribute 'web-mode-html-attr-engine-face nil
:foreground "#94BFF3"
:weight 'bold
:extend 't))
(use-package css-mode
:ensure nil
:mode "\\'.css\\'"
:config
(setq css-indent-offset 2)
(add-hook 'css-mode-hook
(lambda () (subword-mode -1))))
(use-package scala-mode
:mode "\\.scala$")
(use-package yaml-mode
:mode "\\.yaml\\'"
:bind (:map yaml-mode-map
("<return>" . newline-and-indent))
:config
(add-hook 'yaml-mode-hook #'turn-off-flyspell)
;; Fix yaml newline which was broken by https://github.com/yoshiki/yaml-mode/commit/ac21293ee6d66bb04e0fc6ebc332ee038a5a3824
(add-hook 'yaml-mode-hook
(lambda ()
(setq yaml-nested-map-re ".*: *\\(?:&.*\\|{ *\\|!!?[^
]+ *\\)?$"))))
(use-package lua-mode
:mode "\\.lua\\'"
:config
(setq lua-indent-level 2))
(use-package markdown-mode
:mode ("\\.md\\'" "\\.markdown\\'")
:config
(setq markdown-fontify-code-blocks-natively t
markdown-list-indent-width 2
markdown-command "kramdown")
(unbind-key "C-c C-p" markdown-mode-map)
(set-face-attribute 'markdown-bold-face nil
:weight 'bold
:inherit 'font-lock-builtin-face
:extend 't)
(set-face-attribute 'markdown-header-face nil
:font "Raleway"
:height 180
:weight 'bold
:extend 't)
(set-face-attribute 'markdown-pre-face nil
:inherit 'default
:extend 't))
(use-package sh-mode
:ensure nil
:mode ("\\.zsh\\'" "\\.gitignore\\'" "\\.envrc\\'" "\\.flowconfig\\'")
:interpreter "zsh"
:config
(unbind-key "C-c C-c" sh-mode-map)
(setq-default sh-basic-offset 2))
I use prezto and I want to associate zsh files without extension to sh-mode
(add-to-list 'magic-fallback-mode-alist
'((lambda () (and (buffer-file-name)
(s-match ".*prezto.*" (buffer-file-name))))
. sh-mode))
Associate systemd files with conf-mode
(use-package conf-mode
:mode ("\\(?:\\.service\\|\\.target\\|\\.path\\|\\.timer\\)\\'" . conf-unix-mode)
:config
;; Unbind C-c C-p because it conflicts with projectile
(unbind-key "C-c C-p" conf-mode-map))
(use-package haml-mode
:mode ("\\.haml\\'" "\\.haml\\.erb\\'")
:bind (:map haml-mode-map
("<return>" . newline-and-indent))
:config
(add-hook 'haml-mode-hook #'rspec-mode))
(use-package coffee-mode
:mode ("\\.coffee\\'" "\\.coffee\\.erb$")
:config
(setq coffee-compile-jump-to-error nil
coffee-tab-width 2)
(add-hook 'coffee-mode-hook #'rspec-mode))
(use-package sql
:commands sql-mode
:mode ("\\.sql\\'" . sql-mode)
:config
(add-hook 'sql-mode-hook
(lambda ()
(sqlind-minor-mode)
(setq tab-width 4)
(toggle-truncate-lines)))
)
(use-package sql-indent
:config
(defvar my/sql-indentation-offsets-alist
`((select-clause 0)
(insert-clause 0)
(delete-clause 0)
(update-clause 0)
(case-clause +)
(case-clause-item 0)
(in-select-clause +)
(select-join-condition +)
(in-block sqlind-use-anchor-indentation)
,@sqlind-default-indentation-offsets-alist))
(add-hook 'sqlind-minor-mode-hook
(lambda ()
(setq sqlind-basic-offset 4
sqlind-indentation-offsets-alist my/sql-indentation-offsets-alist)))
)
I edit jinja files with names like example.conf.j2
so I want Emacs to strip the .j2
extension and choose the proper major mode
(add-to-list 'auto-mode-alist '("\\.j2\\'" ignore t))
(use-package elm-mode
:mode "\\.elm\\'"
:bind (:map elm-mode-map
("<return>" . newline-and-indent)))
(use-package haskell-mode
:mode "\\.hs\\'")
(use-package swift-mode
:mode "\\.swift\\'"
:config
(setq swift-mode:basic-offset 2))
(use-package feature-mode
:mode "\\.feature\\'")
(use-package graphql-mode
:mode "\\.\\(graphql\\|gql\\)$")
(use-package dockerfile-mode
:mode "Dockerfile")
(use-package go-mode
:mode "\\.go\\'"
:config
(unbind-key "C-c C-d" go-mode-map)
(add-hook 'before-save-hook
(lambda ()
(when (string= major-mode "go-mode")
(gofmt-before-save)))))
Override the go-goto-*
methods to push to the mark ring before jumping
(defun my/go-goto-arguments ()
(interactive)
(push-mark)
(go-goto-arguments))
(defun my/go-goto-docstring ()
(interactive)
(push-mark)
(go-goto-docstring))
(defun my/go-goto-imports ()
(interactive)
(push-mark)
(go-goto-imports))
(defun my/go-goto-function ()
(interactive)
(push-mark)
(go-goto-function))
(defun my/go-goto-function-name ()
(interactive)
(push-mark)
(go-goto-function-name))
(defun my/go-goto-return-value ()
(interactive)
(push-mark)
(go-goto-return-values))
(defun my/go-goto-method-receiver ()
(interactive)
(push-mark)
(go-goto-method-receiver))
(add-hook 'go-mode-hook
(lambda ()
(bind-keys :map go-goto-map
("a" . my/go-goto-arguments)
("d" . my/go-goto-docstring)
("f" . my/go-goto-function)
("i" . my/go-goto-imports)
("m" . my/go-goto-method-receiver)
("n" . my/go-goto-function-name)
("r" . my/go-goto-return-value))))
(use-package rust-mode
:hook ((rust-mode . flycheck-rust-setup))
:config
;; Turn off the rust prettify symbols
(setq rust-prettify-symbols-alist '())
;; Fix flycheck
(use-package flycheck-rust))
(use-package toml-mode
:mode "\\.toml\\'")
(use-package clojure-mode
:mode "\\.clj\\'"
:config
(add-hook 'clojure-mode-hook #'turn-on-eldoc-mode)
(add-hook 'clojure-mode-hook #'aggressive-indent-mode)
(use-package cider
:pin melpa-stable
:bind (:map cider-mode-map
("C-." . cider-find-var)
("C-," . cider-pop-back))
:config
(unbind-key "M-." cider-mode-map)
(unbind-key "M-," cider-mode-map)))
Emacs IPython Notebook mode makes working with jupyter easier
(use-package ein
:mode "\\.ipynb\\'"
:config
(add-hook 'ein:notebook-mode-hook
(lambda ()
(unbind-key "M-," ein:notebook-mode-map)
(unbind-key "M-." ein:notebook-mode-map))))
(use-package yang-mode
:mode "\\.yang\\'"
:config
(unbind-key "C-c C-p" yang-mode-map))
(use-package cc-mode
:mode ("\\.p4\\'" . c++-mode) ; Use cc-mode for P4 files
:config
(unbind-key "C-c C-p" c-mode-map)
(unbind-key "C-c C-d" c-mode-map)
(setq c-basic-offset 4)
(add-hook 'c-mode-common-hook
(lambda ()
(c-set-offset 'brace-list-intro '+)
(c-set-offset 'arglist-intro '++)
(c-set-offset 'arglist-cont-nonempty '++)
(c-set-offset 'inextern-lang 0)
(setq sp-escape-quotes-after-insert nil)
(semantic-mode)))
(add-hook 'c++-mode-hook
(lambda ()
(setq flycheck-clang-language-standard "c++11"
flycheck-clang-pedantic 't)
(when (my/current-buffer-is-a "p4")
(electric-indent-mode nil))))
;; Workaround semantic and company issue
(--each '(semantic-analyze-completion-at-point-function
semantic-analyze-notc-completion-at-point-function
semantic-analyze-nolongprefix-completion-at-point-function)
(remove-hook 'completion-at-point-functions it))
(use-package uncrustify-mode
:bind ("C-c C-f" . uncrustify-buffer)))
(use-package protobuf-mode
:mode "\\.proto\\'"
:config
(c-add-style "my/protobuf-style"
'((c-basic-offset . 2)))
(add-hook 'protobuf-mode-hook
(lambda () (c-set-style "my/protobuf-style"))))
PO mode is used to edit and manipulate gettext
translation files
(use-package po-mode
:config
(add-hook 'po-subedit-mode-hook (lambda ()
(activate-input-method "arabic-mac"))))
Miscellaneous and extra functionality. The dump of my little functions.
This I took from somewhere, it insert a space if I do M-return
between bracket or
parentheses, etc.
(defun my/newline-dwim ()
(interactive)
(let ((break-open-pair (or (and (looking-back "{ ?") (looking-at " ?}"))
(and (looking-back ">") (looking-at "<"))
(and (looking-back "(") (looking-at ")"))
(and (looking-back "\\[") (looking-at "\\]")))))
(newline)
(when break-open-pair
(save-excursion
(newline)
(indent-for-tab-command)))
(indent-for-tab-command)))
(bind-keys ("M-<return>" . my/newline-dwim))
Better comments, taken from here.
(defun my/comment-dwim (&optional arg)
"Replacement for the comment-dwim command.
If no region is selected and current line is not blank and we are not at the end of the line, then comment current line.
Replaces default behaviour of comment-dwim, when it inserts comment at the end of the line."
(interactive "*P")
(comment-normalize-vars)
(if (and (not (region-active-p))
(not (looking-at "[ \t]*$")))
(comment-or-uncomment-region (line-beginning-position) (line-end-position))
(comment-dwim arg)))
(bind-keys ("M-;" . my/comment-dwim))
(defun my/duplicate-line (&optional args)
"duplicate the current line and while saving the current position"
(interactive "P")
(let ((column (current-column))
(times (prefix-numeric-value args)))
(-dotimes times
(lambda (_)
(move-beginning-of-line 1)
(kill-line)
(yank)
(open-line 1)
(next-line 1)
(yank)
(move-beginning-of-line 1)
(move-to-column column)))))
(bind-keys ("C-x C-y" . my/duplicate-line))
(defun my/flip-colons ()
"Move colon `:' to beginning of the world if it's at the end or vice versa"
(interactive)
(let ((word (thing-at-point 'sexp))
(bounds (bounds-of-thing-at-point 'sexp)))
(when (or (s-starts-with-p ":" word)
(s-ends-with-p ":" word))
(delete-region (car bounds) (cdr bounds))
(if (s-starts-with-p ":" word)
(insert (s-append ":" (s-chop-prefix ":" word)))
(insert (s-prepend ":" (s-chop-suffix ":" word)))))))
(bind-keys ("C-:" . my/flip-colons))
Toggle inline rule into multiline, for example:
// from this
h1 { font-size: 30px }
// into this
h1 {
font-size: 30px;
}
(defun my/delete-or-insert-newline ()
(if (looking-at "\n")
(progn
(delete-char 1)
(just-one-space))
(insert "\n")))
(defun my/toggle-brace ()
(interactive)
(let (start)
(save-excursion
(while (not (looking-back "{")) (backward-char))
(setq start (point))
(my/delete-or-insert-newline)
(while (not (looking-at "\n? *}")) (forward-char))
(my/delete-or-insert-newline)
(indent-region start (line-end-position)))))
(bind-key "C-x t [" 'my/toggle-brace)
This is useful when you want to paste sensitive information and do not want it to stay in the kill-ring
variable. Like pasting a password to tramp
.
(defun yank-and-remove-from-killring ()
(interactive)
(yank)
(setq kill-ring
(remove (first kill-ring) kill-ring)))
(bind-keys ("C-M-y" . yank-and-remove-from-killring))
بعض الأحيان أحتاج أمـــــــــــد بعض الكلمات
(defun my/insert-tatweel (arg)
(interactive "P")
(insert-char #x0640 arg))
(bind-keys ("C-x t _" . my/insert-tatweel))
Taken from Magnars’ Emacs
(defun indent-buffer ()
(interactive)
(indent-region (point-min) (point-max)))
(bind-keys ("C-c TAB" . indent-buffer))
Taken from Magnars’ Emacs
(defun cleanup-buffer ()
"Perform a bunch of operations on the whitespace content of a buffer.
Including indent-buffer, which should not be called automatically on save."
(interactive)
(untabify-buffer)
(delete-trailing-whitespace)
(indent-buffer))
Useful for quick calculations, based on this reddit post.
(defun my/calc-insert (arg)
"Look for two numbers with a symbol between them and calculate their expression and replace them with the result"
(interactive "p")
(let (start end)
(if (use-region-p)
(setq start (region-beginning)
end (region-end))
(save-excursion
(setq end (point))
(setq start (search-backward-regexp "[0-9]+ ?[-+*/^] ?[0-9]+"
(line-beginning-position) 1))))
(let ((value (calc-eval (buffer-substring-no-properties start end))))
(if (= arg 4)
(message value)
(delete-region start end)
(insert value)))))
(bind-key "C-=" 'my/calc-insert)
I use open-line
a lot and most of the time I have to manually indent the new line, lets fix this:
(defun my/open-line (prefix)
"Indent the new line after `open-line'"
(interactive "P")
(let ((beginning-of-line-p (= (point) (point-at-bol))))
(save-excursion
(if beginning-of-line-p
(newline)
(if prefix
(newline)
(newline-and-indent)))))
(indent-according-to-mode))
(bind-key "C-o" 'my/open-line)
If we are inside a project and have multiple buffers open then use projectile-switch-buffer
otherwise fallback to the normal switch-buffer
The my/counsel-projectile-switch-to-buffer
is exactly the same as the original one except that it pre-selects the second buffer instead of the first.
(defun my/switch-buffer-dwim (&optional prefix)
(interactive "P")
(if (and (null prefix)
(projectile-project-p)
(> (length (projectile-project-buffers)) 1))
(call-interactively 'counsel-projectile-switch-to-buffer)
(call-interactively 'ivy-switch-buffer)))
(bind-keys ("C-x b" . my/switch-buffer-dwim)
("C-x C-b" . my/switch-buffer-dwim))
Taken from this blog post makes renaming files less painful.
(defun my/rename-file-dwim ()
"Rename the current buffer and file it is visiting."
(interactive)
(let ((filename (buffer-file-name)))
(if (not (and filename (file-exists-p filename)))
(message "Buffer is not visiting a file!")
(let ((new-name (read-file-name "New name: " filename)))
(rename-file filename new-name t)
(set-visited-file-name new-name t t)))))
A helper for parsing timestamps (seconds or microseconds)
(defun my/totime (prefix)
(interactive "p")
(let ((thing (thing-at-point 'number))
(fmt "%Y/%m/%d %a %I:%M:%S.%3N %p")
(out (lambda (val)
(if (< prefix 4)
(message "%s" val)
(progn
(deactivate-mark)
(end-of-line)
(comment-dwim nil)
(insert val))))))
(if (> thing (+ -1 (expt 10 10)))
(funcall out (format-time-string fmt (seconds-to-time (/ thing (expt 10 6))) "Asia/Riyadh"))
(funcall out (format-time-string fmt (seconds-to-time thing) "Asia/Riyadh")))))
This file contains private information, like API keys, that I want to keep out of this repo.
(load "~/.emacs.secrets" t)