helheim-emacs/hel

Helix emulation layer for Emacs

Emacs Lisp

141

1,036 commits

updated Sep 2, 2026

See the code

README

* 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.

Contributors

anuvyklack

1,035 commits

bulldo2er

1 commits

helheim-emacs/hel

Helix emulation layer for Emacs

Emacs Lisp

141

1,036 commits

updated Sep 2, 2026

See the code

README

* 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.

Contributors

anuvyklack

1,035 commits

bulldo2er

1 commits

Languages

Emacs Lisp

100.0%