Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cursed_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type cursedRenderer struct {
mapnl bool
syncdUpdates bool // whether to use synchronized output mode for updates
starting bool // indicates whether the renderer is starting after being stopped
disabledCaps []uv.Capability // capabilities to disable on the terminal renderer
}

var _ renderer = &cursedRenderer{}
Expand Down Expand Up @@ -597,6 +598,7 @@ func reset(s *cursedRenderer) {
scr.SetBackspace(s.backspace)
scr.SetMapNewline(s.mapnl)
scr.SetScrollOptim(runtime.GOOS != "windows") // disable scroll optimization on Windows due to bugs in some terminals
scr.DisableCaps(s.disabledCaps...)
s.scr = scr
}

Expand Down Expand Up @@ -828,3 +830,12 @@ func viewEquals(a, b *View) bool {

return true
}

// setDisabledCaps stores the capabilities to disable and applies them to the
// current terminal renderer. They will also be applied on subsequent reset() calls.
func (s *cursedRenderer) setDisabledCaps(caps []uv.Capability) {
s.mu.Lock()
s.disabledCaps = caps
s.scr.DisableCaps(caps...)
s.mu.Unlock()
}
13 changes: 13 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sync/atomic"

"github.com/charmbracelet/colorprofile"
uv "github.com/charmbracelet/ultraviolet"
)

// ProgramOption is used to set options when initializing a Program. Program can
Expand Down Expand Up @@ -166,3 +167,15 @@ func WithWindowSize(width, height int) ProgramOption {
p.height = height
}
}

// WithoutCaps disables specific ultraviolet terminal capabilities. Use this
// when a terminal emulator is known to mishandle certain escape sequences.
// For example, JediTerm (GoLand/IntelliJ) does not correctly implement CBT
// (Cursor Backward Tab), so you can disable it:
//
// p := tea.NewProgram(model, tea.WithoutCaps(uv.CapCBT))
func WithoutCaps(caps ...uv.Capability) ProgramOption {
return func(p *Program) {
p.disabledCaps = caps
}
}
15 changes: 15 additions & 0 deletions options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"os"
"sync/atomic"
"testing"

uv "github.com/charmbracelet/ultraviolet"
)

func TestOptions(t *testing.T) {
Expand Down Expand Up @@ -94,4 +96,17 @@ func TestOptions(t *testing.T) {
})
})
})

t.Run("without caps", func(t *testing.T) {
p := NewProgram(nil, WithoutCaps(uv.CapCBT, uv.CapREP))
if len(p.disabledCaps) != 2 {
t.Fatalf("expected 2 disabled caps, got %d", len(p.disabledCaps))
}
if p.disabledCaps[0] != uv.CapCBT {
t.Errorf("expected first cap to be CapCBT, got %d", p.disabledCaps[0])
}
if p.disabledCaps[1] != uv.CapREP {
t.Errorf("expected second cap to be CapREP, got %d", p.disabledCaps[1])
}
})
}
4 changes: 4 additions & 0 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,9 @@ type Program struct {
// whether to use backspace to optimize cursor movements
useBackspace bool

// disabledCaps is a list of ultraviolet terminal capabilities to disable.
disabledCaps []uv.Capability

mu sync.Mutex
}

Expand Down Expand Up @@ -1045,6 +1048,7 @@ func (p *Program) Run() (returnModel Model, returnErr error) {
p.width,
p.height,
)
r.setDisabledCaps(p.disabledCaps)
r.setLogger(p.logger)
// XXX: This breaks many things especially when we want the output
// to be compatible with terminals that are not necessary a TTY.
Expand Down