zutils/pkg/formatter/utils.go
selamanapps 5b334071e0 feat: add user-friendly help system and new commands
New Commands:
- z read - Smart file reader with auto-detection (markdown, syntax highlighting)
- z sys - System overview (CPU, RAM, Disk, processes)
- z find - File search with pattern/size/time filters
- z search - User-friendly grep alternative
- z usage - Process resource usage monitor

Enhancements:
- Unified help system with consistent error messages
- Help on error shows usage and examples for each command
- "Command not found" suggests similar commands
- Directory info now detects .gitignore and shows ignored files
- File handler registry for extensible file type detection
- Added Italic, Dim, Underline color constants
2026-05-02 01:12:55 +03:00

135 lines
2.7 KiB
Go

package formatter
import (
"bytes"
"fmt"
"math"
"os"
"path/filepath"
"strings"
"github.com/alecthomas/chroma/v2/formatters"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
)
type FileHandler func(args []string) error
type FileHandlerRegistry struct {
handlers map[string]FileHandler
}
var ReadHandlers = &FileHandlerRegistry{
handlers: make(map[string]FileHandler),
}
func (r *FileHandlerRegistry) Register(ext string, handler FileHandler) {
r.handlers[ext] = handler
}
func (r *FileHandlerRegistry) GetHandler(filename string) FileHandler {
ext := filepath.Ext(filename)
if ext == "" || ext == "." {
basename := filepath.Base(filename)
if strings.HasPrefix(basename, "Dockerfile") {
return r.handlers[".dockerfile"]
}
return nil
}
if handler, ok := r.handlers[ext]; ok {
return handler
}
return nil
}
func ReadFileWithHighlight(file *os.File, filePath string) error {
content := ReadFileContent(file)
lexer := lexers.Match(filepath.Base(filePath))
if lexer == nil {
lexer = lexers.Fallback
}
iterator, err := lexer.Tokenise(nil, content)
if err != nil {
return fmt.Errorf("error tokenizing: %w", err)
}
formatter_ := formatters.TTY256
style := styles.Get("monokai")
if style == nil {
style = styles.Fallback
}
var buf bytes.Buffer
err = formatter_.Format(&buf, style, iterator)
if err != nil {
return fmt.Errorf("error formatting: %w", err)
}
lines := strings.Split(buf.String(), "\n")
for i, line := range lines {
if line != "" {
fmt.Printf("%s%4d: %s%s\n", "\033[90m", i+1, "\033[0m", line)
}
}
return nil
}
// FormatSize converts bytes to human-readable format
func FormatSize(bytes int64) string {
if bytes == 0 {
return "0 B"
}
const unit = 1024
units := []string{"B", "KB", "MB", "GB", "TB", "PB"}
exp := int(math.Log(float64(bytes)) / math.Log(float64(unit)))
if exp > len(units)-1 {
exp = len(units) - 1
}
value := float64(bytes) / math.Pow(float64(unit), float64(exp))
return fmt.Sprintf("%.2f %s", value, units[exp])
}
// CenterText centers text within a given width
func CenterText(text string, width int) string {
if len(text) >= width {
return text[:width]
}
padding := width - len(text)
left := padding / 2
right := padding - left
return fmt.Sprintf("%s%s%s", repeat(" ", left), text, repeat(" ", right))
}
func repeat(s string, count int) string {
result := ""
for i := 0; i < count; i++ {
result += s
}
return result
}
func ReadFileContent(file *os.File) string {
content := make([]byte, 0)
buffer := make([]byte, 4096)
for {
n, err := file.Read(buffer)
if n > 0 {
content = append(content, buffer[:n]...)
}
if err != nil {
break
}
}
return string(content)
}