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:
Binary file not shown.
@@ -12,6 +12,9 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
|||||||
var title: String
|
var title: String
|
||||||
var currentWorkingDirectory: String = "~"
|
var currentWorkingDirectory: String = "~"
|
||||||
private var currentSuggestion: String?
|
private var currentSuggestion: String?
|
||||||
|
private var historyIndex: Int = -1
|
||||||
|
private var historyBuffer: [String] = []
|
||||||
|
private var currentInput: String = ""
|
||||||
|
|
||||||
init(title: String = "Terminal") {
|
init(title: String = "Terminal") {
|
||||||
self.sessionID = UUID()
|
self.sessionID = UUID()
|
||||||
@@ -232,6 +235,80 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
|||||||
return cleaned
|
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 {
|
func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementString: String?) -> Bool {
|
||||||
// Don't allow editing before the prompt
|
// Don't allow editing before the prompt
|
||||||
if range.location < promptPosition {
|
if range.location < promptPosition {
|
||||||
@@ -402,6 +479,11 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate {
|
|||||||
|
|
||||||
let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
|
// Reset history navigation
|
||||||
|
historyIndex = -1
|
||||||
|
currentInput = ""
|
||||||
|
historyBuffer = [] // Reload on next navigation
|
||||||
|
|
||||||
// Check for aliases - match the first word
|
// Check for aliases - match the first word
|
||||||
var actualCommand = trimmed
|
var actualCommand = trimmed
|
||||||
let components = trimmed.split(separator: " ", maxSplits: 1)
|
let components = trimmed.split(separator: " ", maxSplits: 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user