Skip to content

a3ammar/emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

My Emacs Configuration As an Org File

This is is inspired by Sacha’s Emacs configuration.

Emacs Initialization

Setup the necessary things that should happen before anything else.

Setup Default Path Constants

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))

Set native compilation directory

(startup-redirect-eln-cache (concat my/history-dir "eln-cache/"))
(setcar native-comp-eln-load-path (concat my/history-dir "eln-cache/"))

Initialize Packages

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))

Setup use-package

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))

Setup Automatic Compilation

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))

Depend on Dash and S Libraries

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))

Pin Depencies to stable versions

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)))

Are We on OS X Helper

This function will help us determine if we are on OS X

(defun on-osx-p ()
  (eq system-type 'darwin))

Defaults Configuration

Change the default Emacs configuration, from default variables to keybindings.

Personal Information

Let Emacs know who is using it.

(setq user-full-name "Ammar Alammar"
      user-mail-address "ammar@ammasa.net")

Better Emacs Defaults

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)

Enable Every Disabled Command

I just don’t want to be prompted about disabled commands.

(setq disabled-command-function nil)

UTF-8 Everywhere Please

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)

Set the Default Language Environment Variable

This is important to set early on so Emacs initializes with the correct language.

(setenv "LANG" "en_US.UTF-8")

Set the Scratch Buffer’s Default Mode

(setq initial-major-mode 'org-mode)

And protect it from accidental killing

(with-current-buffer "*scratch*"
  (emacs-lock-mode 'kill))

Start Emacs Server

(when window-system (add-hook 'after-init-hook 'server-start t))

Add My Custom Libraries to Emacs Load Path

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))

File Backups

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)

Session History

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)

Visited Files History

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))

Bookmarks File

(setq bookmark-default-file (concat my/history-dir "bookmarks"))

Miscellaneous History Files

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/"))

Load Customization File

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)

Prevent Confirmation Prompt When Killing Process Buffers

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))

Use Spaces for Indentation

(setq-default indent-tabs-mode nil)

Set the Default Indentation Size

(setq-default tab-width 2)

Set the Default Fill Column

For wrapping text with M-q and auto-fill-mode

(setq-default fill-column 90)

Ensure Edited Files End with a New Line

In UNIX, a healthy file always ends with a new line.

(setq-default require-final-newline t)

Show the Current Column Position

Show the current column position in the mode line.

(column-number-mode)

Enable Subword 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

Sentence end with only one space.

(setq sentence-end-double-space nil)

Replace selection on typing

By default Emacs doesn’t change the content of the selection when you type or yank something. This fixes that.

(delete-selection-mode)

Use y And n for Confirmation

No one likes to type a full yes, y is enough as a confirmation.

(setq use-short-answers t)

Automatically Extract Compressed Files

Allow Emacs to extract compressed files and also compress them back after saving the file.

(auto-compression-mode)

Automatically Reload Files With Outside Changes

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)

Automatically Clean Files on Save

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))

Set the Cursor Look

I like my cursor to be a thin line.

(setq-default cursor-type 'bar)

Add Padding to the Window Edges

Add a one pixel padding to the edges of Emacs window.

(set-fringe-mode 1)

A Better Mode Line

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))

Zenburn Theme

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

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))

Prettify Symbols

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)

Highlight the Current Line

For easily identification of the current line.

(global-hl-line-mode)

Set the Default Font

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))

Proportional 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

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))

Eshell

(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))

Ediff

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)

Dired

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

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))

Abbrevs

Useful for defining expandable abbreviations

(setq save-abbrevs t
      abbrev-file-name (concat my/history-dir "abbrev_defs"))
(setq-default abbrev-mode t)

Ispell & Flyspell

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)))

Compilation Result

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)))

Emacs Calculator

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))

Emacs Client

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)))

View Mode

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")))))))

Imenu Mode

(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)

Hide Show Minor Mode

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)

Goto Address 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 default modes persistent files location

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"))

Change Emacs Keybinding

Unbinding

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")

Preferred Binding for Default Commands

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-<tab>" . indent-rigidly)
           ("C-x r w" . copy-rectangle-as-kill))

Window Movement

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))

Quick Switch to Previous Buffer

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)

Window Splitting

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))

OS Specific Configuration

These are configuration specific to OSs. Mostly OS X for now.

Use Dark Frames

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)))

Fix Emacs Environment Variables on OS X

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))

Set Command as Meta

(when (on-osx-p)
  (setq ns-alternate-modifier 'super
        ns-command-modifier 'meta
        ns-control-modifier 'control))

OS X Arabic Keybaord

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"))

Emoji Support 🌈

See my homebrew formula

(when (on-osx-p)
  (set-fontset-font t 'symbol (font-spec :family "Apple Color Emoji") nil 'prepend))

Trash Files Instead of Deleting Them

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))

Switch Back to Terminal After Emacs Client Exit

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

Emacs GUI Default Configuration

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)

Global Modes

Configuration for modes that are always running no matter which buffer we are in.

Smartparens

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

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))

Language Server Mode

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

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

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 Sractch

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

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))

Aggressive Indent

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

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

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))

Vundo

;; (use-package vundo
;;   :bind ("C-M-_" . vundo)
;;   :config
;;   (setq vundo-roll-back-on-quit nil)
;;   )

Smex

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")))

Project Management & Navigation

Modes and configuration specific to managing and navigating projects.

