Skip to content

Latest commit

 

History

History
461 lines (388 loc) · 15.6 KB

programming.org

File metadata and controls

461 lines (388 loc) · 15.6 KB

Programming

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

Git

(use-package git-gutter
  :ensure t
  :commands global-git-gutter-mode
  :delight
  :init
  (add-hook 'after-init-hook 'global-git-gutter-mode))

Compilation

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)

Shell Scripting

If shellcheck is in the $PATH, we can use it with flycheck.

(add-hook 'sh-mode-hook 'flycheck-mode)

Elisp

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

Golang

Basics

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 – call M-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")

GOPATH wrangling

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.

Key and Mouse Bindings

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 means Alt-. 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)

On-the-fly typechecking and docs

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)

Autocompletion

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

./images/company-autocomplete-plus-docs.png

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)

Testing

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)

Markdown

Use markdown mode for editing markdown.

(use-package markdown-mode
  :ensure t)

YAML

Use yaml mode for editing yaml.

(use-package yaml-mode
  :ensure t)   

JSON

Use json mode for editing json.

(use-package json-mode
  :ensure t)

Ruby

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

Mouse bindings

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)

Reduce code duplication

The same trick is described both here and in the golang section. Extract it.

Haskell

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.

Dante

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

Intero

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

Switching between them

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

LaTeX

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

Footnotes

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.