Skip to content

Switch tcell to our fork of the upstream v3 baseline with escape sequence support - #4174

Draft
JoeKar wants to merge 9 commits into
masterfrom
tcell/v3
Draft

JoeKar wants to merge 9 commits into
masterfrom
tcell/v3

Conversation

@JoeKar

@JoeKar JoeKar commented Aug 14, 2026 •

Copy link
Copy Markdown
Member

This work was initially done by @Tubbles and Claude. 😉
Many thanks for this preparation!

With this we are finally able to have proper pastes in the Windows Terminal App.

TODOs:

  • It looks like it causes a redraw bug in case the bracketed paste is done over a selection.
  • Actually it will additionally display PASTE in the left status bar section as a result of the forced paste in the Windows Terminal App.
  • Add full underline style support (styles and colors)

Related to micro-editor/tcell#5.
Fixes #3571
Fixes #4037
Fixes #4173

@JoeKar
JoeKar requested a review from dmaluka August 14, 2026 19:32
@JoeKar
JoeKar marked this pull request as draft August 14, 2026 19:32
@JoeKar

JoeKar commented Aug 14, 2026

Copy link
Copy Markdown
Member Author

Currently I'm unsure if this should be added within micro's v2.x.y releases, because it might cause compatibility issues with some key mappings. Maybe its worth a minor instead of a patch release.

@Andriamanitra:
Did you discover some incompatibilities while testing?

@Andriamanitra

Copy link
Copy Markdown
Collaborator

I just tested all the bindings in my bindings.json (and some random ones) in micro versions before and after this PR. Most notable change is that bunch of key combinations that didn't get parsed correctly before just started working (for example Ctrli, Ctrlm, modifiers with non-ascii letters).

There were however a few key combinations that were emitting valid events before that now show up as a different event in the raw event viewer:

keys pressed event before this PR event now
Backspace Backspace2 (??) Backspace
CtrlBackspace Backspace Ctrl-Backspace
AltShift7 Alt-/ 1 Shift-Alt-7
CtrlSpacebar Ctrl-Space Ctrl- (??)
CtrlShift- Ctrl-_ 2 Shift-Ctrl--

I assume the results will vary a bit depending on OS/terminal emulator/keyboard layout, but on my setup (Linux/Ghostty/Nordic kbd layout) the breakage doesn't seem to be too bad.

Footnotes

  1. Shift7 is / on my keyboard layout ↩

  2. Shift- is _ on my keyboard layout ↩

@JoeKar

JoeKar commented Aug 15, 2026 •

Copy link
Copy Markdown
Member Author
keys pressed event before this PR event now
Backspace Backspace2 (??) Backspace
CtrlBackspace Backspace Ctrl-Backspace
AltShift7 Alt-/ Shift-Alt-7
CtrlSpacebar Ctrl-Space Ctrl- (??)
CtrlShift- Ctrl-_ Shift-Ctrl--

According to tcell's CHANGESv3.md#key-event-changes:

Additionally the following special keys are removed, as they are delivered instead as KeyRune with the relevant rune, and the ModCtrl modifier: KeyCtrlSpace, KeyCtrlLeftSq, KeyCtrlRightSq, KeyCtrlBackslash, and KeyCtrlUnderscore.
[...]
The KeyBackspace2 key is no longer delivered, but is converted to KeyBackspace. (This resolves some inconsistency around e.g. CTRL-H vs DELETE.)

In general it is now more consistent to receive all modifiers instead of special key combinations.
But yes, this might cause problems like here:

config.TryBindKey("Alt-/", "lua:comment.comment", false)
config.TryBindKey("CtrlUnderscore", "lua:comment.comment", false)

to
config.TryBindKey("Alt-/", "lua:comment.comment", false)
config.TryBindKey("Ctrl-_", "lua:comment.comment", false)

via f5e19bb

A 100% downward compatibility is quite hard here. 🤔

| AltShift7 | Alt-/| Shift-Alt-7 |
[...]
| CtrlShift- | Ctrl-_| Shift-Ctrl-- |

This case seems to be terminal dependent, because I still receive Alt-/ and Ctrl-_.

@Andriamanitra

Copy link
Copy Markdown
Collaborator

I noticed that this PR causes a bug where selecting text by dragging with mouse always triggers a copy when clipboard is set to terminal.

