Add command history navigation with up/down arrows
- Up arrow: Navigate to previous commands in history - Down arrow: Navigate to next commands (or back to current input) - Works exactly like standard terminal behavior - Preserves current input when starting navigation - Resets on command execution
This commit is contained in:
@@ -12,6 +12,9 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
||||
var title: String
|
||||
var currentWorkingDirectory: String = "~"
|
||||
private var currentSuggestion: String?
|
||||
private var historyIndex: Int = -1
|
||||
private var historyBuffer: [String] = []
|
||||
private var currentInput: String = ""
|
||||
|
||||
init(title: String = "Terminal") {
|
||||
self.sessionID = UUID()
|
||||
@@ -232,6 +235,80 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
||||
return cleaned
|
||||
}
|
||||
|
||||
func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
|
||||
// Handle up arrow - previous command in history
|
||||
if commandSelector == #selector(NSResponder.moveUp(_:)) {
|
||||
navigateHistory(direction: .previous)
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle down arrow - next command in history
|
||||
if commandSelector == #selector(NSResponder.moveDown(_:)) {
|
||||
navigateHistory(direction: .next)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private enum HistoryDirection {
|
||||
case previous, next
|
||||
}
|
||||
|
||||
private func navigateHistory(direction: HistoryDirection) {
|
||||
// Load history if not already loaded
|
||||
if historyBuffer.isEmpty {
|
||||
historyBuffer = HistoryManager.shared.getRecentCommands(100)
|
||||
}
|
||||
|
||||
guard !historyBuffer.isEmpty else { return }
|
||||
|
||||
// Save current input if we're starting to navigate
|
||||
if historyIndex == -1 {
|
||||
currentInput = getCurrentCommand()
|
||||
}
|
||||
|
||||
// Navigate history
|
||||
switch direction {
|
||||
case .previous:
|
||||
if historyIndex < historyBuffer.count - 1 {
|
||||
historyIndex += 1
|
||||
}
|
||||
case .next:
|
||||
if historyIndex > -1 {
|
||||
historyIndex -= 1
|
||||
}
|
||||
}
|
||||
|
||||
// Update command line
|
||||
let newCommand: String
|
||||
if historyIndex == -1 {
|
||||
// Back to original input
|
||||
newCommand = currentInput
|
||||
} else {
|
||||
// Show history command
|
||||
newCommand = historyBuffer[historyIndex]
|
||||
}
|
||||
|
||||
// Replace current command with history command
|
||||
replaceCurrentCommand(with: newCommand)
|
||||
}
|
||||
|
||||
private func replaceCurrentCommand(with newCommand: String) {
|
||||
let currentLength = textView.string.count
|
||||
let commandLength = currentLength - promptPosition
|
||||
|
||||
if commandLength > 0 {
|
||||
let range = NSRange(location: promptPosition, length: commandLength)
|
||||
textView.textStorage?.replaceCharacters(in: range, with: newCommand)
|
||||
} else {
|
||||
textView.insertText(newCommand, replacementRange: NSRange(location: promptPosition, length: 0))
|
||||
}
|
||||
|
||||
// Move cursor to end
|
||||
textView.setSelectedRange(NSRange(location: textView.string.count, length: 0))
|
||||
}
|
||||
|
||||
func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementString: String?) -> Bool {
|
||||
// Don't allow editing before the prompt
|
||||
if range.location < promptPosition {
|
||||
@@ -402,6 +479,11 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
||||
|
||||
let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Reset history navigation
|
||||
historyIndex = -1
|
||||
currentInput = ""
|
||||
historyBuffer = [] // Reload on next navigation
|
||||
|
||||
// Check for aliases - match the first word
|
||||
var actualCommand = trimmed
|
||||
let components = trimmed.split(separator: " ", maxSplits: 1)
|
||||
|
||||
Reference in New Issue
Block a user