style(ui): redesign new menu (#18)
- 2 mode classic (the old one) and modern a2a17cab3a4bdf27abd2a956f9d53a0c4a685909 - add icon 3a11c7a6beb1dcf11e89977f380376fdea9fba89 - fix some bugs
This commit is contained in:
@@ -47,13 +47,13 @@ func Lookup(input string) []Suggestion {
|
||||
shellAliasesMu.RUnlock()
|
||||
|
||||
if input == "" {
|
||||
return nil
|
||||
return topLevelSuggestions("", aliases)
|
||||
}
|
||||
|
||||
tokens := Tokenize(input)
|
||||
|
||||
if len(tokens) == 1 && tokens[0] == "" {
|
||||
return nil
|
||||
return topLevelSuggestions("", aliases)
|
||||
}
|
||||
// NOTE: remember that wrapper already check if we type nothing, but I just want to make sure
|
||||
// in the future, maybe we will write unit test or using Lookup in another module
|
||||
@@ -277,7 +277,7 @@ func topLevelSuggestions(query string, aliases map[string]string) []Suggestion {
|
||||
for name, target := range aliases {
|
||||
if !seen[name] && (query == "" || HasPrefix(name, query)) {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: target, Desc: "alias: " + name, Icon: "root",
|
||||
Cmd: name, Desc: target, Icon: "alias",
|
||||
})
|
||||
seen[name] = true
|
||||
}
|
||||
@@ -299,7 +299,11 @@ func topLevelSuggestions(query string, aliases map[string]string) []Suggestion {
|
||||
}
|
||||
}
|
||||
if match {
|
||||
results = append(results, Suggestion{Cmd: name, Desc: spec.Description, Icon: name})
|
||||
icon := spec.Icon
|
||||
if icon == "" {
|
||||
icon = name
|
||||
}
|
||||
results = append(results, Suggestion{Cmd: name, Desc: spec.Description, Icon: icon})
|
||||
seen[name] = true
|
||||
}
|
||||
}
|
||||
@@ -307,7 +311,7 @@ func topLevelSuggestions(query string, aliases map[string]string) []Suggestion {
|
||||
for name := range pathCmds {
|
||||
if !seen[name] && (query == "" || HasPrefix(name, query)) {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: name, Desc: "system command", Icon: "root",
|
||||
Cmd: name, Desc: "system command", Icon: "system",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ type Spec struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Description string
|
||||
Icon string
|
||||
Subcommands []Subcommand
|
||||
Options []Option
|
||||
Generator GeneratorFunc
|
||||
@@ -18,6 +19,7 @@ type Subcommand struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Description string
|
||||
Icon string
|
||||
Subcommands []Subcommand
|
||||
Options []Option
|
||||
Generator GeneratorFunc
|
||||
|
||||
+5
-3
@@ -39,9 +39,11 @@ type CoreConfig struct {
|
||||
}
|
||||
|
||||
type UIConfig struct {
|
||||
GhostText bool `toml:"ghost-text"`
|
||||
MaxSuggestions int `toml:"max-suggestions"`
|
||||
MaxHeight int `toml:"max-height"`
|
||||
Style string `toml:"style"`
|
||||
GhostText bool `toml:"ghost-text"`
|
||||
MaxSuggestions int `toml:"max-suggestions"`
|
||||
MaxHeight int `toml:"max-height"`
|
||||
NerdFonts bool `toml:"nerd-fonts"`
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
|
||||
@@ -11,9 +11,11 @@ func DefaultConfig() *Config {
|
||||
Debug: false,
|
||||
},
|
||||
UI: UIConfig{
|
||||
Style: "modern",
|
||||
GhostText: true,
|
||||
MaxSuggestions: 100,
|
||||
MaxHeight: 15,
|
||||
NerdFonts: true,
|
||||
},
|
||||
Git: GitConfig{
|
||||
FilterActiveBranch: true,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package integration
|
||||
|
||||
import "strings"
|
||||
|
||||
var iconMap = map[string]string{
|
||||
"git": "",
|
||||
"docker": "",
|
||||
"docker-compose": "",
|
||||
"go": "",
|
||||
"golang": "",
|
||||
"python": "",
|
||||
"python3": "",
|
||||
"pip": "",
|
||||
"node": "",
|
||||
"npm": "",
|
||||
"npx": "",
|
||||
"bun": "",
|
||||
"yarn": "",
|
||||
"rust": "",
|
||||
"cargo": "",
|
||||
"java": "",
|
||||
"mvn": "",
|
||||
"gradle": "",
|
||||
"nvim": "",
|
||||
"vim": "",
|
||||
"vi": "",
|
||||
"cd": "",
|
||||
"ls": "",
|
||||
"eza": "",
|
||||
"tree": "",
|
||||
"pwd": "",
|
||||
"cat": "",
|
||||
"less": "",
|
||||
"more": "",
|
||||
"bat": "",
|
||||
"grep": "",
|
||||
"ripgrep": "",
|
||||
"find": "",
|
||||
"alias": "",
|
||||
"history": "",
|
||||
"system": "",
|
||||
"root": "",
|
||||
}
|
||||
|
||||
func lookupIcon(key string) string {
|
||||
key = strings.ToLower(strings.TrimSpace(key))
|
||||
if icon, ok := iconMap[key]; ok {
|
||||
return icon
|
||||
}
|
||||
if len(key) > 0 && key[0] >= '0' && key[0] <= '9' {
|
||||
return ""
|
||||
}
|
||||
return "❯"
|
||||
}
|
||||
+234
-57
@@ -10,12 +10,13 @@ import (
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
"github.com/versenilvis/iris/config"
|
||||
"github.com/versenilvis/iris/logger"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const (
|
||||
boxWidth = 72
|
||||
boxWidth = 76 // total visual width, corners included
|
||||
maxItems = 6
|
||||
)
|
||||
|
||||
@@ -130,6 +131,36 @@ func ComputeCursorCol(data []byte) int {
|
||||
return col
|
||||
}
|
||||
|
||||
type Theme struct {
|
||||
Border lipgloss.Color
|
||||
Accent lipgloss.Color
|
||||
Muted lipgloss.Color
|
||||
Text lipgloss.Color
|
||||
TextSel lipgloss.Color
|
||||
Match lipgloss.Color
|
||||
Desc lipgloss.Color
|
||||
DescSel lipgloss.Color
|
||||
SelBg lipgloss.Color
|
||||
ScrollInfo lipgloss.Color
|
||||
}
|
||||
|
||||
var currentTheme = Theme{
|
||||
Border: lipgloss.Color("#a277ff"),
|
||||
Accent: lipgloss.Color("#61ffca"),
|
||||
Muted: lipgloss.Color("#6d6a7f"),
|
||||
Text: lipgloss.Color("#edecee"),
|
||||
TextSel: lipgloss.Color("#ffffff"),
|
||||
Match: lipgloss.Color("#61ffca"),
|
||||
Desc: lipgloss.Color("#9692a8"),
|
||||
DescSel: lipgloss.Color("#edecee"),
|
||||
SelBg: lipgloss.Color("#3d375e"),
|
||||
ScrollInfo: lipgloss.Color("#a277ff"),
|
||||
}
|
||||
|
||||
func SetTheme(t Theme) {
|
||||
currentTheme = t
|
||||
}
|
||||
|
||||
type Overlay struct {
|
||||
mu sync.Mutex
|
||||
Visible bool
|
||||
@@ -151,34 +182,13 @@ func (o *Overlay) SetPromptLen(l int) {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
selBgColor = lipgloss.Color("#44475A")
|
||||
|
||||
iconStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#BD93F9"))
|
||||
titleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#F8F8F2"))
|
||||
descStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#6272A4"))
|
||||
|
||||
iconSel = iconStyle.Background(selBgColor)
|
||||
titleSel = titleStyle.Background(selBgColor)
|
||||
descSel = descStyle.Background(selBgColor)
|
||||
padSel = lipgloss.NewStyle().Background(selBgColor)
|
||||
|
||||
borderColor = lipgloss.Color("#6272A4")
|
||||
borderStyle = lipgloss.NewStyle().Foreground(borderColor)
|
||||
)
|
||||
|
||||
func NewOverlay() *Overlay {
|
||||
return &Overlay{
|
||||
Visible: false,
|
||||
Cursor: 0,
|
||||
StartIdx: 0,
|
||||
}
|
||||
return &Overlay{Visible: false, Cursor: 0, StartIdx: 0}
|
||||
}
|
||||
|
||||
func (o *Overlay) UpdateItems(items []core.Suggestion) {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
|
||||
o.Items = items
|
||||
o.Visible = len(o.Items) > 0
|
||||
o.Cursor = 0
|
||||
@@ -186,8 +196,14 @@ func (o *Overlay) UpdateItems(items []core.Suggestion) {
|
||||
}
|
||||
|
||||
func fixedWidth(s string, width int) string {
|
||||
if width <= 0 {
|
||||
return ""
|
||||
}
|
||||
runes := []rune(s)
|
||||
if len(runes) > width {
|
||||
if width == 1 {
|
||||
return "…"
|
||||
}
|
||||
return string(runes[:width-1]) + "…"
|
||||
}
|
||||
if len(runes) < width {
|
||||
@@ -205,7 +221,6 @@ func (o *Overlay) RenderGhostText(buffer string, userNavigated bool) string {
|
||||
}
|
||||
|
||||
var s strings.Builder
|
||||
|
||||
ghostText := ""
|
||||
if !userNavigated && buffer != "" {
|
||||
topCmd := o.Items[0].Cmd
|
||||
@@ -218,13 +233,10 @@ func (o *Overlay) RenderGhostText(buffer string, userNavigated bool) string {
|
||||
if padLen < 0 {
|
||||
padLen = 0
|
||||
}
|
||||
|
||||
// add extra padding to erase any stray characters left by fast backspaces
|
||||
// before the debounce timer fired, 10 spaces is safe and won't hit right prompts
|
||||
padLen += 10
|
||||
|
||||
if ghostText != "" || padLen > 0 {
|
||||
s.WriteString("\0337") // save cursor at prompt
|
||||
s.WriteString("\0337")
|
||||
if ghostText != "" {
|
||||
s.WriteString("\033[90m")
|
||||
s.WriteString(ghostText)
|
||||
@@ -233,13 +245,40 @@ func (o *Overlay) RenderGhostText(buffer string, userNavigated bool) string {
|
||||
if padLen > 0 {
|
||||
s.WriteString(strings.Repeat(" ", padLen))
|
||||
}
|
||||
s.WriteString("\0338") // restore cursor back to prompt
|
||||
s.WriteString("\0338")
|
||||
o.LastGhostLen = len(ghostText)
|
||||
}
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func renderMatchedTitle(title, typed string, selected bool, w int) string {
|
||||
t := currentTheme
|
||||
textColor := t.Text
|
||||
if selected {
|
||||
textColor = t.TextSel
|
||||
}
|
||||
|
||||
base := lipgloss.NewStyle().Foreground(textColor)
|
||||
match := lipgloss.NewStyle().Foreground(t.Match).Bold(true)
|
||||
if selected {
|
||||
base = base.Background(t.SelBg)
|
||||
match = match.Background(t.SelBg)
|
||||
}
|
||||
|
||||
display := fixedWidth(title, w)
|
||||
|
||||
if typed == "" || !strings.HasPrefix(strings.ToLower(display), strings.ToLower(typed)) {
|
||||
return base.Render(display)
|
||||
}
|
||||
|
||||
matchLen := len(typed)
|
||||
if matchLen > len(display) {
|
||||
matchLen = len(display)
|
||||
}
|
||||
return match.Render(display[:matchLen]) + base.Render(display[matchLen:])
|
||||
}
|
||||
|
||||
func (o *Overlay) Render() string {
|
||||
return o.draw()
|
||||
}
|
||||
@@ -252,6 +291,10 @@ func (o *Overlay) draw() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
t := currentTheme
|
||||
border := lipgloss.NewStyle().Foreground(t.Border)
|
||||
scrollStyle := lipgloss.NewStyle().Foreground(t.ScrollInfo)
|
||||
|
||||
var s strings.Builder
|
||||
s.WriteString("\033[?7l")
|
||||
|
||||
@@ -278,7 +321,6 @@ func (o *Overlay) draw() string {
|
||||
}
|
||||
|
||||
scrolloffUp := 1
|
||||
scrolloffDown := 0
|
||||
if windowSize <= 3 {
|
||||
scrolloffUp = 0
|
||||
}
|
||||
@@ -286,8 +328,8 @@ func (o *Overlay) draw() string {
|
||||
if o.Cursor < o.StartIdx+scrolloffUp {
|
||||
o.StartIdx = o.Cursor - scrolloffUp
|
||||
}
|
||||
if o.Cursor >= o.StartIdx+windowSize-scrolloffDown {
|
||||
o.StartIdx = o.Cursor - windowSize + scrolloffDown + 1
|
||||
if o.Cursor >= o.StartIdx+windowSize {
|
||||
o.StartIdx = o.Cursor - windowSize + 1
|
||||
}
|
||||
if o.StartIdx < 0 {
|
||||
o.StartIdx = 0
|
||||
@@ -317,6 +359,12 @@ func (o *Overlay) draw() string {
|
||||
}
|
||||
}
|
||||
|
||||
inner := boxWidth - 2 // width between the two border pipes/corners
|
||||
|
||||
style := strings.ToLower(config.Get().UI.Style)
|
||||
isClassic := style == "classic" || style == "minimal" || style == "minimalist"
|
||||
|
||||
// top side border with scroll counter
|
||||
s.WriteString("\0338")
|
||||
fmt.Fprintf(&s, "\033[%dB", 1)
|
||||
s.WriteString("\033[2K")
|
||||
@@ -326,14 +374,37 @@ func (o *Overlay) draw() string {
|
||||
if len(o.Items) > windowSize {
|
||||
scrollInfo = fmt.Sprintf(" %d/%d ", o.Cursor+1, len(o.Items))
|
||||
}
|
||||
leftDash := 3
|
||||
if isClassic && scrollInfo != "" {
|
||||
leftDash = (inner - len(scrollInfo)) / 2
|
||||
}
|
||||
rightDash := inner - leftDash - len(scrollInfo)
|
||||
if scrollInfo == "" {
|
||||
leftDash = 0
|
||||
rightDash = inner
|
||||
}
|
||||
fmt.Fprintf(&s, "%s%s%s%s%s",
|
||||
border.Render("╭"),
|
||||
border.Render(strings.Repeat("─", leftDash)),
|
||||
scrollStyle.Render(scrollInfo),
|
||||
border.Render(strings.Repeat("─", rightDash)),
|
||||
border.Render("╮"),
|
||||
)
|
||||
|
||||
borderWidth := boxWidth - len(scrollInfo)
|
||||
topBorder := "╭" + strings.Repeat("─", borderWidth/2) + scrollInfo + strings.Repeat("─", boxWidth-borderWidth/2-len(scrollInfo)) + "╮"
|
||||
s.WriteString(borderStyle.Render(topBorder))
|
||||
|
||||
iconW := 6
|
||||
descW := 22
|
||||
titleW := boxWidth - iconW - descW - 3
|
||||
// left and right side border with item rows
|
||||
descW := 24
|
||||
padGap := 2
|
||||
markerW := 1
|
||||
iconW := 2
|
||||
if isClassic {
|
||||
iconW = 0
|
||||
}
|
||||
sidePad := 1
|
||||
titleW := inner - sidePad*2 - markerW - 1 - iconW
|
||||
if iconW > 0 {
|
||||
titleW--
|
||||
}
|
||||
titleW = titleW - padGap - descW
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
s.WriteString("\0338")
|
||||
@@ -342,33 +413,139 @@ func (o *Overlay) draw() string {
|
||||
moveToTarget()
|
||||
|
||||
it := o.Items[i]
|
||||
rawIcon := fixedWidth(it.Icon, iconW)
|
||||
rawTitle := fixedWidth(it.Cmd, titleW)
|
||||
rawDesc := fixedWidth(it.Desc, descW)
|
||||
selected := i == o.Cursor
|
||||
|
||||
left := borderStyle.Render("│")
|
||||
right := borderStyle.Render("│")
|
||||
left := border.Render("│")
|
||||
right := border.Render("│")
|
||||
|
||||
if i == o.Cursor {
|
||||
icon := iconSel.Render(" " + rawIcon + " ")
|
||||
title := titleSel.Render(rawTitle)
|
||||
pad := padSel.Render(" ")
|
||||
desc := descSel.Render(rawDesc)
|
||||
fmt.Fprintf(&s, "%s%s%s%s%s%s", left, icon, pad, title, desc, right)
|
||||
} else {
|
||||
icon := iconStyle.Render(" " + rawIcon + " ")
|
||||
title := titleStyle.Render(rawTitle)
|
||||
desc := descStyle.Render(rawDesc)
|
||||
fmt.Fprintf(&s, "%s%s %s%s%s", left, icon, title, desc, right)
|
||||
bg := lipgloss.NewStyle()
|
||||
if selected {
|
||||
bg = bg.Background(t.SelBg)
|
||||
}
|
||||
|
||||
marker := " "
|
||||
markerStyle := bg.Foreground(t.Muted)
|
||||
if selected {
|
||||
marker = "▶"
|
||||
markerStyle = bg.Foreground(t.Accent).Bold(true)
|
||||
}
|
||||
|
||||
iconGlyph := lookupIcon(it.Icon)
|
||||
iconColor := t.Muted
|
||||
if selected {
|
||||
iconColor = t.Accent
|
||||
}
|
||||
iconStr := bg.Foreground(iconColor).Render(fixedWidth(iconGlyph, iconW))
|
||||
|
||||
title := renderMatchedTitle(it.Cmd, o.TypedQuery, selected, titleW)
|
||||
|
||||
descColor := t.Desc
|
||||
if selected {
|
||||
descColor = t.DescSel
|
||||
}
|
||||
|
||||
var desc string
|
||||
if isClassic {
|
||||
if it.Icon == "alias" {
|
||||
desc = bg.Foreground(descColor).Render(fixedWidth("alias: "+it.Desc, descW))
|
||||
} else {
|
||||
desc = bg.Foreground(descColor).Render(fixedWidth(it.Desc, descW))
|
||||
}
|
||||
} else {
|
||||
switch it.Icon {
|
||||
case "alias":
|
||||
boxStyle := lipgloss.NewStyle().Background(lipgloss.Color("#2a2342")).Foreground(lipgloss.Color("#a277ff"))
|
||||
if selected {
|
||||
boxStyle = lipgloss.NewStyle().Background(lipgloss.Color("#a277ff")).Foreground(lipgloss.Color("#110f18")).Bold(true)
|
||||
}
|
||||
tag := boxStyle.Render(" alias ")
|
||||
tw := lipgloss.Width(tag)
|
||||
rem := descW - tw - 1
|
||||
if rem < 0 {
|
||||
rem = 0
|
||||
}
|
||||
desc = tag + bg.Render(" ") + bg.Foreground(descColor).Render(fixedWidth(it.Desc, rem))
|
||||
case "history":
|
||||
boxStyle := lipgloss.NewStyle().Background(lipgloss.Color("#1a2d36")).Foreground(lipgloss.Color("#61ffca"))
|
||||
if selected {
|
||||
boxStyle = lipgloss.NewStyle().Background(lipgloss.Color("#61ffca")).Foreground(lipgloss.Color("#110f18")).Bold(true)
|
||||
}
|
||||
tag := boxStyle.Render(" history ")
|
||||
tw := lipgloss.Width(tag)
|
||||
rem := descW - tw
|
||||
if rem < 0 {
|
||||
rem = 0
|
||||
}
|
||||
desc = tag + bg.Render(strings.Repeat(" ", rem))
|
||||
case "system":
|
||||
boxStyle := lipgloss.NewStyle().Background(lipgloss.Color("#1e1d28")).Foreground(lipgloss.Color("#a277ff"))
|
||||
if selected {
|
||||
boxStyle = lipgloss.NewStyle().Background(lipgloss.Color("#a277ff")).Foreground(lipgloss.Color("#110f18")).Bold(true)
|
||||
}
|
||||
tag := boxStyle.Render(" system ")
|
||||
tw := lipgloss.Width(tag)
|
||||
rem := descW - tw
|
||||
if rem < 0 {
|
||||
rem = 0
|
||||
}
|
||||
desc = tag + bg.Render(strings.Repeat(" ", rem))
|
||||
default:
|
||||
desc = bg.Foreground(descColor).Render(fixedWidth(it.Desc, descW))
|
||||
}
|
||||
}
|
||||
|
||||
iconSection := ""
|
||||
if iconW > 0 {
|
||||
iconSection = iconStr + bg.Render(" ")
|
||||
}
|
||||
|
||||
fmt.Fprintf(&s, "%s%s%s%s%s%s%s%s%s%s",
|
||||
left,
|
||||
bg.Render(" "),
|
||||
markerStyle.Render(marker),
|
||||
bg.Render(" "),
|
||||
iconSection,
|
||||
title,
|
||||
bg.Render(strings.Repeat(" ", padGap)),
|
||||
desc,
|
||||
bg.Render(" "),
|
||||
right,
|
||||
)
|
||||
}
|
||||
|
||||
// bottom side border with footer shortcut hints
|
||||
s.WriteString("\0338")
|
||||
fmt.Fprintf(&s, "\033[%dB", windowSize+2)
|
||||
s.WriteString("\033[2K")
|
||||
moveToTarget()
|
||||
bottomBorder := "╰" + strings.Repeat("─", boxWidth) + "╯"
|
||||
s.WriteString(borderStyle.Render(bottomBorder))
|
||||
|
||||
footerInfo := ""
|
||||
if !isClassic {
|
||||
keyStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#a277ff")).Bold(true)
|
||||
tabKey := keyStyle.Render("<Tab>")
|
||||
ctrlRKey := keyStyle.Render("<Ctrl+R>")
|
||||
acceptText := lipgloss.NewStyle().Foreground(t.ScrollInfo).Render(" Accept")
|
||||
modeText := lipgloss.NewStyle().Foreground(t.ScrollInfo).Render(" Mode")
|
||||
footerInfo = fmt.Sprintf(" %s%s • %s%s ", tabKey, acceptText, ctrlRKey, modeText)
|
||||
}
|
||||
|
||||
footerRunes := lipgloss.Width(footerInfo)
|
||||
rightDash = 2
|
||||
leftDash = inner - footerRunes - rightDash
|
||||
if footerInfo == "" {
|
||||
leftDash = 0
|
||||
rightDash = inner
|
||||
}
|
||||
if leftDash < 0 {
|
||||
leftDash = 0
|
||||
}
|
||||
fmt.Fprintf(&s, "%s%s%s%s%s",
|
||||
border.Render("╰"),
|
||||
border.Render(strings.Repeat("─", leftDash)),
|
||||
footerInfo,
|
||||
border.Render(strings.Repeat("─", rightDash)),
|
||||
border.Render("╯"),
|
||||
)
|
||||
|
||||
s.WriteString("\0338")
|
||||
s.WriteString("\033[?7h")
|
||||
|
||||
@@ -51,6 +51,12 @@ mode = "last"
|
||||
debug = 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
|
||||
|
||||
# enable inline ghost text
|
||||
ghost-text = true
|
||||
|
||||
|
||||
@@ -175,6 +175,12 @@ mode = "last"
|
||||
debug = 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
|
||||
|
||||
# enable inline ghost text
|
||||
ghost-text = true
|
||||
|
||||
|
||||
+4
-8
@@ -1,7 +1,6 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/versenilvis/iris/commands/core"
|
||||
@@ -17,11 +16,8 @@ func MergeResults(query string, mode string) []core.Suggestion {
|
||||
deduped := []core.Suggestion{}
|
||||
|
||||
// always call lookup to scan aliases and get spec suggestions
|
||||
var cmdResults []core.Suggestion
|
||||
if query != "" {
|
||||
logger.Debugf("Merge Calling Lookup for '%s'", query)
|
||||
cmdResults = core.Lookup(query)
|
||||
}
|
||||
logger.Debugf("Merge Calling Lookup for '%s'", query)
|
||||
cmdResults := core.Lookup(query)
|
||||
|
||||
// search history if in history mode
|
||||
var histResults []integration.HistResult
|
||||
@@ -53,8 +49,8 @@ func MergeResults(query string, mode string) []core.Suggestion {
|
||||
for _, h := range histResults {
|
||||
addSuggestion(core.Suggestion{
|
||||
Cmd: h.Cmd,
|
||||
Desc: " history",
|
||||
Icon: fmt.Sprintf("%d", h.ID),
|
||||
Desc: "history",
|
||||
Icon: "history",
|
||||
})
|
||||
}
|
||||
for _, s := range cmdResults {
|
||||
|
||||
+42
-2
@@ -323,6 +323,15 @@ func runWrapper() {
|
||||
continue
|
||||
}
|
||||
|
||||
if query == "" {
|
||||
bufferMu.Lock()
|
||||
naiveBuffer = ""
|
||||
cursorOffset = 0
|
||||
bufferMu.Unlock()
|
||||
writeStdout([]byte(overlay.ClearAndDisable()))
|
||||
continue
|
||||
}
|
||||
|
||||
// sync local buffer with actual command line
|
||||
bufferMu.Lock()
|
||||
naiveBuffer = query
|
||||
@@ -388,7 +397,7 @@ func runWrapper() {
|
||||
|
||||
var b strings.Builder
|
||||
if !navCopy {
|
||||
if bufCopy == "" {
|
||||
if bufCopy == "" && !overlay.Visible {
|
||||
writeStdout([]byte(overlay.ClearAndDisable()))
|
||||
return
|
||||
}
|
||||
@@ -722,6 +731,7 @@ func runWrapper() {
|
||||
}
|
||||
userNavigated.Store(false)
|
||||
overlay.UserNavigated = false
|
||||
overlay.Visible = true
|
||||
shouldOverlayDraw = true
|
||||
// enter: enter behavior is a bit different from tab suggestions in code editor
|
||||
// I want it to execute the command anyway and ignore the suggestions
|
||||
@@ -808,7 +818,8 @@ func runWrapper() {
|
||||
|
||||
case 127, 0x08: // backspace: remove character
|
||||
bufferMu.Lock()
|
||||
if len(naiveBuffer) > 0 {
|
||||
wasEmpty := len(naiveBuffer) == 0
|
||||
if !wasEmpty {
|
||||
runes := []rune(naiveBuffer)
|
||||
if cursorOffset <= 0 {
|
||||
if len(runes) > 0 {
|
||||
@@ -825,11 +836,26 @@ func runWrapper() {
|
||||
}
|
||||
}
|
||||
}
|
||||
isEmptyNow := len(naiveBuffer) == 0
|
||||
bufferMu.Unlock()
|
||||
|
||||
if wasEmpty {
|
||||
if overlay.Visible {
|
||||
writeStdout([]byte(overlay.ClearAndDisable()))
|
||||
}
|
||||
userNavigated.Store(false)
|
||||
continue
|
||||
}
|
||||
if isEmptyNow {
|
||||
writeStdout([]byte(overlay.ClearAndDisable()))
|
||||
userNavigated.Store(false)
|
||||
continue
|
||||
}
|
||||
shouldOverlayDraw = true
|
||||
userNavigated.Store(false)
|
||||
case 0x17: // ctrl+w: delete the last word in the buffer
|
||||
bufferMu.Lock()
|
||||
wasEmpty := len(naiveBuffer) == 0
|
||||
trimBuf := strings.TrimRight(naiveBuffer, " ")
|
||||
lastSpace := strings.LastIndex(trimBuf, " ")
|
||||
if lastSpace >= 0 {
|
||||
@@ -838,7 +864,21 @@ func runWrapper() {
|
||||
naiveBuffer = ""
|
||||
}
|
||||
cursorOffset = 0
|
||||
isEmptyNow := len(naiveBuffer) == 0
|
||||
bufferMu.Unlock()
|
||||
|
||||
if wasEmpty {
|
||||
if overlay.Visible {
|
||||
writeStdout([]byte(overlay.ClearAndDisable()))
|
||||
}
|
||||
userNavigated.Store(false)
|
||||
continue
|
||||
}
|
||||
if isEmptyNow {
|
||||
writeStdout([]byte(overlay.ClearAndDisable()))
|
||||
userNavigated.Store(false)
|
||||
continue
|
||||
}
|
||||
shouldOverlayDraw = true
|
||||
userNavigated.Store(false)
|
||||
case 0x0c: // ctrl+l: clear screen but keep buffer and redraw menu
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestLookup(t *testing.T) {
|
||||
if tt.mustContain != "" {
|
||||
found := false
|
||||
for _, r := range results {
|
||||
if strings.Contains(r.Cmd, tt.mustContain) {
|
||||
if strings.Contains(r.Cmd, tt.mustContain) || strings.Contains(r.Desc, tt.mustContain) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user