@JoeKar

JoeKar commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

I noticed that this PR causes a bug where selecting text by dragging with mouse always triggers a copy when clipboard is set to terminal.

You see Copied selection to clipboard:

if nk == tcell.KeyCtrlC && t.HasSelection() {
clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg)
InfoBar.Message("Copied selection to clipboard")

Right?
Then I guess the normalizedTermKey() returns KeyCtrlC in this scenario.

Let us slightly change the perspective:
Using the master with external clipboard and create a selection by dragging the mouse.
It will also cause a copy into the external clipboard, because a middle mouse click inserts the selection with the info Pasted clipboard.
The only missing info is the Copied selection to clipboard.

Is this new behavior then a bug or the normalization of it? 🤔

@Andriamanitra

Copy link
Copy Markdown
Collaborator

I noticed that this PR causes a bug where selecting text by dragging with mouse always triggers a copy when clipboard is set to terminal.

You see Copied selection to clipboard:

if nk == tcell.KeyCtrlC && t.HasSelection() {
clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg)
InfoBar.Message("Copied selection to clipboard")

Right? Then I guess the normalizedTermKey() returns KeyCtrlC in this scenario.

No, I don't get anything in the infobar (Ghostty shows a pop-up that says "Copied to clipboard" though).

Let us slightly change the perspective: Using the master with external clipboard and create a selection by dragging the mouse. It will also cause a copy into the external clipboard, because a middle mouse click inserts the selection with the info Pasted clipboard. The only missing info is the Copied selection to clipboard.

That's not the case, at least on my setup nothing gets copied to clipboard on master.

When I drag with left mouse button with this PR the raw event viewer shows something like this:

# PR #4174
  1 EventMouse: MouseLeft: ""
  2 EventMouse: MouseLeft: ""
  3 EventMouse: MouseLeft: ""
  4 EventMouse: MouseLeft: ""
  5 EventMouse: MouseLeft: ""
  6 EventMouse: MouseLeft: ""
  7 EventMouse: MouseLeft: ""
  8 EventMouse: MouseLeft: ""
  9 EventMouse: MouseLeft: ""
 10 EventMouse: MouseLeft: ""
 11 EventMouse: : ""

# master
  1 EventMouse: MouseLeft: "\x1b[<0;30;4M"
  2 EventMouse: MouseLeft: "\x1b[<32;31;4M"
  3 EventMouse: MouseLeft: "\x1b[<32;32;4M"
  4 EventMouse: MouseLeft: "\x1b[<32;32;5M"
  5 EventMouse: MouseLeft: "\x1b[<32;33;5M"
  6 EventMouse: MouseLeft: "\x1b[<32;34;5M"
  7 EventMouse: MouseLeft: "\x1b[<32;35;6M"
  8 EventMouse: MouseLeft: "\x1b[<32;36;6M"
  9 EventMouse: MouseLeft: "\x1b[<32;37;6M"
 10 EventMouse: MouseLeft: "\x1b[<32;37;7M"
 11 EventMouse: : "\x1b[<0;37;7m"

@Andriamanitra

Andriamanitra commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

After some testing I found that the copy event originates from here:

func (h *BufPane) MouseRelease(e *tcell.EventMouse) bool {
// We could finish the selection based on the release location as in the
// commented out code below, to allow text selections even in a terminal
// that doesn't support mouse motion events. But when the mouse click is
// within the scroll margin, that would cause a scroll and selection
// even for a simple mouse click, which is not good.
// if !h.DoubleClick && !h.TripleClick {
// mx, my := e.Position()
// h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my})
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
// }
if h.Cursor.HasSelection() {
h.Cursor.CopySelection(clipboard.PrimaryReg)
}
return true
}

I guess something changed about how the different clipboards (primary vs clipboard) work and it's now being copied to clipboard rather than primary.

@JoeKar

JoeKar commented Oct 18, 2026

Copy link
Copy Markdown
Member Author

When I drag with left mouse button with this PR the raw event viewer shows something like this: [...]

Worth to note that we currently lose the position printed (only?) in the raw pane. 🤔

After some testing I found that the copy event originates from here:
[...]
I guess something changed about how the different clipboards (primary vs clipboard) work and it's now being copied to clipboard rather than primary.

