переход на bubble tea библиотеку
This commit is contained in:
+111
-37
@@ -5,51 +5,125 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"nshell/executor"
|
"charm.land/bubbles/v2/textinput"
|
||||||
"nshell/suggestions"
|
tea "charm.land/bubbletea/v2"
|
||||||
|
"charm.land/lipgloss/v2"
|
||||||
|
|
||||||
"github.com/c-bata/go-prompt"
|
"nshell/executor"
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var oldStateTerminal *term.State
|
// Сообщение, которое сигнализирует о завершении работы внешней команды
|
||||||
|
type processFinishedMsg struct{ err error }
|
||||||
|
|
||||||
func getColor(color string) prompt.Color {
|
type model struct {
|
||||||
color = strings.ToUpper(color)
|
textInput textinput.Model
|
||||||
|
lastInput string // Храним последнюю введенную строку для красивого лога
|
||||||
|
errText string // Храним ошибку, если команда не найдена
|
||||||
|
}
|
||||||
|
|
||||||
switch color {
|
func initialModel() model {
|
||||||
case "CYAN":
|
ti := textinput.New()
|
||||||
return prompt.Cyan
|
ti.Placeholder = "Type a command..."
|
||||||
case "RED":
|
ti.Focus()
|
||||||
return prompt.Red
|
ti.CharLimit = 250
|
||||||
case "FUCHSIA":
|
ti.SetWidth(60)
|
||||||
return prompt.Fuchsia
|
|
||||||
case "TURQUOISE":
|
// НАСТРОЙКА TRUECOLOR
|
||||||
return prompt.Turquoise
|
prefixStyle := lipgloss.NewStyle().
|
||||||
case "GREEN":
|
Foreground(lipgloss.Color("#FF00FA")).
|
||||||
return prompt.Green
|
Bold(true)
|
||||||
case "PURPLE":
|
|
||||||
return prompt.Purple
|
ti.Prompt = prefixStyle.Render("noko> ")
|
||||||
default:
|
//prefixStyle=lipgloss.NewStyle().Foreground(lipgloss.Color("#00FFD1"))
|
||||||
return prompt.Green
|
|
||||||
|
return model{
|
||||||
|
textInput: ti,
|
||||||
|
lastInput: "",
|
||||||
|
errText: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m model) Init() tea.Cmd {
|
||||||
|
return textinput.Blink
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "ctrl+c", "esc":
|
||||||
|
return m, tea.Quit
|
||||||
|
|
||||||
|
case "enter":
|
||||||
|
inputVal := m.textInput.Value()
|
||||||
|
m.textInput.SetValue("") // Очищаем поле ввода сразу
|
||||||
|
m.errText = "" // Сбрасываем прошлые ошибки
|
||||||
|
|
||||||
|
if strings.TrimSpace(inputVal) == "" {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.lastInput = inputVal
|
||||||
|
|
||||||
|
// Передаем ввод в наш executor для подготовки команды
|
||||||
|
c, err := executor.PrepareCommand(inputVal)
|
||||||
|
if err != nil {
|
||||||
|
if err == executor.ErrBuiltInExit {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
// Если exec.LookPath не нашел бинарник в системе
|
||||||
|
m.errText = fmt.Sprintf("noko-shell: command not found: %s", strings.Fields(inputVal)[0])
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Универсальный запуск процесса (и для ls, и для nano/top/htop)
|
||||||
|
// Мы связываем IO процесса с системным, чтобы stderr и stdout вывелись в терминал напрямую.
|
||||||
|
c.Stdin = os.Stdin
|
||||||
|
c.Stdout = os.Stdout
|
||||||
|
c.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// Вызываем ExecProcess, который сворачивает TUI, выполняет команду и возвращает управление
|
||||||
|
return m, tea.ExecProcess(c, func(err error) tea.Msg {
|
||||||
|
return processFinishedMsg{err: err}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
case processFinishedMsg:
|
||||||
|
if msg.err != nil {
|
||||||
|
// Ошибка самого процесса (например, неверные флаги у команды)
|
||||||
|
m.errText = fmt.Sprintf("process exited with error: %v", msg.err)
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd tea.Cmd
|
||||||
|
m.textInput, cmd = m.textInput.Update(msg)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) View() tea.View {
|
||||||
|
var s strings.Builder
|
||||||
|
|
||||||
|
if m.errText != "" {
|
||||||
|
errStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FF0000"))
|
||||||
|
s.WriteString(errStyle.Render(m.errText))
|
||||||
|
s.WriteString("\n")
|
||||||
|
m.errText = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
s.WriteString(m.textInput.View())
|
||||||
|
|
||||||
|
// Возвращаем объект tea.View, обернув в него строку
|
||||||
|
return tea.NewView(s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Запускаем программу
|
||||||
fd := int(os.Stdin.Fd())
|
p := tea.NewProgram(initialModel())
|
||||||
if state, err := term.GetState(fd); err == nil {
|
if _, err := p.Run(); err != nil {
|
||||||
oldStateTerminal = state
|
fmt.Printf("Error running noko-shell: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Println("Wellcome to noko-shell(nshell) :3")
|
|
||||||
|
|
||||||
options := []prompt.Option{
|
|
||||||
prompt.OptionLivePrefix(func() (string, bool) {
|
|
||||||
return executor.GetVariable("NSHELL_PREFIX_TEXT"), true
|
|
||||||
}),
|
|
||||||
prompt.OptionPrefixTextColor(getColor(executor.GetVariable("NSHELL_PREFIX_COLOR"))),
|
|
||||||
}
|
|
||||||
|
|
||||||
p := prompt.New(executor.BuildExecutor(oldStateTerminal, &fd), suggestions.Completer, options...)
|
|
||||||
p.Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-44
@@ -2,69 +2,54 @@
|
|||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrBuiltInExit = errors.New("exit command")
|
||||||
var localVars = make(map[string]string)
|
var localVars = make(map[string]string)
|
||||||
|
|
||||||
func BuildExecutor(oldStateTerminal *term.State, fd *int) func(string) {
|
func PrepareCommand(in string) (*exec.Cmd, error) {
|
||||||
return func(line string) {
|
in = strings.TrimSpace(in)
|
||||||
line = strings.TrimSpace(line)
|
if in == "" {
|
||||||
|
return nil, nil
|
||||||
if strings.Contains(line, "=") && !strings.HasPrefix(line, "export ") {
|
}
|
||||||
parts := strings.SplitN(line, "=", 2)
|
if strings.Contains(in, "=") && !strings.HasPrefix(in, "export ") {
|
||||||
|
parts := strings.SplitN(in, "=", 2)
|
||||||
key := strings.TrimSpace(parts[0])
|
key := strings.TrimSpace(parts[0])
|
||||||
value := strings.TrimSpace(parts[1])
|
value := strings.TrimSpace(parts[1])
|
||||||
|
|
||||||
localVars[key] = value
|
localVars[key] = value
|
||||||
return
|
return nil,nil
|
||||||
}
|
}
|
||||||
|
|
||||||
line = os.Expand(line, GetVariable)
|
in = os.Expand(in, GetVariable)
|
||||||
|
|
||||||
if line == "" {
|
|
||||||
return
|
parts := strings.Fields(in)
|
||||||
|
head := parts[0]
|
||||||
|
args := parts[1:]
|
||||||
|
|
||||||
|
switch head {
|
||||||
|
case "exit":
|
||||||
|
return nil, ErrBuiltInExit
|
||||||
|
case "help":
|
||||||
|
return exec.Command("echo", "noko-shell: available commands: any system command, help, exit"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
args := strings.Fields(line)
|
// Поиск системной утилиты в PATH (авто-проверка на существование)
|
||||||
command := args[0]
|
fullPath, err := exec.LookPath(head)
|
||||||
commandArgs := args[1:]
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
if command == "exit" {
|
|
||||||
fmt.Println("Bye Bye my small deer :3")
|
|
||||||
if oldStateTerminal != nil {
|
|
||||||
_ = term.Restore(*fd, oldStateTerminal)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if command == "cd" {
|
// Возвращаем подготовленный процесс
|
||||||
if len(commandArgs) < 1 {
|
return exec.Command(fullPath, args...), nil
|
||||||
fmt.Fprintln(os.Stderr, "cd: argument was skipped")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := os.Chdir(commandArgs[0]); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(command, commandArgs...)
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Execution error: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func GetVariable(key string) string {
|
func GetVariable(key string) string {
|
||||||
if val, exists := localVars[key]; exists {
|
if val, exists := localVars[key]; exists {
|
||||||
return val
|
return val
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
module nshell
|
module nshell
|
||||||
|
|
||||||
go 1.27.2
|
go 1.26.8
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/c-bata/go-prompt v0.2.6
|
charm.land/bubbles/v2 v2.2.1
|
||||||
golang.org/x/term v0.47.0
|
charm.land/bubbletea/v2 v2.1.0
|
||||||
|
charm.land/lipgloss/v2 v2.0.6
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-colorable v0.1.7 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4 // indirect
|
||||||
github.com/mattn/go-tty v0.0.3 // indirect
|
github.com/charmbracelet/x/ansi v0.11.9 // indirect
|
||||||
github.com/pkg/term v1.2.0-beta.2 // indirect
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
golang.org/x/sys v0.49.0 // indirect
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.30 // indirect
|
||||||
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/xo/terminfo v1.0.0 // indirect
|
||||||
|
golang.org/x/sync v0.22.0 // indirect
|
||||||
|
golang.org/x/sys v0.47.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,28 +1,44 @@
|
|||||||
github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI=
|
charm.land/bubbles/v2 v2.2.1 h1:Fq1+qm5hV6GkvzLQDhCBpXXE5tLgvh1PRriCLwSvIQU=
|
||||||
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
|
charm.land/bubbles/v2 v2.2.1/go.mod h1:wdMgn+sje1KNXdwFizIWjbf328fIUBxqEmJ/vYPo8yc=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
charm.land/bubbletea/v2 v2.1.0 h1:A9fj9h5LFrvQhQHFNggp8K8rvY6CUfgn9dMbb6p2HIw=
|
||||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
charm.land/bubbletea/v2 v2.1.0/go.mod h1:xWHeyxIX9/wiFU2kgPW04ojOZyTzrWf+8lGjUCaftiU=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
charm.land/lipgloss/v2 v2.0.6 h1:EaGKeuA8FvF+v2BT5VmZd2LoYLaMZJXA5n34th8nCIQ=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
charm.land/lipgloss/v2 v2.0.6/go.mod h1:ipDDJNSGa1hlwDtSfW1s2/xR8Vdhbut4PXh2zEKZd0Q=
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||||
github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI=
|
github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4 h1:hISLzUbwcvJbGoNR6A3L/TpguRRnACBmZOee8LcHLJw=
|
||||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
github.com/charmbracelet/ultraviolet v0.0.0-20261008173134-6b8d4baf91b4/go.mod h1:aJDVk+WOVM1K04rdc/71PMPoUGfjxjhvXfP3Pjz1Zio=
|
||||||
github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw=
|
github.com/charmbracelet/x/ansi v0.11.9 h1:kIrqLBxtTVbLyyHO2Evoi93EjQo1mylWmpNEsfPxXAM=
|
||||||
github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
github.com/charmbracelet/x/ansi v0.11.9/go.mod h1:DG/hpOnaI2QJ/KMMqLL2h0KVhluuJLazf8973P+Vy5w=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||||
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||||
golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
|
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||||
golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk=
|
||||||
|
github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
|
||||||
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/xo/terminfo v1.0.0 h1:2ZpYzqWzyyytjk3TP6aJVDhkMAkc99/1xKQdA3TDTBY=
|
||||||
|
github.com/xo/terminfo v1.0.0/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||||
|
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||||
|
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
// Package suggestions
|
// Package suggestions
|
||||||
package suggestions
|
package suggestions
|
||||||
|
|
||||||
import "github.com/c-bata/go-prompt"
|
import "strings"
|
||||||
|
|
||||||
var defaultSuggestions = []prompt.Suggest{
|
var defaultSuggestions = []string{"exit"}
|
||||||
{Text: "exit", Description: "Exit from shell"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func Completer(d prompt.Document) []prompt.Suggest {
|
func Completer(in string) []string {
|
||||||
// Если пользователь уже начал что-то писать, фильтруем список
|
if in=="" {return nil}
|
||||||
return prompt.FilterHasPrefix(defaultSuggestions, d.GetWordBeforeCursor(), true)
|
var matched []string
|
||||||
|
for _,cmd := range defaultSuggestions {
|
||||||
|
if strings.HasPrefix(cmd,in){
|
||||||
|
matched=append(matched, cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// Package tcolor
|
|
||||||
package tcolor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"regexp"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TColor struct{
|
|
||||||
colorRe *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (t *TColor) Init(){
|
|
||||||
colorRe,colorReErr := regexp.Compile(`#[0-9a-fA-F]{6}`)
|
|
||||||
if colorReErr != nil{
|
|
||||||
fmt.Errorf("Regexp color err: %s",colorReErr.Error())
|
|
||||||
}else{
|
|
||||||
t.colorRe = colorRe
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TColor) HexToANSI(hex string) string {
|
|
||||||
if t.colorRe.MatchString(hex) {
|
|
||||||
hex = hex[1:]
|
|
||||||
}
|
|
||||||
if len(hex) != 6 {
|
|
||||||
return "" // Возвращаем пустую строку при ошибке
|
|
||||||
}
|
|
||||||
r, _ := strconv.ParseUint(hex[0:2], 16, 8)
|
|
||||||
g, _ := strconv.ParseUint(hex[2:4], 16, 8)
|
|
||||||
b, _ := strconv.ParseUint(hex[4:6], 16, 8)
|
|
||||||
|
|
||||||
// Формат TrueColor ESC[38;2;R;G;Bm
|
|
||||||
return fmt.Sprintf("\x1b[38;2;%d;%d;%dm", r, g, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user