1package cmd
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"time"
  7
  8	gansi "github.com/charmbracelet/glamour/ansi"
  9	"github.com/charmbracelet/soft-serve/git"
 10	"github.com/charmbracelet/soft-serve/server/ui/common"
 11	"github.com/charmbracelet/soft-serve/server/ui/styles"
 12	"github.com/muesli/termenv"
 13	"github.com/spf13/cobra"
 14)
 15
 16// commitCommand returns a command that prints the contents of a commit.
 17func commitCommand() *cobra.Command {
 18	var color bool
 19	var patchOnly bool
 20
 21	cmd := &cobra.Command{
 22		Use:               "commit SHA",
 23		Short:             "Print out the contents of a diff",
 24		Args:              cobra.ExactArgs(2),
 25		PersistentPreRunE: checkIfReadable,
 26		RunE: func(cmd *cobra.Command, args []string) error {
 27			cfg, _ := fromContext(cmd)
 28			repoName := args[0]
 29			commitSHA := args[1]
 30
 31			rr, err := cfg.Backend.Repository(repoName)
 32			if err != nil {
 33				return err
 34			}
 35
 36			r, err := rr.Open()
 37			if err != nil {
 38				return err
 39			}
 40
 41			raw_commit, err := r.CommitByRevision(commitSHA)
 42
 43			commit := &git.Commit{
 44				Commit: raw_commit,
 45				Hash:   git.Hash(commitSHA),
 46			}
 47
 48			patch, err := r.Patch(commit)
 49			if err != nil {
 50				return err
 51			}
 52
 53			diff, err := r.Diff(commit)
 54			if err != nil {
 55				return err
 56			}
 57
 58			commonStyle := styles.DefaultStyles()
 59			style := commonStyle.Log
 60
 61			s := strings.Builder{}
 62			commitLine := "commit " + commitSHA
 63			authorLine := "Author: " + commit.Author.Name
 64			dateLine := "Date:   " + commit.Committer.When.Format(time.UnixDate)
 65			msgLine := strings.ReplaceAll(commit.Message, "\r\n", "\n")
 66			statsLine := renderStats(diff, commonStyle, color)
 67			diffLine := renderDiff(patch, color)
 68
 69			if patchOnly {
 70				cmd.Println(
 71					diffLine,
 72				)
 73				return nil
 74			}
 75
 76			if color {
 77				s.WriteString(fmt.Sprintf("%s\n%s\n%s\n%s\n",
 78					style.CommitHash.Render(commitLine),
 79					style.CommitAuthor.Render(authorLine),
 80					style.CommitDate.Render(dateLine),
 81					style.CommitBody.Render(msgLine),
 82				))
 83			} else {
 84				s.WriteString(fmt.Sprintf("%s\n%s\n%s\n%s\n",
 85					commitLine,
 86					authorLine,
 87					dateLine,
 88					msgLine,
 89				))
 90			}
 91
 92			s.WriteString(fmt.Sprintf("\n%s\n%s",
 93				statsLine,
 94				diffLine,
 95			))
 96
 97			cmd.Println(
 98				s.String(),
 99			)
100
101			return nil
102		},
103	}
104
105	cmd.Flags().BoolVarP(&color, "color", "c", false, "Colorize output")
106	cmd.Flags().BoolVarP(&patchOnly, "patch", "p", false, "Output patch only")
107
108	return cmd
109}
110
111func renderCtx() gansi.RenderContext {
112	return gansi.NewRenderContext(gansi.Options{
113		ColorProfile: termenv.TrueColor,
114		Styles:       common.StyleConfig(),
115	})
116}
117
118func renderDiff(patch string, color bool) string {
119
120	c := string(patch)
121
122	if color {
123		var s strings.Builder
124		var pr strings.Builder
125
126		diffChroma := &gansi.CodeBlockElement{
127			Code:     patch,
128			Language: "diff",
129		}
130
131		err := diffChroma.Render(&pr, renderCtx())
132
133		if err != nil {
134			s.WriteString(fmt.Sprintf("\n%s", err.Error()))
135		} else {
136			s.WriteString(fmt.Sprintf("\n%s", pr.String()))
137		}
138
139		c = s.String()
140	}
141
142	return c
143}
144
145func renderStats(diff *git.Diff, commonStyle *styles.Styles, color bool) string {
146	style := commonStyle.Log
147	c := diff.Stats().String()
148
149	if color {
150		s := strings.Split(c, "\n")
151
152		for i, line := range s {
153			ch := strings.Split(line, "|")
154			if len(ch) > 1 {
155				adddel := ch[len(ch)-1]
156				adddel = strings.ReplaceAll(adddel, "+", style.CommitStatsAdd.Render("+"))
157				adddel = strings.ReplaceAll(adddel, "-", style.CommitStatsDel.Render("-"))
158				s[i] = strings.Join(ch[:len(ch)-1], "|") + "|" + adddel
159			}
160		}
161
162		return strings.Join(s, "\n")
163	}
164
165	return c
166}