diff --git a/integration/overlay.go b/integration/overlay.go index 247e5d1..ff9a5e0 100644 --- a/integration/overlay.go +++ b/integration/overlay.go @@ -578,8 +578,6 @@ func (o *Overlay) draw() string { s.WriteString(ansi.ResetModeAutoWrap) typedLen := lipgloss.Width(o.TypedQuery) - targetCol := o.PromptLen + typedLen - width := termWidth() boxWidth := config.Get().UI.MaxWidth @@ -593,13 +591,23 @@ func (o *Overlay) draw() string { boxWidth = 40 // Minimum safe width } + // ComputeCursorCol returns the total visual width, not the column on the + // current line. When the prompt is wider than the terminal the cursor has + // wrapped, so using PromptLen directly overflows the screen and the box + // lands at the wrong horizontal position. + totalCol := o.PromptLen + typedLen + cursorCol := totalCol + if width > 0 { + cursorCol = totalCol % width + } + targetCol := cursorCol if targetCol+boxWidth > width { targetCol = width - boxWidth } if targetCol < 0 { targetCol = 0 } - logger.Debugf("Overlay draw: pLen=%d, typedLen=%d, targetCol=%d, width=%d", o.PromptLen, typedLen, targetCol, width) + logger.Debugf("Overlay draw: pLen=%d, typedLen=%d, totalCol=%d, cursorCol=%d, targetCol=%d, width=%d", o.PromptLen, typedLen, totalCol, cursorCol, targetCol, width) s.WriteString(ansi.SaveCursor) diff --git a/internal/config/config.go b/internal/config/config.go index e331f44..0bffdbd 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -69,6 +69,7 @@ type KeybindingsConfig struct { SelectSuggestion string `toml:"select"` NavigateUp string `toml:"navigate-up"` NavigateDown string `toml:"navigate-down"` + NavigateRight string `toml:"navigate-right"` } type SuggestOnEmptyConfig struct { @@ -207,6 +208,9 @@ func Load() (*Config, error) { if cfg.Keybindings.NavigateDown == "" { cfg.Keybindings.NavigateDown = "down" } + if cfg.Keybindings.NavigateRight == "" { + cfg.Keybindings.NavigateRight = "right" + } if err := validate(cfg); err != nil { return cfg, fmt.Errorf("config: invalid value: %w", err) diff --git a/root/config_cmd.go b/root/config_cmd.go index 302f324..535c973 100644 --- a/root/config_cmd.go +++ b/root/config_cmd.go @@ -104,6 +104,7 @@ toggle-menu = "shift+tab" select = "tab" navigate-up = "up" navigate-down = "down" +navigate-right = "right" ` err = os.WriteFile(path, []byte(defaultContent), 0644) if err != nil { diff --git a/root/init.go b/root/init.go index 3ba0800..2dfb847 100644 --- a/root/init.go +++ b/root/init.go @@ -283,6 +283,7 @@ toggle-menu = "shift+tab" select = "tab" navigate-up = "up" navigate-down = "down" +navigate-right = "right" ` if errWrite := os.WriteFile(path, []byte(defaultContent), 0644); errWrite == nil { fmt.Printf("✓ Initialized default config file at %s\n", path) diff --git a/root/wrapper.go b/root/wrapper.go index c20c4e9..48b0357 100644 --- a/root/wrapper.go +++ b/root/wrapper.go @@ -961,12 +961,36 @@ func runWrapper() { bufferMu.Unlock() isLeftRightArrow = true } else if inputSlice[i+2] == 'C' { + _, navConsumed := config.MatchKey(inputSlice[i:], config.Get().Keybindings.NavigateRight) + if navConsumed == 0 { + if !intercepted { + writeStdout([]byte(overlay.ClearAndDisable())) + disableGhostText.Store(true) + isStandaloneEsc := n == 1 && b == '\033' + if !isStandaloneEsc { + bufferMu.Lock() + naiveBuffer = "" + cursorOffset = 0 + bufferMu.Unlock() + } + _, _ = ptmx.Write([]byte{b}) + for j := i + 1; j < n; j++ { + char := inputSlice[j] + _, _ = ptmx.Write([]byte{char}) + i = j + if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char == '~' { + break + } + } + } + continue + } + i += navConsumed - 1 + intercepted = true bufferMu.Lock() isEmptyQuery := naiveBuffer == "" && (!overlay.IsVisible() || overlay.GetTypedQuery() == "") bufferMu.Unlock() if isEmptyQuery { - intercepted = true - i += 2 continue } @@ -978,16 +1002,14 @@ func runWrapper() { } bufferMu.Unlock() - if len(ghostText) > 0 { - intercepted = true - bufferMu.Lock() + if len(ghostText) > 0 { + bufferMu.Lock() naiveBuffer += ghostText cursorOffset = 0 bufferMu.Unlock() overlay.ClearGhostTextState() _, _ = ptmx.Write([]byte(ghostText)) shouldOverlayDraw = true - i += 2 continue }