(or emacs irrelevant)

hydra.el talk @ london.el

Today I gave a talk on hydra at the London Emacs meet up - london.el. My screen capture is available on youtube, and the slides are here.

Thanks to @dotemacs for organizing the event and inviting me.

Ivy 0.9.0 is out

Intro

Ivy is a completion method that's similar to Ido, but with emphasis on simplicity and customizability.

Overview

The current release constitutes of 339 commits and almost a full year of progress since 0.8.0. Many issues ranging from #493 to #946 were fixed. The number of people who contributed code as grown to 63; thanks, everyone!

Details on changes

Changelog.org has been a part of the repository since 0.6.0, you can get the details of the current and past changes:

Highlights

Many improvements are incremental and don't require any extra code to enable. I'll go over a few selected features that require a bit of information to make a good use of them.

A better action choice interface

For all ivy completions, pressing M-o allows to execute one of the custom actions for the current command. Now you have an option to use hydra for selecting an action. Use this code to turn on the feature:

(require 'ivy-hydra)

One big advantage of the new interface is that you can peak at the action list with M-o without dismissing the candidate list. Press M-o again to go back to candidate selection without selecting an action.

Here's some code from my config that ensures that I always have some extra actions to choose from:

(defun ora-insert (x)
  (insert
   (if (stringp x)
       x
     (car x))))

(defun ora-kill-new (x)
  (kill-new
   (if (stringp x)
       x
     (car x))))

(ivy-set-actions
 t
 '(("i" ora-insert "insert")
   ("w" ora-kill-new "copy")))

Grepping

The new counsel-rg joins the group of grepping commands in counsel (counsel-ag, counsel-git-grep, counsel-grep, counsel-pt). It wraps around the newly popular and very fast ripgrep shell tool.

A nice improvement to the grepping commands is the ability to specify extra flags when you press C-u (universal-argument) before the command. See this gif for an example of excluding *.el from the files searched by ag.

counsel-find-file

  • Press M-o b to change the current directory to one of the virtual buffers' directories. You continue to select a file from that directory.

  • Press M-o r to find the current file as root.

counsel-git-log

You can now customize counsel-git-log-cmd. See #652 for using this to make counsel-git-log work on Windows.

counsel-mode

  • counsel-info-lookup-symbol now substitutes the built in info-lookup-symbol.
  • Pressing C-r while in the minibuffer of eval-expression or shell-command now gives you completion of your previous history.

counsel-yank-pop

Use the new counsel-yank-pop-separator variable to make counsel-yank-pop look like this.

ivy

There was breaking change for alist type collections some months ago. Right now the action functions receive an item from the collection, instead of (cdr item) like before. If anything breaks, the easy fix is to add an extra cdr to the action function.

Unique index for alist completion was added. The uniqueness assumption is that the completion system is passed a list of unique strings, of which one (or more) are selected. Unlike plain string completion, alists may require violating the uniqueness assumption: there may be two elements with the same car but different cdr. Example: C function declaration and definition for tag completion. Until now, whenever two equal strings were sent to ivy-read, only the first one could be selected. Now, each alist car gets an integer index assigned to it as a text property 'idx. So it's possible to differentiate two alist items with the same key.

Action functions don't require using with-ivy-window anymore. This allows for a lot of simplification, e.g. use insert instead of (lambda (x) (with-ivy-window (insert x))).

ivy-switch-buffer

You can now customize faces in ivy-switch-buffer by the mode of each buffer. Here's a snippet from my config:

(setq ivy-switch-buffer-faces-alist
      '((emacs-lisp-mode . swiper-match-face-1)
        (dired-mode . ivy-subdir)
        (org-mode . org-level-4)))

Looks neat, I think:

ivy-switch-buffer-faces-alist

swiper

Customize swiper-include-line-number-in-search if you'd like to match line numbers while using swiper.

New Commands

counsel-bookmark

Offers completion for bookmark-jump. Press M-o d to delete a bookmark and M-o e to edit it.

A custom option counsel-bookmark-avoid-dired, which is off by default, allows to continue completion for bookmarked directories. Turn it on with:

(setq counsel-bookmark-avoid-dired t)

and when you choose a bookmarked directory, the choice will be forwarded to counsel-find-file instead of opening a dired-mode buffer.

counsel-colors-emacs and counsel-colors-web