Ivy - Interactive Completion

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

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

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

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 at Remote

Browse current commit or file at its remote repo.

(use-package browse-at-remote)

Dirnev — Load Project Specific Environment Variables

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

Git Time Machine allows you to step through your changes like a time machine.

(use-package git-timemachine
  :commands git-timemachine)

Clone Github Projects From Emacs

Github Clone allows you to clone or fork a repo on Github without leaving Emacs.

(use-package github-clone
  :commands github-clone)

Insert GitHub gitignore templates

Using gitignore templates

(use-package gitignore-templates
  :commands (gitignore-template-insert))

Invokable Modes

Configuration to modes that are run by a keybinding or from M-x.

Multiple Cursors

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

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))

Move Text Mode

Makes you able to move line or region up or down

(use-package move-text
  :init
  (move-text-default-bindings))

Expand Region

(use-package expand-region
  :bind (("M-2" . er/expand-region)))

Avy

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

Visual Regexp is a replacement for query-regexp-replace

(use-package visual-regexp
  :commands qrr
  :config
  (defalias 'qrr 'vr/query-replace))

Embrace

Embrace mode makes surrounding words with pairs so easy

(use-package embrace
  :bind ("C-'" . embrace-change))

Paradox

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

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 Mode

Which Key displays available keybindings in a popup

(use-package which-key
  :config
  (which-key-setup-side-window-bottom)
  (which-key-mode))

Easy Increment & Decrement Numbers

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 - Emacs Start Up Profiler

ESUP is an Emacs startup profiler.

(use-package esup
  :commands esup)

Shrink Whitespace

It’s a better replacement for the just-one-space command

(use-package shrink-whitespace
  :commands shrink-whitespace
  :bind ("M-\\" . shrink-whitespace))

Writable Grep

wgrep makes refactoring easier

(use-package wgrep-ag)

Dumb Jump

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))

Edit Indirect

Allows you edit region in a separate buffer

(use-package edit-indirect
  :bind (("C-c C-'" . edit-indirect-region)
         ("C-c '" . edit-indirect-region)))

Associative Modes

Configuration to modes that are associated with file extensions.

Text Mode

(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))))

Org Mode

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))

Emacs Lisp Mode

(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))

Ruby Mode

(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))

Javascript Mode

(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))

JSON 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))))

Python Mode

(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))))

Web Mode

(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))

CSS Mode

(use-package css-mode
  :ensure nil
  :mode "\\'.css\\'"
  :config
  (setq css-indent-offset 2)

  (add-hook 'css-mode-hook
            (lambda () (subword-mode -1))))

Scala Mode

(use-package scala-mode
  :mode "\\.scala$")

YAML Mode

(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 ".*: *\\(?:&.*\\|{ *\\|!!?[^
]+ *\\)?$"))))

Lua Mode

(use-package lua-mode
  :mode "\\.lua\\'"
  :config
  (setq lua-indent-level 2))

Markdown Mode

(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))

Shell Conf Mode

(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))

Conf 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))

HAML Mode

(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))

Coffeescript 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))

SQL 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)))
  )

Jinja Mode

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))

Elm mode

(use-package elm-mode
  :mode "\\.elm\\'"
  :bind (:map elm-mode-map
              ("<return>" . newline-and-indent)))

Haskell Mode

(use-package haskell-mode
  :mode "\\.hs\\'")

Swift Mode

(use-package swift-mode
  :mode "\\.swift\\'"
  :config
  (setq swift-mode:basic-offset 2))

Feature Mode

(use-package feature-mode
  :mode "\\.feature\\'")

GraphQL Mode

(use-package graphql-mode
  :mode "\\.\\(graphql\\|gql\\)$")

Dockerfile Mode

(use-package dockerfile-mode
  :mode "Dockerfile")

Go Mode

(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))))

Rust Mode

(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))

TOML Mode

(use-package toml-mode
  :mode "\\.toml\\'")

Clojure Mode

(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)))

EIN mode

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))))

Yang Mode

(use-package yang-mode
  :mode "\\.yang\\'"
  :config
  (unbind-key "C-c C-p" yang-mode-map))

CC Mode

(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)))

Protobuf Mode

(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

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"))))

Extra Functionality

Miscellaneous and extra functionality. The dump of my little functions.

Newline Do What I Mean

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))

Comment Do What I Mean

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))

Duplicate Line

(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))

Flip Colon

(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))

Expand Inline Braces to Multiline

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)

Yank and Remove From History

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))

Insert Arabic Tatweel Character

بعض الأحيان أحتاج أمـــــــــــد بعض الكلمات

(defun my/insert-tatweel (arg)
  (interactive "P")
  (insert-char #x0640 arg))

(bind-keys ("C-x t _" . my/insert-tatweel))

Indent the Buffer

Taken from Magnars’ Emacs

(defun indent-buffer ()
  (interactive)
  (indent-region (point-min) (point-max)))

(bind-keys ("C-c TAB" . indent-buffer))

Clean the 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))

Calculate Expression and Insert It

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)

Open Line and Indent

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)

Switch Buffer — Do What I Mean

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))

Rename File — Do What I Mean

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)))))

Parse Timestamp

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")))))

Load Private Information

This file contains private information, like API keys, that I want to keep out of this repo.

(load "~/.emacs.secrets" t)

Releases

No releases published

Packages

No packages published