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.
- Avy provides an excellent
Filter -> Select -> Act
loop.lasgun.el
generalizes this to allow multiple runs ofFilter -> Select
, andActing
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
- Therefore, using
- Call a
lasgun-mark
function to mark a buffer position viaavy
- 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.
- Actions can be defined with the
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
.
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.
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)
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.
avy
- Optional:
multiple-cursors
forlasgun-make-multiple-cursors
embark
forlasgun-embark-act-all
By “lasgun mark” we mean a buffer position stored in lasgun-mark-ring
.
lasgun-mark-ring-max
: Maximum number of lasgun markslasgun-pop-before-make-cursors
: Placemultiple-cursors
cursors only at lasgun marks (can negate interactively, seelasgun-make-multiple-cursors
docstring)lasgun-also-push-mark-ring
: Also push lasgun marks to buffer-localmark-ring
lasgun-use-lasgun-mark-overlay
: Use visual overlays for lasgun markslasgun-persist-lasgun-mark-ring
: Persistlasgun-mark-ring
after performing action (Can override when defining lasgun actions, seedefine-lasgun-action
docstring.)lasgun-persist-negation-prefix-arg
: Prefix arg with which to negatelasgun-persist-lasgun-mark-ring
behaviorlasgun-mark-face
: Face used to visually indicated lasgun marks
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