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" ) func init() { formatter.ReadHandlers.Register(".md", MarkdownHandler) formatter.ReadHandlers.Register(".markdown", MarkdownHandler) } func MarkdownHandler(args []string) error { 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 := "" for _, line := range lines { 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 } fmt.Printf("%s%s╔═══ %s ═══╗%s\n", colors.Gray, colors.Dim, lang, colors.Reset) lexer := lexers.Match(lang) if lexer == nil { lexer = lexers.Fallback } iterator, _ := lexer.Tokenise(nil, code) formatter_ := formatters.TTY256 style := styles.Get("monokai") if style == nil { style = styles.Fallback } var buf strings.Builder formatter_.Format(&buf, style, iterator) 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) } } fmt.Printf("%s%s╚══════════════╝%s\n", colors.Gray, colors.Dim, colors.Reset) fmt.Println() }