From a9f1df6a89800e05ec4d0d2cc1e95b16afcee042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Sun, 1 Mar 2026 00:50:57 +0000 Subject: [PATCH] Upload files to "internal/cli" --- internal/cli/history.go | 148 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 internal/cli/history.go diff --git a/internal/cli/history.go b/internal/cli/history.go new file mode 100644 index 0000000..f0c72db --- /dev/null +++ b/internal/cli/history.go @@ -0,0 +1,148 @@ +// /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) +}