Jan 05, 2014

Emacs tabbar tuning

I like tabbar-mode and the buffer cycling options it provides. I can cycle between buffers in a single group (especially useful when cycling scope is limited to tabs) and between different groups. This makes it much easier to manage many open buffers within different contexts (such as projects).

(require 'tabbar)
(tabbar-mode t)
(setq tabbar-cycle-scope 'tabs)

(global-set-key (kbd "s-{") 'tabbar-backward-group)
(global-set-key (kbd "s-}") 'tabbar-forward-group)
(global-set-key (kbd "s-[") 'tabbar-backward)
(global-set-key (kbd "s-]") 'tabbar-forward)


The grouping defaults are reasonable (by major mode name), but could be better. We'll use the default tabbar-buffer-groups-function from tabbar.el as a basis. This section contains the entire function budy.

(defun my-tabbar-buffer-groups ()
"Return the list of group names the current buffer belongs to.
Return a list of one element based on major mode."

*Process* group

The default grouping separates "process" buffers into their own group. This is a problem with flycheck, for example. When flycheck does its thing the buffer briefly becomes a "process" buffer, causing tabbar to yank it between groups. This makes for a very jittery tab bar. Let's fix that by removing the "Process" group altogether:

   (setq my-group-by-project nil)
    ;; ((or (get-buffer-process (current-buffer))
    ;;      ;; Check if the major mode derives from `comint-mode' or
    ;;      ;; `compilation-mode'.
    ;;      (tabbar-buffer-mode-derived-p
    ;;       major-mode '(comint-mode compilation-mode)))
    ;;  "Process"
    ;;  )

OK, better. Now our flychecked files stay grouped by major mode.

    ((member (buffer-name)
             '("*scratch*" "*Messages*"))
    ((eq major-mode 'dired-mode)
    ((memq major-mode
           '(help-mode apropos-mode Info-mode Man-mode))
    ((memq major-mode
             rmail-edit-mode vm-summary-mode vm-mode mail-mode
             mh-letter-mode mh-show-mode mh-folder-mode
             gnus-summary-mode message-mode gnus-group-mode
             gnus-article-mode score-mode gnus-browse-killed-mode))

Grouping by project

While working on multiple projects the tab bar quicly becomes unwieldy. I prefer to group mine by "project name:mode name" instead. I use projectile for getting the name of the project.

Also, for small projects I like to be able to group by project name alone. For this I defined a simple toggle variable my-group-by-project.

     ;; Return `mode-name' if not blank, `major-mode' otherwise.
     (let ((group 
            (if (and (stringp mode-name)
                     ;; Take care of preserving the match-data because this
                     ;; function is called when updating the header line.
                     (save-match-data (string-match "[^ ]" mode-name)))
              (symbol-name major-mode))))
       (if (projectile-project-p)
           (if my-group-by-project
             (format "%s:%s" (projectile-project-name) group))

Performance tuning

tabbar-mode calls tabbar-buffer-groups-function A LOT -- for each open buffer for every single keystroke. Since projectile-project-name is not a super fast function this will slow Emacs down. Assuming we never want buffers to change groups, we could quasi-memoize this function and cache group names per every project. We can do this with a pseudo-closure.

(defun my-cached (func)
  "Turn a function into a cache dict."
  (lexical-let ((table (make-hash-table :test 'equal))
                (f func))
    (lambda (key)
      (let ((value (gethash key table)))
        (if value
          (puthash key (funcall f) table))))))

;; evaluate again to clear cache
(setq cached-ppn (my-cached 'my-tabbar-buffer-groups))

(defun my-tabbar-groups-by-project ()
  (funcall cached-ppn (buffer-name)))

(setq tabbar-buffer-groups-function 'my-tabbar-groups-by-project)

Now, tabbar will be fetching a cached group name for existing buffers. To wipe the cache we recreate the closure by reevaluating.

Finally, wire the toggle (we must clear the cache for regrouping to take effect):

(defun my-toggle-group-by-project ()
  (setq my-group-by-project (not my-group-by-project))
  (message "Grouping by project alone: %s"
           (if my-group-by-project "enabled" "disabled"))
  (setq cached-ppn (my-cached 'my-tabbar-buffer-groups)))

Oct 01, 2013

The setup, part 2

Life has changed a little since the last report.

  • A maxed out MacBook Air has replaced the Pro and I love it. The screen hinge is weak, the screen is reflective — those are annoying, yes. But overall, it weighs nothing, runs a VM effortlessly and I can work unplugged the whole day.
  • sshfs and 10.8 are no longer friends. Sublime cannot reliably detect file changes anymore, so instead the guest OS now mounts a shared folder.
  • Having to use mercurial sucks and may deserve a separate post.

May 30, 2013

The setup

Over the past couple of years I have had the fortune to pick up some great habits from great people. It is time to share an updated setup. These are the things my work-life currently depends on.

  • Python

  • Life environment: OSX, iMac, MacBook Pro, and recently back to iPhone.

  • Dev environment: VirtualBox. I still like free, and VBox works just fine.

  • Dev OS: Ubuntu Server. No more esoteric nonsense like Arch. The skinnier Server Edition is preferred as I need very little graphics capability from my Linux. Cannot stand Linux GUIs.

    Now, how it all works together:

    • Linux runs the code and manages packages. All the dev stuff stays there. Linux runs in headless mode.
    • OSX runs the editor (Sublime Text), terminal (iTerm), browsers, etc. and keeps my senses happy with beautiful fonts.
  • sshfs: the sanest way I know to share files between host and guest OSes. I keep a virtual directory mounted locally on OSX and point the editor there.

  • X11: always ready when I do need to bring up a GUI tool over a tunnel (I always ssh -X into my virtual machine), which are:

  • git gui, gitk: if they are not part of your git-fu already, you are missing out
  • tig: very useful for a quick history browse
  • IPython+Notebook: I often run my code as I'm testing it, and IPython is the way to interact with the interpreter. As the experiment grows or whenever working with data plots, firing up Notebook is worth the hassle.
  • nose+mock: I finally learned to stop worrying and love the tests. Yes, tests are always worth having.
  • Sublime Text: this probably deserves a separate post. In short, breaking away from the IDE land has been a happy change.
  • pianobar

Nov 03, 2010

OS X system-wide EQ with Soundflower and AU Lab

Entirely free. Requires XCode.

  1. Download and install Soundflower.

  2. Open Audio MIDI Setup.

  3. Create Aggregate Device (Audio -> Open aggregate device editor), constisting of:

    1. Built-in output (or whatever output you need)
    2. Soundflower
  4. Set default output to Soundflower.

  5. Start audio. At this point you should hear nothing.

  6. Open AU Lab (/Developer/Applications/Audio/AU Lab.app).

  7. Add 1 stereo input track, set Audio Device to "Aggregate device". Click "Done".

  8. Now you should hear sound and see level meters move.

  9. In either Audio 1 or Output 1 strip, click Effects drop-down and select Apple Graphic EQ, or whatever else you want!


Mar 05, 2010

Eclipse plugins fail after installation (on Archlinux)

After installing Atlassian Connector in Eclipse and everything just goes to shit: all installed plugins disappear from preferences. The problem, as it turns out, lies in installing Eclipse as root (sudo pacman -S) and later installing plugins while Eclipse is running as non-root.

So, the right thing to do is either:

  • run Eclipse as root when installing packages; or
  • chown /usr/share/eclipse to the appropriate user.


Feb 09, 2010

Blank Java windows in Awesome

After updating to GWT 2.0.1 (official) and moving our project to a arch/vbox, I had a small adventure trying to get DevMode window to come up: it just wouldn't. The window just came up blank and nothing else happened.

It turns out you are running awesome, Java may not play nice. You need this:


And remember to export GDK_NATIVE_WINDOWS=true to make all your Eclipse buttons work correctly.

Thanks to selckin on ##gwt for wmname suggestion.

Feb 02, 2010

Merging Things.app between multiple Macs with an iPhone

You can sync two Things instances between two Macs easily by copying ~/Library/Application Support/Cultured Code/. But this works if only the donor instance has diverging changes. What if both instances have unique new items?

You can sync Things iPhone to multiple Things.app instances. My problem was that items move from a Mac to the iPhone, but not from the iPhone to the other Mac. And this is the solution for merging Things from one Mac to the other:

  1. Bring up Things on Mac A, sync iPhone, shut down Things.
  2. Bring up Things on Mac B, sync iPhone, shut down Things. Now iPhone has items from both Macs, but Mac B doesn't.
  3. On Mac B, remove/backup ~/Library/Application Support/Cultured Code/Things/Database.xml
  4. Pair and sync iPhone to Mac B again.

Jan 26, 2010

Pretty fonts in Linux

After two weeks of research and experimentation, finally arrived at the settings that stop Eclipse from making you want to gouge your eyes out.

This goes into ~/.Xdefaults:

Xft.dpi: 76 #84 for 27" iMac
Xft.antialias: true
Xft.hinting: true
Xft.hintstyle: hintslight
Xft.rgba: rgb

And this is what's installed (in that order):

drwxr-xr-x 2 root root 4096 Jan 15 11:26 pkgconfig-0.23-1
drwxr-xr-x 2 root root 4096 Jan 15 12:00 freetype2-ubuntu-2.3.11-2
drwxr-xr-x 2 root root 4096 Jan 15 12:02 fontconfig-ubuntu-2.6.0-8
drwxr-xr-x 2 root root 4096 Jan 15 12:29 libxft-ubuntu-2.1.14-2
drwxr-xr-x 2 root root 4096 Jan 15 13:05 cairo-ubuntu-1.8.8-1
drwxr-xr-x 2 root root 4096 Jan 18 10:13 ttf-inconsolata-20090215-1
drwxr-xr-x 2 root root 4096 Jan 18 10:15 ttf-bitstream-vera-1.10-6
drwxr-xr-x 2 root root 4096 Jan 25 22:06 ttf-dejavu-2.30-1

And this is why we like to use Macs.

By the way, the .Xdefaults bit above also helps with X11 on the Mac (yay for wireshark).

Jan 21, 2010

Upgrading Poly Evoler (Rack) OS with Edirol PCR-M1

This was unexpected enough to warrant a mention: the PER hangs when it is sent any of the upgrade sysex'es (OS, DSP, Voice). It shows the "loading" message, but the countdown doesn't start.

The setup:

  • PER v2.0, upgrading to 2.1
  • Edirol PCR-M1 as a MIDI interface
  • 10.6 and 10.5 Macs, latest drivers for the respective OS'es
  • SysEx Librarian, PolyEvolver500 editor

So it appears the PCR-M1 is doing something of which Dave Smith does not approve. I would be curious to find out what.

For the meantime, UM-880 worked just fine.

Jan 19, 2010

Debugging with PyDev

This is surprisingly elusive, so here it is. After creating a debug/run target with Run/Debug as... -> Python run, add this variable in the Environment tab:


If you're debugging Django, add another:


You will also want to add --noreload to your runserver argument.

Next → Page 1 of 2