Feat: working tab completion in shell mode
This commit is contained in:
37
popups.go
37
popups.go
@@ -70,6 +70,10 @@ func showModelSelectionPopup() {
|
|||||||
pages.RemovePage("modelSelectionPopup")
|
pages.RemovePage("modelSelectionPopup")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
|
||||||
|
pages.RemovePage("modelSelectionPopup")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
||||||
@@ -163,6 +167,10 @@ func showAPILinkSelectionPopup() {
|
|||||||
pages.RemovePage("apiLinkSelectionPopup")
|
pages.RemovePage("apiLinkSelectionPopup")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
|
||||||
|
pages.RemovePage("apiLinkSelectionPopup")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
||||||
@@ -229,6 +237,10 @@ func showUserRoleSelectionPopup() {
|
|||||||
pages.RemovePage("userRoleSelectionPopup")
|
pages.RemovePage("userRoleSelectionPopup")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
|
||||||
|
pages.RemovePage("userRoleSelectionPopup")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
||||||
@@ -297,6 +309,10 @@ func showBotRoleSelectionPopup() {
|
|||||||
pages.RemovePage("botRoleSelectionPopup")
|
pages.RemovePage("botRoleSelectionPopup")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
|
||||||
|
pages.RemovePage("botRoleSelectionPopup")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
||||||
@@ -322,6 +338,16 @@ func showFileCompletionPopup(filter string) {
|
|||||||
if len(complMatches) == 0 {
|
if len(complMatches) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// If only one match, auto-complete without showing popup
|
||||||
|
if len(complMatches) == 1 {
|
||||||
|
currentText := textArea.GetText()
|
||||||
|
atIdx := strings.LastIndex(currentText, "@")
|
||||||
|
if atIdx >= 0 {
|
||||||
|
before := currentText[:atIdx]
|
||||||
|
textArea.SetText(before+complMatches[0], true)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
widget := tview.NewList().ShowSecondaryText(false).
|
widget := tview.NewList().ShowSecondaryText(false).
|
||||||
SetSelectedBackgroundColor(tcell.ColorGray)
|
SetSelectedBackgroundColor(tcell.ColorGray)
|
||||||
widget.SetTitle("file completion").SetBorder(true)
|
widget.SetTitle("file completion").SetBorder(true)
|
||||||
@@ -337,6 +363,17 @@ func showFileCompletionPopup(filter string) {
|
|||||||
}
|
}
|
||||||
pages.RemovePage("fileCompletionPopup")
|
pages.RemovePage("fileCompletionPopup")
|
||||||
})
|
})
|
||||||
|
widget.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if event.Key() == tcell.KeyEscape {
|
||||||
|
pages.RemovePage("fileCompletionPopup")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
|
||||||
|
pages.RemovePage("fileCompletionPopup")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return event
|
||||||
|
})
|
||||||
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
||||||
return tview.NewFlex().
|
return tview.NewFlex().
|
||||||
AddItem(nil, 0, 1, false).
|
AddItem(nil, 0, 1, false).
|
||||||
|
|||||||
36
tui.go
36
tui.go
@@ -74,8 +74,6 @@ var (
|
|||||||
[yellow]Ctrl+c[white]: close programm
|
[yellow]Ctrl+c[white]: close programm
|
||||||
[yellow]Ctrl+n[white]: start a new chat
|
[yellow]Ctrl+n[white]: start a new chat
|
||||||
[yellow]Ctrl+o[white]: open image file picker
|
[yellow]Ctrl+o[white]: open image file picker
|
||||||
[yellow]@[white]: file completion (type @ in input to get file suggestions)
|
|
||||||
[yellow]c[white]: (in file picker) set current dir as CodingDir
|
|
||||||
[yellow]Ctrl+p[white]: props edit form (min-p, dry, etc.)
|
[yellow]Ctrl+p[white]: props edit form (min-p, dry, etc.)
|
||||||
[yellow]Ctrl+v[white]: show API link selection popup to choose current API
|
[yellow]Ctrl+v[white]: show API link selection popup to choose current API
|
||||||
[yellow]Ctrl+r[white]: start/stop recording from your microphone (needs stt server or whisper binary)
|
[yellow]Ctrl+r[white]: start/stop recording from your microphone (needs stt server or whisper binary)
|
||||||
@@ -109,6 +107,13 @@ var (
|
|||||||
=== tables (chat history, agent pick, file pick, properties) ===
|
=== tables (chat history, agent pick, file pick, properties) ===
|
||||||
[yellow]x[white]: to exit the table page
|
[yellow]x[white]: to exit the table page
|
||||||
|
|
||||||
|
=== filepicker ===
|
||||||
|
[yellow]c[white]: (in file picker) set current dir as CodingDir
|
||||||
|
[yellow]x[white]: to exit
|
||||||
|
|
||||||
|
=== shell mode ===
|
||||||
|
[yellow]@match->Tab[white]: file completion (type @ in input to get file suggestions)
|
||||||
|
|
||||||
=== status line ===
|
=== status line ===
|
||||||
%s
|
%s
|
||||||
|
|
||||||
@@ -179,10 +184,33 @@ func init() {
|
|||||||
textArea.SetBorder(true).SetTitle("input")
|
textArea.SetBorder(true).SetTitle("input")
|
||||||
// Add input capture for @ completion
|
// Add input capture for @ completion
|
||||||
textArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
textArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
if shellMode && event.Key() == tcell.KeyRune && event.Rune() == '@' {
|
if !shellMode {
|
||||||
showFileCompletionPopup("")
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
// Handle Tab key for file completion
|
||||||
|
if event.Key() == tcell.KeyTab {
|
||||||
|
currentText := textArea.GetText()
|
||||||
|
row, col, _, _ := textArea.GetCursor()
|
||||||
|
// Calculate absolute position from row/col
|
||||||
|
lines := strings.Split(currentText, "\n")
|
||||||
|
cursorPos := 0
|
||||||
|
for i := 0; i < row && i < len(lines); i++ {
|
||||||
|
cursorPos += len(lines[i]) + 1 // +1 for newline
|
||||||
|
}
|
||||||
|
cursorPos += col
|
||||||
|
// Look backwards from cursor to find @
|
||||||
|
if cursorPos > 0 {
|
||||||
|
// Find the last @ before cursor
|
||||||
|
textBeforeCursor := currentText[:cursorPos]
|
||||||
|
atIndex := strings.LastIndex(textBeforeCursor, "@")
|
||||||
|
if atIndex >= 0 {
|
||||||
|
// Extract the partial match text after @
|
||||||
|
filter := textBeforeCursor[atIndex+1:]
|
||||||
|
showFileCompletionPopup(filter)
|
||||||
|
return nil // Consume the Tab event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
textView = tview.NewTextView().
|
textView = tview.NewTextView().
|
||||||
|
|||||||
Reference in New Issue
Block a user