Environment
| Field | Value |
|---|---|
| Micro version | 2.0.16-dev.51 (CGO_ENABLED=1) |
| Distro | CachyOS (Arch Linux) |
| Kernel | 7.0.9-1-cachyos |
| CPU | Intel Core i5-1240P |
| GPU | Intel Iris Xe Graphics [ADL GT2] |
| GPU Driver | i915 |
| Mesa | 26.1.1 |
| Compositor | labwc |
My settings.json
{
"*.bak": {
"filetype": "micro"
},
"*.conf": {
"filetype": "ini"
},
"*.txt": {
"filetype": "micro"
},
"autosu": true,
"clipboard": "external",
"colorscheme": "dukedark_mod-tc",
"diffgutter": true,
"ft:asciidoc": {
"tabsize": 2
},
"ft:asm": {
"tabstospaces": false
},
"ft:awk": {
"tabstospaces": false
},
"ft:c": {
"tabsize": 2
},
"ft:c++": {
"tabsize": 2
},
"ft:clojure": {
"tabsize": 2
},
"ft:cmake": {
"tabsize": 2
},
"ft:crystal": {
"tabsize": 2
},
"ft:css": {
"tabsize": 2
},
"ft:cuda": {
"tabstospaces": false
},
"ft:dart": {
"tabsize": 2
},
"ft:ebuild": {
"tabstospaces": false
},
"ft:elixir": {
"tabsize": 2
},
"ft:forth": {
"tabstospaces": false
},
"ft:gdscript": {
"tabstospaces": false
},
"ft:gleam": {
"tabsize": 2
},
"ft:go": {
"tabstospaces": false
},
"ft:gomod": {
"tabstospaces": false
},
"ft:graphql": {
"tabsize": 2
},
"ft:haskell": {
"tabsize": 2
},
"ft:html": {
"tabsize": 2
},
"ft:html5": {
"tabsize": 2
},
"ft:jinja2": {
"tabsize": 2
},
"ft:json": {
"tabsize": 2
},
"ft:jsonnet": {
"tabsize": 2
},
"ft:justfile": {
"tabstospaces": false
},
"ft:lisp": {
"tabsize": 2
},
"ft:lua": {
"tabsize": 2
},
"ft:makefile": {
"tabstospaces": false
},
"ft:markdown": {
"tabsize": 2
},
"ft:meson": {
"tabsize": 2
},
"ft:msbuild": {
"tabsize": 2
},
"ft:nim": {
"tabsize": 2
},
"ft:nix": {
"tabsize": 2
},
"ft:ocaml": {
"tabsize": 2
},
"ft:proto": {
"tabsize": 2
},
"ft:puppet": {
"tabsize": 2
},
"ft:r": {
"tabsize": 2
},
"ft:ruby": {
"tabsize": 2
},
"ft:scala": {
"tabsize": 2
},
"ft:shell": {
"tabsize": 4
},
"ft:svelte": {
"tabsize": 2
},
"ft:terraform": {
"tabsize": 2
},
"ft:tex": {
"tabsize": 2
},
"ft:typescript": {
"tabsize": 2
},
"ft:vue": {
"tabsize": 2
},
"ft:xml": {
"tabsize": 2
},
"ft:yaml": {
"tabsize": 2
},
"ft:zsh": {
"tabsize": 4
},
"helpsplit": "vsplit",
"hlsearch": true,
"matchbracestyle": "highlight",
"mkparents": true,
"parsecursor": true,
"paste": false,
"rmtrailingws": false,
"ruler": false,
"savecursor": false,
"saveundo": false,
"scrollbar": true,
"scrollbarchar": "│",
"scrollspeed": 3,
"smartpaste": true,
"softwrap": true,
"statusformatl": "$(filename) $(modified)$(overwrite) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
"statusformatr": "$(line):$(col) | $(lines) ",
"tabhighlight": false,
"tabmovement": true,
"tabsize": 4,
"tabstospaces": true,
"truecolor": "on",
"useprimary": true,
"wordwrap": true,
"xterm": false
}My bindings.json
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"\u001bк": "command:toggle ruler",
"\u001bы": "command:setlocal showchars 'tab=›,space=·'",
"\u001b\u0013": "command:reset showchars",
"\u001bе": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"\u001bц": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"\u001b[27;5;13~": "CommandMode",
"\u001b[27;2;13~": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"\u001b[27;5;13~": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}Description
I've encountered two keybinding bugs while configuring Micro in Foot/Kitty with a Cyrillic keyboard layout. Both issues stem from how Micro parses key events internally versus what the terminal actually sends.
Bug 1: Raw escape sequences are ignored in the command pane
Terminals such as Foot or Kitty send specific raw escape sequences for modified keys. For example, Ctrl+Enter sends \x1b[27;5;13~ and Shift+Enter sends \x1b[27;2;13~.
Using the > raw command, I can see Micro receives these sequences correctly in the main buffer. I can bind them in bindings.json:
{
"Alt-Enter": "CommandMode",
"\u001b[27;5;13~": "CommandMode",
"\u001b[27;2;13~": "InsertNewline"
}This works perfectly in the main buffer. However, to create a toggle for the command mode, I need to bind the same key to AbortCommand inside the "command" pane type:
{
"command": {
"Alt-Enter": "AbortCommand",
"\u001b[27;5;13~": "AbortCommand"
}
}Expected behavior: Pressing Ctrl+Enter while the command bar is open should execute AbortCommand and close the command bar (just like Alt-Enter does when bound this way).
Actual behavior: The raw escape sequence binding is completely ignored in the command pane. Pressing the key does nothing. The command pane parser does not support matching raw escape sequences the way the main buffer does.
Bug 2: Alt + Non-Latin keys require manual raw Unicode mappings
When using a non-Latin (Cyrillic ru_RU) keyboard layout, pressing Alt + a key sends an escape sequence containing the Unicode character of the Cyrillic letter, not the physical Latin key.
For example, Alt+r on a Cyrillic layout sends \x1bк (because 'к' is on the same physical key as 'r'). Micro does not map this to the logical Alt-r binding. To make it work, I have to manually figure out the raw sequence and duplicate every Alt binding (as seen in my bindings.json above):
{
"Alt-r": "command:toggle ruler",
"\u001bк": "command:toggle ruler"
}Expected behavior: Micro should ideally map keybindings based on the physical key position, or at least provide a mechanism to define layout-agnostic bindings so users don't have to duplicate all Alt bindings with raw Unicode codes.
Actual behavior: Users with non-Latin layouts must duplicate all Alt keybindings using raw \u001b<unicode_char> sequences, making the config bloated and hard to maintain.
And a few small requests ...
Feature Request 1: Native Shift-Enter support
Currently, to make Shift+Enter insert a newline, users of Foot/Kitty have to manually bind the raw escape sequence (\u001b[27;2;13~) to InsertNewline. It would be much more user-friendly if Micro natively supported the logical binding "Shift-Enter": "InsertNewline" out of the box, abstracting away the terminal-specific escape sequences.
Feature Request 2: Add a native CommandMode toggle
Currently, CommandMode only opens the command bar. To close it, you need a separate action (AbortCommand or Escape) in the "command" pane.
I heavily rely on the command bar for text manipulation using the manipulator-plugin. This plugin provides commands like > brace, > squote, > dquote, > upper, etc. My workflow constantly involves opening the command bar, typing a case conversion command, and executing it.
Having a single CommandMode toggle action (instead of separate open/close actions) would make this workflow significantly faster and more ergonomic.
Feature Request 3: Make Ctrl-e a toggle by default
Following up on the previous point, since working with plugins like manipulator requires frequent access to the command bar, it makes perfect sense for Ctrl-e to act as a toggle by default. Currently, if I press Ctrl-e to open the bar and change my mind, I have to reach for Esc to close it. If Ctrl-e were a toggle (mapped to a ToggleCommandMode action), the workflow would be seamless: press Ctrl-e, type snake, press Enter. Or press Ctrl-e again to cancel.
Summary of requested changes:
- Fix: Allow raw escape sequence bindings (like
\u001b[27;5;13~) to work inside the"command"pane type. - Fix: Improve handling of
Altmodifiers for non-Latin keyboard layouts (physical key mapping). - Feature: Add native support for
"Shift-Enter": "InsertNewline"without requiring raw escape hacks. - Feature: Add a
ToggleCommandModeaction. - Feature: Change the default
Ctrl-ebinding fromCommandModetoToggleCommandMode.
Thank you for your time and for maintaining the best editor
Activity
16It would be easier for us to track separate issues and feature requests.
Regarding the first three points you may want to try out @Tubbles fork with kitty keyboard protocol support: https://github.com/micro-editor/micro/issues/1176#issuecomment-4274589934
For the last two, ToggleCommandMode is unnecessary as it can already be done with existing actions (we could probably include this in the default keybindings too, I don't see any harm in it):
~/.config/micro/bindings.json
{
"command": {
"Ctrl-e": "AbortCommand"
}
}It would be easier for us to track separate issues and feature requests.
Regarding the first three points you may want to try out @Tubbles fork with kitty keyboard protocol support: https://github.com/micro-editor/micro/issues/1176#issuecomment-4274589934
For the last two, ToggleCommandMode is unnecessary as it can already be done with existing actions (we could probably include this in the default keybindings too, I don't see any harm in it):
~/.config/micro/bindings.json
{
"command": {
"Ctrl-e": "AbortCommand"
}
}Bug 1: Maybe its worth adding escape sequence support to the command prompt, because I don't see the reason why we shouldn't. You could try it with this patch:
diff --git a/internal/action/infopane.go b/internal/action/infopane.go
index 6ecd9552..3cab092c 100644
--- a/internal/action/infopane.go
+++ b/internal/action/infopane.go
@@ -85,6 +85,11 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
switch e := event.(type) {
case *tcell.EventResize:
// TODO
+ case *tcell.EventRaw:
+ re := RawEvent{
+ esc: e.EscSeq(),
+ }
+ h.DoKeyEvent(re)
case *tcell.EventKey:
ke := keyEvent(e)
@@ -126,7 +131,7 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
// Returns true if the action was executed OR if there are more keys remaining
// to process before executing an action (if this is a key sequence event).
// Returns false if no action found.
-func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
+func (h *InfoPane) DoKeyEvent(e Event) bool {
action, more := InfoBindings.NextEvent(e, nil)
if action != nil && !more {
action(h)Bug 2: I'm unsure if this really is a bug, because the 'к' is the "main" key with a lower printed 'r', right? The non-Latin layout seems to have precedence.
Bug 1: Maybe its worth adding escape sequence support to the command prompt, because I don't see the reason why we shouldn't. You could try it with this patch:
diff --git a/internal/action/infopane.go b/internal/action/infopane.go
index 6ecd9552..3cab092c 100644
--- a/internal/action/infopane.go
+++ b/internal/action/infopane.go
@@ -85,6 +85,11 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
switch e := event.(type) {
case *tcell.EventResize:
// TODO
+ case *tcell.EventRaw:
+ re := RawEvent{
+ esc: e.EscSeq(),
+ }
+ h.DoKeyEvent(re)
case *tcell.EventKey:
ke := keyEvent(e)
@@ -126,7 +131,7 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
// Returns true if the action was executed OR if there are more keys remaining
// to process before executing an action (if this is a key sequence event).
// Returns false if no action found.
-func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
+func (h *InfoPane) DoKeyEvent(e Event) bool {
action, more := InfoBindings.NextEvent(e, nil)
if action != nil && !more {
action(h)Bug 2: I'm unsure if this really is a bug, because the 'к' is the "main" key with a lower printed 'r', right? The non-Latin layout seems to have precedence.
Unfortunately, that didn't help. I installed the fork https://github.com/Tubbles/micro, but it had no effect
Here are the steps to reproduce:
1.
❯ git clone https://github.com/Tubbles/micro
cd micro
CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin2. Ctrl + Enter
Shift + Enter
It would be easier for us to track separate issues and feature requests.
Regarding the first three points you may want to try out @Tubbles fork with kitty keyboard protocol support: #1176 (comment)
For the last two,
ToggleCommandModeis unnecessary as it can already be done with existing actions (we could probably include this in the default keybindings too, I don't see any harm in it): ...
Yes, thanks. I configured the toggle (CTRL + e) via bindings.json, but I thought it would be convenient for beginners if it was enabled by default
My bindings.json
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"\u001bк": "command:toggle ruler",
"\u001bы": "command:setlocal showchars 'tab=›,space=·'",
"\u001b\u0013": "command:reset showchars",
"\u001bе": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"\u001bц": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"\u001b[27;5;13~": "CommandMode",
"\u001b[27;2;13~": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"\u001b[27;5;13~": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}I will remove Feature Requests 2/3 from the current issue. I'll keep Feature Request 1, as it relates to the current problem
Unfortunately, that didn't help. I installed the fork https://github.com/Tubbles/micro, but it had no effect
Here are the steps to reproduce:
1.
❯ git clone https://github.com/Tubbles/micro
cd micro
CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin2. Ctrl + Enter
Shift + Enter
It would be easier for us to track separate issues and feature requests.
Regarding the first three points you may want to try out @Tubbles fork with kitty keyboard protocol support: #1176 (comment)
For the last two,
ToggleCommandModeis unnecessary as it can already be done with existing actions (we could probably include this in the default keybindings too, I don't see any harm in it): ...
Yes, thanks. I configured the toggle (CTRL + e) via bindings.json, but I thought it would be convenient for beginners if it was enabled by default
My bindings.json
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"\u001bк": "command:toggle ruler",
"\u001bы": "command:setlocal showchars 'tab=›,space=·'",
"\u001b\u0013": "command:reset showchars",
"\u001bе": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"\u001bц": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"\u001b[27;5;13~": "CommandMode",
"\u001b[27;2;13~": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"\u001b[27;5;13~": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}I will remove Feature Requests 2/3 from the current issue. I'll keep Feature Request 1, as it relates to the current problem
@p2ndemic Hey thanks for trying out my fork! Just a control question, did you make sure to check out my specific integration branch, ie. git checkout integration? You can read more about it on Tubbles/micro#1
@p2ndemic Hey thanks for trying out my fork! Just a control question, did you make sure to check out my specific integration branch, ie. git checkout integration? You can read more about it on Tubbles/micro#1
Hi @Tubbles! I wasn't aware of that. I think it would be worth adding this to the fork's README, since I was building the standard version
I did as described in https://github.com/Tubbles/micro/issues/1:
Here are the steps to reproduce:
1.
❯ rm -rf $HOME/Projects/micro
git clone https://github.com/Tubbles/micro $HOME/Projects/micro
cd $HOME/Projects/micro
git fetch
git checkout integration
git reset --hard origin/integration
CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/Full terminal output:
❯ rm -rf ~/Project/micro
git clone https://github.com/Tubbles/micro $HOME/Projects/micro
cd $HOME/Projects/micro
git fetch
git checkout integration
git reset --hard origin/integration
CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/
Клонирование в «/home/admin/Projects/micro»...
remote: Enumerating objects: 24069, done.
remote: Counting objects: 100% (507/507), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 24069 (delta 460), reused 404 (delta 404), pack-reused 23562 (from 2)
Получение объектов: 100% (24069/24069), 14.20 MiB | 9.27 MiB/s, готово.
Определение изменений: 100% (16885/16885), готово.
branch 'integration' set up to track 'origin/integration'.
Переключились на новую ветку «integration»
Указатель HEAD сейчас на коммите 254c1ece Merge branch 'colorscheme-follow-system' into integration
GOOS=linux GOARCH=amd64 go generate ./runtime
CGO_ENABLED=1 go build -trimpath -ldflags "-s -w -X github.com/micro-editor/micro/v2/internal/util.Version=2.0.16-dev.152 -X github.com/micro-editor/micro/v2/internal/util.CommitHash=254c1ece -X 'github.com/micro-editor/micro/v2/internal/util.CompileDate=June 06, 2026' """ ./cmd/micro
~/Projects/micro integration 6s
❯ Bug 1:
It worked! I replaced the raw escape sequence bindings for modified keys in ~/.config/micro/bindings.json with regular ones. Toggle works now
Thanks!
~/.config/micro/bindings.json:`
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"Ctrl-Enter": "CommandMode",
"Shift-Enter": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"Ctrl-Enter": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}Here is the output of the > raw command in micro:
Bug 2: Unfortunately, this had no effect when using non-Latin (Cyrillic ru_RU) keyboard layout. It's necessary to map their Unicode values and bind them separately. This requires some thought. Maybe bindings should rely on physical key positions instead.
Here is the output of the > raw command for non-Latin characters. They differ from the standard build:
Alt - r => Alt - к
Alt - s => Alt - ы
And the standard Ctrl-Q ... etc shortcuts, which used to work in the upstream build, no longer work when switching to a non-Latin keyboard layout. This is a regression
Hi @Tubbles! I wasn't aware of that. I think it would be worth adding this to the fork's README, since I was building the standard version
I did as described in https://github.com/Tubbles/micro/issues/1:
Here are the steps to reproduce:
1.
❯ rm -rf $HOME/Projects/micro
git clone https://github.com/Tubbles/micro $HOME/Projects/micro
cd $HOME/Projects/micro
git fetch
git checkout integration
git reset --hard origin/integration
CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/Full terminal output:
❯ rm -rf ~/Project/micro
git clone https://github.com/Tubbles/micro $HOME/Projects/micro
cd $HOME/Projects/micro
git fetch
git checkout integration
git reset --hard origin/integration
CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/
Клонирование в «/home/admin/Projects/micro»...
remote: Enumerating objects: 24069, done.
remote: Counting objects: 100% (507/507), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 24069 (delta 460), reused 404 (delta 404), pack-reused 23562 (from 2)
Получение объектов: 100% (24069/24069), 14.20 MiB | 9.27 MiB/s, готово.
Определение изменений: 100% (16885/16885), готово.
branch 'integration' set up to track 'origin/integration'.
Переключились на новую ветку «integration»
Указатель HEAD сейчас на коммите 254c1ece Merge branch 'colorscheme-follow-system' into integration
GOOS=linux GOARCH=amd64 go generate ./runtime
CGO_ENABLED=1 go build -trimpath -ldflags "-s -w -X github.com/micro-editor/micro/v2/internal/util.Version=2.0.16-dev.152 -X github.com/micro-editor/micro/v2/internal/util.CommitHash=254c1ece -X 'github.com/micro-editor/micro/v2/internal/util.CompileDate=June 06, 2026' """ ./cmd/micro
~/Projects/micro integration 6s
❯ Bug 1:
It worked! I replaced the raw escape sequence bindings for modified keys in ~/.config/micro/bindings.json with regular ones. Toggle works now
Thanks!
~/.config/micro/bindings.json:`
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"Ctrl-Enter": "CommandMode",
"Shift-Enter": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"Ctrl-Enter": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}Here is the output of the > raw command in micro:
Bug 2: Unfortunately, this had no effect when using non-Latin (Cyrillic ru_RU) keyboard layout. It's necessary to map their Unicode values and bind them separately. This requires some thought. Maybe bindings should rely on physical key positions instead.
Here is the output of the > raw command for non-Latin characters. They differ from the standard build:
Alt - r => Alt - к
Alt - s => Alt - ы
And the standard Ctrl-Q ... etc shortcuts, which used to work in the upstream build, no longer work when switching to a non-Latin keyboard layout. This is a regression
@JoeKar thanks for the patch! It worked!
I did as you suggested. I made the changes to ~/Projects/micro/internal/action/infopane.go and built a new version
Here are the steps I followed:
1. Cloned the repository:
❯ rm -rf $HOME/Projects/micro
git clone https://github.com/micro-editor/micro $HOME/Projects/micro
cd $HOME/Projects/micro2. Replaced the infopane.go file with the new one:
infopane.go:
package action
import (
"bytes"
"github.com/micro-editor/micro/v2/internal/buffer"
"github.com/micro-editor/micro/v2/internal/config"
"github.com/micro-editor/micro/v2/internal/display"
"github.com/micro-editor/micro/v2/internal/info"
"github.com/micro-editor/micro/v2/internal/util"
"github.com/micro-editor/tcell/v2"
)
type InfoKeyAction func(*InfoPane)
var InfoBindings *KeyTree
var InfoBufBindings *KeyTree
func init() {
InfoBindings = NewKeyTree()
InfoBufBindings = NewKeyTree()
}
func InfoMapEvent(k Event, action string) {
config.Bindings["command"][k.Name()] = action
switch e := k.(type) {
case KeyEvent, KeySequenceEvent, RawEvent:
infoMapKey(e, action)
case MouseEvent:
infoMapMouse(e, action)
}
}
func infoMapKey(k Event, action string) {
if f, ok := InfoKeyActions[action]; ok {
InfoBindings.RegisterKeyBinding(k, InfoKeyActionGeneral(f))
} else if f, ok := BufKeyActions[action]; ok {
InfoBufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(f))
}
}
func infoMapMouse(k MouseEvent, action string) {
// TODO: map mouse
if f, ok := BufMouseActions[action]; ok {
InfoBufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
} else {
infoMapKey(k, action)
}
}
func InfoKeyActionGeneral(a InfoKeyAction) PaneKeyAction {
return func(p Pane) bool {
a(p.(*InfoPane))
return true
}
}
type InfoPane struct {
*BufPane
*info.InfoBuf
}
func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane {
ip := new(InfoPane)
ip.InfoBuf = ib
ip.BufPane = NewBufPane(ib.Buffer, w, tab)
ip.BufPane.bindings = InfoBufBindings
return ip
}
func NewInfoBar() *InfoPane {
ib := info.NewBuffer()
w := display.NewInfoWindow(ib)
return NewInfoPane(ib, w, nil)
}
func (h *InfoPane) Close() {
h.InfoBuf.Close()
h.BufPane.Close()
}
func (h *InfoPane) HandleEvent(event tcell.Event) {
switch e := event.(type) {
case *tcell.EventResize:
// TODO
case *tcell.EventRaw:
re := RawEvent{
esc: e.EscSeq(),
}
h.DoKeyEvent(re)
case *tcell.EventKey:
ke := keyEvent(e)
done := h.DoKeyEvent(ke)
hasYN := h.HasYN
if e.Key() == tcell.KeyRune && hasYN {
y := e.Rune() == 'y' || e.Rune() == 'Y'
n := e.Rune() == 'n' || e.Rune() == 'N'
if y || n {
h.YNResp = y
h.DonePrompt(false)
InfoBindings.ResetEvents()
InfoBufBindings.ResetEvents()
}
}
if e.Key() == tcell.KeyRune && !done && !hasYN {
h.DoRuneInsert(e.Rune())
done = true
}
if done && h.HasPrompt && !hasYN {
resp := string(h.LineBytes(0))
hist := h.History[h.PromptType]
if resp != hist[h.HistoryNum] {
h.HistoryNum = len(hist) - 1
hist[h.HistoryNum] = resp
h.HistorySearch = false
}
if h.EventCallback != nil {
h.EventCallback(resp)
}
}
default:
h.BufPane.HandleEvent(event)
}
}
// DoKeyEvent executes a key event for the command bar, doing any overridden actions.
// Returns true if the action was executed OR if there are more keys remaining
// to process before executing an action (if this is a key sequence event).
// Returns false if no action found.
func (h *InfoPane) DoKeyEvent(e Event) bool {
action, more := InfoBindings.NextEvent(e, nil)
if action != nil && !more {
action(h)
InfoBindings.ResetEvents()
return true
} else if action == nil && !more {
InfoBindings.ResetEvents()
// return false //TODO:?
}
if !more {
// If no infopane action found, try to find a bufpane action.
//
// TODO: this is buggy. For example, if the command bar has the following
// two bindings:
//
// "<Ctrl-x><Ctrl-p>": "HistoryUp",
// "<Ctrl-x><Ctrl-v>": "Paste",
//
// the 2nd binding (with a bufpane action) doesn't work, since <Ctrl-x>
// has been already consumed by the 1st binding (with an infopane action).
//
// We should either iterate both InfoBindings and InfoBufBindings keytrees
// together, or just use the same keytree for both infopane and bufpane
// bindings.
action, more = InfoBufBindings.NextEvent(e, nil)
if action != nil && !more {
action(h.BufPane)
InfoBufBindings.ResetEvents()
return true
} else if action == nil && !more {
InfoBufBindings.ResetEvents()
}
}
return more
}
// HistoryUp cycles history up
func (h *InfoPane) HistoryUp() {
h.UpHistory(h.History[h.PromptType])
}
// HistoryDown cycles history down
func (h *InfoPane) HistoryDown() {
h.DownHistory(h.History[h.PromptType])
}
// HistorySearchUp fetches the previous history item beginning with the text
// in the infobuffer before cursor
func (h *InfoPane) HistorySearchUp() {
h.SearchUpHistory(h.History[h.PromptType])
}
// HistorySearchDown fetches the next history item beginning with the text
// in the infobuffer before cursor
func (h *InfoPane) HistorySearchDown() {
h.SearchDownHistory(h.History[h.PromptType])
}
// Autocomplete begins autocompletion
func (h *InfoPane) CommandComplete() {
b := h.Buf
if b.HasSuggestions {
b.CycleAutocomplete(true)
return
}
c := b.GetActiveCursor()
l := b.LineBytes(0)
l = util.SliceStart(l, c.X)
args := bytes.Split(l, []byte{' '})
cmd := string(args[0])
if h.PromptType == "Command" {
if len(args) == 1 {
b.Autocomplete(CommandComplete)
} else if action, ok := commands[cmd]; ok {
if action.completer != nil {
b.Autocomplete(action.completer)
}
}
} else {
// by default use filename autocompletion
b.Autocomplete(buffer.FileComplete)
}
}
// ExecuteCommand completes the prompt
func (h *InfoPane) ExecuteCommand() {
if !h.HasYN {
h.DonePrompt(false)
}
}
// AbortCommand cancels the prompt
func (h *InfoPane) AbortCommand() {
h.DonePrompt(true)
}
// InfoKeyActions contains the list of all possible key actions the infopane could execute
var InfoKeyActions = map[string]InfoKeyAction{
"HistoryUp": (*InfoPane).HistoryUp,
"HistoryDown": (*InfoPane).HistoryDown,
"HistorySearchUp": (*InfoPane).HistorySearchUp,
"HistorySearchDown": (*InfoPane).HistorySearchDown,
"CommandComplete": (*InfoPane).CommandComplete,
"ExecuteCommand": (*InfoPane).ExecuteCommand,
"AbortCommand": (*InfoPane).AbortCommand,
}3. Built the project:
❯ CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/Here is the full terminal output:
~/Projects
❯ rm -rf ~/Project/micro
git clone https://github.com/micro-editor/micro $HOME/Projects/micro
cd $HOME/Projects/micro
Клонирование в «/home/admin/Projects/micro»...
remote: Enumerating objects: 22756, done.
remote: Counting objects: 100% (35/35), done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 22756 (delta 20), reused 9 (delta 9), pack-reused 22721 (from 2)
Получение объектов: 100% (22756/22756), 13.69 MiB | 105.00 KiB/s, готово.
Определение изменений: 100% (15888/15888), готово.
~/Projects/micro master 2m 15s
❯ CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/
GOOS=linux GOARCH=amd64 go generate ./runtime
CGO_ENABLED=1 go build -trimpath -ldflags "-s -w -X github.com/micro-editor/micro/v2/internal/util.Version=2.0.16-dev.51 -X github.com/micro-editor/micro/v2/internal/util.CommitHash=b75cfc79 -X 'github.com/micro-editor/micro/v2/internal/util.CompileDate=June 06, 2026' """ ./cmd/micro
~/Projects/micro master* 6s
❯ Bug 1:
Fixed! Toggle works with the Ctrl + Enter command.
My bindings.json
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"\u001bк": "command:toggle ruler",
"\u001bы": "command:setlocal showchars 'tab=›,space=·'",
"\u001b\u0013": "command:reset showchars",
"\u001bе": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"\u001bц": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"\u001b[27;5;13~": "CommandMode",
"\u001b[27;2;13~": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"\u001b[27;5;13~": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}Regarding
Bug 2:
On the Non-Latin layout (Cyrillic ru_RU.UTF8)
Keys:
r => к
s => ы
...
Respectively:
Alt + r => Alt + к
Alt + s => Alt + ы
... etc
These keys are always physically in the same place. Maybe we should take physical key positions into account for bindings? I'm not exactly sure how to resolve this, it needs some thought, hmm...
Also, I think it's worth rebasing https://github.com/micro-editor/tcell. It hasn't been updated since 2024. The upstream https://github.com/gdamore/tcell has received many useful changes
@JoeKar thanks for the patch! It worked!
I did as you suggested. I made the changes to ~/Projects/micro/internal/action/infopane.go and built a new version
Here are the steps I followed:
1. Cloned the repository:
❯ rm -rf $HOME/Projects/micro
git clone https://github.com/micro-editor/micro $HOME/Projects/micro
cd $HOME/Projects/micro2. Replaced the infopane.go file with the new one:
infopane.go:
package action
import (
"bytes"
"github.com/micro-editor/micro/v2/internal/buffer"
"github.com/micro-editor/micro/v2/internal/config"
"github.com/micro-editor/micro/v2/internal/display"
"github.com/micro-editor/micro/v2/internal/info"
"github.com/micro-editor/micro/v2/internal/util"
"github.com/micro-editor/tcell/v2"
)
type InfoKeyAction func(*InfoPane)
var InfoBindings *KeyTree
var InfoBufBindings *KeyTree
func init() {
InfoBindings = NewKeyTree()
InfoBufBindings = NewKeyTree()
}
func InfoMapEvent(k Event, action string) {
config.Bindings["command"][k.Name()] = action
switch e := k.(type) {
case KeyEvent, KeySequenceEvent, RawEvent:
infoMapKey(e, action)
case MouseEvent:
infoMapMouse(e, action)
}
}
func infoMapKey(k Event, action string) {
if f, ok := InfoKeyActions[action]; ok {
InfoBindings.RegisterKeyBinding(k, InfoKeyActionGeneral(f))
} else if f, ok := BufKeyActions[action]; ok {
InfoBufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(f))
}
}
func infoMapMouse(k MouseEvent, action string) {
// TODO: map mouse
if f, ok := BufMouseActions[action]; ok {
InfoBufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
} else {
infoMapKey(k, action)
}
}
func InfoKeyActionGeneral(a InfoKeyAction) PaneKeyAction {
return func(p Pane) bool {
a(p.(*InfoPane))
return true
}
}
type InfoPane struct {
*BufPane
*info.InfoBuf
}
func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane {
ip := new(InfoPane)
ip.InfoBuf = ib
ip.BufPane = NewBufPane(ib.Buffer, w, tab)
ip.BufPane.bindings = InfoBufBindings
return ip
}
func NewInfoBar() *InfoPane {
ib := info.NewBuffer()
w := display.NewInfoWindow(ib)
return NewInfoPane(ib, w, nil)
}
func (h *InfoPane) Close() {
h.InfoBuf.Close()
h.BufPane.Close()
}
func (h *InfoPane) HandleEvent(event tcell.Event) {
switch e := event.(type) {
case *tcell.EventResize:
// TODO
case *tcell.EventRaw:
re := RawEvent{
esc: e.EscSeq(),
}
h.DoKeyEvent(re)
case *tcell.EventKey:
ke := keyEvent(e)
done := h.DoKeyEvent(ke)
hasYN := h.HasYN
if e.Key() == tcell.KeyRune && hasYN {
y := e.Rune() == 'y' || e.Rune() == 'Y'
n := e.Rune() == 'n' || e.Rune() == 'N'
if y || n {
h.YNResp = y
h.DonePrompt(false)
InfoBindings.ResetEvents()
InfoBufBindings.ResetEvents()
}
}
if e.Key() == tcell.KeyRune && !done && !hasYN {
h.DoRuneInsert(e.Rune())
done = true
}
if done && h.HasPrompt && !hasYN {
resp := string(h.LineBytes(0))
hist := h.History[h.PromptType]
if resp != hist[h.HistoryNum] {
h.HistoryNum = len(hist) - 1
hist[h.HistoryNum] = resp
h.HistorySearch = false
}
if h.EventCallback != nil {
h.EventCallback(resp)
}
}
default:
h.BufPane.HandleEvent(event)
}
}
// DoKeyEvent executes a key event for the command bar, doing any overridden actions.
// Returns true if the action was executed OR if there are more keys remaining
// to process before executing an action (if this is a key sequence event).
// Returns false if no action found.
func (h *InfoPane) DoKeyEvent(e Event) bool {
action, more := InfoBindings.NextEvent(e, nil)
if action != nil && !more {
action(h)
InfoBindings.ResetEvents()
return true
} else if action == nil && !more {
InfoBindings.ResetEvents()
// return false //TODO:?
}
if !more {
// If no infopane action found, try to find a bufpane action.
//
// TODO: this is buggy. For example, if the command bar has the following
// two bindings:
//
// "<Ctrl-x><Ctrl-p>": "HistoryUp",
// "<Ctrl-x><Ctrl-v>": "Paste",
//
// the 2nd binding (with a bufpane action) doesn't work, since <Ctrl-x>
// has been already consumed by the 1st binding (with an infopane action).
//
// We should either iterate both InfoBindings and InfoBufBindings keytrees
// together, or just use the same keytree for both infopane and bufpane
// bindings.
action, more = InfoBufBindings.NextEvent(e, nil)
if action != nil && !more {
action(h.BufPane)
InfoBufBindings.ResetEvents()
return true
} else if action == nil && !more {
InfoBufBindings.ResetEvents()
}
}
return more
}
// HistoryUp cycles history up
func (h *InfoPane) HistoryUp() {
h.UpHistory(h.History[h.PromptType])
}
// HistoryDown cycles history down
func (h *InfoPane) HistoryDown() {
h.DownHistory(h.History[h.PromptType])
}
// HistorySearchUp fetches the previous history item beginning with the text
// in the infobuffer before cursor
func (h *InfoPane) HistorySearchUp() {
h.SearchUpHistory(h.History[h.PromptType])
}
// HistorySearchDown fetches the next history item beginning with the text
// in the infobuffer before cursor
func (h *InfoPane) HistorySearchDown() {
h.SearchDownHistory(h.History[h.PromptType])
}
// Autocomplete begins autocompletion
func (h *InfoPane) CommandComplete() {
b := h.Buf
if b.HasSuggestions {
b.CycleAutocomplete(true)
return
}
c := b.GetActiveCursor()
l := b.LineBytes(0)
l = util.SliceStart(l, c.X)
args := bytes.Split(l, []byte{' '})
cmd := string(args[0])
if h.PromptType == "Command" {
if len(args) == 1 {
b.Autocomplete(CommandComplete)
} else if action, ok := commands[cmd]; ok {
if action.completer != nil {
b.Autocomplete(action.completer)
}
}
} else {
// by default use filename autocompletion
b.Autocomplete(buffer.FileComplete)
}
}
// ExecuteCommand completes the prompt
func (h *InfoPane) ExecuteCommand() {
if !h.HasYN {
h.DonePrompt(false)
}
}
// AbortCommand cancels the prompt
func (h *InfoPane) AbortCommand() {
h.DonePrompt(true)
}
// InfoKeyActions contains the list of all possible key actions the infopane could execute
var InfoKeyActions = map[string]InfoKeyAction{
"HistoryUp": (*InfoPane).HistoryUp,
"HistoryDown": (*InfoPane).HistoryDown,
"HistorySearchUp": (*InfoPane).HistorySearchUp,
"HistorySearchDown": (*InfoPane).HistorySearchDown,
"CommandComplete": (*InfoPane).CommandComplete,
"ExecuteCommand": (*InfoPane).ExecuteCommand,
"AbortCommand": (*InfoPane).AbortCommand,
}3. Built the project:
❯ CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/Here is the full terminal output:
~/Projects
❯ rm -rf ~/Project/micro
git clone https://github.com/micro-editor/micro $HOME/Projects/micro
cd $HOME/Projects/micro
Клонирование в «/home/admin/Projects/micro»...
remote: Enumerating objects: 22756, done.
remote: Counting objects: 100% (35/35), done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 22756 (delta 20), reused 9 (delta 9), pack-reused 22721 (from 2)
Получение объектов: 100% (22756/22756), 13.69 MiB | 105.00 KiB/s, готово.
Определение изменений: 100% (15888/15888), готово.
~/Projects/micro master 2m 15s
❯ CGO_ENABLED=1 make build
sudo mv -f micro /usr/local/bin/
GOOS=linux GOARCH=amd64 go generate ./runtime
CGO_ENABLED=1 go build -trimpath -ldflags "-s -w -X github.com/micro-editor/micro/v2/internal/util.Version=2.0.16-dev.51 -X github.com/micro-editor/micro/v2/internal/util.CommitHash=b75cfc79 -X 'github.com/micro-editor/micro/v2/internal/util.CompileDate=June 06, 2026' """ ./cmd/micro
~/Projects/micro master* 6s
❯ Bug 1:
Fixed! Toggle works with the Ctrl + Enter command.
My bindings.json
{
"Alt-r": "command:toggle ruler",
"Alt-s": "command:setlocal showchars 'tab=›,space=·'",
"Alt-Ctrl-s": "command:reset showchars",
"Alt-t": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"Alt-w": "command:toggle softwrap|command:toggle wordwrap",
"\u001bк": "command:toggle ruler",
"\u001bы": "command:setlocal showchars 'tab=›,space=·'",
"\u001b\u0013": "command:reset showchars",
"\u001bе": "command:togglelocal hltaberrors|command:togglelocal hltrailingws",
"\u001bц": "command:toggle softwrap|command:toggle wordwrap",
"Alt-Enter": "CommandMode",
"\u001b[27;5;13~": "CommandMode",
"\u001b[27;2;13~": "InsertNewline",
"F3": "FindNext",
"Shift-F3": "FindPrevious",
"Alt-Ctrl-Down": "FindNext",
"Alt-Ctrl-Up": "FindPrevious",
"command": {
"Alt-Enter": "AbortCommand",
"\u001b[27;5;13~": "AbortCommand",
"Ctrl-e": "AbortCommand"
}
}Regarding
Bug 2:
On the Non-Latin layout (Cyrillic ru_RU.UTF8)
Keys:
r => к
s => ы
...
Respectively:
Alt + r => Alt + к
Alt + s => Alt + ы
... etc
These keys are always physically in the same place. Maybe we should take physical key positions into account for bindings? I'm not exactly sure how to resolve this, it needs some thought, hmm...
Also, I think it's worth rebasing https://github.com/micro-editor/tcell. It hasn't been updated since 2024. The upstream https://github.com/gdamore/tcell has received many useful changes
By the way, the default hardcoded bindings in Micro (like Ctrl+Q, Ctrl+T, etc.) work regardless of the selected keyboard layout. They function correctly for both Latin and non-Latin layouts. The issue only occurs when manually defining custom bindings via ~/.config/micro/bindings.json
By the way, the default hardcoded bindings in Micro (like Ctrl+Q, Ctrl+T, etc.) work regardless of the selected keyboard layout. They function correctly for both Latin and non-Latin layouts. The issue only occurs when manually defining custom bindings via ~/.config/micro/bindings.json
It would be easier for us to track separate issues and feature requests.
Regarding the first three points you may want to try out @Tubbles fork with kitty keyboard protocol support: https://github.com/micro-editor/micro/issues/1176#issuecomment-4274589934
For the last two,
ToggleCommandModeis unnecessary as it can already be done with existing actions (we could probably include this in the default keybindings too, I don't see any harm in it):~/.config/micro/bindings.json