Yep, the terminal resp. Screen clipboard handling changed in that way, that we cannot request a certain register any longer12. There is no interface to modify the tcell's internal setClipboard (\x1b]52;c;%s\x1b\\), where c is set as constant target.
As a result of that PrimaryReg requests should be passed to the internal clipboard instead of the terminal.

Footnotes

  1. https://github.com/micro-editor/micro/blob/a09e62a1469ff7cdc75bead63675d106f1afc1ec/internal/clipboard/terminal.go#L15-L33 ↩

  2. https://github.com/micro-editor/tcell/blob/dedaf40206a5411f4f753d32ae49b700f293107e/tscreen.go#L1690-L1714 ↩

@JoeKar

JoeKar commented Oct 18, 2026

Copy link
Copy Markdown
Member Author

Can you test it once again with your terminal?
I changed it in the rebase above.

@Andriamanitra

Copy link
Copy Markdown
Collaborator

Now it seems to be working as it used to: highlighted text is copied to primary instead of clipboard and Copy/Paste still work. Thanks!

@rszyma

rszyma commented Nov 7, 2026 •

Copy link
Copy Markdown

I'm testing this branch on Linux, and I'm getting some of the issues with pasting that were previously only Windows-specific:

Very weird, but addmittedly quite amusing that this patchset literally trades some of these bugs between OS-es.

