Delete internal/cli/prompt.go
This commit is contained in:
parent
69fccb8104
commit
a3b78f5946
@ -1,204 +0,0 @@
|
|||||||
// /futriis/internal/cli/prompt.go
|
|
||||||
// Пакет cli реализует интерактивное приглашение командной строки с поддержкой истории.
|
|
||||||
// Обеспечивает редактирование строки ввода, навигацию по истории стрелками,
|
|
||||||
// поддержку Unicode (включая кириллицу) и управление курсором терминала.
|
|
||||||
// Использует raw режим терминала для обработки специальных клавиш.
|
|
||||||
|
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"futriis/pkg/utils"
|
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ANSI escape sequences для управления курсором
|
|
||||||
const (
|
|
||||||
ansiHideCursor = "\033[?25l"
|
|
||||||
ansiShowCursor = "\033[?25h"
|
|
||||||
ansiClearLine = "\033[2K"
|
|
||||||
ansiCarriageReturn = "\r"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Prompt представляет интерактивное приглашение
|
|
||||||
type Prompt struct {
|
|
||||||
history *History
|
|
||||||
buffer []rune
|
|
||||||
pos int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPrompt создаёт новое приглашение
|
|
||||||
func NewPrompt() *Prompt {
|
|
||||||
return &Prompt{
|
|
||||||
history: NewHistory(100),
|
|
||||||
buffer: make([]rune, 0),
|
|
||||||
pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadLine читает строку с поддержкой истории и редактирования
|
|
||||||
func (p *Prompt) ReadLine() (string, error) {
|
|
||||||
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
|
||||||
if err != nil {
|
|
||||||
return p.readSimple()
|
|
||||||
}
|
|
||||||
defer term.Restore(int(os.Stdin.Fd()), oldState)
|
|
||||||
|
|
||||||
// Скрываем курсор в начале строки
|
|
||||||
fmt.Print(ansiHideCursor)
|
|
||||||
|
|
||||||
// Очищаем буфер
|
|
||||||
p.buffer = make([]rune, 0)
|
|
||||||
p.pos = 0
|
|
||||||
|
|
||||||
// Показываем приглашение
|
|
||||||
promptStr := utils.ColorPrompt + "futriis:~> " + utils.ColorReset
|
|
||||||
fmt.Print(promptStr)
|
|
||||||
|
|
||||||
// Показываем курсор только после приглашения (в месте ввода)
|
|
||||||
fmt.Print(ansiShowCursor)
|
|
||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
|
|
||||||
for {
|
|
||||||
r, _, err := reader.ReadRune()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch r {
|
|
||||||
case 3: // Ctrl+C
|
|
||||||
return "", nil
|
|
||||||
|
|
||||||
case 4: // Ctrl+D
|
|
||||||
if len(p.buffer) == 0 {
|
|
||||||
return "exit", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
case 13, 10: // Enter
|
|
||||||
// Скрываем курсор перед завершением
|
|
||||||
fmt.Print(ansiHideCursor)
|
|
||||||
fmt.Println()
|
|
||||||
cmd := string(p.buffer)
|
|
||||||
if cmd != "" {
|
|
||||||
p.history.Add(cmd)
|
|
||||||
}
|
|
||||||
p.history.Reset()
|
|
||||||
|
|
||||||
// Логируем команду
|
|
||||||
if logger := utils.GetLogger(); logger != nil && cmd != "" {
|
|
||||||
logger.Log("CMD", cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd, nil
|
|
||||||
|
|
||||||
case 127: // Backspace
|
|
||||||
if p.pos > 0 {
|
|
||||||
// Удаляем символ перед курсором
|
|
||||||
p.buffer = append(p.buffer[:p.pos-1], p.buffer[p.pos:]...)
|
|
||||||
p.pos--
|
|
||||||
p.refreshLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
case 27: // Escape sequence (стрелки)
|
|
||||||
// Читаем следующие два символа
|
|
||||||
r2, _, _ := reader.ReadRune()
|
|
||||||
r3, _, _ := reader.ReadRune()
|
|
||||||
|
|
||||||
if r2 == '[' {
|
|
||||||
switch r3 {
|
|
||||||
case 'A': // Up arrow
|
|
||||||
prev := p.history.GetPrevious()
|
|
||||||
if prev != "" {
|
|
||||||
p.buffer = []rune(prev)
|
|
||||||
p.pos = len(p.buffer)
|
|
||||||
p.refreshLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'B': // Down arrow
|
|
||||||
next := p.history.GetNext()
|
|
||||||
p.buffer = []rune(next)
|
|
||||||
p.pos = len(p.buffer)
|
|
||||||
p.refreshLine()
|
|
||||||
|
|
||||||
case 'C': // Right arrow
|
|
||||||
if p.pos < len(p.buffer) {
|
|
||||||
p.pos++
|
|
||||||
p.refreshLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'D': // Left arrow
|
|
||||||
if p.pos > 0 {
|
|
||||||
p.pos--
|
|
||||||
p.refreshLine()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Добавляем символ (поддержка Unicode, включая русский)
|
|
||||||
if r >= 32 { // Печатные символы
|
|
||||||
// Вставляем символ в позицию курсора
|
|
||||||
if p.pos == len(p.buffer) {
|
|
||||||
p.buffer = append(p.buffer, r)
|
|
||||||
} else {
|
|
||||||
p.buffer = append(p.buffer[:p.pos], append([]rune{r}, p.buffer[p.pos:]...)...)
|
|
||||||
}
|
|
||||||
p.pos++
|
|
||||||
p.refreshLine()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// refreshLine обновляет текущую строку с правильным позиционированием курсора
|
|
||||||
func (p *Prompt) refreshLine() {
|
|
||||||
// Скрываем курсор во время перерисовки
|
|
||||||
fmt.Print(ansiHideCursor)
|
|
||||||
|
|
||||||
// Возврат в начало строки и очистка
|
|
||||||
fmt.Print(ansiCarriageReturn + ansiClearLine)
|
|
||||||
|
|
||||||
// Печатаем приглашение
|
|
||||||
promptStr := utils.ColorPrompt + "futriis:~> " + utils.ColorReset
|
|
||||||
fmt.Print(promptStr)
|
|
||||||
|
|
||||||
// Печатаем текущий буфер
|
|
||||||
if len(p.buffer) > 0 {
|
|
||||||
fmt.Print(string(p.buffer))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Вычисляем ширину приглашения
|
|
||||||
promptWidth := runewidth.StringWidth("futriis:~> ")
|
|
||||||
|
|
||||||
// Вычисляем позицию курсора
|
|
||||||
cursorPos := promptWidth
|
|
||||||
for i := 0; i < p.pos; i++ {
|
|
||||||
cursorPos += runewidth.RuneWidth(p.buffer[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Перемещаем курсор на правильную позицию и показываем его
|
|
||||||
fmt.Printf("\033[%dG", cursorPos+1)
|
|
||||||
fmt.Print(ansiShowCursor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// readSimple читает строку без специальной обработки (fallback)
|
|
||||||
func (p *Prompt) readSimple() (string, error) {
|
|
||||||
fmt.Print(utils.GetPrompt())
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
|
|
||||||
cmd, err := reader.ReadString('\n')
|
|
||||||
if err == nil {
|
|
||||||
cmd = cmd[:len(cmd)-1] // Убираем \n
|
|
||||||
// Логируем команду
|
|
||||||
if logger := utils.GetLogger(); logger != nil && cmd != "" {
|
|
||||||
logger.Log("CMD", cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd, err
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user