Vim to Emacs

6 minute read

I used vim for atleast 18 months

Lately (been around 20 days) I’ve been migrating to emacs.

Why?

  • I was getting a bit more serious with my investments in learning lisp and wanted a good environment that didn’t force me to use the mouse.
  • Also wanted to learn more of vim (was reading ‘learning vimscript the hard way’) but then Elisp pops up to be a much attractive choice than vimscript for a lot of reasons.
  • Wanted to do everything in one place without a lot of context switching (then : setup included vim, tmux and a lot of shell utilities)
    • everything … in one place : that is an attractive proposition

What is this?

A cache of indices pointing to resources, opinions on the same and some thoughts on my future with emacs.

I review my init.el along with some commentary: this summarizes my environment fairly quickly.

I use bleeding edge GCCEMACS 28.0.50 (native comp):

GNU Emacs 28.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.20, cairo version 1.16.0) of 2021-05-15

Basics

(require 'server)
(unless (server-running-p)
  (server-start))

(set-frame-font "CaskaydiaCove NF" nil t)

;;no, thank you
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
  • Running emacs as a daemon to allow for jumping in quickly with emacsclienct as the default editor.
  • don’t want any gui ugliness around : only the good parts
  • a good font that can handle all there is : ligatures, looks good, and the norm ..

Package management : straight

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

(straight-use-package 'use-package)
(setq straight-path "~/.emacs.d/straight/")
  • wanted to choose a approach that would stay the most relevant the longest rather than the default
  • nice integration with use-package and custom recipes
  • don’t like an interface to install things : the vim way was nice hence this

General : generalize basics

(use-package general
  :straight t
  :config
  (general-unbind "C-M-n" ; free up for org-roam *Notes*
    ))
  • convenience utility for hooks, keymaps and simulating keys

Dashboard

(use-package dashboard
  :straight t
  :config
  (dashboard-setup-startup-hook)
  (general-define-key
   "C-c h" (lambda () (interactive)(view-buffer "*dashboard*")))
  (setq
   initial-buffer-choice (lambda () (get-buffer "*dashboard*"))
   dashboard-center-content t
   dashboard-startup-banner 'logo
   ))
  • a nice welcoming dashboard with summaries of past sessions that evangelizes emacs

EVIl

(use-package evil
  :straight t
  :init
  (use-package undo-fu
    :straight t
    :config
    (general-define-key
     :states 'normal
     "u" #'undo-fu-only-undo
     "\C-r" #'undo-fu-only-redo)
    (setq evil-undo-system 'undo-fu))
  (setq
   evil-want-keybinding nil
   evil-want-integration t)
  :config 
  (evil-mode 1))


(use-package evil-collection
  :straight t
  :after evil
  :config
  (evil-collection-init))

(use-package evil-org
  :straight t
  :after org
  :config
  (general-add-hook 'org-mode
		    (list #'evil-org-mode))
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))


(use-package evil-surround
  :straight t
  :config
  (global-evil-surround-mode 1))
  • Not using emacs without this : modal editing is revolutionary
  • auxiliary packages to make the interop as smooth as possible

Helm

(use-package helm
  :straight t
  :config
  (general-define-key
   "M-x" #'helm-M-x
   "C-h a" #'helm-apropos
   "M-o" #'helm-occur
   "C-x C-f" #'helm-find-files)
  (helm-mode 1))
  • Powerful.
  • Everywhere.

Dired-x

(setq find-file-visit-truename t)
(general-add-hook 'dired-load-hook
		  (list (lambda ()
			  (load "dired-x")
			  ;; Set dired-x global variables here.  For example:
			  ;; (setq dired-guess-shell-gnutar "gtar")
			  ;; (setq dired-x-hands-off-my-keys nil)
			  )))
  • File-explorer a.k.a directory editor
  • augmented dired

Modeline

(use-package doom-modeline
  :straight t
  :init
  (use-package all-the-icons :straight t)
  (use-package minions :straight t)
  :config
  (setq doom-modeline-height 30)
  (setq doom-modeline-bar-width 3)
  (setq doom-modeline-window-width-limit fill-column)
  (setq doom-modeline-icon (display-graphic-p))
  (setq doom-modeline-major-mode-icon t)
  (setq doom-modeline-continuous-word-count-modes '(org-mode))
  (setq doom-modeline-modal-icon t)
  (setq doom-modeline-mu4e t)
  (setq doom-modeline-minor-modes (featurep 'minions))
  (general-add-hook 'after-init-hook
		    (list #'doom-modeline-mode
			  #'minions-mode)))
  • all at a quick glance

Relative Numbering

(use-package nlinum-relative
  :straight t
  :config
  (nlinum-relative-setup-evil)                    
  (general-add-hook 'prog-mode-hook
		    (list #'nlinum-relative-mode))
  (setq nlinum-relative-redisplay-delay 0)      
  (setq nlinum-relative-current-symbol "->")      
  (setq nlinum-relative-offset 0)) 
  • this was a pain to setup (compared to vim)
  • don’t know why this is not a default
  • taught me a little about what all the editor does that one might take for granted

Quick Edits

(defun edit-init ()
  (interactive)
  (message (concat "editing user-init-file @ " user-init-file))
  (find-file user-init-file))
(general-define-key
 :prefix "C-c"
 "e" #'edit-init)
  • my <space> ev port from .vimrc
  • convenience

Winner

(use-package winner
  :straight t
  :config
  (winner-mode 1)
  (general-define-key
   "<C-M-left>" #'winner-undo
   "<C-M-right>" #'winner-redo))
  • history of past 200 window configurations
  • pretty handy

Eyebrowse

(use-package eyebrowse
  :straight t
  :config
  (eyebrowse-mode t))
  • want vim-like tabs

Ibuffer

(general-define-key "C-c i" #'ibuffer)
  • amazing control over all the buffers
  • similarly use C-x c p for process management

ACE-JUMP

(use-package ace-jump-mode
  :straight t
  :config
  (general-define-key
   "C-M-j" 'ace-jump-mode))
  • jump anywhere in an expected number of 1 chord and 2 keystrokes (almost always)
  • not yet muscle-memorized, still use vim-like motions

WHICH-KEY

(use-package which-key
  :straight t
  :config
  (which-key-mode)
  (general-define-key "C-h C-k" #'which-key-show-top-level)
  (which-key-setup-side-window-bottom))
  • prompts all possible completions in the global operator pending mode
  • amazing for learning
  • will switch to hydra or transient soon
  • this + mini-modes that reduce the need for pinky penalizing chords

Browsing

(setq browse-url-browser-function 'eww-browse-url)
(setq shr-max-image-proportion 0.6)
  • good for quick documentation lookups

PDF INTEROP

(use-package pdf-tools
  :straight t
  :config
  (pdf-loader-install))
  • everything in emacs

MAGIT

(use-package magit :straight t)
  • Game changer
    • learned more about git in the past 10 days compared to the past 1 year
  • makes new functionalites ultra-obvious
  • this and org are enough to pull people into the church of emacs

ORG-OPS

(use-package org
  :straight t
  :config
  (general-define-key
   :prefix "C-c"
   "l" #'org-store-link
   "a" #'org-agenda
   "c" #'org-capture)
  (setq org-directory (file-truename "~/links/source/org")
	org-default-notes-file (concat org-directory "/gtd/GTD_HQ.org")
	org-startup-with-inline-images t
	org-startup-truncated nil)
  (general-add-hook 'org-mode-hook
		    (list #'toggle-word-wrap
			  #'nlinum-relative-mode))
  (defun update-org-latex-fragments ()
    (org-latex-preview '(64))
    (plist-put org-format-latex-options :scale (* 1.5 text-scale-mode-amount))
    (org-latex-preview '(16)))
  (general-add-hook 'text-scale-mode-hook
		    (list #'update-org-latex-fragments))
  (setq org-clock-persist 'history)
  (org-clock-persistence-insinuate)
  (general-define-key
   :prefix "C-c"
   "r" #'org-refile
   "C-x C-g" #'org-clock-goto))

(use-package org-bullets
  :straight t
  :config
  (general-add-hook
   'org-mode-hook (lambda () (org-bullets-mode 1))))

(defun +org/opened-buffer-files ()
  "Return the list of files currently opened in emacs"
  (delq nil
        (mapcar (lambda (x)
                  (if (and (buffer-file-name x)
                           (string-match "\\.org$"
                                         (buffer-file-name x)))
                      (buffer-file-name x)))
                (buffer-list))))

(setq org-refile-targets '((+org/opened-buffer-files :maxlevel . 3)))

(setq org-capture-templates
      '(("n" "Next Action" entry (file+headline "~/links/source/org/gtd/GTD_HQ.org" "NA")
         "* TODO %?\n  %i\n  %a")
	("e" "Event" entry (file+headline "~/links/source/org/gtd/GTD_HQ.org" "INQ")
         "* %?\nSCHEDULED: %T\n  %i")
        ("i" "IN" entry (file+headline "~/links/source/org/gtd/GTD_HQ.org" "INQ")
         "* %?\nEntered on %U\n  %i\n  %a")
	("t" "Tickler" entry (file+headline "~/links/source/org/gtd/GTD_HQ.org" "Tickler")
	 "* %?\nDEFER THOUGHT TO: %T\n %i"))) 
  • Getting things done in org
  • replaces Notion
  • Text, is all there is to it

GTD quick access

(defun gtd()
  "open the GTD workspace"
  (interactive)
  (let ((gtd-dir (concat org-directory "/gtd/GTD_HQ.org")))
    (message (concat "opening GTD workspace @ " gtd-dir))
    (find-file gtd-dir)))

(general-define-key
 :prefix "C-c"
 "g" #'gtd)
  • getting used to it
  • algorithmic productivity is nice

ORG-ROAM

(use-package org-roam
  :straight (org-roam :host github
		     :repo "org-roam/org-roam"
		     :branch "v2")
  :config
  (setq org-id-method 'ts)
  (setq org-roam-directory (file-truename "/mnt/c/Users/Raj Patil/source/org/org-roam/"))
  (setq org-roam-file-extensions '("org"))
  (org-roam-setup)
  (general-define-key
   :prefix "C-M-n"
   "f" #'org-roam-node-find
   "i" #'org-roam-node-insert
   "c" #'org-roam-capture
   "d s" #'org-roam-db-sync
   "t a" #'org-roam-tag-add
   "o" #'org-open-at-point-global
   "t d" #'org-roam-tag-remove
   "r" #'org-roam-buffer-toggle)
  (add-to-list 'display-buffer-alist
	       '(; org-roam buffer toggle config
		 (".org-roam.*"
		  (display-buffer-in-side-window)
		  (window-width . 0.25)
		  (side . left)
		  (slot 0)))))
  • replaces Obsidian
    • built upon the all-powerful fantastical Unicorn
  • use v2 hence org-roam server config is commented out as of now

Complete Anywhere

(use-package company
  :straight t
  :config
  (setq company-idle-delay 0.2)
  (setq company-minimum-prefix-length 2)
  (add-to-list 'company-backends 'company-capf)
  (setq company-ignore-case t)
  (global-company-mode 1))

(use-package company-box
  :straight t
  :config
  (general-add-hook 'company-mode-hook
		    (list 'company-box-mode)))

(use-package company-lsp
  :straight t
  :requires company
  :config
  (push 'company-lsp company-backends)
  ;;disable client side cache as LSP does is better
  (setq company-transformers nil
	company-lsp-async t
	company-lsp-cache-candidates nil))
  • completes anywhere

LSP : Language Server Protocol

(use-package lsp-mode
  :straight t
  :config
  (setq lsp-prefer-flymake nil)
  (general-define-key "C-M-l" (general-simulate-key "s-l")) ;; no super key
  (general-add-hook
   (list 'c++-mode-hook
	 'python-mode-hook
	 'racket-mode-hook)
   (list #'lsp))
  (general-add-hook
   'lsp-mode-hook
   (list #'lsp-enable-which-key-integration))
  (setq lsp-clients-clangd-args '("-j=4" "-background-index" "-log-error")))

;(use-package lsp-pyright
;  :straight t
;  :config
;  (general-add-hook 'python-mode #'(lambda () (lsp)) ))

(use-package lsp-racket
  :straight '(lsp-racket
	      :type git
	      :host github
	      :repo "mullikine/lsp-racket-el"))

(use-package dap-mode
  :straight t)

(use-package lsp-ui
  :straight t
  :requires lsp-mode flycheck
  :config
  (setq lsp-ui-doc-enable t
	lsp-ui-doc-use-childframe t
	lsp-ui-doc-position 'at-point
	lsp-ui-doc-include-signature t
	lsp-ui-sidline-enable t
	lsp-ui-flycheck-list-position 'right
	lsp-ui-flycheck-live-reporting t
	lsp-ui-peek-enable t
	lsp-ui-peek-enable t
	lsp-ui-peek-list-width 60
	lsp-ui-peek-peek-height 25)
  (general-add-hook 'lsp-mode-hook (list 'lsp-ui-mode)))
  • complexity reduction : multiplicative to additive
  • unified interface for all languages

REMOTE-OPS

(defun remote-shell-specifics ()
  (when (and (fboundp 'company-mode)
	     (file-remote-p default-directory))
    (company-mode -1)))
(general-add-hook 'shell-mode-hook #'remote-shell-specifics)

(use-package tramp
  :straight t
  :config
  (setq tramp-default-method "ssh"
	vc-ignore-dir-regexp (format "%s\\|%s"
				     vc-ignore-dir-regexp
				     tramp-file-name-regexp)
	tramp-verbose 4)

  (defun gpu_dgx_50.93 ()
    "ssh into the .50.93 DGX station"
    (interactive)
    (add-to-list 'tramp-remote-path "/raid/cs18btech11039/anaconda3/bin")
    (find-file "/ssh:cs18btech11039@192.168.50.93:/home/cs18btech11039"))

  ;;Remote python lsp tramp
  (lsp-register-client
   (make-lsp-client :new-connection (lsp-tramp-connection "pylsp")
		    :major-modes '(python-mode)
		    :remote? t
		    :server-id 'pylsp-remote))
  (general-define-key
   "C-c C-r C-a" #'gpu_dgx_50.93))
  • Tramp is amazing
  • Tramp is slow
    • still use vim + tmux for remote operations
    • will profile this sometime soon resume remote work in host emacs

What’s Left?

  • Mail (mu4e)
  • Window Manager (EXWM)
  • Eshell (yet to explore completely)
  • lots more

Leave a comment