Skip to content

ErikPrantare/hatty.el

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

95 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hatty.el

An Emacs package for rendering hats over characters, allowing words to be indexed quickly from any point in the buffer.

Screenshots

With dark theme (modus-vivendi)

./images/hatty-dark.png

With light theme (modus-operandi)

./images/hatty-light.png

Video demo

https://youtu.be/iKWEEAKMZq0

Description

This package provides a mechanism for building interfaces overcoming a core limitation of tools like avy. With avy, it is necessary to perform a command to make the interface for choosing a position visible. This in turn means that performing an action cannot be done as one fluent operation.

In contrast, hatty always renders a small symbol over one of the characters in each word. This makes it possible to refer to the word immediately, without the intermediate step of bringing up an interface.

This library was designed while attempting to make voice control of Emacs more fluent, where the two-step system of avy made it impossible to string together a sequence of actions in a single utterance. This method of indexing positions was inspired by the cursorless project, whose hat designs this project reuses.

Contents

Installation

This package is available on MELPA as hatty.

Manual installation

Clone this repo and add the following to your configuration:
(add-to-list 'load-path "path/to/hatty/directory")

Terminology

  • Hat: The small symbol rendered above a glyph by hatty.
  • Token: The region of the buffer indicated by the hat. In general, this corresponds to a word or a special character such as punctuation.
  • Style: The appearance of a hat is decided by its color and shape. The combination of a color and shape is called a style.

Usage

Add the following to your configuration:
(require 'hatty)
(global-hatty-mode 1)

This starts rendering hats in visited buffers. To get the token of a hat, use hatty-locate-token. For example,

(hatty-locate-token ?n 'yellow 'bolt)

will return the token that has a yellow bolt over the character “n”. The bounds or markers, ensuring stability of the positions across editing operations. See the Emacs lisp manual for more information on markers.

The available colors and shapes are specified by the variables hatty-colors and hatty-shapes. The keys in these alists are the symbols used to invoke hatty-locate. The special identifier default denotes the color or shape to use whenever the argument is nil or absent.

If you want to disable special shapes while getting used to hatty, see Disabling specific colors and shapes.

Example usage

The following snippet will define three keyboard commands for jumping, copying, and inserting a token as indexed by the hat. After entering C-c j, C-c c or C-c b, Emacs will wait for a key sequence referring to a hat. The sequence is terminated by SPC.

If the hat does not have color and is oval, you will not need to specify the color or shape. In this case, if the word you want to jump to has a hat over its “w”, enter C-c j w SPC.

If the hat is red, enter the corresponding character from my/color-lookup before “w”: C-c j r w SPC. Similarly, if it has the bolt shape (but no color), enter C-c j z w SPC. If you specify both color and shape, you do not need to terminate with SPC. In this case, if the hat is a red bolt, you can jump to it with C-c j r z w.

(defvar my/color-lookup
  '((?y . yellow)
    (?r . red)
    (?b . blue)
    (?p . pink)
    (?g . green)))

(defvar my/shape-lookup
  '((?z . bolt)
    (?c . curve)
    (?v . fox)
    (?f . frame)
    (?> . play)
    (?a . wing)
    (?o . hole)
    (?x . ex)
    (?t . cross)
    (?i . eye)))

(defun my/apply-hat-action (f)
  "Apply F to token of hat read from input."
  (let ((input '()))
    (while (and (< (length input) 3) (not (eq (car input) ?\ )))
      (push (read-char) input))

    (when (eq (car input) ?\ )
      (setq input (cdr input)))
    (setq input (reverse input))

    (let ((color nil)
          (shape nil)
          (character nil))
      (while (> (length input) 1)
        (when (alist-get (car input) my/color-lookup)
          (setq color (alist-get (car input) my/color-lookup)))
        (when (alist-get (car input) my/shape-lookup)
          (setq shape (alist-get (car input) my/shape-lookup)))
        (pop input))
      (setq character (car input))

      (funcall f (hatty-locate-token character color shape)))))

(defun my/hat-jump ()
  (interactive)
  (my/apply-hat-action (lambda (region)
                         (goto-char (car region)))))

(defun my/hat-copy ()
  (interactive)
  (my/apply-hat-action (lambda (region)
                         (copy-region-as-kill (car region) (cdr region)))))

(defun my/hat-bring ()
  (interactive)
  (my/apply-hat-action (lambda (region)
                         (insert
                          (buffer-substring-no-properties (car region) (cdr region))))))

(global-set-key (kbd "C-c j") #'my/hat-jump)
(global-set-key (kbd "C-c c") #'my/hat-copy)
(global-set-key (kbd "C-c b") #'my/hat-bring)

To see how hatty can be used to build a more complex interface, see cursorfree.el.

Customization

Hatty variables may be customized via the hatty customization group. After doing M-x customize RET, click Convenience and then Hatty. As usual, customization can also be done in your configuration file.

Style penalties

If you want a prioritize certain colours or shapes, change the alists hatty-color-penalties and hatty-shape-penalties. The penalty for a style is the sum of the color and shape penalty. Styles with lower penalties will be placed closer to the cursor.

The default settings are optimized for voice control, with penalties corresponding to the amount of utterances needed to refer to the style.

Hat colors

When loading hatty, it will attempt to set the color themes for the hats appropriately. To change the colors to better fit your theme, customize hatty-colors.

hatty-colors should be an association list mapping an identifier symbol to a color. A color can be a hex code like "#aa7023" or a named color like "magenta​" (M-x list-colors-display RET to see available color names). hatty-colors may contain or exclude arbitrary identifiers and colors, allowing you to remove, add or change them to your liking.

Disabling specific colors and shapes

To disable a given colour or shape, remove it from hatty-colors or hatty-shapes.

For example, to disable all special shapes, remove all shapes except for the default shape. In Elisp:

(setq hatty-shapes (list (assq 'default hatty-shapes)))

Performance

If hat rendering freezes Emacs for a significant amount of time, make sure Emacs is compiled with rsvg. People on macOS in particular seem to have this issue.

Versioning system

This project uses semantic versioning. While still on 0.y.z, an increment of y signals breaking changes.

Running tests

emacs -Q -l hatty.el -l test.el --eval '(ert t)'

It is not possible to run the ERT tests in batch mode, as the tests require a graphical display to measure the size of rendered text.

About

Index buffer locations through character hats

Resources

License

Stars

Watchers

Forks

Packages

No packages published