(I'm testing in foot terminal v1.28.0)

@Andriamanitra

Andriamanitra commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

@rszyma could you provide a step-by-step reproduction (starting with micro -config-dir $(mktemp -d) to get a clean config) for those issues? I've been using this branch for a while and haven't had any issues with pasting (using Ghostty 1.1.3 but I just tried foot v1.28.0 too and pasting seemed fine).

edit: Ok I managed to reproduce the issue by using CtrlShiftv to paste into the terminal (instead of going through micro's internal Paste event which is bound to Ctrlv by default). Setting paste to on also fixes the issues (not sure why we can't just always enable bracketed paste?).

Comment thread internal/action/bufpane.go Outdated
switch e.Key() {
case tcell.KeyTab:
h.pasteBuf.WriteByte('\t')
case tcell.KeyEnter:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least in Ghostty and Foot terminals tcell sends KeyCtrlJ rather than KeyEnter for newlines, which means you don't get any newlines when paste is set.

Suggested change
case tcell.KeyEnter:
case tcell.KeyEnter:
case tcell.KeyCtrlJ:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, KeyEnter (\r) and KeyCtrlJ (\n) still seem to be handled separately, so your suggestion is correct.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added tcell.KeyCtrlJ to 509d321.

@JoeKar

JoeKar commented Nov 8, 2026 •

Copy link
Copy Markdown
Member Author

Setting paste to on also fixes the issues (not sure why we can't just always enable bracketed paste?).

Good to know, because I already thought about the same.
Then we wont need this Windows specific 8ba594b and could remove the PASTE from the status bar. Bracketed paste seems to be quite common between Linux and Windows now.

I change it in general with 3dc9e40.
We have to see, if this really causes problems on quick typing. I was not able to trigger a misbehavior with ptyxis yet.

JoeKar and others added 9 commits November 8, 2026 22:00
…ro-editor`

With the fork we can still use raw escape sequences until something similar is
present upstream.

Adapt to the style and clipboard interface changes:
- replace Decompose() with dedicated getters
- replace attributes with dedicated getters
- replace underline attribute with new interface
- remove clipboard register from terminal requests, because there is now only
  one left and addressable in the Screen; use the internal clipboard for the
  primary register as fallback

We have to update our required go toolchain version to 1.26 due to the fact
that tcell updated its toolchain dependency to 1.26.
Several small API adaptations in the screen wrapper:

  - Screen.SetPaste(bool) is replaced by Screen.EnablePaste() and
    Screen.DisablePaste().
  - Screen.CanDisplay is no longer exposed; drop the pass-through.
  - Screen.RegisterRawSeq / UnregisterRawSeq are gone. The v3 input
    processor handles the CSI-u keyboard protocol natively, covering
    the cases raw-seq registration was used for.
  - Add InitMockScreen helper for tests that migrated from the
    removed SimulationScreen to vt.MockTerm.

Assisted-by: Claude:claude-opus-4-7
  - EventKey.Rune() is replaced by Str(). Call sites take the first
    rune where a scalar is still required. Multi-rune grapheme
    clusters get truncated to their first rune on the keystroke path;
    the paste path handles wide input via accumulation (below).
  - NewEventKey takes a string instead of a rune.
  - EventPaste no longer carries payload text. Between Start and End
    the v3 input processor streams EventKey events, so bufpane
    accumulates them into a buffer and replays the whole block
    through paste() on End. This preserves single-undo and
    smartpaste behaviour for bracketed pastes and prevents
    plugin-driven autoclose etc. from firing per-character.
  - command.go: SetPaste(bool) call is split into EnablePaste /
    DisablePaste.
  - events.go: drop the *EventRaw branch from ConstructEvent and
    eventEscSeq. CSI-u covers the original use case natively.
  - rawpane: the EscSeq accessor is no longer a method on the event
    type; use the internal eventEscSeq helper.
  - bindings.go: tcell v3 dropped the KeyCtrlSpace / KeyCtrlLeftSq /
    KeyCtrlBackslash / KeyCtrlRightSq / KeyCtrlCarat /
    KeyCtrlUnderscore constants, and its CSI-u disambiguate path
    delivers Ctrl+Shift+letter, Ctrl+Alt+letter, and Ctrl+non-letter
    as KeyRune+str+mod rather than the KeyCtrlX form. Add
    ctrlNameAliases for the long-form names, gate the KeyCtrlX
    return on mod == ModCtrl exactly to mirror tcell's NewEventKey
    fold (key.go:282-291), and lowercase the rune in the
    single-character emission so the unshifted base kitty sends
    matches bindings written as CtrlShiftP etc. Plain Ctrl+letter
    keeps the legacy KeyCtrlX path. CtrlLeftSq parses but does not
    fire because tcell consumes 0x1B as the start of an escape
    sequence, matching v2 behaviour.
  - tcell v3 drops KeyBackspace2 and only populates KeyBackspace

Assisted-by: Claude:claude-opus-4-7

action tcell
  - tcell v3 exposes an EventQ() channel directly. The PollEvent
    goroutine micro used to plumb events into its own channel is no
    longer needed; select on Screen.EventQ() alongside the existing
    timer / redraw channels.
  - EventError.Err() is gone. Use errors.Is(e, io.EOF) to distinguish
    clean EOF from real errors.

Assisted-by: Claude:claude-opus-4-7
  - Mirror the bracketed-paste accumulator on the termpane side.
    Between EventPaste{Start} and EventPaste{End} the intervening
    EventKeys are buffered together with \x1b[200~ / \x1b[201~
    framing and the whole frame is written to the child pty in one
    go. This preserves bracketed-paste semantics for programs
    running inside termpane (so e.g. bash skips alias expansion on
    the pasted text).
  - When forwarding keystrokes to the pty, translate CSI-u
    Ctrl+letter events (KeyRune + ModCtrl + single-letter Str) back
    to the legacy single-byte C0 control. Child shells and readline
    speak the legacy byte protocol regardless of the outer
    terminal's keyboard encoding.
  - Use the eventEscSeq() helper for raw key forwarding now that
    EscSeq is not a method on the event type.

Assisted-by: Claude:claude-opus-4-7
tcell v3 drops SimulationScreen. The package's vt.MockTerm is the
closest replacement: it satisfies tcell.Screen with an in-memory cell
grid and event queue, so the existing test harness adapts with
minimal changes.

PostEvent(NewEventResize(...)) replaces the old InjectResize helper.

Assisted-by: Claude:claude-opus-4-7
tcell v3 removes the legacy KeyCtrlSpace / KeyCtrlLeftSq /
KeyCtrlBackslash / KeyCtrlRightSq / KeyCtrlCarat / KeyCtrlUnderscore
constants. Micro's bindings parser previously accepted the matching
string names ("CtrlSpace", "CtrlLeftSq", ...) as aliases; those are
now invalid and the parser emits a TermMessage warning when it sees
one.

Update the shipped comment plugin and the keybindings help page to
use the supported forms.

Assisted-by: Claude:claude-opus-4-7
This enables bracketed paste by default to handle micro's and the terminal's
paste the same.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants