Hidden Gem Packages

Table of Contents

Author Lee Hinman (lee@elastic.co)
Date 2017-11-02 16:04:23

Introduction

Let's talk about some "mini" packages that don't warrant a whole talk, but are really useful in a pinch nonetheless!

Hideshow

Hideshow is a buffer-local minor mode that allows you to selectively display portions of a program, called "blocks".

It's turned on and off with hs-minor-mode and most of the bindings hide (hah get it!) behind C-c @:

Key Action
C-c @ C-h Hide the current block (hs-hide-block)
C-c @ C-s Show the current block (hs-show-block)
C-c @ C-c Toggle visibility of the current block (hs-toggle-hiding)
S-mouse-2 Toggle hiding/showing for the block (hs-mouse-toggle-hiding)
C-c @ C-M-h Hide all the top level blocks (hs-hide-all)
C-c @ C-M-s Show all top level blocks (hs-show-all)
C-c @ C-l Hide blocks n levels below this block (hs-hide-level)

And there is a customization I'd recommend also:

;; Automatically open a block if you search for something where it matches
(setq hs-isearch-open t)

Here's what I'd consider a minimal configuration for it:

(use-package hideshow
  :bind (("C-c TAB" . hs-toggle-hiding)
         ("C-\\" . hs-toggle-hiding)
         ("M-+" . hs-show-all))
  :init (add-hook #'prog-mode-hook #'hs-minor-mode)
  :diminish hs-minor-mode
  :config
  ;; Add `json-mode' and `javascript-mode' to the list
  (setq hs-special-modes-alist
        (mapcar 'purecopy
                '((c-mode "{" "}" "/[*/]" nil nil)
                  (c++-mode "{" "}" "/[*/]" nil nil)
                  (java-mode "{" "}" "/[*/]" nil nil)
                  (js-mode "{" "}" "/[*/]" nil)
                  (json-mode "{" "}" "/[*/]" nil)
                  (javascript-mode  "{" "}" "/[*/]" nil)))))

The main way I use this is to put the point (represented as | in the code below) inside of an expression and then hit C-c TAB or C-\, which changes

{
  "foo": {|
    "bar": "baz"
  },
  "eggplant": 11
}

Into

{
  "foo": { ... },
  "eggplant": 11
}

I have another helper that I really like (especially for Java files) that looks like:

(defvar eos/hs-level 2
  "Default level to hide at when calling
    `eos/fold-show-only-methods'. This is buffers may set this to
    be buffer-local.")

(setq eos/hs-fold-show-only-methods-active-p nil)
(defun eos/hs-fold-show-only-methods ()
  "Toggle between hiding all methods using `eos/hs-level' or
showing them."
  (interactive)
  (save-excursion
    (if eos/hs-fold-show-only-methods-active-p
        (progn
          (hs-show-all)
          (setq-local eos/hs-fold-show-only-methods-active-p nil))
      (progn
        (goto-char (point-min))
        (hs-hide-level eos/hs-level)
        (setq-local eos/hs-fold-show-only-methods-active-p t)))))

(global-set-key (kbd "M-\\") 'eos/hs-fold-show-only-methods)

I can then use M-\ to only show method names and signatures, hiding the bodies.

Expand-region

Expand-region let's you take the current point and expand the selection, this is great with things like transient-mark-mode and delete-selection-mode!

(use-package expand-region
  :ensure t
  :defer t
  :bind (("C-c e" . er/expand-region)
         ;; I almost never use this, so it has an awkward binding
         ("C-M-@" . er/contract-region)))

Since this is hard to explain, I'll show what it does!

Git tools

Besides Magit, there are a bunch of other helpful tools for git, and vcs in general

git-gutter

Git-gutter is a way to show what parts of a file were changed. It pops up configurable symbols (+, -, =) in the margin. It's also a great way to navigate through a file by jumping through the parts that have been changed.

Here's how I like to configure it. Note that the git-gutter-fringe part is entirely optional and I only use for making the fringe look "nice" (I'll demo this). If you don't want it you can remove the custom bitmap definitions.

