harpoon skewers the monotony of hooking into (mainly language) major
modes by providing a simple use-package-like macro.
It grew out of my configuration and therefore is currently still
heavily geared towards packages I use (like corfu and lsp-mode). But I
aim to slowly decouple its functionality from any particular package.
If you use straight or quelpa, you know what to do.
If you’re on Emacs >29, I recommend using package-vc-install.
Alternatively, provided you have Cask, you can install the package
with make package-install.
Let’s take a look at a form using all currently available features.
(harpoon some-mode
:bind t
:completion (:provider corfu :prefix 1 :delay 0.1)
:before (some-fun)
:after (other-fun)
:ligatures ("::" ">>")
:lsp (:function lsp-deferred :dir-ignore-list lsp-file-watch-ignored-list :ignore-dirs (".bucket") :hints t)
:messages ("Something in the way" "She moves")
:prog-like t
:tabs t
:checker flycheck-mode
:whitespace delete
(some-other-fun)
(setq some-other-var t))Setting :bind t will bind the value of harpoon-bind-key to a
symbol named some-mode-<harpoon-bind-name-suffix> . In my
configuration I set harpoon-bind-name-suffix to “-major”; this is a
transient map for the current major-mode. You can also pass a
symbol that will be bound.
The setting for :completion is a plist configuring the
harpoon-completion-provider (only corfu is currently supported) to
enable automatic completion with a prefix length of 1 and a delay of
0.1. This does not set values that aren’t explicitly set. You don’t
need to set provider if the default (harpoon-completion-provider)
should be used.
The list of symbols passed to :before and :after are called on the
condition that they are bound, called at the head and at the tail end
of the created function.
The list of strings passed to :ligatures will be set for some-mode
using package ligature provided it can be required. Have a look at
harpoon-common-ligatures for a list of ligatures always set up.
The plist passed to :lsp will add directory “.bucket” to a ignore
list and call lsp-deferred when some-mode is enabled. Note that
you don’t need to set :function if you set harpoon-lsp-function,
but you could set eglot-ensure here instead to have some other mode
use that. The ignore list will default to
harpoon-lsp-dir-ignore-list. If you pass :hints t inlay hints will
be enabled.
Entries in the list of strings passed to :messages will be displayed
randomly when some-mode is enabled.
If :prog-like is t, hook prog-like-hook is run. This is useful
when some-mode is not derived from prog-mode but kind of like one
(think yaml-mode) and you want to set up the same minor-modes for it
that you do for real prog modes.
The symbol passed to :tabs can be either t, always or never.
The latter two will set indent-tabs-mode to t or nil. Symbol t
will setting indent-tabs-mode to t dependent on the value of
harpoon-prefer-tabs which is a custom variable that should be set
using a .dir-locals.el file.
The symbol passed to :checker should be the name of a syntax
checker. You normally should instead just set
harpoon-checker-function but if you have modes that should use
another checker, set it here. You can also pass symbol disabled to
instead don’t enable any checker even if harpoon-checker-function is
set.
Any form after the keyword arguments will also be evaluated when
some-mode is enabled.
The macro will set up function some-mode-harpoon that will be added
to some-mode-hook. Note that if there is an alternative treesit
mode that can be used the hook is created for it instead. So if you
have the grammar for JavaScript, for example, and set up harpoon for
js-mode, the hook will be created for js-ts-mode. Since the
pattern for mode => language, and mode => treesitter variant isn’t
uniform, this currently only works for a small number of
modes/languages.
If you don’t want this behavior, you may add a mode name
harpoon-treesit-blacklist to ensure treesit isn’t used for it.
If you pass :flat t completion and syntax checking is not set up.
This can be useful if you have some configuration for a mode and
others for those derived from it.
Setting :whitespace delete means a before-save-hook will be
installed to call delete-trailing-whitespace. This can also be
enabled generally by setting harpoon-whitespace to delete. If you
do that but want to exclude some modes, you can pass :whitespace
keep for them.
You can set harpoon-log to t in your init file if you want to see
what expanding your harpoon form(s) did. You can switch to the logs
using harpoon-pop-to-logs.
Please also have a look, again, at my configuration for usage examples.