Table of Contents
- My Configuration
- LSP configuration
- python configuration
- Java configuration
- Additional bits-o-configuration
This is my literate and portable Emacs initialization.
git clone this repository into
git clone https://github.com/Atman50/emacs-config.git =/.emacs.d
Starting Emacs for the first time on a new machine loads all the packages/configuration loads. It takes some time on this first
load since all the packages referenced need to download and compile. On subsequent Emacs invocations startup time is much better.
The ability to simply clone and start is what makes this configuration portable. Note that some of the Emacs customization (see
custom.el) are system (file system) dependent. I handle this by using git to create a stash of the localized changes for
custom.el and then apply it whenever I take updated configurations from the repository.
Why a literate configuration
Well mostly I wanted to learn how to do it, but also I was having issues with managing my initialization/configuration. FWIW
this is the approach that I came up with. My
init.el file is simple and relies on the fact that customizations are saved to
its own file and that these customizations are read in before the packages are loaded.
What I've gotten out of all this work is a portable and documented configuration that works well for me. Please feel free to take whatever portion(s) you wish from this and make it your own.
I have tried to make this configuration 100% portable meaning that on a new system (Linux or Windows at this point) with Emacs
installed. I simple git clone this repository to
~/.emacs.d and then fire up Emacs. Should work every time.
init.el, short and sweet
11 (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) 12 (load custom-file t) 13 (prefer-coding-system 'utf-8) 14 (unless (boundp 'package-user-dir) 15 (unless (boundp 'package-archive-contents) 16 (package-initialize)) 17 (unless (assoc 'use-package package-archive-contents) 18 (package-refresh-contents) 19 (package-install (elt (cdr (assoc 'org-plus-contrib package-archive-contents)) 0)) 20 (package-install (elt (cdr (assoc 'use-package package-archive-contents)) 0)))) 21 (setq use-package-enable-imenu-support t) 22 (require 'use-package) 23 (use-package org) 24 (defcustom my/cfg-file (concat user-emacs-directory "README") 25 "The base name for the .org file to use for Emacs initialization." 26 :group 'my-configuration 27 :type 'string) 28 (when (file-newer-than-file-p (concat my/cfg-file ".org") (concat my/cfg-file ".el")) 29 (org-babel-tangle-file (concat my/cfg-file ".org"))) 30 (load my/cfg-file)
|11||Breaking the custom setting out into its own file allows it to be loaded in the next line|
|12||Load the custom file. All custom settings will now be honored by packages when loaded.|
|13||Just a fix for ELPA packages with (certain?) Unicode characters in them.|
|14||If there's no `'package-user-dir` defined; `package.el` isn't loaded|
|15||If there's no `'package-archive-contents` defined; package archives have not been read|
|16||Initialize the package system|
|17||If we've not load loaded the definition for the `use-package` ELPA package, then|
|18||Refresh (read) the package archives. Note: `'package-archives` from `custom.el` **key** here|
|19||Load up the org-mode. The pre-packaged org-mode does not have Babel!|
|20||Set the variable to allow `use-package` to use counsel for imenus|
|22||Make sure `use-package` is available|
|23||Make sure `org` is available|
|24||Create customizable config variable|
|26||… it's nice having a customizable group for personal configuration settings|
|28||If the .el file doesn't exist or is older than this file then…|
|29||… create the tangled output of this file|
|30||Load the tangled output of this file|
Loading the custom file first
One of the nicest things about Emacs is the extensive and quite useful customization engine. You can customize variables and faces with ease and make the settings work for you.
Loading the customized variables before the package (using
use-package of course) means that you can now use the customization
facility in Emacs to modify the variables and have them stick between Emacs invocations. I see lots of configurations with the
:config section of a
use-package invocation performing variable setting. The problem with this is that if you want to change
it using Emacs, the loading of the customizations first causes your new customizations getting overwritten on the next start of
Emacs. You can do it in the reverse order;
use-package first, then customize, but then you couldn't customize the variable
using the customization system.
Therefore I try to minimize
use-package customizations and mostly use the
The most important custom variable for this configuration is
package-archives, which is used by the loading of the various
extension packages used by this configuration.
|package-archives||(("gnu" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")
("org" . "https://orgmode.org/elpa/"))
Customizing the configuration
I've started to use the
defcustom function to describe those "variables that impact initialization" and are all placed into
'my-configuration group. Each of the configuration variables can be accessed using
M-x customize-group my-configuration.
This allows me to select features to turn on or off selectively and make them sticky if so desired.
Another reason to load the customization file first.
Here are my configuration bits. All of the following code snippets are tangled from this file into an
.el file that gets loaded
from the initialization file. Feel free to take as little or as much as you like from here.
First make sure that we are doing lexical scoping for speed. See Some Performance Advantages of Lexical Scope blog.
;;; README.el --- a file generated from README.org - do not edit by hand!!!! ;; -*- lexical-binding: t; -*- ;;; Commentary: ;;; Org tangled from README.org. Edit the org file to chnage this configuration ;;; Code:
This is a little piece of code that I picked up that might make things faster when downloading and installing all the packages. This turns down the garbage collector during the use-package loading when it has to do some compiling. Set it back when done with init.
(setq gc-cons-threshold 64000000) (add-hook 'after-init-hook (lambda () (setq gc-cons-threshold 800000)))
Here are some general packages I use
Speed up line movement
I ran into this little tidbit while reading Sacha Chua's posts from Emacs. It is described here, but to summarize the
next-line defun triggers
line-move-partial which leads to excessive processing. By setting the variable here, the speed of
next-line gets very cut down.
(setq auto-window-vscroll nil)
Use Ctrl-Z for personal bindings
Ctrl-c was supposed to be left for personal customization but seems to get used more than it should, therefore I've started to bind things to Ctrl-Z, which had the annoying and useless minimize functionality.
(bind-keys :map global-map ;; get rid of pesky "\C-z" and use for personal bindings :prefix-map my-ctrl-z-prefix-map :prefix "C-z" ("C-d" . dired-jump) ("c" . comment-region) ("d" . docker) ("f" . magit-find-file-other-window) ("g" . magit-status) ("h a" . helpful-at-point) ("h c" . helpful-command) ("h C" . helpful-callable) ("h f" . helpful-function) ("h k" . helpful-key) ("h m" . helpful-macro) ("h v" . helpful-variable) ("l" . magit-log-buffer-file) ("n" . linum-mode) ("r" . revert-buffer) ("t" . toggle-truncate-lines) ("u" . uncomment-region))
I manage a lot of docker stuff. The docker package is quite useful.
flymake (built-in) with
flycheck (see flycheck a flymake replacement).
(use-package flycheck :config (global-flycheck-mode))
This is a free synonyms plug-in for Emacs that uses Wordnet brought to you by Princeton University. Note that you will have to install Wordnet on your system and add it to your path to make this package work properly.
Handy mode to make the modeline more succinct by allowing a diminished mode line string. Sometimes the fact that mode is there is fine and it doesn't need to be on the mode line (diminish it to "").
(use-package diminish :defer t)
Much better binding capabilities (in later versions this is already loaded via
(use-package bind-key :defer t)
Helpful provides contextual help and other features. Here are two blogs that provide good information: initial Helpful blog and Helpful, one year in. More in-depth help along with lots of other information like references, edebug capabilities, …
A great built-in that allows us to have a history file. This means certain elements are saved between sessions of Emacs. This
history file is kept in
~/.emacs.d/savehist. Note that in later versions of Emacs this package is already built-in, so check
the built-ins before issuing the
use-package. In later versions of Emacs seems the
savehist package is built-in so ignore
(unless (package-built-in-p 'savehist) (use-package savehist :defer t))
Set the following variables to control
savehist (use customize).
|savehist-additional-variables||(tablist-named-filter kill-ring search-ring regexp-search-ring)|
Themes and mode line
My progression of modelines has gone from
moody and now
doom-modeline package is pretty good and
not as much fuss as I had with
moody. All the stuff I need there and makes this configuration much easier. You must go
install the fonts from the
all-the-icons package (which is loaded as a dependency) according to the instructions found on the
doom-modeline website: Run
M-x all-the-icons-install-fonts and then, on Windows, install the font ttf file by right clicking
on it and doing install.
(use-package leuven-theme :config (load-theme 'leuven t)) (use-package doom-modeline :hook (after-init . doom-modeline-mode))
The current modifications I make to the
doom-modeline default face settings when using the
doom-modeline-project-dir. You can look in the
custom.el file for those settings.
These packages are useful when doing presentations.
(use-package command-log-mode :defer t)
Perhaps one of the most useful extensions, this little gem will provide a list in the mini-buffer of the relevant keystrokes and the functions to which they are bound (or a prefix). Many times I've found unknown features by simply looking at the various options. This is, IMO, a great way to learn Emacs key-bindings.
(use-package which-key :diminish "") (use-package which-key-posframe)
Very large files
Since I deal with potentially gigantic log files, this package allows the file to be carved up and 'paged' through. Get to the
vlf stuff through the default prefix
(use-package vlf :defer t :pin melpa)
I got the
vlf package from a really good paper on how to use Emacs to deal with logs. If you currently or are going to deal
with logs in your day to day, then this article is invaluable. I've yet to adopt some of the other features described by the
article but I have no need as of yet. Soon maybe.
Since I'm doing more and more work with Jenkins adding support for Groovy is, well, groovy.
Other useful packages
OK, a little tired of documenting each package on it's own. These packages are just generally useful. Some of these packages have become so useful that they've found their way into the list of Emacs built-in packages. In those cases, the package is checked here against the list of built-ins to avoid warnings when loading a later version of Emacs.
(use-package realgud) ;; A "better" gud (use-package projectile :bind (:map projectile-mode-map ("C-c p" . projectile-command-map) ;; traditional binding ("C-z C-p" . projectile-command-map) ;; my binding ("C-z p" . projectile-command-map)) ;; all paths get to projectile :config (projectile-mode t)) (use-package ibuffer-projectile :defer t) (use-package xterm-color :defer t) (unless (package-built-in-p 'sh-script) (use-package sh-script :defer t)) (unless (package-built-in-p 'desktop) (use-package desktop)) (set-variable 'desktop-path (cons default-directory desktop-path)) (use-package lispy :config (add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1))) (add-hook 'minibuffer-setup-hook (lambda () (when (eq this-command 'eval-expression) (lispy-mode 1))))) (use-package powershell :if mswindows-p)
Note that the setting of
desktop-path allows the multiple
.emacs.desktop files, each in the directory where
desktop-path is changed outside
custom.el, I've included it here in the table below so you can see that
the default is augmented with the start-up directory which in this case is
Customized variables of interest here:
|desktop-path||("`/toast/git-repos/lunchbox/" "~/.emacs.d/" "`")|
(use-package company :diminish)
I used to be a
helm user, but switched to
ivy. Lots of nice features in
ivy and very easy to configure comparatively.
(use-package ivy :diminish "" :bind (:map ivy-minibuffer-map ("C-w" . ivy-yank-word) ;; make work like isearch ("C-r" . ivy-previous-line)) :config (ivy-mode 1) (setq ivy-initial-inputs-alist nil) ;; no regexp by default (setq ivy-re-builders-alist ;; allow input not in order '((t . ivy--regex-ignore-order)))) (use-package counsel :bind (("C-z j" . counsel-imenu))) (use-package counsel-projectile :config (counsel-projectile-mode t)) (use-package counsel-codesearch) (use-package ivy-hydra) (use-package swiper :bind (("C-S-s" . isearch-forward) ;; Keep isearch-forward on Shift-Ctrl-s ("C-s" . swiper) ;; Use swiper for search and reverse search ("C-S-r" . isearch-backward) ;; Keep isearch-backward on Shift-Ctrl-r ("C-r" . swiper))) (use-package avy :bind (("C-:" . avy-goto-char)))
posframe IS MESSED UP - NOT TANGLING
(use-package posframe) (use-package ivy-posframe :config (setq ivy-display-function #'ivy-posframe-display) (ivy-posframe-enable))
I ran into a nice article that fixes a problem that I often have with Ivy: using a name that is not in the list of candidates (for
example when trying to write to a buffer to a new file name). To fix this, setting
t makes going
back before the first candidate to a "verbatim" prompt.
prescient provides "simple but effective sorting and filtering for Emacs."
(use-package prescient) (use-package ivy-prescient) (use-package company-prescient)
yasnippet is a truly awesome package. Local modifications should go in
Just love the
yasnippet package. I only wish there were more templates out there. Creating new ones and placing them the
appropriate (mode-named) subdirectory of
(use-package yasnippet :diminish (yas-minor-mode . "") :config (yas-reload-all) ;; fix tab in term-mode (add-hook 'term-mode-hook (lambda() (yas-minor-mode -1))) ;; Fix yas indent issues (add-hook 'python-mode-hook (lambda () (set (make-local-variable 'yas-indent-line) 'fixed))) ;; Setup to allow for yasnippets to use code to expand (require 'warnings) (add-to-list 'warning-suppress-types '(yasnippet backquote-change))) (use-package yasnippet-snippets)
(defvar my/company-point nil) (advice-add 'company-complete-common :before (lambda () (setq my/company-point (point)))) (advice-add 'company-complete-common :after (lambda () (when (equal my/company-point (point)) (yas-expand))))
Customizations of interest:
The most awesome git porcelain. Most here are part of magit,
git-time-machine is not, but well worth using.
(use-package git-commit) (use-package magit ;; Make the default action a branch checkout, not a branch visit when in branch mode :bind (:map magit-branch-section-map ([remap magit-visit-thing] . magit-branch-checkout))) (use-package magit-filenotify) (use-package magit-find-file) (use-package git-timemachine)
|magit-repository-directories||(("~/repos" . 1))|
htmlize package allows the HTML and Markdown exporters to work (underlying code). It also allows to export your files all
fontified: for example, you can export all or part of, say, a Python file and it will come out all colorized for publishing.
(use-package org-bullets :config (add-hook 'org-mode-hook (lambda () (toggle-truncate-lines -1) (auto-fill-mode 1) (org-bullets-mode)))) (use-package org-autolist) (use-package htmlize) (add-hook 'org-mode-hook #'flyspell-mode) ;; (use-package ox-reveal) ;; (require 'ox-reveal)
I've started using
ox-reveal for generating presentations from
org-mode. Here's a good article on getting started. I've set
org-reveal-root to point to http://cdn.jsdelivr.net/reveal.js/3.0.0/ so that you do not need to install it on your system.
If you want to use your own customized theme, see the instructions at https://github.com/hakimel/reveal.js/. NB: I have removed
ox-reveal from the normal package load because it has a dependency on the
org package, but we already install
ox-reveal, I guess, doesn't recognize. Leaving the code here to make it easy to bring in if you are
working with reveal.js and presentations.
Customized variables for org-mode:
|org-html-postamble-format||(("en" "<p class=\"author\">Author: %a (%e)</p>\n<p class=\"date\">Date: %T</p>\n<p class=\"creator\">%c</p>"))|
org-mode export hacks for HTML and Markdown
I export into markdown for github. I do not use the
ox-gfm package because when I tried it, it modified the source file because
of this file's use of the
#+CALL construct (each call adds the table to the source file). So I use the built in
exporter. However, it just indents the code blocks rather put the
```emacs-lisp code snippet prefix and
``` postfix but
rather just indents. First we load the library so it turns up in the export menu (
C-x C-e). Then we override the output method
for the code.
(load-library "ox-md") (cl-defun org-md-example-block (example-block _contents info) "My modified: Transcode EXAMPLE-BLOCK element into Markdown format. CONTENTS is nil. INFO is a plist used as a communication channel." (concat "```emacs-lisp\n" (org-remove-indentation (org-export-format-code-default example-block info)) "```\n"))
To support the using of dynamic custom vars table using the library of Babel, the export text for Markdown and HTML goes through
orgtbl-to-orgtbl which turns the list returned in the an org-mode table. After
htmlize package turns
it into a HTML table. The adviser changes all the spaces after a
entities and surrounds them with inline
HTML. This is necessary because
orgtbl-to-orgtbl strips text between the
@@ used to inline HTML. The adviser also protects
any underscores in the table with inline HTML.
(cl-defun my-md-export-hack(text) "Fix up md export on writing my README.org file. Converts a <br> followed by zero or more spaces into inline html format. For example: an in put of \"hello<br>there<br> my<br> friend<br>\" becomes \"hello@@html:<br>@@there@@html:<br> @@my@@html:<br> @@friend@@html:<br>@@\" This function also adds inline HTML around '_' in the text." (when (stringp text) (let ((result text) (replacements '(("<br>\[[:space:]\]*" (lambda (match) (concat "@@html:<br>" (apply 'concat (make-list (- (length match) 4) " ")) "@@"))) ("\"\\(https?:\[^\"\]*\\)" "\"@@html:<a href=\"\\1\">\\1</a>@@") ("_" "@@html:_@@") ("<\\(p.*?\\)>" "@@html:<\\1>@@") ("</p>" "@@html:</p>@@")))) (cl-loop for rep in replacements do (setq result (replace-regexp-in-string (nth 0 rep) (nth 1 rep) result))) result))) (advice-add #'orgtbl-to-orgtbl :filter-return #'my-md-export-hack)
Use of babel
To do literate programming you need to include the languages to "tangle". Here I've added more than just the standard
emacs-lisp value. Added Python, PlantUML, and shell.
|org-babel-load-languages||((shell . t)
(plantuml . t)
(python . t)
(emacs-lisp . t))
LSP (Language Server Protocol) is a new Microsoft-defined interface for IDEs:
The Language Server Protocol (LSP) defines the protocol used between an editor or IDE and a language server that provides language features like auto complete, go to definition, find all references etc.
Although I'm currently using
elpy for Python, I'll be using LSP for Java development.
(use-package lsp-mode :demand t :config (add-hook 'lsp-after-open-hook #'lsp-enable-imenu) (use-package lsp-ui :config (add-hook 'lsp-mode-hook 'lsp-ui-mode)) (use-package company-lsp :config (push 'company-lsp company-backends)) (add-hook 'lsp-after-initialize-hook (lambda () (let ((lsp-cfg `(:pyls (:configurationSources ("flake8"))))) (lsp--set-configuration lsp-cfg)))))
At one point I was using anaconda but have switched back to elpy. I really like
eply-config that tells you if everything is
working properly. I've been using a
virtualenv for my python development and couldn't be happier. Perhaps the only thing that
bothers me is that when an object is returned, PyCharm will give you list and dictionary methods while
eply=/=company does not.
Seems to be the only real issue at this point.
The tale of two IDEs
I've decided to take the Language Server Protocol out for a spin. Unfortunately it might be a while before I decide to switch
since there are some things I find a little annoying, like initial startup speed of loading a large Python file into Emacs,
lsp-mode is initializing. Either way, the Python IDE is selected using the.
To switch between the two IDEs might take a bit of futzing - I've had to go remove
elpy entries from
switch to the
(defcustom my/use-elpy t "Setting to t uses elpy as the Python IDE. Set to nil to use lsp." :group 'my-configuration :type '(choice (const :tag "lsp" nil) (const :tag "elpy" t)))
The tried and true
elpy Python IDE. I run
flycheck rather than
flymake now, so rebind the error navigation keys (C-c C-n/C-p).
(when my/use-elpy (use-package elpy :demand t :bind (:map elpy-mode-map ("C-c C-p" . flycheck-previous-error) ("C-c C-n" . flycheck-next-error) ("C-z ." . elpy-goto-definition)) :config (elpy-enable) (add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter") (use-package company-jedi :config (push 'company-jedi company-backends))))
elpy-modules is updated in my
custom.el file so that the
flymake module is removed:
|elpy-modules||(elpy-module-company elpy-module-eldoc elpy-module-pyvenv elpy-module-highlight-indentation elpy-module-yasnippet elpy-module-sane-defaults)|
This is a newer mode based on the Language Server Protocol.
(unless my/use-elpy (use-package lsp-python))
Python IDE-agnostic configuration
(use-package pylint) (use-package python-docstring :config (python-docstring-install)) (use-package python :config (add-hook 'python-mode-hook (lambda () (unless my/use-elpy (lsp-python-enable)) (company-mode) )))
Customized variables used in this python configuration:
|python-shell-prompt-regexp||"In \\\[[0-9]+\\]: "|
Tried to get LSP working, but to no avail. Using Meghanada mode.
(use-package gradle-mode) (use-package meghanada :bind (:map meghanada-mode-map ("C-c C-n" . flycheck-next-error) ("C-c C-p" . flycheck-previous-error)) :config (add-hook 'java-mode-hook (lambda () (meghanada-mode t))))
which-function which is used on the mode-line has no maximum method/function signature. This handy adviser limits the name to
(defcustom my/which-function-max-width 64 "The maximum width of the which-function string." :group 'my-configuration :type 'integer) (advice-add #'which-function :filter-return (lambda (s) (when (stringp s) (if (< (string-width s) my/which-function-max-width) s (concat (truncate-string-to-width s (- my/which-function-max-width 3)) "...")))))
Allows me to name my ANSI terms. Was very useful when I used more ANSI shells (so that tabs were interpreted by the shell). Some other modes and shells make this less useful these days.
(cl-defun my-ansi-term (term-name cmd) "Create an ansi term with a name - other than *ansi-term* given TERM-NAME and CMD." (interactive "sName for terminal: \nsCommand to run [/bin/bash]: ") (ansi-term (if (= 0 (length cmd)) "/bin/bash" cmd)) (rename-buffer term-name))
Understand file type by shebang
When a file is opened and it is determined there is no mode (fundamental-mode) this code reads the first line of the file looking for an appropriate shebang for either python or bash and sets the mode for the file.
(cl-defun my-find-file-hook () "If `fundamental-mode', look for script type so the mode gets properly set. Script-type is read from #!/... at top of file." (if (eq major-mode 'fundamental-mode) (ignore-errors (save-excursion (goto-char (point-min)) (re-search-forward "^#!\s*/.*/\\(python\\|bash\\).*$") (if (string= (match-string 1) "python") (python-mode) (sh-mode)))))) (add-hook 'find-file-hook 'my-find-file-hook)
React to screen width changes
Because I use posframe quite a bit now (so that the mini-buffer doesn't continue to change sizes, which I find a little annoying), this code reacts to the width changes and will set the custom variables accordingly.
Does not seem to work with newest POSFRAME (as of 2019-05-02). Needs work.
(cl-defun my/window-size-change (&optional _) "My very own resize defun for modifying the posframe size" (unless (= (window-pixel-width-before-size-change) (window-pixel-width)) (customize-set-value 'ivy-posframe-width (window-body-width)))) (add-hook 'window-size-change-functions 'my/window-size-change)
eldoc mode, use
y-or-n-p instead of
yes-or-no-p. Key bindings…
(add-hook 'emacs-lisp-mode-hook #'eldoc-mode) ;; Run elisp with eldoc-mode (diminish 'eldoc-mode "Doc") ;; Diminish eldoc-mode (fset #'list-buffers #'ibuffer) ;; prefer ibuffer over list-buffers (fset #'yes-or-no-p #'y-or-n-p) ;; for lazy people use y/n instead of yes/no ;; Some key bindings (bind-key "C-x p" #'pop-to-mark-command) (bind-key "C-h c" #'customize-group) (bind-key "C-+" #'text-scale-increase) (bind-key "C--" #'text-scale-decrease) (bind-key "<up>" #'enlarge-window ctl-x-map) ;; note: C-x (bind-key "<down>" #'shrink-window ctl-x-map) ;; note: C-x (bind-key "C-z" 'nil ctl-x-map) ;; get rid of annoying minimize "\C-x\C-z" (setq-default ediff-ignore-similar-regions t) ;; Not a variable but controls ediff ;; Enable some stuff that's normally disabled (put 'narrow-to-region 'disabled nil) (put 'downcase-region 'disabled nil) (put 'upcase-region 'disabled nil) (put 'scroll-left 'disabled nil)
A post-amble to make the tangled
.el file has no errors/warnings.
;;; README.el ends here