Let’s get started with the configuration! This format is inspired by Emacs from Scratch Configuration and Patrick Thomson’s org configuration.
(setq lexical-binding t)
First, I want to load private variables that are not checked into source control. I provide defaults so that I don’t throw any errors during usage later. By convention I’m storing my private configuration inside of a `private` directory inside of my emacs directory.
(defvar es/github-team-members []
"Individuals on my team. I avoid checking these files in to
source control so that I don't expose anything potentially
private.")
(add-to-list 'load-path (file-name-concat user-emacs-directory "private"))
(condition-case err
(load "private-variables")
((file-error)
(message "Initialization Error: %S" err)
(yes-or-no-p "Could not find private-variables. Just a warning.")))
Not sure if I should be using both this and Counsel+Ivy+Swiper
;; TODO: Figure out why this doesn't seem to load company-snippets
(use-package company
:diminish (company-mode . "\u24B8") ;; Circled C
:ensure t
:config
(setq company-dabbrev-downcase nil)
(setq company-minimum-prefix-length 3)
(setq company-tooltip-margin 1)
(setq company-tooltip-minimum-width 40)
(global-company-mode)
:bind
(("C-'" . company-complete)))
I should abandon this in favor of custom transients
(use-package major-mode-hydra
:ensure t
:bind
("C-M-m" . major-mode-hydra))
I’ve made some customizations to the status buffer for this mode. In order to reap these benefits you need to use magit forge. First, you need to configure the mode by generating a token (on github at least).
Next, if you would like to see pull requests scoped to your team you need to configure `es/github-team-members`. This vector is empty by default and I `setq` the values in an elisp file named `private-variables` which is not checked into source control.
I replace the default show all pull requests with `es/forge-insert-team-pullreqs`. Conceptually, this could be configured as an alist/plist that uses specific individuals by forge name.
(use-package magit
:ensure t
:diminish ((magit-mode . "") (magit-status-mode . ""))
:bind (("C-c g" . magit-status)
:map magit-status-mode-map
("q" . magit-quit-session))
:config
(defadvice magit-status (around magit-fullscreen activate)
(magit-save-window-configuration)
ad-do-it
(delete-other-windows))
(defun magit-quit-session ()
"Restores the previous window configuration and kills the magit buffer"
(interactive)
(magit-restore-window-configuration))
(use-package forge
:ensure t
:disabled
:after magit
:config
(defvar es/forge-pull-request-topic-list-columns
'(("#" 5 forge-topic-list-sort-by-number (:right-align t) number nil)
("State" 6 t nil state nil)
("Author" 20 t nil author nil)
("Title" 45 t nil title nil)
))
(defun es/forge-list-team-pullreqs (id)
"List pull-requests of the current repository in a separate buffer."
(interactive (list (oref (forge-get-repository t) id)))
(forge-topic-list-setup #'forge-pullreq-list-mode id nil es/forge-pull-request-topic-list-columns
(lambda ()
(forge-sql [:select $i1
:from pullreq
:where
(and (= repository $s2)
(in pullreq:author $v3)
(or (= state 'open)
(= state 'draft)))]
(forge--tablist-columns-vector)
id
es/github-team-members))))
(defun es/forge-insert-team-pullreqs ()
"Insert a list of mostly recent and/or open pull-requests matching team members from `es/github-team-members'.
Also see option `forge-topic-list-limit'."
(when forge-display-in-status-buffer
(when-let ((repo (forge-get-repository nil)))
(unless (oref repo sparse-p)
(progn
(message (oref repo name))
(forge-insert-topics "Team pull requests"
(es/forge-ls-recent-team-topics repo 'pullreq)
(forge--topic-type-prefix repo 'pullreq)))))))
(cl-defmethod es/forge-ls-recent-team-topics ((repo forge-repository) table)
"This is modeled after `forge-ls-recent-topics' with overloaded
sql that queries for individuals on my team so I can give them
priority review."
(magit--with-repository-local-cache (list 'forge-ls-recent-topics table)
(let* ((id (oref repo id))
(limit forge-topic-list-limit)
(open-limit (if (consp limit) (car limit) limit))
(closed-limit (if (consp limit) (cdr limit) limit))
(topics '())
(class (if (eq table 'pullreq)
'forge-pullreq
'forge-issue)))
(mapc (lambda (row)
(cl-pushnew row topics :test #'equal))
(forge-sql [:select * :from $i1
:where (and (= repository $s2)
(isnull closed)
(in author $v3))
:order-by [(desc updated)]
:limit $s4]
table
id
es/github-team-members
(or open-limit 20)))
(cl-sort (mapcar (lambda (row)
(closql--remake-instance class (forge-db) row))
topics)
(cdr forge-topic-list-order)
:key (lambda (it) (eieio-oref it (car forge-topic-list-order))))))))
;; Replace the hook that lists all open pull requests
;; (magit-add-section-hook 'magit-status-sections-hook 'es/forge-insert-team-pullreqs 'forge-insert-pullreqs 'replace)
)
Tool used to create a link to the point that goes to the appropriate server. Use `M-x git-link` to add the link to the paste ring. I wrote a custom formatter because of a bug that sets branch to “” when in a detached head state.
(use-package git-link
:ensure t
:custom
(setq git-link-use-commit t)
:config
(defun es/git-link-github (hostname dirname filename branch commit start end)
"This method overrides the github formatter because of a bug when
you are detached from head. If we receive an empty string for a
branch we fall back to the commit. If `git-link-use-commit` is
`nil` then try to use the branch."
(format "https://%s/%s/blob/%s/%s"
hostname
dirname
(if (string= "" branch)
commit
(or branch commit))
(concat filename
(when start
(concat (if (git-link--should-render-plain filename) "?plain=1#" "#")
(if end
(format "L%s-L%s" start end)
(format "L%s" start)))))))
(progn
(setq git-link-remote-alist (assoc-delete-all "github" git-link-remote-alist))
(add-to-list 'git-link-remote-alist
'("github" es/git-link-github))))
(use-package git-gutter
:ensure t
:diminish git-gutter-mode
:config
(global-git-gutter-mode 1))
(use-package git-timemachine
:ensure t)
(use-package avy
:ensure t
:bind (("C-c SPC" . avy-goto-word-1))
:config
(setq avy-background t))
Usage needs to be tweaked to use xref mode Dumb jump documentation
(use-package dumb-jump
:disabled
:ensure t
:bind
("C-M-g" . dumb-jump-go))
(use-package expand-region
:ensure t
:bind (("C-=" . er/expand-region)
("M-=" . er/contract-region)))
(use-package multiple-cursors
:ensure t
:bind (("C-S-c C-S-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-M->" . mc/skip-to-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-M-<" . mc/skip-to-previous-like-this)
("C-c C-<" . mc/mark-all-like-this)))
Make navigating parens and s-exps easier with smartparents. I
keep the disabled paredit
because I’m not sure why I had both of them
enabled.
(use-package paredit
:disabled
:ensure t)
(use-package smartparens
:ensure t
:diminish smartparens-mode
:config
(require 'smartparens-config)
(smartparens-global-mode t)
(sp-use-paredit-bindings))
(use-package undo-tree
:ensure t
:commands (global-undo-tree-mode undo-tree-mode undo-tree-visualize)
:bind (("C-x u" . undo-tree-visualize))
:custom
(undo-tree-history-directory-alist `((".*" . ,(locate-user-emacs-file ".undo-tree"))))
(undo-tree-auto-save-history t)
(undo-tree-visualizer-timestamps t)
(undo-tree-visualizer-relative-timestamps t)
:config
(global-undo-tree-mode))
(defun es/projectile-test-suffix (project-type)
"This is the default ending for javascript test files"
"-spec")
(defun es/projectile-find-implementation-or-test-other-window ()
"Toggle between the implementation and test in the other window"
(interactive)
(find-file-other-window (projectile-find-implementation-or-test (buffer-file-name))))
(use-package projectile
;; https://docs.projectile.mx/en/latest/
:ensure t
:diminish (projectile-mode . "\u24C5") ;; Ⓟ
:bind (:map projectile-mode-map
("C-c p" . 'projectile-command-map)
("s-p" . 'projectile-command-map)
:map projectile-command-map
("s r" . rg-project))
:config
(projectile-mode 1)
(counsel-projectile-mode 1)
(add-hook 'after-init-hook
(lambda ()
'(progn
(eval-after-load 'magit
'(setq projectile-switch-project-action #'magit-status)))))
:custom
(projectile-completion-system 'ivy)
(projectile-switch-project-action #'magit-status)
(projectile-project-search-path '("~/workspace"))
(projectile-generic-command "fd . -0")
;; 'hybrid indexing is a little slower but respects .projectile config
(projectile-indexing-method 'hybrid)
;; These should be setq'd
(projectile-test-files-suffices
'("_test" "_spec" "Spec" "Test" "-test" "-spec" ".spec"))
(projectile-test-suffix-function #'es/projectile-test-suffix))
(use-package projectile-rails
;; https://github.com/asok/projectile-rails
:disabled
:config
(projectile-rails-global-mode)
:bind (:map projectile-rails-mode-map
("s-r" . 'hydra-projectile-rails/body)))
(use-package treemacs
:ensure t
:bind
(([f7] . treemacs)
:map treemacs-mode-map
([mouse-1] . #'treemacs-single-click-expand-action)))
(use-package treemacs-projectile
:ensure t)
(use-package rg
:ensure t
:custom
(rg-group-result t "Group the results by filename"))
(use-package counsel
:ensure t)
(use-package counsel-projectile
:ensure t)
(use-package ivy
:ensure t
:config
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format "%d/%d ")
(setq ivy-height 16)
:bind (("C-s" . swiper)))
(use-package flycheck
:ensure t
:diminish (flycheck-mode . "\u24BB") ;; Circled F
:bind (:map flycheck-mode-map
([f8] . flycheck-next-error)
([S-f8] . flycheck-list-errors))
:config
(setq flycheck-disabled-checkers '(javascript-jshint json-jsonlist typescript-tide))
(setq flycheck-checkers '(javascript-eslint typescript-tslint))
(flycheck-add-mode 'javascript-eslint 'js-mode)
(add-hook 'after-init-hook #'global-flycheck-mode))
This is a hybrid of keyboard macros and yasnippet. You create the snippet on the go, usually to be used just in the one place. It’s fast, because you’re not leaving the current buffer, and all you do is enter the code you’d enter anyway, just placing ~ where you’d like yasnippet fields and mirrors to be.
(use-package auto-yasnippet
:ensure t)
(use-package yasnippet
:ensure t
:diminish (yas-minor-mode . "\u24CE")
:demand t
:config
(yas-global-mode)
(define-key yas-keymap (kbd "<return>") 'yas-next-field))
YATemplate creates templates from files listed in .emacs.d/templates
. I’m
not using these a ton right now because they were originally
really useful for AngularJS development. I keep them around
because some of the templates of non-trivial mirrors.
(use-package yatemplate
:ensure t
:demand t
:init (auto-insert-mode)
:config (yatemplate-fill-alist))
(use-package dap-mode
:ensure t)
(use-package lsp-mode
:ensure t
:commands lsp
:bind (:map lsp-mode-map
("M-?" . #'lsp-ui-peek-find-references)
([remap xref-find-definitions] . #'lsp-ui-peek-find-definitions))
:hook ((rust-mode . lsp)
(rust-mode . company-mode)
(elixir-mode . lsp)))
(use-package lsp-ui
:ensure t
:commands lsp-ui-mode)
(use-package bazel
:disabled
:ensure t
:diminish bazel)
Make sure gopls
is installed for lsp support to work correctly.
Go mode docs
(use-package go-mode
:hook ((before-save-hook . gofmt-before-save)
(go-mode . subword-mode)
(go-mode . electric-pair-mode))
:ensure t
:config
(setq tab-width 4)
(use-package go-eldoc))
(use-package elixir-mode
:ensure t)
;; elixir-ts-mode
;; TODO Need to install the grammar
;; Need to set the lsp directory based on a project config of
;; `.devenv/profile/elixir-ls`
;; set ASDF_DIR with (setenv VARIABLE &optional VALUE SUBSTITUTE-ENV-VARS)
;; These packages might need to know how to look in the local
;; path. More important to set the right path variable.
;; ~inf-elixi~ found the wrong mix
(use-package inf-elixir
:ensure t)
;; Install elixir-mix
(use-package mix
:ensure t)
(use-package elm-mode
:commands (lsp lsp-deferred lsp-format-buffer)
:init
(add-hook 'elm-mode-hook #'lsp-deferred)
(add-hook 'before-save-hook #'lsp-format-buffer))
(use-package clojure-mode
:commands (lsp lsp-deferred lsp-format-buffer)
:init
(add-hook 'clojure-mode-hook #'lsp-deferred)
(add-hook 'before-save-hook #'lsp-format-buffer))
My mode for easily working on Jasmine tests
(use-package jasminejs-mode
:ensure t
:diminish jasminejs-mode
:config
(add-hook 'jasminejs-mode-hook
(lambda ()
(local-set-key (kbd "C-c j") 'jasminejs-prefix-map))))
- [ ] Move hooks to their own section
(use-package json-mode
:ensure t
:custom
;; Maybe not best for here?
(js-indent-level 2)
:config
(add-hook 'json-mode-hook #'hs-minor-mode))
(use-package haskell-mode
:ensure t
:custom
(haskell-indentation-left-offset 4)
(haskell-indent-spaces 4))
(use-package markdown-mode
:ensure t)
(use-package nix-mode
:ensure t)
(use-package ruby-mode
:mode
(("\\.rb$" . ruby-mode)
("Gemfile" . ruby-mode)
("Rakefile" . ruby-mode)
("\\.rake$" . ruby-mode)))
(use-package rustic
:ensure t)
(use-package typescript-mode
:ensure t
:mode ("\\.ts\\'" . typescript-mode)
:init (setq typescript-indent-level 2)
:config
;; (add-hook 'flycheck-mode-hook #'es/use-tslint-from-node-modules)
(add-hook 'typescript-mode-hook #'hs-minor-mode)
(add-hook 'typescript-mode-hook #'subword-mode))
In typescript I like having some keywords stand out a little more than the default mode. In the future this may be fixed in the mode. This is mainly a reference because I’d want this evaluated in the context of the buffer it is running.
(defun es/typescript-mode-extra-font-locks ()
(font-lock-add-keywords nil
(list '("\\<\\(constructor\\|type\\|declare\\|var\\|interface\\|static\\|public\\|private\\|this\\|implements\\|let\\|function\\|const\\|new\\|false\\|true\\)\\>" 1 'font-lock-keyword-typescript-face prepend))))
Load the bespoke highlighting through Tide. Tide is a pretty good mode which makes editing Typscript decent.
(use-package tide
:bind
(:map tide-mode-map
([f2] . tide-rename-symbol))
:custom
(tide-completion-enable-autoimport-suggestions t)
:config
;; Highlight identifier at points
(defface font-lock-keyword-typescript-face
'((t :foreground "SlateBlue1"))
"My custom face for typescript keywords"
:group 'font-lock-faces)
(add-hook 'typescript-mode-hook
(lambda ()
(interactive)
(tide-setup)
(flycheck-mode +1)
(setq flycheck-check-syntax-automatically '(save mode-enabled))
(eldoc-mode +1)
;; company is an optional dependency. You have to
;; install it separately via package-install
(company-mode +1)
(tide-hl-identifier-mode +1)
(setq company-tooltip-align-annotations t)
(font-lock-add-keywords nil
(list
'("\\<\\(constructor\\|type\\|declare\\|var\\|interface\\|static\\|public\\|private\\|this\\|implements\\|let\\|function\\|const\\|new\\|false\\|true\\)\\>" 1 'font-lock-keyword-typescript-face prepend)))))
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . web-mode)))
(use-package yaml-mode
:mode ("\\.yml" . yaml-mode))
Emmet mode is helpful for expanding shorthand notation into full HTML tags.
(use-package emmet-mode
:ensure t
:config
(setq emmet-indentation 2)
(add-hook 'web-mode-hook #'emmet-mode))
(use-package restclient
:ensure t)
Utility for encoding/decoding urls. This is useful when looking at
a really long encoded url, urlenc:decode-region
.
(use-package urlenc
:ensure t)
I’ve been using Emacs for decades but that doesn’t mean I only run it in a terminal. Sometimes bells and whistles make me feel good.
When emacs is first installed it probably doesn’t have any of the fancy fonts installed. Don’t forget to execute `all-the-icons-install-fonts`.
I haven’t gone full in on doom but I really like the doom-one color scheme. It’s subtly different than spacemacs-dark. There is a collection of screenshots for doom themes.
(use-package doom-themes
:ensure t
:config (load-theme 'doom-one t))
(use-package rainbow-delimiters
:ensure t
:hook ((emacs-lisp-mode . rainbow-delimiters-mode)
(clojure-mode . rainbow-delimiters-mode)))
This package adds beautiful icons to Emacs and makes it feel more modern.
(use-package all-the-icons
:ensure t)
(use-package all-the-icons-ivy
;; :disabled
:after ivy
:ensure t
:init
(add-hook 'after-init-hook 'all-the-icons-ivy-setup)
:config
(setq all-the-icons-ivy-file-commands
'(counsel-find-file
counsel-file-jump
counsel-recentf
counsel-projectile-find-file
counsel-projectile-find-dir)))
Get more context with Company Box while using Company Mode
(use-package company-box
:ensure t
:after company
:hook (company-mode . company-box-mode))
When I screenshare with someone there are times when I need to scale up all my buffers font size at once for all buffers. This is an incredibly useful library.
(use-package default-text-scale
:ensure t
:config
(setq default-text-scale-amount 8)
:bind
;; Plus makes it better
("M-+" . default-text-scale-increase)
;; Underscore makes it smaller (- is already bound)
("M-_" . default-text-scale-decrease))
Change the visual appearance of a TODO/FIXME item inside of comments.
(use-package fic-mode
:ensure t
:hook ((js2-mode-hook . fic-mode)
(html-mode . fic-mode)
(ruby-mode . fic-mode)
(js-mode . fic-mode)
(typescript-mode . fic-mode)))
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(setq inhibit-startup-message t)
You can list out all the fonts available in the system using
(dolist (font (x-list-fonts "*")) (insert (format "%s\n" font)))
It is noisy but helps.
Install the FiraCode Nerd Font from NerdFonts.
(if (fboundp 'set-frame-font)
(if (eq system-type 'darwin)
(set-frame-font "Monaco-16")
(set-frame-font "FiraCode Nerd Font")))
(if (eq system-type 'darwin)
(progn
(require 'ls-lisp)
(setq ls-lisp-use-insert-directory-program nil)
(setq mac-command-modifier 'meta)
(setq mac-option-modifier 'super)
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time
(setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling
(setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse
(setq scroll-step 1) ;; keyboard scroll one line at a time
(global-set-key (kbd "M-`") 'other-frame)
;; The popup message box destroys the system
(defadvice yes-or-no-p (around prevent-dialog activate)
"Prevent yes-or-no-p from activating a dialog"
(let ((use-dialog-box nil))
ad-do-it))
(defadvice y-or-n-p (around prevent-dialog-yorn activate)
"Prevent y-or-n-p from activating a dialog"
(let ((use-dialog-box nil))
ad-do-it))
(defadvice message-box (around prevent-dialog activate)
"Prevent message-box from activating a dialog"
(apply #'message (ad-get-args 0)))
))
(defadvice async-shell-command (before buffer-named-with-command
(command &optional output-buffer error-buffer))
(when (null output-buffer)
(setq output-buffer (switch-to-buffer (concat "*Async: " command "*")))))
(ad-activate 'async-shell-command)
(defadvice shell-command (before buffer-named-with-command
(command &optional output-buffer error-buffer))
(when (null output-buffer)
(setq output-buffer (switch-to-buffer (concat "*Shell: " command "*")))))
(ad-activate 'shell-command)
(defun es/toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))))
(defun es/rotate-windows ()
"Rotate your windows"
(interactive)
(let* ((i 0)
(numWindows 0))
(cond ((not (> (count-windows) 1))
(message "You can't rotate a single window!"))
(t
(setq i 1)
(setq numWindows (count-windows))
(while (< i numWindows)
(let* (
(w1 (elt (window-list) i))
(w2 (elt (window-list) (+ (% i numWindows) 1)))
(b1 (window-buffer w1))
(b2 (window-buffer w2))
(s1 (window-start w1))
(s2 (window-start w2))
)
(set-window-buffer w1 b2)
(set-window-buffer w2 b1)
(set-window-start w1 s2)
(set-window-start w2 s1)
(setq i (1+ i))))))))
(defun es/open-line-below ()
(interactive)
(end-of-line)
(newline-and-indent)
(indent-for-tab-command))
(defun es/open-line-above ()
(interactive)
(beginning-of-line)
(newline-and-indent)
(forward-line -1)
(indent-for-tab-command))
;; Re-indent pastes
;; This came from the emacs wiki
;; http://emacswiki.org/emacs/AutoIndentation
(dolist (command '(yank yank-pop))
(eval `(defadvice ,command (after indent-region activate)
(and (not current-prefix-arg)
(member major-mode '(emacs-lisp-mode js2-mode web-mode typescript-mode))
(let ((mark-even-if-inactive transient-mark-mode))
(indent-region (region-beginning) (region-end) nil))))))
;; Remove indent when kill line at end of line
(defadvice kill-line (before check-position activate)
(if (member major-mode
'(emacs-lisp-mode js2-mode web-mode))
(if (and (eolp) (not (bolp)))
(progn (forward-char 1)
(just-one-space 0)
(backward-char 1)))))
;; This should be removable now
(defun es/grab-constructor-name ()
"Grab the name of the constructor being used in js class.
This above the current snippet expansion to find the name of the constructor used before the first use of .prototype."
(save-excursion
(save-match-data
(save-restriction
(progn
(widen)
(goto-char (point-min))
(if (re-search-forward "\\b\\(.*?\\)\\.prototype\\." nil t)
(match-string-no-properties 1)
"Class"))))))
(defun es/find-class-from-module-string (str)
"Given a dot separated module string this yields the last
component"
(car (last (s-split "\\." str))))
;;;
(defun es/find-template-other-window ()
"See if there is a directive template and jump there"
(interactive)
(let* ((directive-template-path (es/guess-template-file)))
(if (and directive-template-path (file-readable-p directive-template-path))
(find-file-other-window directive-template-path)
(message (format "Could not find template file %s" directive-template-path)))))
(defun es/guess-template-file ()
"Guesses the template file for an angular directive"
(save-excursion
(save-match-data
(beginning-of-buffer)
(let* ((app-root-dir (if (boundp 'es/angular-project-root)
es/angular-project-root
""))
(found-template-p (re-search-forward "^\s*templateUrl\s*:\s*'\\(.*?\.html\\)'\s*,?\s*$" nil t)))
(if found-template-p
(let* ((matched-text (match-string 1))
(is-relative-path (not (s-prefix-p "/" matched-text))))
(if is-relative-path
matched-text
(concat app-root-dir (match-string 1)))))))))
(defmacro es/search-and-collapse (search-cmd str-or-regex)
"Search using the provided function and string
search-cmd is typically 're-search-forward or
'search-forward. str-or-regexp is self explanatory"
`(save-excursion
(save-match-data
(beginning-of-buffer)
(while (,search-cmd ,str-or-regex nil t)
(end-of-line)
(js2-mode-hide-element)))))
(defun es/collapse-all-functions ()
"Collapse all named functions and prototype functions"
(interactive)
;; Angular specific patterns
(es/search-and-collapse re-search-forward "^\s*vm\..*function")
;; Jasmine related functions
(es/search-and-collapse search-forward "it(")
(es/search-and-collapse re-search-forward "beforeEach.*function")
;; Straight JS functions
(es/search-and-collapse re-search-forward "^\s*function\s")
(es/search-and-collapse re-search-forward "^\s*this\..*function")
(es/search-and-collapse search-forward ".prototype."))
;;; Merge ediff region A and B into C
(defun es/ediff-copy-both-to-C ()
(interactive)
(ediff-copy-diff ediff-current-difference nil 'C nil
(concat
(ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer)
(ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer))))
(defun es/add-d-to-ediff-mode-map () (define-key ediff-mode-map "d" 'es/ediff-copy-both-to-C))
(add-hook 'ediff-keymap-setup-hook 'es/add-d-to-ediff-mode-map)
;;; collapse multiple blank lines down to one
(defun es/remove-multiple-emtpy-lines ()
"Removes multiple empty lines from a file"
(interactive)
(let* ((blank-line-re "^\n\\{2,\\}")
(replacement "\n"))
(save-excursion (progn
(goto-char (point-min))
(while (re-search-forward blank-line-re nil t)
(replace-match replacement nil nil))))))
(defun es/file-exists-at-point ()
"Find if the path under the cursor exists.
This reports to the message buffer if we can find the file or
not."
(interactive)
(if (file-exists-p (ffap-string-at-point))
(message "File exists")
(message "Cannot find file")))
(defvar es/git-server
"http://remote.repo.com/path#"
"Used for replacing contents in NPM for testing")
(defun es/replace-branch-name-selection-with-git-branch ()
"This will generate the NPM location from the branch provided from es/git-server
string at point. To use, highlight region and it will be prefixed by a git path"
(interactive)
(if (use-region-p)
(let*
((selected-region (delete-and-extract-region (region-beginning) (region-end))))
(insert (concat es/git-server selected-region)))
(message "You must have an active region to replace")))
(defun es/use-tslint-from-node-modules ()
"Load tslint from local node_modules if available.
Given to me by Surya."
(let* ((root (locate-dominating-file
(or (buffer-file-name) default-directory)
"node_modules"))
(tslint (and root
(expand-file-name "node_modules/.bin/tslint" root))))
(when (and tslint (file-executable-p tslint))
(setq-local flycheck-typescript-tslint-executable tslint))))
(defun es/typescript-helm-projectile-insert-file-at-point ()
"Insert a file at point from your git tree"
(interactive)
(let* ((project-root (projectile-project-root))
(project-files (projectile-current-project-files))
(files (projectile-select-files project-files)))
(if (= (length files) 1)
(insert (expand-file-name (car files) (projectile-project-root)))
(helm :sources (helm-build-sync-source "Projectile files"
:candidates (if (> (length files) 1)
(helm-projectile--files-display-real files project-root)
(helm-projectile--files-display-real project-files project-root))
:fuzzy-match helm-projectile-fuzzy-match
:action-transformer 'helm-find-files-action-transformer
:keymap helm-projectile-find-file-map
:help-message helm-ff-help-message
:mode-line helm-read-file-name-mode-line-string
:action (lambda (filename)
(let* ((relative-file (file-relative-name filename default-directory))
(trimmed-file (s-replace-all '((".d.ts" . "") (".ts" . "") (".css" . "") (".js" . "")) relative-file)))
(insert trimmed-file)))
:persistent-action #'helm-projectile-file-persistent-action
:persistent-help "Preview file")
:buffer "*helm projectile*"
:truncate-lines helm-projectile-truncate-lines
:prompt (projectile-prepend-project-name "Find file: ")))))
(defun es/neotree-dir-up ()
"Go up a directory in neotree"
(interactive)
(neotree-dir ".."))
(defun es/comment-and-copy-line ()
"Copies the current line, comments it and duplicates below.
This was taken from the following gist:
https://gist.github.com/rejeep/2922929
"
(interactive)
(let* ((beg (line-beginning-position))
(end (line-end-position))
(line (buffer-substring-no-properties beg end))
(column (current-column)))
(comment-region beg end)
(goto-char (line-end-position))
(newline)
(insert line)
(move-to-column column)))
(defun es/copy-buffer-file-name-to-clipboard ()
"Copies the buffer file name to the clipboard"
(let ((buf-name (buffer-file-name)))
(if buf-name
(with-temp-buffer
(insert buf-name)
(copy-region-as-kill (point-min) (point-max))
(message "Copied %s to clipboard" buf-name))
(message "Your buffer is not backed by a file"))))
(defun es/convert-vscode-snippet-to-yasnippet (file)
"Given a vscode snippet we convert it to yasnippet"
(interactive)
(json-read-file file))
;; Quickly jump to a line
(global-set-key [(meta g)] 'goto-line)
(global-set-key [S-f8] 'compile)
(global-set-key [f8] 'recompile)
;; Quick switch to the last buffer
(global-set-key [backtab] (lambda ()
(interactive)
(switch-to-buffer (other-buffer))))
(global-set-key [(meta !)] 'async-shell-command)
(global-set-key [(control meta !)] 'shell-command)
(global-set-key (kbd "C-c r") 'rgrep)
(global-set-key (kbd "C-c d") 'es/find-template-other-window)
(global-set-key (kbd "C-c c") 'es/collapse-all-functions)
(global-set-key (kbd "<C-return>") 'es/open-line-below)
(global-set-key (kbd "<C-S-return>") 'es/open-line-above)
(global-set-key [f9] 'es/toggle-window-split)
(global-set-key [f10] 'es/rotate-windows)
(global-set-key (kbd "<s-mouse-1>") 'hs-toggle-hiding)
(global-set-key [f5] 'helm-do-ag)
(global-set-key [S-f5] 'helm-swoop)
(global-set-key [f1] 'delete-other-windows)
(global-set-key [S-f1] 'delete-window)
(global-set-key (kbd "C-c C-d") #'es/comment-and-copy-line)
;; Org-mode
(global-set-key (kbd "C-c l") #'org-store-link)
;; This file is for overriding or configuring emacs settings
;; Save place mode
(if (/= 24 emacs-major-version)
(save-place-mode 1)
(progn
(require 'saveplace)
(setq-default save-place t)))
(ansi-color-for-comint-mode-on)
(defvar browse-url-generic-program)
(defvar browse-url-browser-function)
;; Get to the browser
(require 'cl-lib)
(cl-loop for executable in '("google-chrome" "chromium-browser" "firefox")
do (progn
(let ((browser-path (executable-find executable)))
(when browser-path
(setq browse-url-generic-program browser-path
browse-url-browser-function 'browse-url-generic)
(cl-return browser-path)))))
;; BAD tabs, bad.
(setq-default indent-tabs-mode nil)
;; Kill that trailing whitespace
(add-hook 'before-save-hook 'delete-trailing-whitespace)
(setq default-line-spacing 4)
(show-paren-mode t)
(custom-set-variables
'(show-paren-style 'parenthesis))
;; (require 'ansi-color)
;; (defun colorize-compilation-buffer ()
;; (toggle-read-only)
;; (ansi-color-apply-on-region (point-min) (point-max))
;; (toggle-read-only))
;; (add-hook 'compilation-filter-hook 'colorize-compilation-buffer)
;;; Dired customizations
(defun dired-back-to-top ()
(interactive)
(beginning-of-buffer)
(dired-next-line 4))
(define-key dired-mode-map
(vector 'remap 'beginning-of-buffer) 'dired-back-to-top)
(defun dired-jump-to-bottom ()
(interactive)
(end-of-buffer)
(dired-next-line -1))
(define-key dired-mode-map
(vector 'remap 'end-of-buffer) 'dired-jump-to-bottom)
;;; Backup
(custom-set-variables
;; don't clobber symlinks
'(backup-by-copying t)
;; Don't litter
'(backup-directory-alist '(("." . "~/.saves")))
'(delete-old-versions t)
'(kept-new-versions 6)
'(kept-old-versions 2)
'(version-control t)
'(create-lockfiles nil))
;; IBuffer
(setq ibuffer-formats
(quote
((mark modified read-only " "
(name 60 60 :left :elide)
" "
(mode 14 14 :left :elide)
" " filename-and-process)
(mark " "
(name 12 -1)
" " filename))))
I’m not sure that I need these anymore.
(custom-set-variables
'(safe-local-variable-values
(quote
((projectile-test-suffix-function lambda
(project-type)
"" "Spec")
(eval progn
(require
(quote projectile))
(puthash
(projectile-project-root)
(concat haskell-process-path-stack " build")
projectile-compilation-cmd-map)
(puthash
(projectile-project-root)
(concat haskell-process-path-stack " test")
projectile-test-cmd-map)))))
'(compilation-ask-about-save nil)
'(compilation-scroll-output (quote first-error))
'(org-agenda-files
(quote
("~/Documents/deft")))
'(org-clock-clocktable-default-properties (quote (:maxlevel 3 :scope file)))
'(org-clock-idle-time 15)
'(org-clock-into-drawer "LOGBOOK")
'(org-clock-out-remove-zero-time-clocks t)
'(org-clocktable-defaults
(quote
(:maxlevel 3 :lang "en" :scope file :block nil :tstart nil :tend nil :step nil :stepskip0 nil :fileskip0 nil :tags nil :emphasize nil :link nil :narrow 40! :indent t :formula nil :timestamp nil :level nil :tcolumns nil :formatter nil)))
'(org-enforce-todo-checkbox-dependencies t)
'(org-enforce-todo-dependencies t)
'(org-fontify-emphasized-text t)
'(org-fontify-whole-heading-line t)
'(org-src-fontify-natively t)
'(org-habit-following-days 5)
'(org-habit-show-habits-only-for-today t)
'(org-habit-today-glyph 124)
'(org-hide-emphasis-markers t)
'(org-hide-leading-stars t)
'(org-log-done (quote time))
'(org-modules
())
'(org-tags-column -120)
'(org-todo-keyword-faces (quote (("TODO" . "#b58900") ("NEXT" . "#2aa198")))))
(custom-set-variables
'(ediff-window-setup-function (quote ediff-setup-windows-plain)))
(custom-set-variables
'(tab-width 4 nil nil "Set from custom settings"))
;; I hate typing the whole word
(defalias 'yes-or-no-p 'y-or-n-p)
;; More buffer functionality. These days I use helm a lot more.
(defalias 'list-buffers 'ibuffer)