## Summary Closes #108 This PR fixes two issues related to terminal interaction and rendering: - Make Right Arrow ghost acceptance configurable instead of hard-coded. - Correct overlay positioning when the shell prompt wraps across terminal lines. ## Changes ### Right Arrow configuration - Add a new `navigate-right` keybinding to the configuration. - Add `navigate-right = "right"` to the default configuration to preserve existing behavior. - Update the wrapper to use the configured binding instead of a hard-coded Right Arrow check. - Forward the Right Arrow escape sequence to the PTY when `navigate-right` is unset, allowing the shell or terminal to handle it normally. ### Overlay positioning - Compute the cursor's current column from the total visual width instead of using the accumulated width directly. - Correctly handle wrapped prompts by calculating the cursor column modulo the terminal width. - Clamp the overlay position so it never extends beyond the terminal width. - Extend debug logging with the computed cursor position to aid troubleshooting. ## Why Right Arrow ghost acceptance was previously implemented as a hard-coded behavior, making it impossible to disable or remap through configuration. This change makes it consistent with the existing configurable keybindings while preserving the default behavior. Overlay positioning assumed that the prompt length represented the cursor's current column. When the prompt wrapped onto a new terminal line, this assumption caused the suggestion overlay to be positioned incorrectly. Computing the cursor column from the wrapped position ensures the overlay is aligned with the actual cursor. ## Testing - [x] Default configuration retains Right Arrow ghost acceptance. - [x] Setting `navigate-right = ""` forwards Right Arrow to the shell. - [x] Existing keybindings continue to function as before. - [x] Overlay is correctly positioned for both short and wrapped prompts. - [x] Overlay remains within terminal bounds.
134 lines
2.8 KiB
Go
134 lines
2.8 KiB
Go
package root
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
"github.com/spf13/cobra"
|
|
"github.com/versenilvis/iris/internal/config"
|
|
)
|
|
|
|
var ConfigCmd = &cobra.Command{
|
|
Use: "config",
|
|
Short: "manage iris configuration",
|
|
}
|
|
|
|
var ConfigInitCmd = &cobra.Command{
|
|
Use: "init",
|
|
Short: "initialize default configuration file with comments",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
path, err := config.ConfigPath()
|
|
if err != nil {
|
|
fmt.Printf("failed to get config path: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if _, statErr := os.Stat(path); statErr == nil {
|
|
fmt.Printf("config file already exists at %s\n", path)
|
|
return
|
|
}
|
|
|
|
_ = os.MkdirAll(filepath.Dir(path), 0755)
|
|
|
|
defaultContent := `# ~/.config/iris/config.toml
|
|
# iris configuration file
|
|
|
|
[core]
|
|
# schema version
|
|
# do not edit this field manually
|
|
version = 1
|
|
|
|
# override shell: "bash", "zsh", "fish", keep empty for auto detection
|
|
shell = ""
|
|
|
|
# run the selected shell as a login shell
|
|
shell-login = false
|
|
|
|
# startup mode: "last", "spec", "history"
|
|
# "last" = remember last mode used
|
|
mode = "last"
|
|
|
|
# enable debug logging
|
|
debug = false
|
|
|
|
# automatically expand aliases on space
|
|
expand-alias = true
|
|
|
|
# automatically execute command after accepting suggestion
|
|
auto-execute = false
|
|
|
|
[ui]
|
|
# visual style: "modern" (icons, category pills, shortcut footer) or "classic" (minimalist, centered number, no icons)
|
|
style = "modern"
|
|
|
|
# enable Nerd Fonts icons in overlay menu
|
|
nerd-fonts = true
|
|
|
|
# show hidden files with dot prefix
|
|
hidden-files = false
|
|
|
|
# enable inline ghost text
|
|
ghost-text = true
|
|
|
|
# maximum suggestions to display
|
|
max-suggestions = 100
|
|
|
|
# maximum height of the overlay
|
|
max-height = 15
|
|
|
|
# maximum width of the overlay (0 = responsive to terminal)
|
|
max-width = 0
|
|
|
|
[git]
|
|
# hide current branch in checkout/switch list
|
|
filter-active-branch = true
|
|
|
|
# merge remote and local branches with same name
|
|
deduplicate-branches = true
|
|
|
|
[updater]
|
|
# check for updates on startup
|
|
check-on-startup = true
|
|
|
|
# update channel: "stable", "nightly"
|
|
channel = "stable"
|
|
|
|
# interval between update checks, e.g. "24h", "6h", "30m"
|
|
check-interval = "24h"
|
|
|
|
[keybindings]
|
|
toggle-mode = "ctrl+r"
|
|
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 {
|
|
fmt.Printf("failed to write config file: %v\n", err)
|
|
return
|
|
}
|
|
fmt.Printf("initialized config file at %s\n", path)
|
|
},
|
|
}
|
|
|
|
var ConfigShowCmd = &cobra.Command{
|
|
Use: "show",
|
|
Short: "show the resolved configuration",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
enc := toml.NewEncoder(cmd.OutOrStdout())
|
|
if err := enc.Encode(config.Get()); err != nil {
|
|
fmt.Fprintf(cmd.ErrOrStderr(), "failed to encode config: %v\n", err)
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
ConfigCmd.AddCommand(ConfigInitCmd)
|
|
ConfigCmd.AddCommand(ConfigShowCmd)
|
|
rootCmd.AddCommand(ConfigCmd)
|
|
}
|