Last update on .

There comes a time in life of every GNU Emacs user when they just have to let their old configuration go...

For me, that time came about one or two months ago when I sat down and decided to get my new laptop fully operational - all of that via Ansible, of course :)

While revisiting what pieces of software I need to have configured on the new machine, my trusty GNU Emacs editor/OS/Nobel prize nominee (ok, that last one is a bit of an exaggeration :P) came up on the list as well.

Some months ago I had a pleasure of attending a Stockholm Emacs Meetup group. In addition to meeting a bunch of interesting people, and seeing a couple of cool lectures, one thing that I found out, but did not know about beforehand, was that GNU Emacs apparently has a package manager! Plus the guy who was showing us how to make a small GNU Emacs package had cooler-looking set-up than I did :)

Now, if you are regular Emacs user, you'll probably go "D'oh, that was introduced with Emacs 24 in 2012". Well, just so you know, I started using Emacs probably something around 2009/2010. And, once I had the necessary bits and pieces going, I never really bothered going into much details and examining the new features. As long as my configuration kept working after upgrades, I was a happy camper.

Armed with this new knowledge, I started wondering if I could come-up with better set-up than now. Main reuqirements for me were:

  • Finding a good substitute for gpicker - fuzzy-match file finder. While I love gpicker (and might even miss it a bit), one issue with it is that I need to build it, and it won't work under terminal (since it's a GTK+ application.
  • Session saving. So I would preserve my layouts, open files etc from the last time.
  • Automating GNU Emacs package installation. I want to be able to easily reproduce my development/editing tools on new machines at a whim.

In addition to those, there was a plethora of other minor tweaks that I wanted moved over from my old configuration - either directly or by using some package.

Since I was quite curious on what other people do for common tasks, I did a couple of searches for most used GNU Emacs packages etc. At the end of the day, I settled on a very good list of packages called Awesome Emacs. It was neatly organised, and seemed to be quite comprehensive.

Finding replacements for most of my current set-up was fairly easy. There was a couple of tough choices, and quite a bit of testing, particularly when it comes down to navigation tools. For example, I was quite keen on trying out Helm for file-finding and navigation, but in the end got too annoyed by how it treats the TAB key (see issues 495 and 222), and instead went for simpler (although also feature-poorer) Projectile + Grizzl. While I understand the logic of Helm developers, this does not help with the fact that I use GNU Emacs on various servers as well, and this is usually a stock set-up where TAB behaves in standard way.

At this point there's still two things I somewhat miss from gpicker:

  • Ability to set base directory without creating .projectile file (useful to me when I want to browse through unpacked source archive quickly).
  • I preferred gpicker's semi-intelligent fuzzy-matching that was taking into account directory structure when typing in forward slashes (/). Still, the Grizzl fuzzy-matcher works just fine - maybe with a keystore or two more in some cases.