(use-package git-gutter
  :ensure t
  :when window-system
  :defer t
  :bind (("C-x P" . git-gutter:popup-hunk)
         ("C-x p" . git-gutter:previous-hunk)
         ("C-x n" . git-gutter:next-hunk)
         ("C-c G" . git-gutter:popup-hunk))
  :diminish ""
  :init
  (add-hook 'prog-mode-hook #'git-gutter-mode)
  (add-hook 'text-mode-hook #'git-gutter-mode)
  :config
  (use-package git-gutter-fringe
    :ensure t
    :init
    (require 'git-gutter-fringe)
    (when (fboundp 'define-fringe-bitmap)
      (define-fringe-bitmap 'git-gutter-fr:added
        [224 224 224 224 224 224 224 224 224 224 224 224 224
             224 224 224 224 224 224 224 224 224 224 224 224]
        nil nil 'center)
      (define-fringe-bitmap 'git-gutter-fr:modified
        [224 224 224 224 224 224 224 224 224 224 224 224 224
             224 224 224 224 224 224 224 224 224 224 224 224]
        nil nil 'center)
      (define-fringe-bitmap 'git-gutter-fr:deleted
        [0 0 0 0 0 0 0 0 0 0 0 0 0 128 192 224 240 248]
        nil nil 'center))))

The other helpful thing is git-gutter:revert-hunk which can be used to revert only a single chunk of change without having to jump back into Magit.

git-timemachine

Git-timemachine allows you to go back through the history of a file, seeing how it's been changed. There really isn't much configuration:

(use-package git-timemachine
  :ensure t)

And then do M-x git-timemachine, you can use p and n to jump between the previous and next revision of the file, w or W to copy the short/full hash of the commit you're looking at, and q to quit.

There are a few more keybinds, so check out the project to see what else you can do!

git-messenger

Along the same lines, sometimes all you want to see is the message for the particular line you're at. Git-messenger lets you pop this up and copy the relevant information. I like to bind mine to C-c M:

(use-package git-messenger
  :ensure t
  :commands git-messenger:popup-message
  :bind (("C-c M" . git-messenger:popup-message))
  :config
  (setq git-messenger:show-detail t))

git-messenger.png

browse-at-remote

Finally, sometimes you need to send someone a link, and you want to open whatever you're currently looking at in the Github, Gitlab, Bitbucket, etc UI. Browse-at-remote lets you do this, and even highlights what ever you have selected in the UI.

(use-package browse-at-remote
  :ensure t
  :commands browse-at-remote
  :bind ("C-c g g" . browse-at-remote))

Let's see what it looks like!

Alert

Alert is for notifications, and very easy to use

(use-package alert
  :ensure t
  :config
  (when (eq system-type 'darwin)
    (setq alert-default-style 'notifier))
  (when (eq system-type 'gnu/linux)
    (setq alert-default-style 'notifications)))

And then you can do:

(alert "Test alert")
(alert "Give the presentation!" :title "Denver Emacs")
(alert "Don't mess up!" :title "Emacs" :severity 'high)

Use it when your write your own helpers! Like this one that alerts when compilation (M-x compile) finishes:

(defun eos/compile-finished (buffer msg)
  (alert msg :title buffer :category 'compilation))

(add-to-list 'compilation-finish-functions 'eos/compile-finished)

Which-key

Which-key shows you possible completions when you half-type a shortcut. It's hard to explain so trying it is best, install it with the following:

(use-package which-key
  :ensure t
  :init
  (which-key-mode 1))

And then press C-h, wait a few seconds, and then a popup comes up with all the options you can choose. Very helpful! It works with any kind of prefix (try Helm's prefix key, C-x, or C-c for instance)

Prodigy

Sometimes it can be helpful to start up a service, for instance, if you are a Ruby dev you might want to start a Rails server, or if you want to run some service you need for development.

Prodigy lets you have a great UI for this, as well as seeing the state, and viewing the logs

(use-package prodigy
  :ensure t
  :defer t
  :config
  (setq prodigy-services '())
  (prodigy-define-service
    :name "Elasticsearch 5.6.3"
    :cwd "~/ies/elasticsearch-5.6.3"
    :command "~/ies/elasticsearch-5.6.3/bin/elasticsearch"
    :tags '(work test es)
    :port 9200)
  (prodigy-define-service
    :name "Simple HTTP server"
    :cwd "~/"
    :command "python"
    :args '("-m" "SimpleHTTPServer" "8000")
    :port 8000
    :tags '(http)))

You can then do M-x prodigy to bring up the UI and see what you can run. s starts a command, S stops it, and $ jumps to a buffer showing the logs from the process (g refreshes the view).

Author: Lee Hinman

Created: 2017-11-02 Thu 16:04