Use flycheck for on-the-fly type checking.
(use-package flycheck
:ensure t
:delight
:commands flycheck-mode)
Use company mode for autocompletion of All The Things. Tweak a few parameters while we’re at it:
- Summon autocompletion with
C-c C-n
orC-c M-n
. - Have the autocompletion popup appear in 0.2 seconds rather than the default 0.5 seconds.
- Except when we’re writing plain text – that would just be irritating. In plain text files, we can always summon autocompletion explicitly if we want..
- Make the autocompletion popup bigger – show 20 candidates at a time instead of 10.
(use-package company
:ensure t
:delight
:commands global-company-mode
:init
(add-hook 'after-init-hook 'global-company-mode)
:config
(global-set-key (kbd "C-c C-n") 'company-complete)
(global-set-key (kbd "C-c M-n") 'company-complete)
(setq company-idle-delay .2)
(add-hook 'text-mode-hook (lambda ()
(set (make-local-variable 'company-idle-delay) nil))))
(setq company-tooltip-limit 20)
Use the YASnippet template system for typing common things (boilerplate, etc) in various languages.
(use-package yasnippet
:ensure t
:delight
:init
(add-hook 'after-init-hook 'yas-global-mode))
(use-package git-gutter
:ensure t
:commands global-git-gutter-mode
:delight
:init
(add-hook 'after-init-hook 'global-git-gutter-mode))
Compilation buffers aren’t coloured by default. This means ginkgo output is almost impossible to read. Let’s try to fix that.
(require 'ansi-color)
(defun colorize-compilation-buffer ()
(toggle-read-only)
(ansi-color-apply-on-region compilation-filter-start (point))
(toggle-read-only))
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)
If shellcheck is in the $PATH
, we can use it with flycheck.
(add-hook 'sh-mode-hook 'flycheck-mode)
Use paredit to make it harder to accidentally write syntactically incorrect lisp.
(use-package paredit
:ensure t
:delight
:commands paredit-mode
:init
(add-hook 'emacs-lisp-mode-hook 'paredit-mode))
Use rainbow-parens to make it easier to read the lisp we’re writing
(use-package rainbow-delimiters
:ensure t
:delight
:commands rainbow-delimiters-mode
:init
(add-hook 'emacs-lisp-mode-hook 'rainbow-delimiters-mode))
The emacs go mode will provide a bunch of useful things on its own. Including:
- integration with
gofmt
- integration with
godoc
- integration the Playground
- automatic import management
- code introspection with
godef
- test coverage displays
- various handy code navigation shortcuts
- semi-automatic
$GOPATH
detection – callM-x go-set-project
if you think your$GOPATH
isn’t currently set right.
;; Setup all the golang magic
(use-package go-mode
:ensure t)
Use goimports instead of gofmt. It’s just better.
(setq gofmt-command "goimports")
…and gofmt when we save
(add-hook 'before-save-hook 'gofmt-before-save)
YASnippet-go is a collection of snippets for go dev. They’re submoduled into this repo, and we enable them like so:
(add-to-list 'yas-snippet-dirs "~/.emacs.d/yasnippet-go")
If you start emacs from a shell which has a $GOPATH
already set
in it, we’ll just use that. However, sometimes when emacs is
started from OSX Finder or a Linux window manager, the environment
isn’t properly set up for go development. So, if we detect that
there’s no $GOPATH
set, we’ll use exec-path-from-shell to grab
it, and the $PATH
variable too.
(unless (getenv "GOPATH")
(use-package exec-path-from-shell
:ensure t
:delight
:config
(exec-path-from-shell-initialize)
(exec-path-from-shell-copy-env "GOPATH")
(exec-path-from-shell-copy-env "PATH")))
Notice that we only do this if the $GOPATH
is currently
unset. If you want to use direnv to set a different $PATH
and
$GOPATH
for every different project in your home dir, then
that’s fine – you can start a separate emacs in each project, and
they’ll all have the $GOPATH
and $PATH
variables you expected.
However, it’s often useful to have a single command history, kill
ring, and so on for all your projects. For this reason, you might
like to try just opening a single emacs, and using M-x
go-set-project
to manage your $GOPATH
.
We need a little setup to enable IDE-like control-click for code introspection. First a function to move the cursor to where you just clicked, and then perform the introspection.
(defun my-golang-introspect (event)
"Move the point to the mouse, and try to do godef-jump.
For IDE-like code introspection on mouse events like Control-Click"
(interactive "e")
(mouse-set-point event)
(godef-jump (point)))
Unfortunately there is already a function globally bound to the mouse-down portion of control-click. When we try to bind our introspection function to the whole click, this mouse-down function will get in the way. We’re going to want to keep it for everything except golang buffers, so we’ll have to overwrite it locally with something harmless.
(defun my-do-nothing ()
(interactive))
Now actually bind the keys:
- Use
C-c m
to manually trigger a go format (otherwise it’ll happen automatically on save anyway) - Use
C-c C-e
to ask what compile error is under point. - Use
M-.
(which meansAlt-.
on practically all keyboards these days) or Control-Click for code introspection (“go to definition”).
(defun my-go-keybindings ()
(local-set-key (kbd "C-c m") 'gofmt)
(local-set-key (kbd "C-c C-e") 'flymake-popup-current-error-menu)
(local-set-key (kbd "M-.") 'godef-jump)
(local-set-key (kbd "C-<mouse-1>") 'my-golang-introspect)
(local-set-key (kbd "C-<down-mouse-1>") 'my-do-nothing))
(add-hook 'go-mode-hook 'my-go-keybindings)
The go backend for flymake (to give us on-the-fly typechecking) isn’t available in MELPA, so we provide it as a git submodule. The go backend for eldoc (which shows the type and argument list of the function you’re calling) is in MELPA, so we can get that the usual way.
(use-package go-flymake
:load-path "~/.emacs.d/goflymake"
:delight)
(add-hook 'go-mode-hook 'flymake-mode)
(use-package go-eldoc
:ensure t
:delight)
(add-hook 'go-mode-hook 'go-eldoc-setup)
Use the company-go backend to provide smart (type-driven) autocompletion for go.
(use-package company-go
:ensure t
:delight)
In particular, note that this backend supports godocs. This means
that if you’re looking at a bunch of possible completions for your
line, and want to learn more about the function you’re about to
call, you can just highlight the candidate completion, and hit
<f1>
.
Use the go-backend precisely (always and only) when we’re editing golang files.
(defun my-company-go-backend ()
(set (make-local-variable 'company-backends) '(company-go))
(company-mode))
(add-hook 'go-mode-hook 'my-company-go-backend)
We can use gotest to run tests in a fine-grained way.
(use-package gotest
:ensure t
:delight)
The gotest home page suggests the following keybindings. I’m
ignoring the suggested benchmark keybinding (C-x b
) because it
clashes with switch-to-buffer
.
(define-key go-mode-map (kbd "C-x f") 'go-test-current-file)
(define-key go-mode-map (kbd "C-x t") 'go-test-current-test)
(define-key go-mode-map (kbd "C-x p") 'go-test-current-project)
(define-key go-mode-map (kbd "C-x x") 'go-run)
(define-key go-mode-map (kbd "C-x c") 'go-test-current-coverage)
Use markdown mode for editing markdown.
(use-package markdown-mode
:ensure t)
Use yaml mode for editing yaml.
(use-package yaml-mode
:ensure t)
Use json mode for editing json.
(use-package json-mode
:ensure t)
Use enhanced ruby mode for better colouring and syntax checking.
(use-package enh-ruby-mode
:ensure t)
(add-to-list 'auto-mode-alist
'("\\(?:\\.rb\\|ru\\|rake\\|thor\\|jbuilder\\|gemspec\\|podspec\\|/\\(?:Gem\\|Rake\\|Cap\\|Thor\\|Vagrant\\|Guard\\|Pod\\)file\\)\\'" . enh-ruby-mode))
Use yard mode for handling ruby yardocs.
(use-package yard-mode
:ensure t
:delight)
(add-hook 'enh-ruby-mode-hook 'yard-mode)
Use rubocop for on-the-fly linting.
(use-package rubocop
:ensure t
:delight)
(add-hook 'enh-ruby-mode-hook 'rubocop-mode)
Use robe mode for IDE-like features.
(use-package robe
:ensure t
:delight)
(add-hook 'enh-ruby-mode-hook 'robe-mode)
From the robe readme, note these dependencies:
- pry
- pry-doc >= 0.6.0 (on MRI)
- method_source >= 0.8.2 (for compatibility with the latest Rubinius)
Note that if your project is using Bundler, the dependencies have to be added to the Gemfile.
Use robe-mode’s autocompletion kit with the company autocompletion framework we set up earlier.
(eval-after-load 'company
'(push 'company-robe company-backends))
Just as in golang, we can bind control-click to code introspection.
(defun my-ruby-introspect (event)
"Move the point to the mouse, and try to do robe-jump.
For IDE-like code introspection on mouse events like Control-Click"
(interactive "e")
(mouse-set-point event)
(robe-jump (point)))
(defun my-ruby-keybindings ()
(local-set-key (kbd "C-<mouse-1>") 'my-ruby-introspect)
(local-set-key (kbd "C-<down-mouse-1>") 'my-do-nothing))
(add-hook 'enh-ruby-mode-hook 'my-ruby-keybindings)
The same trick is described both here and in the golang section. Extract it.
First, we’ll definitely need the basic haskell mode that other haskell goodies are built on.
(use-package haskell-mode
:ensure t)
Now we want some IDE-like features. There are a few options here, but recent popular ones appear to be Intero and Dante. It seems that Intero works best if your haskell project happens to use stack1, but Dante is better for casual scripting with ghci or just cabal2.
Let’s make Dante our default, but retain the option to use Intero if we want it.
This snippit is copied verbatim from the Dante README.
(use-package dante
:ensure t
:after haskell-mode
:commands 'dante-mode
:init
(add-hook 'haskell-mode-hook 'dante-mode)
(add-hook 'haskell-mode-hook 'flycheck-mode))
…and we can activate the hlint checker too.
(add-hook 'dante-mode-hook
'(lambda () (flycheck-add-next-checker 'haskell-dante
'(warning . haskell-hlint))))
First, we should ensure that intero is available if we want it, and that its hlint function is turned on.
(use-package intero
:ensure t
:config
(flycheck-add-next-checker 'intero '(warning . haskell-hlint)))
To switch from Dante to Intero and back, we provide
gds-haskell-switch-to-intero
and
gds-haskell-switch-to-dante
. These will have no effect on any
currently open haskell buffers, but will allow you to switch the
defaults for any buffers you open in future.
So the workflow for editing a stack project is:
M-x gds-haskell-switch-to-intero
C-x C-f ~/my-stack-project/main.hs
(or similar)
(defun gds-haskell-switch-to-intero ()
"Use Intero instead of Dante for all future haskell editing."
(interactive)
(remove-hook 'haskell-mode-hook 'dante-mode)
(remove-hook 'haskell-mode-hook 'flycheck-mode)
(add-hook 'haskell-mode-hook 'intero-mode))
(defun gds-haskell-switch-to-dante ()
"Use Dante instead of Intero for all future haskell editing."
(interactive)
(remove-hook 'haskell-mode-hook 'intero-mode)
(add-hook 'haskell-mode-hook 'dante-mode)
(add-hook 'haskell-mode-hook 'flycheck-mode))
(use-package latex-extra
:ensure t
:config
(add-hook 'LaTeX-mode-hook #'latex-extra-mode))
(use-package latex-preview-pane
:ensure t
:config
(latex-preview-pane-enable))
1 For example, if your stack project has different targets with
different build-depends lines, then intero can understand which
imports are available in any/all of them using M-x intero-targets
.
2 Intero doesn’t work at all without stack. Dante does.