cli/command/pr_diff.go
2020-05-19 14:08:20 -05:00

138 lines
2.9 KiB
Go

package command
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
"github.com/cli/cli/api"
"github.com/cli/cli/utils"
"github.com/spf13/cobra"
)
var prDiffCmd = &cobra.Command{
Use: "diff {<number> | <url>}",
Short: "View a pull request's changes.",
RunE: prDiff,
}
func init() {
prDiffCmd.Flags().StringP("color", "c", "auto", "Whether or not to output color: {always|never|auto}")
prCmd.AddCommand(prDiffCmd)
}
func prDiff(cmd *cobra.Command, args []string) error {
color, err := cmd.Flags().GetString("color")
if err != nil {
return err
}
if !validColorFlag(color) {
return fmt.Errorf("did not understand color: %q. Expected one of always, never, or auto", color)
}
ctx := contextForCommand(cmd)
apiClient, err := apiClientForContext(ctx)
if err != nil {
return err
}
baseRepo, err := determineBaseRepo(apiClient, cmd, ctx)
if err != nil {
return fmt.Errorf("could not determine base repo: %w", err)
}
// begin pr resolution boilerplate
var prNum int
branchWithOwner := ""
if len(args) == 0 {
prNum, branchWithOwner, err = prSelectorForCurrentBranch(ctx, baseRepo)
if err != nil {
return fmt.Errorf("could not query for pull request for current branch: %w", err)
}
} else {
prArg, repo := prFromURL(args[0])
if repo != nil {
baseRepo = repo
} else {
prArg = strings.TrimPrefix(args[0], "#")
}
prNum, err = strconv.Atoi(prArg)
if err != nil {
return errors.New("could not parse pull request argument")
}
}
if prNum < 1 {
pr, err := api.PullRequestForBranch(apiClient, baseRepo, "", branchWithOwner)
if err != nil {
return fmt.Errorf("could not find pull request: %w", err)
}
prNum = pr.Number
}
// end pr resolution boilerplate
diff, err := apiClient.PullRequestDiff(baseRepo, prNum)
if err != nil {
return fmt.Errorf("could not find pull request diff: %w", err)
}
out := cmd.OutOrStdout()
if color == "auto" {
color = "never"
isTTY := false
if outFile, isFile := out.(*os.File); isFile {
isTTY = utils.IsTerminal(outFile)
if isTTY {
color = "always"
}
}
}
if color == "never" {
fmt.Fprint(out, diff)
return nil
}
out = colorableOut(cmd)
for _, diffLine := range strings.Split(diff, "\n") {
output := diffLine
switch {
case isHeaderLine(diffLine):
output = utils.Bold(diffLine)
case isAdditionLine(diffLine):
output = utils.Green(diffLine)
case isRemovalLine(diffLine):
output = utils.Red(diffLine)
}
fmt.Fprintln(out, output)
}
return nil
}
func isHeaderLine(dl string) bool {
prefixes := []string{"+++", "---", "diff", "index"}
for _, p := range prefixes {
if strings.HasPrefix(dl, p) {
return true
}
}
return false
}
func isAdditionLine(dl string) bool {
return strings.HasPrefix(dl, "+")
}
func isRemovalLine(dl string) bool {
return strings.HasPrefix(dl, "-")
}
func validColorFlag(c string) bool {
return c == "auto" || c == "always" || c == "never"
}