Skip to content

Avy-backed, actionable placement of multiple marks

Notifications You must be signed in to change notification settings

aatmunbaxi/lasgun.el

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lasgun.el

lasgun-demo.gif

lasgun.el (lays-gun) provides avy-backed, actionable placement of multiple inactive marks in the current buffer. Once these marks have been collected, you can act on the marks in bulk, without disturbing your point (with some obvious exceptions). If this sounds familiar to how avy works, it is! lasgun simply generalizes the Filter -> Select -> Act from avy to one that works on multiple selected candidates.

More examples on usage can be found in the demo by examples.

Why lasgun.el?

  • Avy provides an excellent Filter -> Select -> Act loop. lasgun.el generalizes this to allow multiple runs of Filter -> Select, and Acting when all candidates are selected.
    • Therefore, using lasgun.el only makes sense when acting on multiple candidates in bulk
    • This being said, it should not be considered a strict superset of avy’s features

Usage

  • Call a lasgun-mark function to mark a buffer position via avy
    • Repeat until desired positions are marked
  • Decide to act on each of these positions or not
    • Actions can be defined with the define-lasgun-action macro. See its docstring for information.

If not acting at lasgun marks, it might be useful to set lasgun-also-push-mark-ring to t, so lasgun marks remain in the mark-ring after clearing the lasgun-mark-ring.

Installation

For example, via straight:

(straight-use-package
 '(lasgun :type git :host github :repo "aatmunbaxi/lasgun.el")

DOOM emacs, in packages.el:

(package! lasgun :recipe (:host github "aatmunbaxi/lasgun.el"))

If your package manager does not support git recipes, a simple git clone and placement of

(add-to-list 'load-path "path/to/lasgun.el")
(require 'lasgun)

in your init.el will do.

Example transient Configuration

The demo gif shows an example UI with hercules. u/Hammar_Morty kindly provided a close translation making use of transient:

(require 'transient)

;; Defines some lasgun actions
(define-lasgun-action lasgun-action-upcase-word t upcase-word)
(define-lasgun-action lasgun-action-downcase-word t downcase-word)
(define-lasgun-action lasgun-action-kill-word nil kill-word)

(transient-define-prefix lasgun-transient ()
  "Main transient for lasgun."
  [["Marks"
    ("c" "Char timer" lasgun-mark-char-timer :transient t)
    ("w" "Word" lasgun-mark-word-0 :transient t)
    ("l" "Begin of line" lasgun-mark-line :transient t)
    ("s" "Symbol" lasgun-mark-symbol-1 :transient t)
    ("o" "Whitespace end" lasgun-mark-whitespace-end :transient t)
    ("x" "Clear lasgun mark ring" lasgun-clear-lasgun-mark-ring :transient t)
    ("u" "Undo lasgun mark" lasgun-pop-lasgun-mark :transient t)]
   ["Actions"
    ("SPC" "Make cursors" lasgun-make-multiple-cursors)
    ("." "Embark act all" lasgun-embark-act-all)
    ("U" "Upcase" lasgun-action-upcase-word)
    ("l" "Downcase" lasgun-action-downcase-word)
    ("K" "Kill word" lasgun-action-kill-word)
    ("q" "Quit" transient-quit-one)]])

(global-set-key (kbd "C-c t g") 'lasgun-transient)

Example Embark Action Menu

If transients (or their half-siblings hydra, hercules, etc) aren’t your thing, here is a skeleton for an embark dispatch menu for lasgun actions.

  (defun my-embark-lasgun-mark ()
    "Provides `embark-target-finders' function for lasgun marks
when point is on lasgun mark"
    ;; can make this logic as complicated/simple as you want
    (when (ring-member lasgun-mark-ring (point))
      `(lasgun-mark
          ,(buffer-substring-no-properties (point)  (point))
          ,(point) . ,(1+ (point)))))

  ;; tell embark to search for lasgun-marks
  (add-to-list 'embark-target-finders #'my-embark-lasgun-mark)

  (defvar-keymap embark-lasgun-mark-actions
    :doc "Embark action keymap for lasgun targets"
    ;; your keybinds and actions here
    "SPC" #'lasgun-make-multiple-cursors)

  ;; embark will use your keymap for lasgun-marks dispatch menu
  (add-to-list 'embark-keymap-alist '(lasgun-mark . embark-lasgun-mark-actions))

The targeting function my-embark-lasgun-mark will match a lasgun mark if your point is currently on a lasgun mark, meaning you’d have to jump to one such mark before invocation of embark-act (does this defeat the purpose of lasgun? Up to you).

Fortunately, the logic of such a targeting function is limited only by your ability to write elisp. Here’s one that will intercept all calls of embark-act to target lasgun marks so long as the lasgun-mark-ring is nonempty:

(defun my-embark-lasgun-mark ()
  "Use lasgun embark actions so long as lasgun marks exist"
  (unless (ring-empty-p lasgun-mark-ring)
    (let ((lgmark (ring-ref lasgun-mark-ring 0)))
      `(lasgun-mark  ,(buffer-substring-no-properties lgmark lgmark)
        ,lgmark . ,lgmark))))

See the docstring for embark-target-finders information if you want to hack on the targeting function.

Add your keys for defined actions to embark-lasgun-mark-actions to expand the functionality! More information on defining your own embark target actions can be found in the embark documentation.

Dependencies

  • avy
  • Optional:
    • multiple-cursors for lasgun-make-multiple-cursors
    • embark for lasgun-embark-act-all

Customizing

By “lasgun mark” we mean a buffer position stored in lasgun-mark-ring.

  • lasgun-mark-ring-max: Maximum number of lasgun marks
  • lasgun-pop-before-make-cursors: Place multiple-cursors cursors only at lasgun marks (can negate interactively, see lasgun-make-multiple-cursors docstring)
  • lasgun-also-push-mark-ring: Also push lasgun marks to buffer-local mark-ring
  • lasgun-use-lasgun-mark-overlay: Use visual overlays for lasgun marks
  • lasgun-persist-lasgun-mark-ring: Persist lasgun-mark-ring after performing action (Can override when defining lasgun actions, see define-lasgun-action docstring.)
  • lasgun-persist-negation-prefix-arg: Prefix arg with which to negate lasgun-persist-lasgun-mark-ring behavior
  • lasgun-mark-face: Face used to visually indicated lasgun marks

lasgun-mark functions

Lasgun provides analogues to nearly every avy-goto function. They are listed below. IMHO, it is an overwhelming number of choices; they are simply provided for completeness. It is recommended that you stick to a few staples, unless you’re using something to remember where each function is bound, like hercules or hydra.

  • lasgun-mark-end-of-line
  • lasgun-mark-line
  • lasgun-mark-word
  • lasgun-mark-char-2
  • lasgun-mark-symbol-1
  • lasgun-mark-subword-0
  • lasgun-mark-subword-1
  • lasgun-mark-char-timer
  • lasgun-mark-char-2-above
  • lasgun-mark-char-2-below
  • lasgun-mark-word-0-above
  • lasgun-mark-word-0-below
  • lasgun-mark-symbol-1-above
  • lasgun-mark-symbol-1-below
  • lasgun-mark-whitespace-end
  • lasgun-mark-whitespace-end-above
  • lasgun-mark-whitespace-end-below

About

Avy-backed, actionable placement of multiple marks

Topics

Resources

Stars

Watchers

Forks