Completion for colors by name:

  • the default action inserts the color name.
  • M-o h inserts the color hex name.
  • M-o N copies the color name to the kill ring.
  • M-o H copies the color hex name to the kill ring.

The colors are displayed in the minibuffer, it looks really cool:

counsel-colors-emacs

You also get 108 shades of grey to choose from, for some reason.

counsel-faces

Completion for faces by name:

counsel-faces

counsel-command-history

Shows the history of the Emacs commands executed and lets you select and eval one again. See #826 for a nice screenshot.

counsel-company

Picks up company's candidates and inserts the result into the buffer.

counsel-dired-jump and counsel-file-jump

Jump to a directory or a file in the current directory.

counsel-dpkg and counsel-rpm

Wrap around the popular system package managers.

counsel-package

Install or uninstall Emacs packages with completion.

counsel-mark-ring

Navigate the current buffer's mark ring.

counsel-semantic

Navigate the current buffer's tags.

counsel-outline

Navigate the current buffer's outlines.

counsel-recentf

Completion for recentf.

counsel-find-library

Completion for find-library.

counsel-hydra-heads

Completion for the last hydra's heads.

counsel-org-agenda-headlines

Completion for headlines of files in your org-agenda-files.

Outro

Again, thanks to all the contributors. Happy hacking!

Using Emacs as a C++ IDE

Recently, I've had to code some C++ at work. And I saw it as a good opportunity to step up my Emacs' IDE game. I've eschewed clang-based tools until now, but GCC isn't adding AST support any time soon, and CEDET is too slow and too clumsy with macros for the particular project that I had. Here's the line in Eigen that broke the camel's back. Basically it's 30 lines of macros that expand to 30 lines of typedefs. Maybe it's a valid implementation choice, I'd rather avoid the macros altogether, but in any case I couldn't get CEDET to parse that.

Use Rtags for navigation

The first thing I tried was rtags. My project was CMake-based, so I just put this line in my subdirectory Makefile:

cmake:
    cd ../build && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..

The -DCMAKE_EXPORT_COMPILE_COMMANDS=1 causes a compile_commands.json file to be emitted during the actual compilation. This file describes the compile flags for every source file. These flags are essential in helping the parser understand what's going on.

Then, in the build directory I start:

rdm & rc -J .

Finally, rtags-find-symbol-at-point should work now. I still like to use CEDET as backup, it's pretty good at tracking variables defined in the current function:

(defun ciao-goto-symbol ()
  (interactive)
  (deactivate-mark)
  (ring-insert find-tag-marker-ring (point-marker))
  (or (and (require 'rtags nil t)
           (rtags-find-symbol-at-point))
      (and (require 'semantic/ia)
           (condition-case nil
               (semantic-ia-fast-jump (point))
             (error nil)))))
(define-key c++-mode-map (kbd "M-.") 'ciao-goto-symbol)
(define-key c++-mode-map (kbd "M-,") 'pop-tag-mark)

For my other C++ projects which aren't CMake-based, I use the excellent bear tool to emit the compile_commands.json file. It's as easy as:

make clean
bear make

Use Irony for completion

It didn't take long to figure out that rtags isn't great at completion. I almost accepted that's just the way it is. But this morning I decided to make some changes and try irony-mode. And it worked beautifully for completion! What's ironic, is that irony-mode doesn't have goto-symbol, so the time spent to figure out rtags was worth it.

Here's my Irony setup; I only changed the C-M-i binding to the newly written counsel-irony, now available in the counsel package on MELPA:

(add-hook 'c++-mode-hook 'irony-mode)
(add-hook 'c-mode-hook 'irony-mode)

(defun my-irony-mode-hook ()
  (define-key irony-mode-map
      [remap completion-at-point] 'counsel-irony)
  (define-key irony-mode-map
      [remap complete-symbol] 'counsel-irony))
(add-hook 'irony-mode-hook 'my-irony-mode-hook)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)

And here are some screenshots of counsel-irony:

screenshot-1

First of all, the completion is displayed inline, similarly to modern IDEs. You can use all of Ivy's regex tricks to complete your candidate:

screenshot-2

Note how the power of regex matching allows me to narrow the initial 1622 candidates to only 22 functions that have src1 and src2 as arguments. One of the candidates is cut off for being longer than the window width. You can still match against the invisible text, but you won't see it. It's possible to use C-c C-o (ivy-occur) to store the current candidates into a buffer:

screenshot-3

Clicking the mouse on any of the lines in the new buffer will insert the appropriate symbol into the C++ buffer.

Outro

I'd like to thank the authors of rtags and irony-mode for these nice packages. Hopefully, counsel-irony is a nice addition. Happy hacking!

Quickly ediff files from dired

ediff.el --- a comprehensive visual interface to diff & patch

I wrote about ediff years ago. Today, I'll just reference a useful ediff snippet from my config that I've added some time ago and refined only recently.

The premise is quite simple: press e in dired-mode to immediately ediff two marked files, no questions asked:

(define-key dired-mode-map "e" 'ora-ediff-files)

And here's the code, with a few bells and whistles:

;; -*- lexical-binding: t -*-
(defun ora-ediff-files ()
  (interactive)
  (let ((files (dired-get-marked-files))
        (wnd (current-window-configuration)))
    (if (<= (length files) 2)
        (let ((file1 (car files))
              (file2 (if (cdr files)
                         (cadr files)
                       (read-file-name
                        "file: "
                        (dired-dwim-target-directory)))))
          (if (file-newer-than-file-p file1 file2)
              (ediff-files file2 file1)
            (ediff-files file1 file2))
          (add-hook 'ediff-after-quit-hook-internal
                    (lambda ()
                      (setq ediff-after-quit-hook-internal nil)
                      (set-window-configuration wnd))))
      (error "no more than 2 files should be marked"))))

Some notes on how the extra code adds convenience:

  1. In case no files are marked, the file at point is used as the first file, and read-file-name is used for the second file. Since I have the magic (setq dired-dwim-target t) in my config, in case a second dired buffer is open, dired-dwim-target-directory will offer it as the starting directory during completion. Very useful to compare two files in two different directories.

  2. Depending on the order of the arguments to ediff-files, the changes will appear either as added or removed; file-newer-than-file-p tries to put the arguments in a logical order by looking at the files' last change times.

  3. ediff-after-quit-hook-internal is used to restore the previous window configuration after I quit ediff with q.

That's about it. Hopefully, it's useful. Happy hacking.

Make it so. file1 -> Makefile -> file2

Intro

make-it-so is an old package of mine that I haven't yet highlighted on the blog. This package helps you manage a collection of makefiles that are used to generate new files from existing files using shell commands.

You can think of these makefiles as a directory of shell functions, arranged by the extension of the files that they operate on:

$ cd make-it-so && find recipes -name Makefile
recipes/ipynb/to-md/Makefile
recipes/ogv/crop/Makefile
recipes/ogv/trim/Makefile
recipes/ogv/to-gif/Makefile
recipes/pdf/to-txt/Makefile
recipes/md/to-org/Makefile
recipes/md/to-html/Makefile
recipes/cue/split/Makefile
recipes/dot/to-png/Makefile
recipes/m4a/to-mp3/Makefile
recipes/flac/to-mp3/Makefile
recipes/gif/gifsicle/Makefile
recipes/svg/to-png/Makefile
recipes/chm/to-pdf/Makefile
recipes/txt/encode-utf8/Makefile
recipes/mp4/to-mp3/Makefile
recipes/mp4/trim/Makefile
recipes/mp4/replace-audio/Makefile
recipes/png/to-gif/Makefile

When you call make-it-so on a particular file, you get completion for the recipes that are available for that file extension, along with an option to create a new recipe.

Example 1: convert pdf to txt

Suppose you want to convert a PDF file test.pdf to a text file test.txt.

In case the recipe is in your collection, you don't have to remember the command or the command switches to do it anymore:

  1. Navigate to test.pdf in dired and press , (bound to make-it-so).
  2. Select the recipe you want using completion: to-txt is already provided.
  3. Your file and the makefile recipe are moved to the staging area:

    ./to-txt_test.pdf/test.pdf
    ./to-txt_test.pdf/Makefile
    
  4. The makefile is opened in a new buffer with the following bindings:

    • f5 (mis-save-and-compile) will run compile, creating test.txt in the current directory.
    • C-, (mis-finalize) will finalize the operation, moving test.pdf and test.txt to the parent directory (where test.pdf was before), and deleting the staging directory.
    • C-M-, (mis-abort) will move test.pdf back to its initial location and delete all generated files. This command is effectively an undo for make-it-so.

It takes a large chunk of text to describe everything, but the key sequence for doing all this is quite short:

  1. , - make-it-so.
  2. RET - select to-txt.
  3. f5 - create test.txt.
  4. C-, - finalize.

Example 2: make a gif from a series of png images

I'll describe the process of creating a high quality gif like this one, which describes the effect of the C key in lispy:

lispy-convolute

First, I use kazam to take two png screenshots of my Emacs screen:

$ ls -1 *.png
Screenshot 2017-02-25 16:14:49.png
Screenshot 2017-02-25 16:15:10.png

I plan to use gifsicle to sequence the still images into a gif. But it only takes gif as the input format, so first I have to convert my png files to non-animated gif files.

I open the dired buffer where they are located and mark them with m (dired-mark). Then call make-it-so with , and select to-gif recipe. This recipe has no parameters, so there's nothing else to do but f5 C-,. Two new files are created:

$ ls -1 *.png *.gif
Screenshot_2017-02-25 16:14:49.gif
Screenshot_2017-02-25 16:14:49.png
Screenshot_2017-02-25 16:15:10.gif
Screenshot_2017-02-25 16:15:10.png

Note that the file names (the defaults of kazam) are problematic when used with makefiles, since they contain spaces and colons. The Elisp layer of make-it-so takes care of that. It renames the files back and forth so that the logic in the makefiles remains simple.

Next, I mark the two gif files using *% (dired-mark-files-regexp), press , once more and select the gifsicle recipe. I'm presented a makefile with the following contents:

# ——— parameters —————————————————————————————————

# delay between frames in hundredths of a second
delay = 60

# ——— implementation —————————————————————————————
DIRGIF = $(shell ls *.gif | grep -v anime.gif)

all: anime.gif

anime.gif: Makefile $(DIRGIF)
    rm -f anime.gif
    gifsicle --delay=$(delay) --colors=256 --loop $(DIRGIF) > $@
    echo $@ >> provide

clean:
    rm -f anime.gif provide

install-tools:
    sudo apt-get install gifsicle

.PHONY: all install-tools clean

The most commonly useful parameter, the delay between frames, is nicely documented at the top. I don't have to remember that the switch name is --delay or that the switch style --delay=60 is used. I simply change the number above until I get the result that I want.

Example 3: add a new recipe

As a sample scenario, assume you want to convert *.svg to *.png.

Step 1

An internet search leads to Stack Overflow and this command:

inkscape -z -e test.png -w 1024 -h 1024 test.svg

Navigate to the file(s) in dired and call make-it-so with ,. No default actions are available, so just type "to-png" and hit RET. The "to-" prefix signifies that this is a conversion, adapting the Makefile to this form:

# This is a template for the Makefile.
# Parameters should go in the upper half as:
#     width = 200
# and be referenced in the command as $(width)

# ____________________________________________

DIRSVG = $(shell dir *.svg)

DIRPNG = $(DIRSVG:.svg=.png)

all: clean Makefile $(DIRPNG)

%.png: %.svg
    echo "add command here"
    echo $@ >> provide

clean:
    rm -f *.png provide

# Insert the install command here.
# e.g. sudo apt-get install ffmpeg
install-tools:
    echo "No tools required"

.PHONY: all install-tools clean

If the action name doesn't have a "to-" prefix, the transformation is assumed to be e.g. "svg" -> "out.svg". You can change this of course by editing the Makefile.

Step 2

In case the command needs additional packages in order to work you might want to change echo "No tools required" to the appropriate package install instruction, e.g. sudo apt-get install inkscape.

When you're on a new system, this will serve as a reminder of what you should install in order for the Makefile to work. Simply call:

make install-tools

Step 3

Replace echo "add command here" with:

    inkscape -z -e $@ -w $(width) -h $(height) $^
  • The parameters width and height will go to the top of the Makefile, where they can be customized.

  • $@ refers to the output file, test.png in this case.

  • $^ refers to the input file, test.svg in this case.

That's it. You can see the final Makefile here. Test if the command works with f5 from the Makefile. If you're happy with it, call mis-finalize with C-, from dired. The Makefile will be saved for all future calls to make-it-so.

Outro

To summarize the advantages of make-it-so:

  • Write the recipe one time, never have to look up how to do the same thing a few months from now.
  • A chance to write the recipe zero times, if someone in the community has already done it and shared the recipe with you.
  • The Elisp layer takes care of hairy file names.
  • Parallel commands on multiple files, i.e. make -j8, are provided for free.

The most important usage tip: until you're sure that the command and the Makefile work properly make backups. In fact, make backups period. Happy hacking!