Author Archives: Ben Maughan

Pop up a quick shell with shell-pop

There are several ways of running a shell inside Emacs. I don’t find that I need to use it very often as I do so much within Emacs these days, but when I do it’s handy to quickly bring up the shell, run a command the then dismiss it again. The shell-pop package does this very smartly. One key combo (I use C-t) pops up a shell window for the directory containing the file you are currently editing, and then C-t dismisses the shell window when you are done.

The github page has lots of details on how to configure it, and I use a fairly minimal setup and use the ansi-term terminal emulator and zsh as my shell. Here is my configuration:

(use-package shell-pop
  :bind (("C-t" . shell-pop))
  :config
  (setq shell-pop-shell-type (quote ("ansi-term" "*ansi-term*" (lambda nil (ansi-term shell-pop-term-shell)))))
  (setq shell-pop-term-shell "/bin/zsh")
  ;; need to do this manually or not picked up by `shell-pop'
  (shell-pop--set-shell-type 'shell-pop-shell-type shell-pop-shell-type))

The last line is needed but I can’t remember where I got it from!

Even more PDF-tools tweaks

Once I started tweaking pdf-tools I couldn’t resist a few more bits of streamlining my workflow. I wanted to be able to commit an annotation by hitting RET rather than C-c C-c and then use SHIFT and RET to actually enter a newline, a bit like in many messaging apps. The problem was that the necessary commands are in pdf-annot-edit-contents-minor-mode-map which is a key map that is not available until that minor mode is loaded. This causes an error if you try to define a key in that map in the same way as I did for the keys to add annotations. This is remedied by wrapping the definitions in with-eval-after-load which waits to execute the code until the file that defines the pdf-annot mode is loaded.

The other thing I wanted to do was have the pdf file auto-save every time I commit an annotation. I did this by advising the function pdf-annot-edit-contents-commit to run save-buffer afterwards. This also had to be inside with-eval-after-load as the pdf-annot-edit-contents-commit function is not available to be advised until the file defining the pdf-annot mode is loaded. The only other issue I had was that when the advice to pdf-annot-edit-contents-commit called save-buffer, it passed its argument (the text of the annotation) to save-buffer which then failed. I hacked around this by writing a simple wrapper to save buffer that ignores any arguments. There is probably a better way to do this!

Putting it all together with my previous tweaks, my pdf-tools config looks like this:

;; wrapper for save-buffer ignoring arguments
(defun bjm/save-buffer-no-args ()
  "Save buffer ignoring arguments"
  (save-buffer))

(use-package pdf-tools
 :pin manual ;;manually update
 :config
 ;; initialise
 (pdf-tools-install)
 (setq-default pdf-view-display-size 'fit-page)
 ;; automatically annotate highlights
 (setq pdf-annot-activate-created-annotations t)
 ;; use isearch instead of swiper
 (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
 ;; turn off cua so copy works
 (add-hook 'pdf-view-mode-hook (lambda () (cua-mode 0)))
 ;; more fine-grained zooming
 (setq pdf-view-resize-factor 1.1)
 ;; keyboard shortcuts
 (define-key pdf-view-mode-map (kbd "h") 'pdf-annot-add-highlight-markup-annotation)
 (define-key pdf-view-mode-map (kbd "t") 'pdf-annot-add-text-annotation)
 (define-key pdf-view-mode-map (kbd "D") 'pdf-annot-delete)
 ;; wait until map is available
 (with-eval-after-load "pdf-annot"
   (define-key pdf-annot-edit-contents-minor-mode-map (kbd "<return>") 'pdf-annot-edit-contents-commit)
   (define-key pdf-annot-edit-contents-minor-mode-map (kbd "<S-return>") 'newline)
   ;; save after adding comment
   (advice-add 'pdf-annot-edit-contents-commit :after 'bjm/save-buffer-no-args)))

More PDF-tools tweaks

I’ve added a couple more tweaks to my pdf-tools configuration.

First, I’ve found that CUA mode interferes with the ability to copy text from the pdf, so let’s turn it off in pdf-view-mode

;; turn off cua so copy works
(add-hook 'pdf-view-mode-hook (lambda () (cua-mode 0)))

Next, I want more fine grained zooming with + and - than the default 25%, so I’ll set it to 10%

;; more fine-grained zooming
(setq pdf-view-resize-factor 1.1)

Finally, for my most commonly used annotation tools (adding a highlight, adding a text note and deleting an annotation), I want quicker shortcuts. With the following I just hit h, t or D respectively for those tools (instead of e.g. C-c C-a h to highlight).

;; keyboard shortcuts
(define-key pdf-view-mode-map (kbd "h") 'pdf-annot-add-highlight-markup-annotation)
(define-key pdf-view-mode-map (kbd "t") 'pdf-annot-add-text-annotation)
(define-key pdf-view-mode-map (kbd "D") 'pdf-annot-delete)

Putting it all together, my current setup looks like this:

(use-package pdf-tools
 :pin manual ;; manually update
 :config
 ;; initialise
 (pdf-tools-install)
 ;; open pdfs scaled to fit page
 (setq-default pdf-view-display-size 'fit-page)
 ;; automatically annotate highlights
 (setq pdf-annot-activate-created-annotations t)
 ;; use normal isearch
 (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
 ;; turn off cua so copy works
 (add-hook 'pdf-view-mode-hook (lambda () (cua-mode 0)))
 ;; more fine-grained zooming
 (setq pdf-view-resize-factor 1.1)
 ;; keyboard shortcuts
 (define-key pdf-view-mode-map (kbd "h") 'pdf-annot-add-highlight-markup-annotation)
 (define-key pdf-view-mode-map (kbd "t") 'pdf-annot-add-text-annotation)
 (define-key pdf-view-mode-map (kbd "D") 'pdf-annot-delete))

View and annotate PDFs in Emacs with PDF-tools

The pdf-tools packages allows you to read and annotate PDF documents in Emacs. The installation process is described on the github page, but on my Mac, I needed to install poppler (I used macports) and add

export PKG_CONFIG_PATH=/opt/local/lib/pkgconfig

to my zshrc. Then after installing the package in Emacs with the following code

(use-package pdf-tools
 :pin manual ;; manually update
 :config
 ;; initialise
 (pdf-tools-install)
 ;; open pdfs scaled to fit page
 (setq-default pdf-view-display-size 'fit-page)
 ;; automatically annotate highlights
 (setq pdf-annot-activate-created-annotations t)
 ;; use normal isearch
 (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward))

I ran M-x pdf-tools-install, ignored an error message about epdfinfo and then restarted emacs, and all was well.

I use the :pin manual option in use-package to stop pdf-tools being automatically updated when I update the rest of my packages, since it would need the installation command and restart each time it updated.

There are lots of nice features in pdf-tools but I’ll just mention the ones I use most often. You can search pdf files like normal buffers, but the enhanced search tool swiper doesn’t work with pdf-tools so I set C-s to call the normal isearch when in this mode.

You can also make annotations in pdf-tools and I set my configuration above so that when an annotation (like highlighting some text) is created, a buffer opens prompting for a text note to go with the annotation. So for example, to highlight a comment on some text I

  1. Select the text in the pdf (mouse required for this unfortunately)
  2. Use C-c C-a h to highlight it yellow
  3. Type some notes in the annotation buffer that pops up and use C-c C-c to complete the annotation.

Other useful annotations are C-c C-a t and then mouse click to add a text note somewhere to the pdf page, C-c C-a o to strike-through text, and C-c C-a D and then click to delete an annotation.

Saving the pdf buffer as normal saves all the annotations, and they will be readable in any PDF viewer so this works well when collaborating with the unenlightened!

Tree-style directory views in dired with dired-subtree

By default, Emacs’ file browser/manager dired usually presents you with a flat list of files in a given directory. Entering a subdirectory then opens a new buffer with the listing of the subdirectory. Sometimes you might want to be able to see the contents of the subdirectory and the current directory in the same view. Many GUI file browsers visualise this with a tree structure with nodes that can be expanded or collapsed. In Emacs there is a built-in function dired-insert-subdir that inserts a listing of the subdirectory under the cursor, at the bottom of the current buffer instead of in a new buffer, but I’ve never found that very helpful.

The dired-subtree package (part of the magnificent dired hacks) improves on this by allowing you to expand subdirectories in place, like a tree structure. To install the package, use the following code:

(use-package dired-subtree
  :config
  (bind-keys :map dired-mode-map
             ("i" . dired-subtree-insert)
             (";" . dired-subtree-remove)))

This sets up the keybinds so that in dired, hitting i on a subdirectory expands it in place with an indented listing. You can expand sub-subdirectories in the same way, and so on. Hitting ; inside an expanded subdirectory collapses it.

Happily, some of my other favourite tools from dired hacks like dynamically narrowing the directory listing or copying and pasting files work as you would want in these expanded subdirectories.

Get that spacemacs look without spacemacs

Spacemacs is an active, popular, and very comprehensive modular Emacs configuration. I’ve heard lots of good things about it but am happy with my own personal configuration so don’t want to switch. However, I really like the appearance of spacemacs. Happily, the key components are available stand-alone without needing the full spacemacs configuration.

I used the zenburn theme for a long time but more recently have switched to the spacemacs theme. I set this up using the following code in my emacs config file

(use-package spacemacs-theme
  :ensure t
  :init
  (load-theme 'spacemacs-dark t)
  (setq spacemacs-theme-org-agenda-height nil)
  (setq spacemacs-theme-org-height nil))

This contains a couple of options to stop the theme using variable heights for org-mode agenda items and headings. However, they didn’t have the desired effect for me to I also needed to add the following lines to my org-mode configuration:

;; set sizes here to stop spacemacs theme resizing these
(set-face-attribute 'org-level-1 nil :height 1.0)
(set-face-attribute 'org-level-2 nil :height 1.0)
(set-face-attribute 'org-level-3 nil :height 1.0)
(set-face-attribute 'org-scheduled-today nil :height 1.0)
(set-face-attribute 'org-agenda-date-today nil :height 1.1)
(set-face-attribute 'org-table nil :foreground "#008787")

The other aspect of the spacemacs appearance I like is the modeline. This is replicated using the spaceline package

(use-package spaceline
  :demand t
  :init
  (setq powerline-default-separator 'arrow-fade)
  :config
  (require 'spaceline-config)
  (spaceline-spacemacs-theme))

Easily manage Emacs workspaces with eyebrowse

You know how many windows managers have workspaces you can switch between? These are variously called “virtual desktops” (e.g. KDE) or “spaces” on OS X, but the idea is the same; you have one workspace with a collection of windows/apps (say for mail and browsing) and another with the windows/apps for a particular project, and you can quickly switch between them. The eyebrowse packages gives a nice simple interface to the same experience in Emacs.

I install and configure eyebrowse with the following code in my emacs config file:

(use-package eyebrowse
  :diminish eyebrowse-mode
  :config (progn
            (define-key eyebrowse-mode-map (kbd "M-1") 'eyebrowse-switch-to-window-config-1)
            (define-key eyebrowse-mode-map (kbd "M-2") 'eyebrowse-switch-to-window-config-2)
            (define-key eyebrowse-mode-map (kbd "M-3") 'eyebrowse-switch-to-window-config-3)
            (define-key eyebrowse-mode-map (kbd "M-4") 'eyebrowse-switch-to-window-config-4)
            (eyebrowse-mode t)
            (setq eyebrowse-new-workspace t)))

The enables the shortcuts M-1 to M-4 to access 4 virtual desktops (N.B. you will have to disable the M- numeric prefixes first). Of course you can add more than 4 if you need to.

Now you will start by default in workspace 1. If you hit M-2 you will switch to a new empty workspace, numbered 2 in the modeline. It will initially just contain the scratch buffer, since we used (setq eyebrowse-new-workspace t). Open whichever buffers and window arrangements you like then hit M-1 to switch back to the first desktop where you will see the windows and buffers you had set up there.

A useful command is C-c C-w , (N.B. the comma is part of the command!) which runs eyebrowse-rename-window-config allowing you to name a workspace, and that name then appears in the modeline instead of the workspace number.