mirror of
https://github.com/charmbracelet/crush.git
synced 2025-08-02 05:20:46 +03:00
fix: hide completions tui when no results (#206)
* fix: hide completions tui when no results
* fix: gitignore
* Revert "fix(tui): completions should not close on no results (#198)"
This reverts commit 833eede1c1.
* fix: completions
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
* fix: accept
* fix: improvements
* chore(deps): update bubbles
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
* fix: improvements
* fix: accept
---------
Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
f664df60bc
commit
a700b64a28
1
.gitignore
vendored
1
.gitignore
vendored
@@ -48,3 +48,4 @@ Thumbs.db
|
||||
|
||||
manpages/
|
||||
completions/
|
||||
!internal/tui/components/completions/
|
||||
|
||||
2
go.mod
2
go.mod
@@ -16,7 +16,7 @@ require (
|
||||
github.com/aymanbagabas/go-udiff v0.3.1
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1
|
||||
github.com/charlievieth/fastwalk v1.0.11
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250710161907-a4c42b579198
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5
|
||||
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1
|
||||
github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674
|
||||
github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
|
||||
|
||||
4
go.sum
4
go.sum
@@ -68,8 +68,8 @@ github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/charlievieth/fastwalk v1.0.11 h1:5sLT/q9+d9xMdpKExawLppqvXFZCVKf6JHnr2u/ufj8=
|
||||
github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250710161907-a4c42b579198 h1:CkMS9Ah9ac1Ego5JDC5NJyZyAAqu23Z+O0yDwsa3IxM=
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250710161907-a4c42b579198/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5 h1:GTcMIfDQJKyNKS+xVt7GkNIwz+tBuQtIuiP50WpzNgs=
|
||||
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
|
||||
github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2 h1:Gj/vSk7h96TxUU/GSuwbYkr9H0ze+ElAQjcl25wB0+U=
|
||||
github.com/charmbracelet/bubbletea-internal/v2 v2.0.0-20250716142813-5d1379f56ba2/go.mod h1:m240IQxo1/eDQ7klblSzOCAUyc3LddHcV3Rc/YEGAgw=
|
||||
github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
|
||||
|
||||
@@ -171,6 +171,8 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
m.attachments = append(m.attachments, msg.Attachment)
|
||||
return m, nil
|
||||
case completions.CompletionsOpenedMsg:
|
||||
m.isCompletionsOpen = true
|
||||
case completions.CompletionsClosedMsg:
|
||||
m.isCompletionsOpen = false
|
||||
m.currentQuery = ""
|
||||
@@ -183,9 +185,6 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// If the selected item is a file, insert its path into the textarea
|
||||
value := m.textarea.Value()
|
||||
value = value[:m.completionsStartIndex]
|
||||
if len(value) > 0 && value[len(value)-1] != ' ' {
|
||||
value += " "
|
||||
}
|
||||
value += item.Path
|
||||
m.textarea.SetValue(value)
|
||||
m.isCompletionsOpen = false
|
||||
@@ -199,37 +198,15 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case tea.KeyPressMsg:
|
||||
switch {
|
||||
// Completions
|
||||
case msg.String() == "/" && !m.isCompletionsOpen:
|
||||
case msg.String() == "/" && !m.isCompletionsOpen &&
|
||||
// only show if beginning of prompt, or if previous char is a space:
|
||||
(len(m.textarea.Value()) == 0 || m.textarea.Value()[len(m.textarea.Value())-1] == ' '):
|
||||
m.isCompletionsOpen = true
|
||||
m.currentQuery = ""
|
||||
cmds = append(cmds, m.startCompletions)
|
||||
m.completionsStartIndex = len(m.textarea.Value())
|
||||
case msg.String() == "space" && m.isCompletionsOpen:
|
||||
m.isCompletionsOpen = false
|
||||
m.currentQuery = ""
|
||||
m.completionsStartIndex = 0
|
||||
cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
|
||||
cmds = append(cmds, m.startCompletions)
|
||||
case m.isCompletionsOpen && m.textarea.Cursor().X <= m.completionsStartIndex:
|
||||
cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
|
||||
case msg.String() == "backspace" && m.isCompletionsOpen:
|
||||
if len(m.currentQuery) > 0 {
|
||||
m.currentQuery = m.currentQuery[:len(m.currentQuery)-1]
|
||||
cmds = append(cmds, util.CmdHandler(completions.FilterCompletionsMsg{
|
||||
Query: m.currentQuery,
|
||||
}))
|
||||
} else {
|
||||
m.isCompletionsOpen = false
|
||||
m.currentQuery = ""
|
||||
m.completionsStartIndex = 0
|
||||
cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
|
||||
}
|
||||
default:
|
||||
if m.isCompletionsOpen {
|
||||
m.currentQuery += msg.String()
|
||||
cmds = append(cmds, util.CmdHandler(completions.FilterCompletionsMsg{
|
||||
Query: m.currentQuery,
|
||||
}))
|
||||
}
|
||||
}
|
||||
if key.Matches(msg, DeleteKeyMaps.AttachmentDeleteMode) {
|
||||
m.deleteMode = true
|
||||
@@ -281,6 +258,36 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
m.textarea, cmd = m.textarea.Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
if m.textarea.Focused() {
|
||||
kp, ok := msg.(tea.KeyPressMsg)
|
||||
if ok {
|
||||
if kp.String() == "space" || m.textarea.Value() == "" {
|
||||
m.isCompletionsOpen = false
|
||||
m.currentQuery = ""
|
||||
m.completionsStartIndex = 0
|
||||
cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
|
||||
} else {
|
||||
word := m.textarea.Word()
|
||||
if strings.HasPrefix(word, "/") {
|
||||
// XXX: wont' work if editing in the middle of the field.
|
||||
m.completionsStartIndex = strings.LastIndex(m.textarea.Value(), word)
|
||||
m.currentQuery = word[1:]
|
||||
m.isCompletionsOpen = true
|
||||
cmds = append(cmds, util.CmdHandler(completions.FilterCompletionsMsg{
|
||||
Query: m.currentQuery,
|
||||
Reopen: m.isCompletionsOpen,
|
||||
}))
|
||||
} else {
|
||||
m.isCompletionsOpen = false
|
||||
m.currentQuery = ""
|
||||
m.completionsStartIndex = 0
|
||||
cmds = append(cmds, util.CmdHandler(completions.CloseCompletionsMsg{}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package completions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/v2/key"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/crush/internal/tui/components/core/list"
|
||||
@@ -23,11 +25,14 @@ type OpenCompletionsMsg struct {
|
||||
}
|
||||
|
||||
type FilterCompletionsMsg struct {
|
||||
Query string // The query to filter completions
|
||||
Query string // The query to filter completions
|
||||
Reopen bool
|
||||
}
|
||||
|
||||
type CompletionsClosedMsg struct{}
|
||||
|
||||
type CompletionsOpenedMsg struct{}
|
||||
|
||||
type CloseCompletionsMsg struct{}
|
||||
|
||||
type SelectCompletionMsg struct {
|
||||
@@ -126,11 +131,7 @@ func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
case CloseCompletionsMsg:
|
||||
c.open = false
|
||||
c.query = ""
|
||||
return c, tea.Batch(
|
||||
c.list.SetItems([]util.Model{}),
|
||||
util.CmdHandler(CompletionsClosedMsg{}),
|
||||
)
|
||||
return c, util.CmdHandler(CompletionsClosedMsg{})
|
||||
case OpenCompletionsMsg:
|
||||
c.open = true
|
||||
c.query = ""
|
||||
@@ -143,21 +144,41 @@ func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
items = append(items, item)
|
||||
}
|
||||
c.height = max(min(c.height, len(items)), 1) // Ensure at least 1 item height
|
||||
cmds := []tea.Cmd{
|
||||
return c, tea.Batch(
|
||||
c.list.SetSize(c.width, c.height),
|
||||
c.list.SetItems(items),
|
||||
}
|
||||
return c, tea.Batch(cmds...)
|
||||
util.CmdHandler(CompletionsOpenedMsg{}),
|
||||
)
|
||||
case FilterCompletionsMsg:
|
||||
c.query = msg.Query
|
||||
if !c.open {
|
||||
return c, nil // If completions are not open, do nothing
|
||||
if !c.open && !msg.Reopen {
|
||||
return c, nil
|
||||
}
|
||||
if msg.Query == c.query {
|
||||
// PERF: if same query, don't need to filter again
|
||||
return c, nil
|
||||
}
|
||||
if len(c.list.Items()) == 0 &&
|
||||
len(msg.Query) > len(c.query) &&
|
||||
strings.HasPrefix(msg.Query, c.query) {
|
||||
// PERF: if c.query didn't match anything,
|
||||
// AND msg.Query is longer than c.query,
|
||||
// AND msg.Query is prefixed with c.query - which means
|
||||
// that the user typed more chars after a 0 match,
|
||||
// it won't match anything, so return earlier.
|
||||
return c, nil
|
||||
}
|
||||
c.query = msg.Query
|
||||
var cmds []tea.Cmd
|
||||
cmds = append(cmds, c.list.Filter(msg.Query))
|
||||
itemsLen := len(c.list.Items())
|
||||
c.height = max(min(maxCompletionsHeight, itemsLen), 1)
|
||||
cmds = append(cmds, c.list.SetSize(c.width, c.height))
|
||||
if itemsLen == 0 {
|
||||
cmds = append(cmds, util.CmdHandler(CloseCompletionsMsg{}))
|
||||
} else if msg.Reopen {
|
||||
c.open = true
|
||||
cmds = append(cmds, util.CmdHandler(CompletionsOpenedMsg{}))
|
||||
}
|
||||
return c, tea.Batch(cmds...)
|
||||
}
|
||||
return c, nil
|
||||
@@ -165,12 +186,9 @@ func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
// View implements Completions.
|
||||
func (c *completionsCmp) View() string {
|
||||
if !c.open {
|
||||
if !c.open || len(c.list.Items()) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(c.list.Items()) == 0 {
|
||||
return c.style().Render("No completions found")
|
||||
}
|
||||
|
||||
return c.style().Render(c.list.View())
|
||||
}
|
||||
|
||||
6
vendor/github.com/charmbracelet/bubbles/v2/filepicker/filepicker.go
generated
vendored
6
vendor/github.com/charmbracelet/bubbles/v2/filepicker/filepicker.go
generated
vendored
@@ -518,9 +518,9 @@ func (m Model) canSelect(file string) bool {
|
||||
}
|
||||
|
||||
// HighlightedPath returns the path of the currently highlighted file or directory.
|
||||
func (M Model) HighlightedPath() string {
|
||||
if len(M.files) == 0 || M.selected < 0 || M.selected >= len(M.files) {
|
||||
func (m Model) HighlightedPath() string {
|
||||
if len(m.files) == 0 || m.selected < 0 || m.selected >= len(m.files) {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(M.CurrentDirectory, M.files[M.selected].Name())
|
||||
return filepath.Join(m.CurrentDirectory, m.files[m.selected].Name())
|
||||
}
|
||||
|
||||
35
vendor/github.com/charmbracelet/bubbles/v2/textarea/textarea.go
generated
vendored
35
vendor/github.com/charmbracelet/bubbles/v2/textarea/textarea.go
generated
vendored
@@ -722,6 +722,41 @@ func (m *Model) Reset() {
|
||||
m.SetCursorColumn(0)
|
||||
}
|
||||
|
||||
// Word returns the word at the cursor position.
|
||||
// A word is delimited by spaces or line-breaks.
|
||||
func (m *Model) Word() string {
|
||||
line := m.value[m.row]
|
||||
col := m.col - 1
|
||||
|
||||
if col < 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// If cursor is beyond the line, return empty string
|
||||
if col >= len(line) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// If cursor is on a space, return empty string
|
||||
if unicode.IsSpace(line[col]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Find the start of the word by moving left
|
||||
start := col
|
||||
for start > 0 && !unicode.IsSpace(line[start-1]) {
|
||||
start--
|
||||
}
|
||||
|
||||
// Find the end of the word by moving right
|
||||
end := col
|
||||
for end < len(line) && !unicode.IsSpace(line[end]) {
|
||||
end++
|
||||
}
|
||||
|
||||
return string(line[start:end])
|
||||
}
|
||||
|
||||
// san initializes or retrieves the rune sanitizer.
|
||||
func (m *Model) san() runeutil.Sanitizer {
|
||||
if m.rsan == nil {
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -241,7 +241,7 @@ github.com/bmatcuk/doublestar/v4
|
||||
github.com/charlievieth/fastwalk
|
||||
github.com/charlievieth/fastwalk/internal/dirent
|
||||
github.com/charlievieth/fastwalk/internal/fmtdirent
|
||||
# github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250710161907-a4c42b579198
|
||||
# github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5
|
||||
## explicit; go 1.23.0
|
||||
github.com/charmbracelet/bubbles/v2/cursor
|
||||
github.com/charmbracelet/bubbles/v2/filepicker
|
||||
|
||||
Reference in New Issue
Block a user