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
12 changes: 10 additions & 2 deletions cursed_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ func (s *cursedRenderer) setOptimizations(hardTabs, backspace, mapnl bool) {
s.hardTabs = hardTabs
s.backspace = backspace
s.mapnl = mapnl
s.scr.SetTabStops(s.width)
if s.hardTabs {
s.scr.SetTabStops(s.width)
} else {
s.scr.SetTabStops(-1)
}
s.scr.SetBackspace(s.backspace)
s.scr.SetMapNewline(s.mapnl)
s.mu.Unlock()
Expand Down Expand Up @@ -593,7 +597,11 @@ func reset(s *cursedRenderer) {
scr.SetColorProfile(s.profile)
scr.SetRelativeCursor(true) // Always start in inline mode
scr.SetFullscreen(false) // Always start in inline mode
scr.SetTabStops(s.width)
if s.hardTabs {
scr.SetTabStops(s.width)
} else {
scr.SetTabStops(-1)
}
scr.SetBackspace(s.backspace)
scr.SetMapNewline(s.mapnl)
scr.SetScrollOptim(runtime.GOOS != "windows") // disable scroll optimization on Windows due to bugs in some terminals
Expand Down
9 changes: 9 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ func WithColorProfile(profile colorprofile.Profile) ProgramOption {
}
}

// WithoutHardTabs disables the hard-tab cursor movement optimization. This is
// useful when your View() output relies on exact column alignment, since hard
// tabs can shift columns depending on tab stop positions.
func WithoutHardTabs() ProgramOption {
return func(p *Program) {
p.disableHardTabs = true
}
}

// WithWindowSize sets the initial size of the terminal window. This is useful
// when you need to set the initial size of the terminal window, for example
// during testing or when you want to run your program in a non-interactive
Expand Down
7 changes: 7 additions & 0 deletions options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ func TestOptions(t *testing.T) {
})
})

t.Run("without hard tabs", func(t *testing.T) {
p := NewProgram(nil, WithoutHardTabs())
if !p.disableHardTabs {
t.Errorf("expected hard tabs to be disabled")
}
})

t.Run("startup options", func(t *testing.T) {
exercise := func(t *testing.T, opt ProgramOption, fn func(*Program)) {
p := NewProgram(nil, opt)
Expand Down
4 changes: 3 additions & 1 deletion tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,8 @@ type Program struct {

// whether to use hard tabs to optimize cursor movements
useHardTabs bool
// whether to disable hard tab optimization (user override)
disableHardTabs bool
// whether to use backspace to optimize cursor movements
useBackspace bool

Expand Down Expand Up @@ -1054,7 +1056,7 @@ func (p *Program) Run() (returnModel Model, returnErr error) {
// don't change and the we end up working in cooked mode instead of
// raw mode. See issue #1572.
mapNl := runtime.GOOS != "windows" && p.ttyInput == nil
r.setOptimizations(p.useHardTabs, p.useBackspace, mapNl)
r.setOptimizations(p.useHardTabs && !p.disableHardTabs, p.useBackspace, mapNl)
p.renderer = r
}
}
Expand Down
2 changes: 1 addition & 1 deletion testdata/TestViewModel/altscreen_autoexit.golden
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[>4m[=0;1u[?1049h[?25l[?2004h[>4;2m[=1;1usuccess[>4m[=0;1u[?1049l[?25h[?2004l[?2026$p[?2027$p
[>4m[=0;1u[?1049h[?25l[?2004h[>4;2m[=1;1usuccess[>4m[=0;1u[?1049l[?25h[?2004l[?2026$p[?2027$p