Session saving got replaced with Workgroups2. This worked quite well, although it took me some searching to figure out that this needs to be the last mode invoked during initialisation of GNU Emacs (otherwise it can't open the previously saved session/workspace).

Now, automation of package installation was a bit harder nut to crack. I was thinking of deploying some small piece of Elisp code that would install the necessary packages if they are not present. However, at some point I gave Cask a try instead, and it worked wonderfully for me.

A bit more sifting through the Awesome Emacs list, getting some new modes going, and integrating a couple of old snippets that I really got used to, and I ended up with a new satisfactory set-up. It has been a couple of weeks now already, and so far no major issues.

My next step will probably be to actually learn some Elisp in order to be able to customize a couple of things. In particular, I would like to close all open buffers, while taking into account some might need to be saved, via a key combo. Another thing I would love to have is ability to specify base directory for Projectile.

As a final treat, should anyone find it useful, here is the Cask file I ended-up with:

(source gnu)
(source melpa)

(depends-on "bm")
(depends-on "discover-my-major")
(depends-on "grizzl")
(depends-on "jedi")
(depends-on "jinja2-mode")
(depends-on "magit")
(depends-on "markdown-mode")
(depends-on "multiple-cursors")
(depends-on "php-mode")
(depends-on "projectile")
(depends-on "smex")
(depends-on "virtualenvwrapper")
(depends-on "web-mode")
(depends-on "which-key")
(depends-on "workgroups2")
(depends-on "yasnippet")

And here is my .emacs configuration file (you may want to fix path to Cask installation at top, and alter the username and e-mail address below - search for string John Doe):

;;;;;;;;;;
;; Cask ;;
;;;;;;;;;;

;; Make sure Cask is the first thing that is initialised.
(require 'cask "~/.cask/cask.el")
(cask-initialize)


;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General improvements ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;

;; IDO-based completion for M-x.
(require 'smex)
(smex-initialize)

;; Improved completion throughout Emacs.
(require 'ido)
(ido-mode t)

;; Make sure buffer names are unique (helps distinguish between files with same
;; names).
(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)

;; Pop-up for key combinations.
(require 'which-key)
(which-key-mode)

;; Disable Emacs startup screen.
(setq inhibit-startup-screen t)

;; Scroll one line at a time when moving around with keyboard.
(setq scroll-step 1)

;; Scroll one line at a time when moving around with mouse.
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))


;;;;;;;;;;;;;;;;
;; Appearance ;;
;;;;;;;;;;;;;;;;

;; Hide toolbar.
(tool-bar-mode -1)

;; Use white foreground and black background.
(add-to-list 'default-frame-alist '(foreground-color . "white"))
(add-to-list 'default-frame-alist '(background-color . "black"))

;; Set default font and font size.
(add-to-list 'default-frame-alist '(font . "DejaVu Sans Mono-12" ))
(set-face-attribute 'default t :font "DejaVu Sans Mono-12" )


;;;;;;;;;;;;;
;; Editing ;;
;;;;;;;;;;;;;

;; Visual bookmarks in a buffer.
(require 'bm)

;; Multiple-cursor editing.
(require 'multiple-cursors)

;; Advanced snippet support.
(require 'yasnippet)
(yas-global-mode 1)

;; When hitting ENTER, add new line and indent (instead of just new line).
(define-key global-map (kbd "RET") 'newline-and-indent)

;; Show line numbers on the left side.
(global-linum-mode 1)
;; Fix for auto-complete (Jedi at least) and linum mode (to prevent expand on
;; line numbers when popup is shown). Taken from
;; http://stackoverflow.com/questions/11484225/fix-an-auto-complete-mode-and-linum-mode-annoyance
(defadvice linum-update
  (around tung/suppress-linum-update-when-popup activate)
  (unless (ac-menu-live-p)
    ad-do-it))

;; Helper function that moves buffer downwards by one screen or jumps to end of
;; a file.
(defun sfp-page-down ()
  (interactive)
  (setq this-command 'next-line)
  (next-line
   (- (window-text-height)
      next-screen-context-lines)))

;; Helper function that moves buffer upward by one screen or jumps to beginning
;; of a file.
(defun sfp-page-up ()
  (interactive)
  (setq this-command 'previous-line)
  (previous-line
   (- (window-text-height)
      next-screen-context-lines)))

;; Helper function that can be used to enable TAB-based indentation.
(defun tabs-indentation ()
  (interactive)
  (setq indent-tabs-mode t)
  (setq tab-width 4)
  (setq c-basic-offset 4))

;; Set a bit wider fill column.
(setq fill-column 80)

;; Disable use of tabs for indentation.
(setq indent-tabs-mode nil)

;; Prompt used for note when a task is marked as complete (in org-mode).
(setq org-log-done 'note)


;;;;;;;;;;;;;;;;;;;;;;;;
;; Project management ;;
;;;;;;;;;;;;;;;;;;;;;;;;

;; Project file navigation.
(require 'projectile)
(projectile-mode)

;; Session management with support for multiple workgroups.
(require 'workgroups2)

;; Fuzzy-matching.
(require 'grizzl)
;; Fuzzy-matching in Projectile.
(setq projectile-completion-system 'grizzl)


;;;;;;;;;;;;;;;;;
;; Development ;;
;;;;;;;;;;;;;;;;;

;; Implementation of virtualenvwrapper in pure Elisp. Functionally mostly
;; equivalent to the original implementation.
(require 'virtualenvwrapper)
(venv-initialize-interactive-shells)
(venv-initialize-eshell)

;; A multi-purpose mode tailored for web development (mixed code and HTML
;; mostly).
(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
;; Enable auto-closing and pairing of tags in web-mode even in terminal.
(setq web-mode-enable-auto-closing t)
(setq web-mode-enable-auto-pairing t)

;; Git management.
(require 'magit)

;; Mode for editing Jinja2 templates.
(require 'jinja2-mode)
(add-to-list 'auto-mode-alist '("\\.j2\\'" . jinja2-mode))

;; Pyton code completion.
(require 'jedi)
;; Use a custom virtualenv with pre-installed Jedi server.
(setq jedi:environment-root "~/.virtualenvs/emacs-jedi")
(setq jedi:server-command '("~/.virtualenvs/emacs-jedi/bin/jediepcserver"))
(add-hook 'python-mode-hook 'jedi:setup)
(setq jedi:complete-on-dot t)

;; Use symmetric style for Python docstrings.
(setq python-fill-docstring-style 'symmetric)

;; Enforce PEAR coding rules for PHP mode.
(setq php-mode-force-pear t)


;;;;;;;;;;;;;;;;;;;
;; Miscelanneous ;;
;;;;;;;;;;;;;;;;;;;

;; Default user information
(setq user-full-name "John Doe")
(setq user-mail-address "john.doe@example.com")


;;;;;;;;;;;;;;;;;;;;;;;;
;; Keyboard shortcuts ;;
;;;;;;;;;;;;;;;;;;;;;;;;

;; Use Meta + arrows for switching between windows.
(windmove-default-keybindings 'meta)

;; Find file in current project.
(global-set-key (kbd "C-x p") 'projectile-find-file)

;; Prefix for Projectile.
(setq projectile-keymap-prefix (kbd "C-c C-p"))

;; Git status screen.
(global-set-key (kbd "C-x g") 'magit-status)

;; Shortcuts for handling visual bookmarks in a buffer.
(global-set-key (kbd "C-c b SPC") 'bm-toggle)
(global-set-key (kbd "C-c b n")   'bm-next)
(global-set-key (kbd "C-c b p") 'bm-previous)

;; Shortcuts for multiple-cursor editing.
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)

;; Improved buffer viewer.
(global-set-key (kbd "C-x C-b") 'ibuffer)

;; Use Smex for command completion.
(global-set-key (kbd "M-x") 'smex)

;; Plain command completion (in case it's needed).
(global-set-key (kbd "C-c C-c M-x") 'execute-extended-command)

;; Shortcuts for modifying window sizes.
(define-key global-map (kbd "S-M-<left>") 'shrink-window-horizontally)
(define-key global-map (kbd "S-M-<right>") 'enlarge-window-horizontally)
(define-key global-map (kbd "S-M-<down>") 'shrink-window)
(define-key global-map (kbd "S-M-<up>") 'enlarge-window)

;; Shortcuts for better-behaving page-up/page-down functions (jump up/down one
;; screen, or jump to beginning/ending of a file).
(global-set-key [next] 'sfp-page-down)
(global-set-key [prior] 'sfp-page-up)

;; Remap yasnippets shortcut.
(define-key yas-minor-mode-map (kbd "<tab>") nil)
(define-key yas-minor-mode-map (kbd "TAB") nil)
(define-key yas-minor-mode-map (kbd "C-x y") 'yas-expand)

;; Shortcut for showing current or provided major mode info.
(global-set-key (kbd "C-h C-m") 'discover-my-major)
(global-set-key (kbd "C-h M-m") 'discover-my-mode)

;;;;;;;;;;;;;;;;;;;;;
;; End of start-up ;;
;;;;;;;;;;;;;;;;;;;;;

;; Workgroups mode has to be enabled as the last thing during
;; initialisation. Otherwise, last workgroup might not get loaded correctly.
(workgroups-mode 1)

Pingbacks

Pingbacks are closed.

Comments

Comments are closed.