1package cmd
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"time"
  7
  8	gansi "github.com/charmbracelet/glamour/ansi"
  9	"github.com/charmbracelet/lipgloss"
 10	"github.com/charmbracelet/soft-serve/git"
 11	"github.com/charmbracelet/soft-serve/pkg/backend"
 12	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 13	"github.com/charmbracelet/soft-serve/pkg/ui/styles"
 14	"github.com/spf13/cobra"
 15)
 16
 17// commitCommand returns a command that prints the contents of a commit.
 18func commitCommand(renderer *lipgloss.Renderer) *cobra.Command {
 19	var color bool
 20	var patchOnly bool
 21
 22	cmd := &cobra.Command{
 23		Use:               "commit SHA",
 24		Short:             "Print out the contents of a diff",
 25		Args:              cobra.ExactArgs(2),
 26		PersistentPreRunE: checkIfReadable,
 27		RunE: func(cmd *cobra.Command, args []string) error {
 28			ctx := cmd.Context()
 29			be := backend.FromContext(ctx)
 30			repoName := args[0]
 31			commitSHA := args[1]
 32
 33			rr, err := be.Repository(ctx, repoName)
 34			if err != nil {
 35				return err
 36			}
 37
 38			r, err := rr.Open()
 39			if err != nil {
 40				return err
 41			}
 42
 43			commit, err := r.CommitByRevision(commitSHA)
 44			if err != nil {
 45				return err
 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(renderer)
 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.UTC().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 renderDiff(patch string, color bool) string {
112	c := patch
113
114	if color {
115		var s strings.Builder
116		var pr strings.Builder
117
118		diffChroma := &gansi.CodeBlockElement{
119			Code:     patch,
120			Language: "diff",
121		}
122
123		err := diffChroma.Render(&pr, common.StyleRenderer())
124
125		if err != nil {
126			s.WriteString(fmt.Sprintf("\n%s", err.Error()))
127		} else {
128			s.WriteString(fmt.Sprintf("\n%s", pr.String()))
129		}
130
131		c = s.String()
132	}
133
134	return c
135}
136
137func renderStats(diff *git.Diff, commonStyle *styles.Styles, color bool) string {
138	style := commonStyle.Log
139	c := diff.Stats().String()
140
141	if color {
142		s := strings.Split(c, "\n")
143
144		for i, line := range s {
145			ch := strings.Split(line, "|")
146			if len(ch) > 1 {
147				adddel := ch[len(ch)-1]
148				adddel = strings.ReplaceAll(adddel, "+", style.CommitStatsAdd.Render("+"))
149				adddel = strings.ReplaceAll(adddel, "-", style.CommitStatsDel.Render("-"))
150				s[i] = strings.Join(ch[:len(ch)-1], "|") + "|" + adddel
151			}
152		}
153
154		return strings.Join(s, "\n")
155	}
156
157	return c
158}