* Hel — [[https://helix-editor.com/][Helix]] Emulation Layer for Emacs
#+html: <a href="https://melpa.org/#/hel"><img alt="MELPA" src="https://melpa.org/packages/hel-badge.svg"/></a>
#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://img.shields.io/badge/GNU%20Emacs-29.1%2B-7F5AB6?logo=gnuemacs&logoColor=white"></a>
#+html: <a href="LICENSE"><img alt="License: GPLv3" src="https://img.shields.io/badge/license-GPLv3-blue"></a>
https://github.com/user-attachments/assets/09d1a77d-2a3e-4cae-a005-e69ae03e5a7e
https://github.com/user-attachments/assets/df9f6785-a359-4191-908d-2b2abb274d22
** Key features
- Multiple cursors based modal editing inside Emacs!
- Undo/redo that plays well with multiple cursors.
- PCRE regexps by default (thanks to [[https://github.com/joddie/pcre2el][pcre2el]]).
- Smooth scrolling commands out of the box.
** Can I use Hel without knowing Emacs keys?
#+begin_quote
[!IMPORTANT]
What Vim, Helix, and other modal editors call Normal and Insert *modes*, Hel
refers to as *states*. This is because the word "mode" in Emacs is already used
for its [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Major-Modes.html][major]] and [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Minor-Modes.html][minor]] modes.
#+end_quote
When several years ago I came to Emacs from Neovim, I was in love with Vim
editing model, found Emacs native keybindings ugly and had zero interest in
learning them. I want Emacs not as text editor (which it obviously lacks of)
but as an operation system with Lisp and all its power. So the first question
I asked myself was "Can I use Evil without learning Emacs keys?"
The answer is yes. I have never used — and still don't know — most Emacs
keys. I know only a few that you need when something breaks early during
Emacs startup and you don't have your Hel keys available. They are:
- =M-x= — Command palette. The main key you need; all other commands can be
invoked from it.
- =M-w= — Copy the selected text to google the error message or feed it
to LLM.
- =C-x C-s= — Save current buffer.
- =C-x C-c= — Exit Emacs.
That's it.
Hel and Emacs do not interfere much, because Emacs is not a modal editor:
letters and numbers are self-inserting, and most command key chords begin
with =C-x= or =C-c= (e.g. =C-x n d=). Due to this, Hel works as a layer on top
of Emacs.
In Normal state you have selection-based editing, multiple cursors, and all
the other Hel features. In Insert state, Hel steps aside and standard Emacs
keys work as usual. Also Hel doesn't touch =C-x= and =C-c= so they are always
available. This allows you to mix Hel and Emacs in any proportion.
** Kakoune vs Helix
The main difference between Kakoune and Helix, in terms of text editing, is
how they handle expanding selections: Kakoune uses =Shift= + motions, while Helix
uses a separate state on the =v= key. Since I originally came from Vim, I prefer
Helix's =v= key, so I chose Helix. However, Kakoune (as far as I know) was the
original inventor of this keyboard-driven multiple-selections approach, and it
deserves credit.
** Installation
#+begin_quote
[!TIP]
If you want a Spacemacs / Doom like configuration framework but for Hel
try [[https://github.com/helheim-emacs/helheim][Helheim Emacs]].
#+end_quote
Hel is on [[https://melpa.org/#/hel][MELPA]].
*** Emacs built-in package manager
This is the most minimal example of the =init.el= file:
#+begin_src emacs-lisp
;;; init.el -*- lexical-binding: t; no-byte-compile: t; -*-
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(use-package hel
:ensure t
:custom (inhibit-startup-screen t)
:config (hel-mode))
;;; init.el ends here
#+end_src
*** [[https://github.com/progfolio/elpaca][Elpaca]]
#+begin_src emacs-lisp
(elpaca 'hel
(setopt inhibit-startup-screen t)
(hel-mode))
#+end_src
*** [[https://github.com/radian-software/straight.el][Straight]]
#+begin_src emacs-lisp
(straight-use-package 'hel)
(setopt inhibit-startup-screen t)
(hel-mode)
#+end_src
** Documentation
- [[file:docs/keybindings.org][Keybindings]]
- [[file:docs/customization.org][Customizations]]
** Differences from Helix text editor
This package is not a one-to-one emulation. Some commands are implemented in
a slightly different way (improved from the author's point of view), and some
features like keyboard macros, registers, and jumplists already have their
alternatives in Emacs.
- In Emacs the cursor ("point" in Emacs terms) is located *between* two characters
rather than *on* a character like in Helix or Vim. I decided to keep this
behavior, instead of emulating original one, as Evil does, because the primary
object of interaction in Helix approach is a selection, not the cursor itself.
This has consequences. In Helix, the cursor is a one-character selection, so
Helix always operates on a selection. Hel does not. It has two distinct states:
either selection exists, or not. Hel provides some DWIM ("do what I mean"):
- =d= / =D=
- When there is a selection, both =d= and =D= remove it from the buffer: =d=
copies the deleted text to the clipboard, =D= does not (Vim taught me
that you don't always want to copy deleted text to the clipboard).
- When there is no selection, common text editors provide two keys for
deleting a character: =Backspace= deletes the character before the cursor,
=Delete= — after it. Hel adopts this idea: =d= deletes the character before
the cursor, =D= — after. None of them touches the clipboard, since it
makes no sense to copy a single character into it.
- =r= / =R=
- When there is a selection they behave the same as in Helix.
- With no selection =r= replaces character before cursor, =R= — after.
- =x= and =X= commands expand or contract line-wise selections down when the
cursor is at the end of the selection, or up when the cursor is at the
beginning of the selection.
- Inner objects are additionally available directly under =m= prefix to reduce
keystrokes: =mw= is the same as =miw= — select word.
- Mark commands accept numeric arguments: =m2ip= or =2mip= — select 2 paragraphs.
- You can restore last multiple selections with =gv=.
- =gs=, =gh=, and =gl= create selections. This is for consistency, since all other
motions also create selections. In Helix they only move the cursor without
creating a selection.
- Keys that are relevant only when multiple cursors are present will be active
only in that case (e.g. =K=, =&=, =,= — full list is in ~hel-multiple-cursors-mode-map~
keymap). This allows you to reuse, for example, =K= for documentation lookup or =,= for
localleader while there is only one cursor in the buffer.
- Scrolling keybindings are taken from Vim instead of Helix.
- =gg= and =G= are taken from Vim. With numeric argument:
- =gg= — goes to N/10 of the way from the beginning of the buffer;
- =G= — goes to line N.
- Six easymotion commands are provided:
- =gw= / =gb= — choose and mark word forward/backward.
- =gW= / =gB= — choose and mark WORD forward/backward.
- =gj= / =gk= — go to line down/up.
Helix provides only =gw= to place 2-char hints at the beginning of each word.
- =f=, =F=, =t=, =T= commands to move to char are enhanced: they show hints for
targets, and while hints are active, they can be repeated with =n= / =N= keys.
- When you search backward with =?= command, while hints are active =n= and =N= keys
are swapped: =n= will repeat search backward and =N= — forward, like in Vim.
** Undo
*** Undo tree
Helix stores undo history in a tree, which you can traverse with =M-u= /
=M-U=. Emacs' undo history can also be represented as a tree. Hel doesn't
provide =M-u=, =M-U= commands, but there is an even better option — the
excellent [[https://elpa.gnu.org/packages/vundo.html][vundo]] package, which gives you a real undo-tree UI.
([[https://elpa.gnu.org/packages/undo-tree.html][undo-tree]] is not supported — it implements its own undo system instead of
using Emacs' native one.)
*** Using custom undo commands
You can use your own undo commands, for example [[https://codeberg.org/ideasman42/emacs-undo-fu][undo-fu]]:
#+begin_src emacs-lisp
(hel-keymap-global-set :state 'normal
"u" #'undo-fu-only-undo
"U" #'undo-fu-only-redo)
#+end_src
But they must meet two conditions: they must work with the native Emacs
undo system, and they must deactivate the mark before undoing.
#+begin_quote
[!IMPORTANT]
Hel doesn't support Emacs' undo in region. It conflicts with multiple
cursors, and in Normal state a selection is active most of the time, so
region-aware undo by default makes no sense. Undo commands must therefore
deactivate the mark before undoing.
#+end_quote
=undo-fu= deactivates the mark by default (see ~undo-fu-allow-undo-in-region~).
** Commands that are not implemented
- =M-u=, =M-U= — traverse undo tree. See [[* Undo tree][undo tree]] section.
- =q=, =Q= — record keyboard macros
** Extensions
- [[https://github.com/helheim-emacs/hel-leader][hel-leader]] — use =Space= as a leader key
- [[https://github.com/helheim-emacs/hel-collection][hel-collection]] — keybindings for built-in and third-party packages
- [[https://github.com/helheim-emacs/hel-org][hel-org]] — for [[https://orgmode.org/][Org-mode]]
- [[https://github.com/helheim-emacs/hel-paredit][hel-paredit]] — structural editing for S-expressions
- [[https://github.com/helheim-emacs/hel-ghostel][hel-ghostel]] — for [[https://github.com/dakra/ghostel][ghostel]] terminal
- [[https://github.com/helheim-emacs/hel-vterm][hel-vterm]] — for [[https://github.com/akermu/emacs-libvterm][vterm]] terminal emulator
** Tips
#+begin_quote
[!TIP]
By default, Hel uses a bar cursor for Normal state and a box cursor for Insert
state — the opposite of what Vim does. Your first instinct may be to switch
them back to what you're used to, but I recommend not doing so. This was the
first I done myself, and went through all the stages of acceptance, give
default settings a try — the bar cursor is better suited for Normal state.
#+end_quote
#+begin_quote
[!TIP]
You can set localleader keymap to =,=. It will act as the local leader while
there is only one cursor in the buffer, and will delete all secondary cursors
when there are multiple cursors.
#+end_quote
#+begin_quote
[!TIP]
Bind =Caps Lock= to =Esc=, and configure =Space= to tap+hold behavior: =Space= on
tap and =Ctrl= on hold. You can use any of the these tools: [[https://github.com/pqrs-org/Karabiner-Elements][kanata]], [[https://github.com/pqrs-org/Karabiner-Elements][kmonad]],
[[https://github.com/pqrs-org/Karabiner-Elements][keyd]] (Linux), [[https://github.com/pqrs-org/Karabiner-Elements][Karabiner-Elements]] (Mac).
#+end_quote
** Acknowledgments
Hel depends on [[https://github.com/magnars/dash.el][dash.el]], [[https://github.com/joddie/pcre2el][pcre2el]], [[https://github.com/abo-abo/avy][avy]] and [[https://github.com/jdtsmith/ultra-scroll][ultra-scroll]] wonderful packages.
Hel is heavily inspired by:
- [[https://github.com/emacs-evil/evil][evil]]
- [[https://github.com/magnars/multiple-cursors.el][multiple-cursors.el]]
- [[https://github.com/aome510/kak.el][kak.el]]
- [[https://github.com/mkleehammer/surround][surround]]
- [[https://github.com/meow-edit/meow][meow]]
- isearch (built-in)
- [[https://github.com/karb94/neoscroll.nvim][neoscroll.nvim]]
- [[https://github.com/doomemacs/doomemacs][doomemacs]]
- [[https://github.com/bbatsov/crux][crux]]
You are welcome to go and give them all at least a star!
** Contributing
*** Share
A quick post about this package on your blog or social network could bring
new users to Emacs, which would be great!
*** Support the development
Hel was developed on an old laptop with a cracked screen, and I worked on it
instead of grinding LeetCode. If you'd like to support Hel's development, you
can do so with a donation:
- [[https://www.paypal.me/anuvyklack][PayPal]]
Every contribution is greatly appreciated.
Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.
1,035 commits
1 commits
Emacs Lisp
100.0%
* Hel — [[https://helix-editor.com/][Helix]] Emulation Layer for Emacs
#+html: <a href="https://melpa.org/#/hel"><img alt="MELPA" src="https://melpa.org/packages/hel-badge.svg"/></a>
#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://img.shields.io/badge/GNU%20Emacs-29.1%2B-7F5AB6?logo=gnuemacs&logoColor=white"></a>
#+html: <a href="LICENSE"><img alt="License: GPLv3" src="https://img.shields.io/badge/license-GPLv3-blue"></a>
https://github.com/user-attachments/assets/09d1a77d-2a3e-4cae-a005-e69ae03e5a7e
https://github.com/user-attachments/assets/df9f6785-a359-4191-908d-2b2abb274d22
** Key features
- Multiple cursors based modal editing inside Emacs!
- Undo/redo that plays well with multiple cursors.
- PCRE regexps by default (thanks to [[https://github.com/joddie/pcre2el][pcre2el]]).
- Smooth scrolling commands out of the box.
** Can I use Hel without knowing Emacs keys?
#+begin_quote
[!IMPORTANT]
What Vim, Helix, and other modal editors call Normal and Insert *modes*, Hel
refers to as *states*. This is because the word "mode" in Emacs is already used
for its [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Major-Modes.html][major]] and [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Minor-Modes.html][minor]] modes.
#+end_quote
When several years ago I came to Emacs from Neovim, I was in love with Vim
editing model, found Emacs native keybindings ugly and had zero interest in
learning them. I want Emacs not as text editor (which it obviously lacks of)
but as an operation system with Lisp and all its power. So the first question
I asked myself was "Can I use Evil without learning Emacs keys?"
The answer is yes. I have never used — and still don't know — most Emacs
keys. I know only a few that you need when something breaks early during
Emacs startup and you don't have your Hel keys available. They are:
- =M-x= — Command palette. The main key you need; all other commands can be
invoked from it.
- =M-w= — Copy the selected text to google the error message or feed it
to LLM.
- =C-x C-s= — Save current buffer.
- =C-x C-c= — Exit Emacs.
That's it.
Hel and Emacs do not interfere much, because Emacs is not a modal editor:
letters and numbers are self-inserting, and most command key chords begin
with =C-x= or =C-c= (e.g. =C-x n d=). Due to this, Hel works as a layer on top
of Emacs.
In Normal state you have selection-based editing, multiple cursors, and all
the other Hel features. In Insert state, Hel steps aside and standard Emacs
keys work as usual. Also Hel doesn't touch =C-x= and =C-c= so they are always
available. This allows you to mix Hel and Emacs in any proportion.
** Kakoune vs Helix
The main difference between Kakoune and Helix, in terms of text editing, is
how they handle expanding selections: Kakoune uses =Shift= + motions, while Helix
uses a separate state on the =v= key. Since I originally came from Vim, I prefer
Helix's =v= key, so I chose Helix. However, Kakoune (as far as I know) was the
original inventor of this keyboard-driven multiple-selections approach, and it
deserves credit.
** Installation
#+begin_quote
[!TIP]
If you want a Spacemacs / Doom like configuration framework but for Hel
try [[https://github.com/helheim-emacs/helheim][Helheim Emacs]].
#+end_quote
Hel is on [[https://melpa.org/#/hel][MELPA]].
*** Emacs built-in package manager
This is the most minimal example of the =init.el= file:
#+begin_src emacs-lisp
;;; init.el -*- lexical-binding: t; no-byte-compile: t; -*-
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
(use-package hel
:ensure t
:custom (inhibit-startup-screen t)
:config (hel-mode))
;;; init.el ends here
#+end_src
*** [[https://github.com/progfolio/elpaca][Elpaca]]
#+begin_src emacs-lisp
(elpaca 'hel
(setopt inhibit-startup-screen t)
(hel-mode))
#+end_src
*** [[https://github.com/radian-software/straight.el][Straight]]
#+begin_src emacs-lisp
(straight-use-package 'hel)
(setopt inhibit-startup-screen t)
(hel-mode)
#+end_src
** Documentation
- [[file:docs/keybindings.org][Keybindings]]
- [[file:docs/customization.org][Customizations]]
** Differences from Helix text editor
This package is not a one-to-one emulation. Some commands are implemented in
a slightly different way (improved from the author's point of view), and some
features like keyboard macros, registers, and jumplists already have their
alternatives in Emacs.
- In Emacs the cursor ("point" in Emacs terms) is located *between* two characters
rather than *on* a character like in Helix or Vim. I decided to keep this
behavior, instead of emulating original one, as Evil does, because the primary
object of interaction in Helix approach is a selection, not the cursor itself.
This has consequences. In Helix, the cursor is a one-character selection, so
Helix always operates on a selection. Hel does not. It has two distinct states:
either selection exists, or not. Hel provides some DWIM ("do what I mean"):
- =d= / =D=
- When there is a selection, both =d= and =D= remove it from the buffer: =d=
copies the deleted text to the clipboard, =D= does not (Vim taught me
that you don't always want to copy deleted text to the clipboard).
- When there is no selection, common text editors provide two keys for
deleting a character: =Backspace= deletes the character before the cursor,
=Delete= — after it. Hel adopts this idea: =d= deletes the character before
the cursor, =D= — after. None of them touches the clipboard, since it
makes no sense to copy a single character into it.
- =r= / =R=
- When there is a selection they behave the same as in Helix.
- With no selection =r= replaces character before cursor, =R= — after.
- =x= and =X= commands expand or contract line-wise selections down when the
cursor is at the end of the selection, or up when the cursor is at the
beginning of the selection.
- Inner objects are additionally available directly under =m= prefix to reduce
keystrokes: =mw= is the same as =miw= — select word.
- Mark commands accept numeric arguments: =m2ip= or =2mip= — select 2 paragraphs.
- You can restore last multiple selections with =gv=.
- =gs=, =gh=, and =gl= create selections. This is for consistency, since all other
motions also create selections. In Helix they only move the cursor without
creating a selection.
- Keys that are relevant only when multiple cursors are present will be active
only in that case (e.g. =K=, =&=, =,= — full list is in ~hel-multiple-cursors-mode-map~
keymap). This allows you to reuse, for example, =K= for documentation lookup or =,= for
localleader while there is only one cursor in the buffer.
- Scrolling keybindings are taken from Vim instead of Helix.
- =gg= and =G= are taken from Vim. With numeric argument:
- =gg= — goes to N/10 of the way from the beginning of the buffer;
- =G= — goes to line N.
- Six easymotion commands are provided:
- =gw= / =gb= — choose and mark word forward/backward.
- =gW= / =gB= — choose and mark WORD forward/backward.
- =gj= / =gk= — go to line down/up.
Helix provides only =gw= to place 2-char hints at the beginning of each word.
- =f=, =F=, =t=, =T= commands to move to char are enhanced: they show hints for
targets, and while hints are active, they can be repeated with =n= / =N= keys.
- When you search backward with =?= command, while hints are active =n= and =N= keys
are swapped: =n= will repeat search backward and =N= — forward, like in Vim.
** Undo
*** Undo tree
Helix stores undo history in a tree, which you can traverse with =M-u= /
=M-U=. Emacs' undo history can also be represented as a tree. Hel doesn't
provide =M-u=, =M-U= commands, but there is an even better option — the
excellent [[https://elpa.gnu.org/packages/vundo.html][vundo]] package, which gives you a real undo-tree UI.
([[https://elpa.gnu.org/packages/undo-tree.html][undo-tree]] is not supported — it implements its own undo system instead of
using Emacs' native one.)
*** Using custom undo commands
You can use your own undo commands, for example [[https://codeberg.org/ideasman42/emacs-undo-fu][undo-fu]]:
#+begin_src emacs-lisp
(hel-keymap-global-set :state 'normal
"u" #'undo-fu-only-undo
"U" #'undo-fu-only-redo)
#+end_src
But they must meet two conditions: they must work with the native Emacs
undo system, and they must deactivate the mark before undoing.
#+begin_quote
[!IMPORTANT]
Hel doesn't support Emacs' undo in region. It conflicts with multiple
cursors, and in Normal state a selection is active most of the time, so
region-aware undo by default makes no sense. Undo commands must therefore
deactivate the mark before undoing.
#+end_quote
=undo-fu= deactivates the mark by default (see ~undo-fu-allow-undo-in-region~).
** Commands that are not implemented
- =M-u=, =M-U= — traverse undo tree. See [[* Undo tree][undo tree]] section.
- =q=, =Q= — record keyboard macros
** Extensions
- [[https://github.com/helheim-emacs/hel-leader][hel-leader]] — use =Space= as a leader key
- [[https://github.com/helheim-emacs/hel-collection][hel-collection]] — keybindings for built-in and third-party packages
- [[https://github.com/helheim-emacs/hel-org][hel-org]] — for [[https://orgmode.org/][Org-mode]]
- [[https://github.com/helheim-emacs/hel-paredit][hel-paredit]] — structural editing for S-expressions
- [[https://github.com/helheim-emacs/hel-ghostel][hel-ghostel]] — for [[https://github.com/dakra/ghostel][ghostel]] terminal
- [[https://github.com/helheim-emacs/hel-vterm][hel-vterm]] — for [[https://github.com/akermu/emacs-libvterm][vterm]] terminal emulator
** Tips
#+begin_quote
[!TIP]
By default, Hel uses a bar cursor for Normal state and a box cursor for Insert
state — the opposite of what Vim does. Your first instinct may be to switch
them back to what you're used to, but I recommend not doing so. This was the
first I done myself, and went through all the stages of acceptance, give
default settings a try — the bar cursor is better suited for Normal state.
#+end_quote
#+begin_quote
[!TIP]
You can set localleader keymap to =,=. It will act as the local leader while
there is only one cursor in the buffer, and will delete all secondary cursors
when there are multiple cursors.
#+end_quote
#+begin_quote
[!TIP]
Bind =Caps Lock= to =Esc=, and configure =Space= to tap+hold behavior: =Space= on
tap and =Ctrl= on hold. You can use any of the these tools: [[https://github.com/pqrs-org/Karabiner-Elements][kanata]], [[https://github.com/pqrs-org/Karabiner-Elements][kmonad]],
[[https://github.com/pqrs-org/Karabiner-Elements][keyd]] (Linux), [[https://github.com/pqrs-org/Karabiner-Elements][Karabiner-Elements]] (Mac).
#+end_quote
** Acknowledgments
Hel depends on [[https://github.com/magnars/dash.el][dash.el]], [[https://github.com/joddie/pcre2el][pcre2el]], [[https://github.com/abo-abo/avy][avy]] and [[https://github.com/jdtsmith/ultra-scroll][ultra-scroll]] wonderful packages.
Hel is heavily inspired by:
- [[https://github.com/emacs-evil/evil][evil]]
- [[https://github.com/magnars/multiple-cursors.el][multiple-cursors.el]]
- [[https://github.com/aome510/kak.el][kak.el]]
- [[https://github.com/mkleehammer/surround][surround]]
- [[https://github.com/meow-edit/meow][meow]]
- isearch (built-in)
- [[https://github.com/karb94/neoscroll.nvim][neoscroll.nvim]]
- [[https://github.com/doomemacs/doomemacs][doomemacs]]
- [[https://github.com/bbatsov/crux][crux]]
You are welcome to go and give them all at least a star!
** Contributing
*** Share
A quick post about this package on your blog or social network could bring
new users to Emacs, which would be great!
*** Support the development
Hel was developed on an old laptop with a cracked screen, and I worked on it
instead of grinding LeetCode. If you'd like to support Hel's development, you
can do so with a donation:
- [[https://www.paypal.me/anuvyklack][PayPal]]
Every contribution is greatly appreciated.
Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.
1,035 commits
1 commits
Emacs Lisp
100.0%