package gitexplorer import ( "fmt" "strings" ) // FormatResolveResult formats a ResolveResult as markdown. func FormatResolveResult(r *ResolveResult) string { var sb strings.Builder sb.WriteString(fmt.Sprintf("**Ref:** %s\n", r.Ref)) sb.WriteString(fmt.Sprintf("**Type:** %s\n", r.Type)) sb.WriteString(fmt.Sprintf("**Commit:** %s\n", r.Commit)) return sb.String() } // FormatLogEntries formats a slice of LogEntry as markdown. func FormatLogEntries(entries []LogEntry) string { if len(entries) == 0 { return "No commits found." } var sb strings.Builder sb.WriteString(fmt.Sprintf("## Commit Log (%d commits)\n\n", len(entries))) for _, e := range entries { sb.WriteString(fmt.Sprintf("### %s %s\n", e.ShortHash, e.Subject)) sb.WriteString(fmt.Sprintf("**Author:** %s <%s>\n", e.Author, e.Email)) sb.WriteString(fmt.Sprintf("**Date:** %s\n\n", e.Date.Format("2006-01-02 15:04:05"))) } return sb.String() } // FormatCommitInfo formats a CommitInfo as markdown. func FormatCommitInfo(info *CommitInfo) string { var sb strings.Builder sb.WriteString("## Commit Details\n\n") sb.WriteString(fmt.Sprintf("**Hash:** %s\n", info.Hash)) sb.WriteString(fmt.Sprintf("**Author:** %s <%s>\n", info.Author, info.Email)) sb.WriteString(fmt.Sprintf("**Date:** %s\n", info.Date.Format("2006-01-02 15:04:05"))) sb.WriteString(fmt.Sprintf("**Committer:** %s\n", info.Committer)) sb.WriteString(fmt.Sprintf("**Commit Date:** %s\n", info.CommitDate.Format("2006-01-02 15:04:05"))) if len(info.Parents) > 0 { sb.WriteString(fmt.Sprintf("**Parents:** %s\n", strings.Join(info.Parents, ", "))) } if info.Stats != nil { sb.WriteString(fmt.Sprintf("**Changes:** %d file(s), +%d -%d\n", info.Stats.FilesChanged, info.Stats.Additions, info.Stats.Deletions)) } sb.WriteString("\n### Message\n\n") sb.WriteString("```\n") sb.WriteString(info.Message) if !strings.HasSuffix(info.Message, "\n") { sb.WriteString("\n") } sb.WriteString("```\n") return sb.String() } // FormatDiffResult formats a DiffResult as markdown. func FormatDiffResult(r *DiffResult) string { if len(r.Files) == 0 { return "No files changed." } var sb strings.Builder sb.WriteString(fmt.Sprintf("## Files Changed (%d files)\n\n", len(r.Files))) sb.WriteString(fmt.Sprintf("**From:** %s\n", r.FromCommit[:7])) sb.WriteString(fmt.Sprintf("**To:** %s\n\n", r.ToCommit[:7])) sb.WriteString("| Status | Path | Changes |\n") sb.WriteString("|--------|------|--------|\n") for _, f := range r.Files { path := f.Path if f.OldPath != "" { path = fmt.Sprintf("%s → %s", f.OldPath, f.Path) } changes := fmt.Sprintf("+%d -%d", f.Additions, f.Deletions) sb.WriteString(fmt.Sprintf("| %s | %s | %s |\n", f.Status, path, changes)) } return sb.String() } // FormatFileContent formats a FileContent as markdown. func FormatFileContent(c *FileContent) string { var sb strings.Builder sb.WriteString(fmt.Sprintf("## File: %s\n\n", c.Path)) sb.WriteString(fmt.Sprintf("**Commit:** %s\n", c.Commit[:7])) sb.WriteString(fmt.Sprintf("**Size:** %d bytes\n\n", c.Size)) // Determine language hint from extension ext := "" if idx := strings.LastIndex(c.Path, "."); idx != -1 { ext = c.Path[idx+1:] } sb.WriteString(fmt.Sprintf("```%s\n", ext)) sb.WriteString(c.Content) if !strings.HasSuffix(c.Content, "\n") { sb.WriteString("\n") } sb.WriteString("```\n") return sb.String() } // FormatAncestryResult formats an AncestryResult as markdown. func FormatAncestryResult(r *AncestryResult) string { var sb strings.Builder sb.WriteString("## Ancestry Check\n\n") sb.WriteString(fmt.Sprintf("**Ancestor:** %s\n", r.Ancestor[:7])) sb.WriteString(fmt.Sprintf("**Descendant:** %s\n", r.Descendant[:7])) if r.IsAncestor { sb.WriteString("\nāœ“ **Yes**, the first commit is an ancestor of the second.\n") } else { sb.WriteString("\nāœ— **No**, the first commit is not an ancestor of the second.\n") } return sb.String() } // FormatCommitRange formats a CommitRange as markdown. func FormatCommitRange(r *CommitRange) string { if r.Count == 0 { return "No commits in range." } var sb strings.Builder sb.WriteString(fmt.Sprintf("## Commits Between (%d commits)\n\n", r.Count)) sb.WriteString(fmt.Sprintf("**From:** %s (exclusive)\n", r.FromCommit[:7])) sb.WriteString(fmt.Sprintf("**To:** %s (inclusive)\n\n", r.ToCommit[:7])) for _, e := range r.Commits { sb.WriteString(fmt.Sprintf("- **%s** %s (%s)\n", e.ShortHash, e.Subject, e.Author)) } return sb.String() } // FormatBranchList formats a BranchList as markdown. func FormatBranchList(r *BranchList) string { if r.Total == 0 { return "No branches found." } var sb strings.Builder sb.WriteString(fmt.Sprintf("## Branches (%d total)\n\n", r.Total)) if r.Current != "" { sb.WriteString(fmt.Sprintf("**Current branch:** %s\n\n", r.Current)) } sb.WriteString("| Branch | Commit | Type |\n") sb.WriteString("|--------|--------|------|\n") for _, b := range r.Branches { branchType := "local" if b.IsRemote { branchType = "remote" } marker := "" if b.IsHead { marker = " āœ“" } sb.WriteString(fmt.Sprintf("| %s%s | %s | %s |\n", b.Name, marker, b.Commit[:7], branchType)) } return sb.String() } // FormatSearchResult formats a SearchResult as markdown. func FormatSearchResult(r *SearchResult) string { if r.Count == 0 { return fmt.Sprintf("No commits found matching '%s'.", r.Query) } var sb strings.Builder sb.WriteString(fmt.Sprintf("## Search Results for '%s' (%d matches)\n\n", r.Query, r.Count)) for _, e := range r.Commits { sb.WriteString(fmt.Sprintf("### %s %s\n", e.ShortHash, e.Subject)) sb.WriteString(fmt.Sprintf("**Author:** %s <%s>\n", e.Author, e.Email)) sb.WriteString(fmt.Sprintf("**Date:** %s\n\n", e.Date.Format("2006-01-02 15:04:05"))) } return sb.String() }