Skip to content

fix: auto-fix CBT use in JediTerm for GoLand/JetBrains' Run/Debug TTY#101

Open
mikeschinkel wants to merge 1 commit intocharmbracelet:mainfrom
mikeschinkel:feat/jediterm-autodetect
Open

fix: auto-fix CBT use in JediTerm for GoLand/JetBrains' Run/Debug TTY#101
mikeschinkel wants to merge 1 commit intocharmbracelet:mainfrom
mikeschinkel:feat/jediterm-autodetect

Conversation

@mikeschinkel
Copy link
Copy Markdown

@mikeschinkel mikeschinkel commented Mar 27, 2026

Summary

Automatically detect JediTerm (GoLand/IntelliJ terminal) and disable Cursor Backward Tab (CBT) to prevent invalid rendering — no action required from application developers.

Motivation

JetBrains GoLand and all IntelliJ-based IDEs use JediTerm as their built-in terminal emulator. JediTerm reports TERM=xterm-256color but does not correctly handle Cursor Backward Tab (CBT), which can result in invalid rendering when ultraviolet's differential renderer emits it.

This may seem like a niche edge case; however a large number of Bubble Tea apps will be run inside GoLand and may hit this bug, including when Delve debugging as GoLand is the 2nd most popular IDE for Go.

The rendering breaks in ways that are very difficult to diagnose. In my case, it took roughly a week of investigation as I kept assuming the bug was in my own code, not in terminal capability handling. I would hate for other developers to hit the same wall and come away frustrated with the Bubble Tea ecosystem when the fix is straightforward.

Without this fix, every Go developer using GoLand who builds a Bubble Tea app with view transitions would need to independently discover the issue, diagnose it, and manually work around it. Auto-detection eliminates this entirely.

How it works

JediTerm detection uses four environment checks to cover both the built-in terminal, Run/Debug configurations where TERMINAL_EMULATOR is not propagated, and JetBrains Toolbox installs:

# Check Platforms Context
1 TERMINAL_EMULATOR == "JetBrains-JediTerm" All Terminal tool window
2 __CFBundleIdentifier starts with "com.jetbrains." macOS Run/Debug TTY
3 XPC_SERVICE_NAME contains "com.jetbrains." macOS Run/Debug TTY
4 TOOLBOX_VERSION is non-empty All All Toolbox installs

Check 1 covers the Terminal tool window on all platforms. Checks 2–3 cover the "Emulate terminal in output console" Run/Debug path on macOS. Check 4 covers all contexts on all platforms for users who installed their IDE via JetBrains Toolbox (the default/recommended install method).

When detected, capCBT is removed from the xterm default capability set in xtermCaps(). No application code changes are needed — existing Bubble Tea apps will just work correctly in GoLand.

I have filled these which could make the problem a non-issue:

  • Add CBT (Cursor Backward Tabulation) issue #328 that would make this change unnecessry, but Jedit has 7 open PRs some open since 2021, and 43 open issues so I do not have high confidence they will address this anytime soon, if ever.

  • YouTrack issue G0-20196 requesting that JetBrains set TERMINAL_EMULATOR=JetBrains-JediTerm in the "Emulate terminal" Run/Debug code path, which would make checks 2–4 unnecessary but check 1 would still be necessary.

And these which would allow end-users of apps to fix broken terminal output for any edge-case terminals that ultraviolet does not currently detect:

Reproduction

A minimal standalone app that reproduces the rendering issue is available as a Gist: jediterm-bug. Run it inside GoLand's Run/Debug terminal (or ForceTerm) and press space to toggle between the two layouts — the second layout will render incorrectly.

Note: There is a standalone JediTerm you can use for testing named ForceTerm if you prefer not to install GoLand.

Changes

  • terminal_renderer.go — Add isJediTerm() function with four environment checks. In xtermCaps(), disable capCBT in the xterm default case when JediTerm is detected.
  • terminal_renderer_test.go — Tests for isJediTerm() detection via all four environment variables, and verification that xtermCaps() strips CBT when in JediTerm while preserving other capabilities.

Test plan

  • TestIsJediTerm — verifies detection via TERMINAL_EMULATOR, __CFBundleIdentifier, XPC_SERVICE_NAME, and TOOLBOX_VERSION; verifies non-detection for empty env and other terminals
  • TestXtermCapsJediTermDisablesCBT — verifies CBT is present for normal xterm, absent for JediTerm xterm, and other caps (VPA, CHA) are preserved
  • Manual testing with JediTerm (GoLand terminal) confirms rendering issue is resolved
  • All existing tests pass

