// /futriis/internal/cli/history.go // Пакет cli реализует управление историей команд для интерактивного режима. // History хранит ограниченное количество последних команд с кольцевым буфером, предотвращает добавление последовательных дубликатов. // Предоставляет навигацию по истории с помощью стрелок вверх/вниз для быстрого повторного выполнения команд. // Интегрируется с Prompt для обеспечения полноценного интерфейса командной строки. package cli import ( "os" "strings" "golang.org/x/term" ) // History управляет историей команд type History struct { commands []string position int maxSize int } // NewHistory создаёт новую историю команд func NewHistory(maxSize int) *History { return &History{ commands: make([]string, 0, maxSize), position: -1, // Изначально позиция -1 (не в истории) maxSize: maxSize, } } // Add добавляет команду в историю func (h *History) Add(cmd string) { if cmd == "" { return } // Не добавляем дубликаты подряд if len(h.commands) > 0 && h.commands[len(h.commands)-1] == cmd { return } // Если достигнут максимум, удаляем самую старую команду if len(h.commands) >= h.maxSize { h.commands = h.commands[1:] } h.commands = append(h.commands, cmd) h.position = len(h.commands) // Устанавливаем позицию в конец } // GetPrevious возвращает предыдущую команду из истории func (h *History) GetPrevious() string { if len(h.commands) == 0 { return "" } // Если мы в начале или не в истории, начинаем с последней команды if h.position == -1 || h.position > len(h.commands)-1 { h.position = len(h.commands) - 1 return h.commands[h.position] } // Если есть предыдущая команда if h.position > 0 { h.position-- return h.commands[h.position] } // Мы уже на самой первой команде return h.commands[0] } // GetNext возвращает следующую команду из истории func (h *History) GetNext() string { if len(h.commands) == 0 { return "" } // Если мы не в истории, возвращаем пустую строку if h.position == -1 { return "" } // Если есть следующая команда if h.position < len(h.commands)-1 { h.position++ return h.commands[h.position] } // Достигли конца истории, сбрасываем позицию h.position = -1 return "" } // Search выполняет поиск команд по подстроке func (h *History) Search(query string) []string { if query == "" { // Возвращаем все команды в обратном порядке (новые сначала) result := make([]string, len(h.commands)) for i := 0; i < len(h.commands); i++ { result[i] = h.commands[len(h.commands)-1-i] } return result } // Ищем команды, содержащие запрос (регистронезависимо) queryLower := strings.ToLower(query) result := make([]string, 0) // Идем с конца, чтобы новые команды были в начале результатов for i := len(h.commands) - 1; i >= 0; i-- { if strings.Contains(strings.ToLower(h.commands[i]), queryLower) { result = append(result, h.commands[i]) } } return result } // Reset сбрасывает позицию в истории func (h *History) Reset() { h.position = -1 } // GetAll возвращает все команды истории func (h *History) GetAll() []string { result := make([]string, len(h.commands)) copy(result, h.commands) return result } // Size возвращает размер истории func (h *History) Size() int { return len(h.commands) } // SetupRawMode устанавливает терминал в raw-режим для обработки клавиш func SetupRawMode() (*term.State, error) { fd := int(os.Stdin.Fd()) return term.MakeRaw(fd) } // RestoreMode восстанавливает режим терминала func RestoreMode(oldState *term.State) error { fd := int(os.Stdin.Fd()) return term.Restore(fd, oldState) }