(or emacs irrelevant)

Extended syntax for hydra docstrings

I've been getting more and more organized in tracking my tasks and time with Org-mode. Still using the usual suspects, of course: GTD and Pomodoro, I'm just getting more diligent with them than in the previous years.

So today I wanted to prettify the good old org-agenda-view-mode-dispatch, which is bound to v in org-agenda-mode. Currently, it's just a boring static message and read-char combination. Why not do it with a hydra instead?

Here's the current full code, that uses the newly extended doc syntax:

(define-key org-agenda-mode-map
    "v" 'hydra-org-agenda-view/body)

(defun org-agenda-cts ()
  (let ((args (get-text-property
               (min (1- (point-max)) (point))
               'org-last-args)))
    (nth 2 args)))

(defhydra hydra-org-agenda-view (:hint nil)
  "
_d_: ?d? day        _g_: time grid=?g? _a_: arch-trees
_w_: ?w? week       _[_: inactive      _A_: arch-files
_t_: ?t? fortnight  _f_: follow=?f?    _r_: report=?r?
_m_: ?m? month      _e_: entry =?e?    _D_: diary=?D?
_y_: ?y? year       _q_: quit          _L__l__c_: ?l?"
  ("SPC" org-agenda-reset-view)
  ("d" org-agenda-day-view
       (if (eq 'day (org-agenda-cts))
           "[x]" "[ ]"))
  ("w" org-agenda-week-view
       (if (eq 'week (org-agenda-cts))
           "[x]" "[ ]"))
  ("t" org-agenda-fortnight-view
       (if (eq 'fortnight (org-agenda-cts))
           "[x]" "[ ]"))
  ("m" org-agenda-month-view
       (if (eq 'month (org-agenda-cts)) "[x]" "[ ]"))
  ("y" org-agenda-year-view
       (if (eq 'year (org-agenda-cts)) "[x]" "[ ]"))
  ("l" org-agenda-log-mode
       (format "% -3S" org-agenda-show-log))
  ("L" (org-agenda-log-mode '(4)))
  ("c" (org-agenda-log-mode 'clockcheck))
  ("f" org-agenda-follow-mode
       (format "% -3S" org-agenda-follow-mode))
  ("a" org-agenda-archives-mode)
  ("A" (org-agenda-archives-mode 'files))
  ("r" org-agenda-clockreport-mode
       (format "% -3S" org-agenda-clockreport-mode))
  ("e" org-agenda-entry-text-mode
       (format "% -3S" org-agenda-entry-text-mode))
  ("g" org-agenda-toggle-time-grid
       (format "% -3S" org-agenda-use-time-grid))
  ("D" org-agenda-toggle-diary
       (format "% -3S" org-agenda-include-diary))
  ("!" org-agenda-toggle-deadlines)
  ("["
   (let ((org-agenda-include-inactive-timestamps t))
     (org-agenda-check-type t 'timeline 'agenda)
     (org-agenda-redo)))
  ("q" (message "Abort") :exit t))

And here's how it looks like in action, I simply pressed v while in the agenda:

hydra-sexp-docstring.png

Since many functions that org-agenda-view-mode-dispatch calls are toggles, it makes sense for hydra-org-agenda-view to display the status of these toggles.

And it's actually convenient to toggle a whole lot of things at once, and the default red hydra keys really come in handy here.

Quick explanation of the syntax

Each head of a hydra looks like:

(key cmd &optional doc &rest plist)

The fairly new bit that I'm using here is the ability to use a sexp instead of a plain string in the doc part. This sexp will be evaluated each time the doc is re-displayed. This means that it can represent a changing variable, for instance the state of a minor mode or a variable.

And here's the best part: the doc of each head can be quoted in the hydra's docstring by using the corresponding key, e.g. ?g?. This allows to have very complex docstrings while keeping them easily aligned in a tabular format.

Here is only the hydra's docstring, copied from the above code:

_d_: ?d? day        _g_: time grid=?g? _a_: arch-trees
_w_: ?w? week       _[_: inactive      _A_: arch-files
_t_: ?t? fortnight  _f_: follow=?f?    _r_: report=?r?
_m_: ?m? month      _e_: entry =?e?    _D_: diary=?D?
_y_: ?y? year       _q_: quit          _L__l__c_: ?l?

Doesn't that look simple?

Emacs completion for launching Linux desktop apps.

I'd like to highlight the new command counsel-linux-app that I recently added to the counsel package:

counsel-linux-app-1.png

This command looks through your /usr/share/applications/*.desktop and offers to launch them, just like the launcher that you normally get when you press the super key. My current system is Ubuntu, and the launcher here is really horrible, so I'm glad to have a much faster and reliable replacement.

Here's another screenshot, I open the keyboard settings quite often:

counsel-linux-app-2.png

Using rsync in dired

Here's a code snippet I've found long ago on the internet (the source seems to be no longer accessible), that has proven valuable time and time again:

;;;###autoload
(defun ora-dired-rsync (dest)
  (interactive
   (list
    (expand-file-name
     (read-file-name
      "Rsync to:"
      (dired-dwim-target-directory)))))
  ;; store all selected files into "files" list
  (let ((files (dired-get-marked-files
                nil current-prefix-arg))
        ;; the rsync command
        (tmtxt/rsync-command
         "rsync -arvz --progress "))
    ;; add all selected file names as arguments
    ;; to the rsync command
    (dolist (file files)
      (setq tmtxt/rsync-command
            (concat tmtxt/rsync-command
                    (shell-quote-argument file)
                    " ")))
    ;; append the destination
    (setq tmtxt/rsync-command
          (concat tmtxt/rsync-command
                  (shell-quote-argument dest)))
    ;; run the async shell command
    (async-shell-command tmtxt/rsync-command "*rsync*")
    ;; finally, switch to that window
    (other-window 1)))

(define-key dired-mode-map "Y" 'ora-dired-rsync)

Lets you copy huge files and directories without Emacs freezing up and with convenient progress bar updates. That is all.

Thanks to tmtxt, the mysterious hacker-person from whom the snippet likely originated . Good luck with getting your blog back up.

Visiting URLs and issues with counsel-find-file

Many experienced Emacs users are aware of ffap command:

Find FILENAME, guessing a default from text around point. If ffap-url-regexp is not nil, the FILENAME may also be an URL.

It's a great way to open an link, if you plan things in advance. But for me it was usually C-x C-f (annoyed grunt) C-g M-x ffap RET.

Now, thanks to counsel-find-file, it's C-x C-f (anticipated annoyance, followed by a sigh of relief) M-n.

With Ivy completion, M-n calls ivy-next-history-element, which tries to

predict the history element in case you've reached history's edge. The prediction usually simply inserts thing-at-point into the minibuffer. My favorite applications of this are:

  • C-s M-n - swiper thing-at-point, to get the occurrences of the current symbol in the current file.
  • C-c j M-n - counsel-git-grep thing-at-point, to get the mentions within the current project.
  • C-c g M-n - counsel-git thing-at-point to open a file to which the current symbol links.

One thing I've recently added is the \_<...\_> wrapper for when major-mode derives from prog-mode. Since the \_< regex matches the symbol start, and \_> matches the symbol end, there's no chance of getting partial matches. You can call undo or press M-n again in case the symbol bounds aren't useful.

Finally, C-x C-f M-n can be used to open URLs. Recently, I've added functionality to counsel-find-file that allows me to also visit Github issues by simply pointing at the plain issue number, e.g. #123 in either a version-controlled file or in a Magit buffer. The command will query:

$ git remote get-url origin

and fill in all the details. So I no longer bother with bug-reference-url-format and bug-reference-mode - now it's all automatic.

It's also possible to make it work for places other than Github, for instance this code (already included in counsel) makes it work for the Emacs Git repository:

(defun counsel-emacs-url-p ()
  "Return a Debbugs issue URL at point."
  (when (and (looking-at "#[0-9]+")
             (or
              (eq (vc-backend (buffer-file-name)) 'Git)
              (memq major-mode '(magit-commit-mode))))
    (let ((url (match-string-no-properties 0))
          (origin (shell-command-to-string
                   "git remote get-url origin")))
      (when (string-match "git.sv.gnu.org:/srv/git/emacs.git"
                          origin)
        (format "http://debbugs.gnu.org/cgi/bugreport.cgi?bug=%s"
                (substring url 1))))))

(add-to-list 'ivy-ffap-url-functions 'counsel-emacs-url-p)

avy 0.4.0 is out

This release consists of 77 commits done over the course of the last 7 months by me and many contributors. Similarly to the 0.3.0 release, the release notes are in Changelog.org. You can read them either at github or inside Emacs. Big thanks to all contributors.

avy.png

Highlights

A lot of new code is just straight upgrades, you don't need to do anything extra to use them. Below, I'll describe the other part of the new code, which is new commands and custom vars.

avy-goto-char-timer

This command now allows as many characters as you like, which makes it similar to a isearch + avy-isearch combination. As you type, you get an isearch-like highlight, then after a short delay you automatically get the avy selection.

Switch the action midway from goto to kill/mark/copy

This is similar to the cool feature of ace-window that allows you to switch the action after you get the avy prompt.

For example, suppose you have:

(global-set-key (kbd "M-t") 'avy-goto-word-1)

Here's what you can do now to a word that starts with a "w" and is select-able with "a":

  • To jump there: M-t w a.
  • To copy the word instead of jumping to it: M-t w na
  • To mark the word after jumping to it: M-t w ma.
  • To kill the word after jumping to it: M-t w xa.

You can customize avy-dispatch-alist to modify these actions, and also ensure that it plays nicely with your avy-keys, if you customized them. By default, it works fine, since avy-keys is '(?a ?s ?d ?f ?g ?h ?j ?k ?l) and the keys on avy-dispatch-alist are '(?x ?m ?n).

avy-pop-mark

This command reverses avy-push-mark which most of avy commands call. It has its own history and works across multiple windows and frames. I'm using it currently as an upgrade to my old (set-mark-command 4) lambda:

(global-set-key (kbd "M-p") 'avy-pop-mark)

Here's a line to make avy-pop-mark work also for swiper:

(advice-add 'swiper :before 'avy-push-mark)