JetBrains GoLand and all IntelliJ-based IDEs use JediTerm as
their built-in terminal emulator. JediTerm reports
TERM=xterm-256color but mishandles CBT (Cursor Backward Tab),
causing invalid rendering when ultraviolet's differential
renderer emits it.

Add isJediTerm() detection via four environment checks
(TERMINAL_EMULATOR, __CFBundleIdentifier, XPC_SERVICE_NAME,
TOOLBOX_VERSION) and automatically disable capCBT in xtermCaps()
when detected.
@mikeschinkel
Copy link
Copy Markdown
Author

BTW, here is research by Claude explaining the issue in depth: https://claude.ai/public/artifacts/921b2eb6-435f-469b-9f80-1bc74e1501f4

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.49%. Comparing base (0b88c25) to head (28fb40b).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #101      +/-   ##
==========================================
+ Coverage   58.41%   58.49%   +0.07%     
==========================================
  Files          52       52              
  Lines        6695     6707      +12     
==========================================
+ Hits         3911     3923      +12     
  Misses       2539     2539              
  Partials      245      245              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mikeschinkel
Copy link
Copy Markdown
Author

Just FYI, as far as I can tell all those lint failures are pre-existing failures not related to my PR.

@aymanbagabas
Copy link
Copy Markdown
Contributor

Hi @mikeschinkel, thank you for sending this patch. I agree with you that this is a bug in JediTerm, however, I strongly feel that this should be fixed upstream so that we're not stacking exceptions here in Ultraviolet.

Ultraviolet expects terminal advertising themselves as xterm-256color to support CBT as it's part of the Terminfo database for that terminal type. You can see that with infocmp -x1 xterm-256color | grep cbt.

@mikeschinkel
Copy link
Copy Markdown
Author

mikeschinkel commented Apr 7, 2026

Thank you for looking at this, Ayman. I genuinely understand the position as stacking terminal-specific exceptions is ugly. In an ideal world every terminal that advertises xterm-256color would faithfully implement the full capability set. I share that instinct; I don't enjoy submitting patches that work around someone else's bug.

That said, I want to make the pragmatic case for reconsidering, because the practical impact on Charm's users is potentially significant.

I've already gone upstream. I filed a YouTrack issue with JetBrains and notified them via Slack. But realistically, JediTerm's CBT gap affects a narrow slice of JetBrains' user base; Go developers using Bubble Tea inside GoLand's terminal. JetBrains has millions of developers across dozens of languages using their IDEs; a missing escape sequence that only surfaces with one Go TUI framework is unlikely to climb their priority list quickly, if ever. 😢

Meanwhile, GoLand is the 2nd most popular Go IDE at 28% per the 2025 Go Developer Survey. That is almost 1/3 of BubbleTea's likely user base. So a substantial share of Charm's user base runs Bubble Tea apps inside GoLand's integrated terminal and debugger which is the only terminal GoLand offers is JediTerm.

So there is a good chance many of those Goland-using developers will hit this bug. It took me a full week to diagnose because I assumed the problem was in my code, not in a terminal capability mismatch buried three layers deep. Most developers won't have the patience or the terminal-internals knowledge to trace it that far. They may well conclude Bubble Tea is too broken and just move on.

Postel's Law says:

"Be conservative in what you do, be liberal in what you accept from others"

I would suggest that it would be in Bubble Tea's best interest ultraviolet should behave correctly even when the terminal lies about its capabilities. The patch I submitted only activates when other known-good terminals aren't matched, so it's conservative in its reach.

I think there is a valid analogy to how browser vendors handle malformed HTML. Browsers could refuse to render anything that is not well-formed and insist that the upstream (the site author) fix their markup. But in practice, browsers that are strict lose users to browsers that are liberal. Users don't understand or care about the distinction, they just see one browser rendering the page and the other not.

Similarly, if a Bubble Tea breaks in GoLand, developers probably won't blame JediTerm, they will blame Bubble Tea.


OTOH, if none of those arguments move you I would ask to you consider making ultraviolet's renderer dependency injectable on Bubble Tea's side.

Currently TerminalRenderer is a concrete struct embedded in cursedRenderer, so there's no way for a Bubble Tea consumer to intercept or wrap the renderer. If Bubble Tea consumed a renderer as an interface, or if there were a hook to override capability detection, developers like me could address terminal-specific issues ourselves without forking both ultraviolet and Bubble Tea. That would not stop developers from running into the problem and having to debug their way through to the other end, but at least it would give them a solution once the discovered the issue.

Either way, I appreciate your thoughtful response and the work you and the rest of the team has put ultraviolet. The ncurses-style diff renderer is genuinely impressive engineering, and the entire Charm/Bubble Tea collection of packages has enabled me to do great things I would never otherwise have been able to achieve in the time allotted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants