2026-05-02 00:43:22 +03:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/alecthomas/chroma/v2/formatters"
|
|
|
|
|
"github.com/alecthomas/chroma/v2/lexers"
|
|
|
|
|
"github.com/alecthomas/chroma/v2/styles"
|
|
|
|
|
"github.com/zemenawi/zutils/pkg/colors"
|
|
|
|
|
"github.com/zemenawi/zutils/pkg/formatter"
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-02 01:12:55 +03:00
|
|
|
func init() {
|
|
|
|
|
formatter.ReadHandlers.Register(".md", MarkdownHandler)
|
|
|
|
|
formatter.ReadHandlers.Register(".markdown", MarkdownHandler)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MarkdownHandler(args []string) error {
|
2026-05-02 00:43:22 +03:00
|
|
|
if len(args) < 1 {
|
|
|
|
|
return fmt.Errorf("please specify a markdown file path")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filePath := args[0]
|
|
|
|
|
|
|
|
|
|
file, err := os.Open(filePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error opening file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
fileInfo, err := file.Stat()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error getting file info: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fileInfo.IsDir() {
|
|
|
|
|
return fmt.Errorf("'%s' is a directory, not a readable file", filePath)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content := formatter.ReadFileContent(file)
|
|
|
|
|
|
|
|
|
|
fmt.Printf("%s%s╔══════════════════════════════════════╗%s\n", colors.Cyan, colors.Bold, colors.Reset)
|
|
|
|
|
fmt.Printf("%s%s║%s %-38s %s%s║%s\n", colors.Cyan, colors.Bold, colors.Reset, filePath, colors.Cyan, colors.Bold, colors.Reset)
|
|
|
|
|
fmt.Printf("%s%s╚══════════════════════════════════════╝%s\n", colors.Cyan, colors.Bold, colors.Reset)
|
|
|
|
|
fmt.Println()
|
|
|
|
|
|
|
|
|
|
lines := strings.Split(content, "\n")
|
|
|
|
|
inCodeBlock := false
|
|
|
|
|
codeBuffer := &strings.Builder{}
|
|
|
|
|
codeLang := ""
|
|
|
|
|
|
2026-05-02 01:12:55 +03:00
|
|
|
for _, line := range lines {
|
2026-05-02 00:43:22 +03:00
|
|
|
if strings.HasPrefix(line, "```") {
|
|
|
|
|
if !inCodeBlock {
|
|
|
|
|
inCodeBlock = true
|
|
|
|
|
codeLang = strings.TrimPrefix(line, "```")
|
|
|
|
|
codeBuffer.Reset()
|
|
|
|
|
} else {
|
|
|
|
|
inCodeBlock = false
|
|
|
|
|
renderCodeBlock(codeBuffer.String(), codeLang)
|
|
|
|
|
codeBuffer.Reset()
|
|
|
|
|
codeLang = ""
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if inCodeBlock {
|
|
|
|
|
if codeBuffer.Len() > 0 {
|
|
|
|
|
codeBuffer.WriteString("\n")
|
|
|
|
|
}
|
|
|
|
|
codeBuffer.WriteString(line)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.HasPrefix(line, "# ") {
|
|
|
|
|
fmt.Printf("%s%s%s%s\n", colors.Bold, colors.Yellow, line[2:], colors.Reset)
|
|
|
|
|
} else if strings.HasPrefix(line, "## ") {
|
|
|
|
|
fmt.Printf("%s%s%s%s\n", colors.Bold, colors.Green, line[3:], colors.Reset)
|
|
|
|
|
} else if strings.HasPrefix(line, "### ") {
|
|
|
|
|
fmt.Printf("%s%s%s%s\n", colors.Bold, colors.Cyan, line[4:], colors.Reset)
|
|
|
|
|
} else if strings.HasPrefix(line, "- [ ]") {
|
|
|
|
|
fmt.Printf(" %s☐%s %s\n", colors.Gray, colors.Reset, line[5:])
|
|
|
|
|
} else if strings.HasPrefix(line, "- [x]") {
|
|
|
|
|
fmt.Printf(" %s☑%s %s\n", colors.Green, colors.Reset, line[5:])
|
|
|
|
|
} else if strings.HasPrefix(line, "- ") && !strings.HasPrefix(line, "- [") {
|
|
|
|
|
fmt.Printf(" %s•%s %s\n", colors.Blue, colors.Reset, line[2:])
|
|
|
|
|
} else if strings.HasPrefix(line, "> ") {
|
|
|
|
|
fmt.Printf("%s%s%s%s\n", colors.Gray, colors.Italic, line[2:], colors.Reset)
|
|
|
|
|
} else if strings.TrimSpace(line) != "" {
|
|
|
|
|
fmt.Printf("%s\n", line)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderCodeBlock(code, lang string) {
|
|
|
|
|
if code == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 01:12:55 +03:00
|
|
|
fmt.Printf("%s%s╔═══ %s ═══╗%s\n", colors.Gray, colors.Dim, lang, colors.Reset)
|
|
|
|
|
|
2026-05-02 00:43:22 +03:00
|
|
|
lexer := lexers.Match(lang)
|
|
|
|
|
if lexer == nil {
|
|
|
|
|
lexer = lexers.Fallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator, _ := lexer.Tokenise(nil, code)
|
2026-05-02 01:12:55 +03:00
|
|
|
formatter_ := formatters.TTY256
|
2026-05-02 00:43:22 +03:00
|
|
|
style := styles.Get("monokai")
|
|
|
|
|
if style == nil {
|
|
|
|
|
style = styles.Fallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var buf strings.Builder
|
2026-05-02 01:12:55 +03:00
|
|
|
formatter_.Format(&buf, style, iterator)
|
2026-05-02 00:43:22 +03:00
|
|
|
|
|
|
|
|
output := buf.String()
|
|
|
|
|
for _, line := range strings.Split(output, "\n") {
|
|
|
|
|
if line != "" {
|
|
|
|
|
fmt.Printf("%s%s│ %s%s\n", colors.Gray, colors.Dim, colors.Reset, line)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-02 01:12:55 +03:00
|
|
|
fmt.Printf("%s%s╚══════════════╝%s\n", colors.Gray, colors.Dim, colors.Reset)
|
2026-05-02 00:43:22 +03:00
|
|
|
fmt.Println()
|
|
|
|
|
}
|