feat: up/down arrow key now can choose last commands
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ import (
|
|||||||
// mergeResults collects and dedupes suggestions for a query and mode
|
// mergeResults collects and dedupes suggestions for a query and mode
|
||||||
// example: mergeResults("git ", "spec")
|
// example: mergeResults("git ", "spec")
|
||||||
func mergeResults(query string, mode string) []core.Suggestion {
|
func mergeResults(query string, mode string) []core.Suggestion {
|
||||||
if query == "" {
|
if query == "" && mode != "history" {
|
||||||
debugLog("[Merge] Query empty, returning nil")
|
debugLog("[Merge] Query empty, returning nil")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+74
-7
@@ -166,14 +166,18 @@ func runWrapper() {
|
|||||||
|
|
||||||
var disableGhostText atomic.Bool
|
var disableGhostText atomic.Bool
|
||||||
var userNavigated bool
|
var userNavigated bool
|
||||||
|
var renderOverlay func()
|
||||||
|
|
||||||
isExecuting := func() bool {
|
isExecuting := func() bool {
|
||||||
pgrp, err := unix.IoctlGetInt(int(ptmx.Fd()), unix.TIOCGPGRP)
|
pgrp, err := unix.IoctlGetInt(int(ptmx.Fd()), unix.TIOCGPGRP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// when another command starts, it puts it in a new process group and gives it the PTY
|
shellPGID, err := unix.Getpgid(core.ShellPID)
|
||||||
return pgrp != core.ShellPID
|
if err != nil {
|
||||||
|
return pgrp != core.ShellPID
|
||||||
|
}
|
||||||
|
return pgrp != shellPGID
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen for suggestion requests from shell scripts via the ipc pipe
|
// listen for suggestion requests from shell scripts via the ipc pipe
|
||||||
@@ -227,7 +231,7 @@ func runWrapper() {
|
|||||||
var renderMu sync.Mutex
|
var renderMu sync.Mutex
|
||||||
|
|
||||||
// renderOverlay decides whether to draw the suggestion menu based on current state
|
// renderOverlay decides whether to draw the suggestion menu based on current state
|
||||||
renderOverlay := func() {
|
renderOverlay = func() {
|
||||||
renderMu.Lock()
|
renderMu.Lock()
|
||||||
defer renderMu.Unlock()
|
defer renderMu.Unlock()
|
||||||
|
|
||||||
@@ -243,7 +247,7 @@ func runWrapper() {
|
|||||||
modeCopy := mode
|
modeCopy := mode
|
||||||
navCopy := userNavigated
|
navCopy := userNavigated
|
||||||
|
|
||||||
if bufCopy == "" {
|
if bufCopy == "" && !navCopy {
|
||||||
os.Stdout.Write([]byte(overlay.ClearAndDisable()))
|
os.Stdout.Write([]byte(overlay.ClearAndDisable()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -255,7 +259,7 @@ func runWrapper() {
|
|||||||
if isExecuting() {
|
if isExecuting() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
if !navCopy {
|
if !navCopy {
|
||||||
debugLog("[Render] query: '%s', mode: %s", bufCopy, modeCopy)
|
debugLog("[Render] query: '%s', mode: %s", bufCopy, modeCopy)
|
||||||
@@ -288,6 +292,23 @@ func runWrapper() {
|
|||||||
|
|
||||||
renderOverlay()
|
renderOverlay()
|
||||||
|
|
||||||
|
renderNow := func() {
|
||||||
|
renderMu.Lock()
|
||||||
|
if renderTimer != nil {
|
||||||
|
renderTimer.Stop()
|
||||||
|
}
|
||||||
|
renderMu.Unlock()
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
if overlay.Visible {
|
||||||
|
if !disableGhostText.Load() {
|
||||||
|
b.WriteString(overlay.RenderGhostText(naiveBuffer, userNavigated))
|
||||||
|
}
|
||||||
|
b.WriteString(overlay.Render())
|
||||||
|
}
|
||||||
|
os.Stdout.Write([]byte(b.String()))
|
||||||
|
}
|
||||||
|
|
||||||
// reads from stdin and decides what to forward or intercept
|
// reads from stdin and decides what to forward or intercept
|
||||||
// for most cases, I just handle the already have terminal shortcuts
|
// for most cases, I just handle the already have terminal shortcuts
|
||||||
// for some shortcuts like tab, enter, shift tab, ctrl r,
|
// for some shortcuts like tab, enter, shift tab, ctrl r,
|
||||||
@@ -329,6 +350,9 @@ func runWrapper() {
|
|||||||
if overlay.Visible && (inputSlice[i+2] == 'A' || inputSlice[i+2] == 'B') {
|
if overlay.Visible && (inputSlice[i+2] == 'A' || inputSlice[i+2] == 'B') {
|
||||||
intercepted = true
|
intercepted = true
|
||||||
userNavigated = true
|
userNavigated = true
|
||||||
|
|
||||||
|
os.Stdout.Write([]byte(overlay.Clear())) // clear old menu
|
||||||
|
|
||||||
if inputSlice[i+2] == 'A' { // up arrow
|
if inputSlice[i+2] == 'A' { // up arrow
|
||||||
overlay.Cursor--
|
overlay.Cursor--
|
||||||
if overlay.Cursor < 0 {
|
if overlay.Cursor < 0 {
|
||||||
@@ -340,13 +364,56 @@ func runWrapper() {
|
|||||||
overlay.Cursor = len(overlay.Items) - 1
|
overlay.Cursor = len(overlay.Items) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selected := overlay.Items[overlay.Cursor].Cmd
|
selected := overlay.Items[overlay.Cursor].Cmd
|
||||||
ptmx.Write([]byte{0x15}) // ctrl+u to clear line
|
ptmx.Write([]byte{0x15}) // ctrl+u to clear line
|
||||||
ptmx.Write([]byte(selected))
|
ptmx.Write([]byte(selected))
|
||||||
naiveBuffer = selected
|
naiveBuffer = selected
|
||||||
|
|
||||||
shouldOverlayDraw = true
|
renderNow()
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
} else if !overlay.Visible && naiveBuffer == "" && (inputSlice[i+2] == 'A' || inputSlice[i+2] == 'B') { // up/down arrow on empty prompt
|
||||||
|
intercepted = true
|
||||||
|
mode = "history"
|
||||||
|
saveMode(mode)
|
||||||
|
|
||||||
|
results := mergeResults("", "history")
|
||||||
|
if len(results) > 0 {
|
||||||
|
limit := 10
|
||||||
|
if len(results) < limit {
|
||||||
|
limit = len(results)
|
||||||
|
}
|
||||||
|
var top10 []core.Suggestion
|
||||||
|
|
||||||
|
if inputSlice[i+2] == 'A' {
|
||||||
|
// Up arrow: Reverse the list so newest is at the bottom
|
||||||
|
for j := limit - 1; j >= 0; j-- {
|
||||||
|
top10 = append(top10, results[j])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Down arrow: Normal order, newest is at the top
|
||||||
|
for j := 0; j < limit; j++ {
|
||||||
|
top10 = append(top10, results[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.UpdateItems(top10)
|
||||||
|
|
||||||
|
if inputSlice[i+2] == 'A' {
|
||||||
|
overlay.Cursor = len(top10) - 1 // Up arrow: Start at the bottom
|
||||||
|
} else {
|
||||||
|
overlay.Cursor = 0 // Down arrow: Start at the top
|
||||||
|
}
|
||||||
|
|
||||||
|
selected := overlay.Items[overlay.Cursor].Cmd
|
||||||
|
ptmx.Write([]byte{0x15}) // ctrl+u to clear line
|
||||||
|
ptmx.Write([]byte(selected))
|
||||||
|
naiveBuffer = selected
|
||||||
|
|
||||||
|
userNavigated = true
|
||||||
|
renderNow()
|
||||||
|
}
|
||||||
i += 2
|
i += 2
|
||||||
continue
|
continue
|
||||||
} else if overlay.Visible && !disableGhostText.Load() && inputSlice[i+2] == 'C' { // right arrow
|
} else if overlay.Visible && !disableGhostText.Load() && inputSlice[i+2] == 'C' { // right arrow
|
||||||
|
|||||||
Reference in New Issue
Block a user