From acf453b0e931cb0b7f6aefdf0ac6383a0a11ec4a Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Fri, 18 Dec 2020 17:12:17 +0100 Subject: [PATCH 1/8] add interactive `tea pr review` it's amazingly simple --- cmd/pulls.go | 1 + cmd/pulls/review.go | 40 ++++++++++ modules/interact/pull_review.go | 80 ++++++++++++++++++++ modules/task/pull_review.go | 128 ++++++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 cmd/pulls/review.go create mode 100644 modules/interact/pull_review.go create mode 100644 modules/task/pull_review.go diff --git a/cmd/pulls.go b/cmd/pulls.go index 6b1ab61..760a1a8 100644 --- a/cmd/pulls.go +++ b/cmd/pulls.go @@ -33,6 +33,7 @@ var CmdPulls = cli.Command{ &pulls.CmdPullsCreate, &pulls.CmdPullsClose, &pulls.CmdPullsReopen, + &pulls.CmdPullsReview, }, } diff --git a/cmd/pulls/review.go b/cmd/pulls/review.go new file mode 100644 index 0000000..ac402d1 --- /dev/null +++ b/cmd/pulls/review.go @@ -0,0 +1,40 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package pulls + +import ( + "fmt" + + "code.gitea.io/tea/cmd/flags" + "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/interact" + "code.gitea.io/tea/modules/utils" + + "github.com/urfave/cli/v2" +) + +// CmdPullsReview starts an interactive review session +var CmdPullsReview = cli.Command{ + Name: "review", + Usage: "Interactively review a pull request", + Description: "Interactively review a pull request", + ArgsUsage: "", + Action: func(cmd *cli.Context) error { + ctx := context.InitCommand(cmd) + ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) + + if ctx.Args().Len() != 1 { + return fmt.Errorf("Must specify a PR index") + } + + idx, err := utils.ArgToIndex(ctx.Args().First()) + if err != nil { + return err + } + + return interact.ReviewPull(ctx, idx) + }, + Flags: flags.AllDefaultFlags, +} diff --git a/modules/interact/pull_review.go b/modules/interact/pull_review.go new file mode 100644 index 0000000..351a5f4 --- /dev/null +++ b/modules/interact/pull_review.go @@ -0,0 +1,80 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package interact + +import ( + "fmt" + "os" + + "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/task" + + "code.gitea.io/sdk/gitea" + "github.com/AlecAivazis/survey/v2" +) + +var reviewStates = map[string]gitea.ReviewStateType{ + "approve": gitea.ReviewStateApproved, + "comment": gitea.ReviewStateComment, + "request changes": gitea.ReviewStateRequestChanges, +} +var reviewStateOptions = []string{"comment", "request changes", "approve"} + +// ReviewPull interactively reviews a PR +func ReviewPull(ctx *context.TeaContext, idx int64) error { + var state gitea.ReviewStateType + var comment string + var codeComments []gitea.CreatePullReviewComment + var err error + + // codeComments + var reviewDiff bool + promptDiff := &survey.Confirm{Message: "Review & comment the diff?", Default: false} + if err = survey.AskOne(promptDiff, &reviewDiff); err != nil { + return err + } + if reviewDiff { + if codeComments, err = DoDiffReview(ctx, idx); err != nil { + fmt.Printf("Error during diff review: %s\n", err) + } + fmt.Printf("Found %d code comments in your review\n", len(codeComments)) + } + + // state + var stateString string + promptState := &survey.Select{Message: "Your assessment:", Options: reviewStateOptions, VimMode: true} + if err = survey.AskOne(promptState, &stateString); err != nil { + return err + } + state = reviewStates[stateString] + + // comment + var promptOpts survey.AskOpt + if state == gitea.ReviewStateComment || state == gitea.ReviewStateRequestChanges { + promptOpts = survey.WithValidator(survey.Required) + } + err = survey.AskOne(&survey.Multiline{Message: "Concluding comment:"}, &comment, promptOpts) + if err != nil { + return err + } + + return task.CreatePullReview(ctx, idx, state, comment, codeComments) +} + +// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $EDITOR to comment on diff, +// (3) parses resulting file into code comments. +func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) { + tmpFile, err := task.SavePullDiff(ctx, idx) + if err != nil { + return nil, err + } + defer os.Remove(tmpFile) + + if err = task.OpenFileInEditor(tmpFile); err != nil { + return nil, err + } + + return task.ParseDiffComments(tmpFile) +} diff --git a/modules/task/pull_review.go b/modules/task/pull_review.go new file mode 100644 index 0000000..1cef147 --- /dev/null +++ b/modules/task/pull_review.go @@ -0,0 +1,128 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package task + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" + + "code.gitea.io/tea/modules/context" + + "code.gitea.io/sdk/gitea" + unidiff "gitea.com/noerw/unidiff-comments" +) + +var diffReviewHelp = `# This is the current diff of PR #%d on %s. +# To add code comments, just insert a line inside the diff with your comment, +# prefixed with '# '. For example: +# +# - foo: string, +# - bar: string, +# + foo: int, +# # This is a code comment +# + bar: int, + +` + +// CreatePullReview submits a review for a PR +func CreatePullReview(ctx *context.TeaContext, idx int64, status gitea.ReviewStateType, comment string, codeComments []gitea.CreatePullReviewComment) error { + c := ctx.Login.Client() + + review, _, err := c.CreatePullReview(ctx.Owner, ctx.Repo, idx, gitea.CreatePullReviewOptions{ + State: status, + Body: comment, + Comments: codeComments, + }) + if err != nil { + return err + } + + fmt.Println(review.HTMLURL) + return nil +} + +// SavePullDiff fetches the diff of a pull request and stores it as a temporary file. +// The path to the file is returned. +func SavePullDiff(ctx *context.TeaContext, idx int64) (string, error) { + diff, _, err := ctx.Login.Client().GetPullRequestDiff(ctx.Owner, ctx.Repo, idx) + if err != nil { + return "", err + } + writer, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("pull-%d-review-*.diff", idx)) + if err != nil { + return "", err + } + defer writer.Close() + + // add a help header before the actual diff + if _, err = fmt.Fprintf(writer, diffReviewHelp, idx, ctx.RepoSlug); err != nil { + return "", err + } + + if _, err = writer.Write(diff); err != nil { + return "", err + } + return writer.Name(), nil +} + +// ParseDiffComments reads a diff, extracts comments from it & returns them in a gitea compatible struct +func ParseDiffComments(diffFile string) ([]gitea.CreatePullReviewComment, error) { + reader, err := os.Open(diffFile) + if err != nil { + return nil, fmt.Errorf("couldn't load diff: %s", err) + } + defer reader.Close() + + changeset, err := unidiff.ReadChangeset(reader) + if err != nil { + return nil, fmt.Errorf("couldn't parse patch: %s", err) + } + + var comments []gitea.CreatePullReviewComment + for _, file := range changeset.Diffs { + for _, c := range file.LineComments { + comment := gitea.CreatePullReviewComment{ + Body: c.Text, + Path: c.Anchor.Path, + } + comment.Path = strings.TrimPrefix(comment.Path, "a/") + comment.Path = strings.TrimPrefix(comment.Path, "b/") + switch c.Anchor.LineType { + case "ADDED": + comment.NewLineNum = c.Anchor.Line + case "REMOVED", "CONTEXT": + comment.OldLineNum = c.Anchor.Line + } + comments = append(comments, comment) + } + } + + return comments, nil +} + +// OpenFileInEditor opens filename in a text editor, and blocks until the editor terminates. +func OpenFileInEditor(filename string) error { + editor := os.Getenv("EDITOR") + if editor == "" { + fmt.Println("No $EDITOR env is set, defaulting to vim") + editor = "vim" + } + + // Get the full executable path for the editor. + executable, err := exec.LookPath(editor) + if err != nil { + return err + } + + cmd := exec.Command(executable, filename) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} -- 2.40.1 From 0b31a776b8af1450c1c8017390373cb730bab0d7 Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Fri, 18 Dec 2020 17:12:46 +0100 Subject: [PATCH 2/8] vendor gitea.com/noerw/unidiff-comments --- go.mod | 3 + go.sum | 6 + .../noerw/unidiff-comments/changeset.go | 47 ++ .../unidiff-comments/changeset_reader.go | 455 ++++++++++++++++++ .../unidiff-comments/changeset_writer.go | 54 +++ .../unidiff-comments/changeset_writer_tpl.go | 81 ++++ .../noerw/unidiff-comments/comment.go | 121 +++++ .../gitea.com/noerw/unidiff-comments/diff.go | 61 +++ .../gitea.com/noerw/unidiff-comments/hunk.go | 10 + .../gitea.com/noerw/unidiff-comments/line.go | 29 ++ .../noerw/unidiff-comments/segment.go | 39 ++ vendor/github.com/seletskiy/tplutil/LICENSE | 201 ++++++++ vendor/github.com/seletskiy/tplutil/README.md | 1 + vendor/github.com/seletskiy/tplutil/util.go | 113 +++++ vendor/modules.txt | 4 + 15 files changed, 1225 insertions(+) create mode 100644 vendor/gitea.com/noerw/unidiff-comments/changeset.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/comment.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/diff.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/hunk.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/line.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/segment.go create mode 100644 vendor/github.com/seletskiy/tplutil/LICENSE create mode 100644 vendor/github.com/seletskiy/tplutil/README.md create mode 100644 vendor/github.com/seletskiy/tplutil/util.go diff --git a/go.mod b/go.mod index 1b5f86f..e1fc7b5 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( code.gitea.io/gitea-vet v0.2.1 code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e + gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 github.com/AlecAivazis/survey/v2 v2.2.2 github.com/Microsoft/go-winio v0.4.15 // indirect github.com/adrg/xdg v0.2.2 @@ -23,6 +24,8 @@ require ( github.com/muesli/termenv v0.7.4 github.com/olekukonko/tablewriter v0.0.4 github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/seletskiy/godiff v0.0.0-20160229093234-ad7fb8d1f905 // indirect + github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.6.1 github.com/urfave/cli/v2 v2.3.0 diff --git a/go.sum b/go.sum index d958507..5db7cd6 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e h1:oJOoT5TGbSYRNGUhEiiEz3MqFjU6wELN0/liCZ3RmVg= code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= +gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 h1:1nyuvVnt3q2rXITHxkpXMfOaOQrbK5Bi78m+n2vVXYs= +gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9/go.mod h1:1YlCscxedF3xz1q1LjYAi0hlWXbwQxHFaFatUtlJFQ4= github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY= github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -146,6 +148,10 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= +github.com/seletskiy/godiff v0.0.0-20160229093234-ad7fb8d1f905 h1:R9HQcQ3PgW9jmzq1M96mTkRI6QLRKKsB1XU7YwZScus= +github.com/seletskiy/godiff v0.0.0-20160229093234-ad7fb8d1f905/go.mod h1:5RG/Xp1wt6OPVCa/EWs6Hnc7pllTFwE7ZVdkb5O5dso= +github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA= +github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset.go b/vendor/gitea.com/noerw/unidiff-comments/changeset.go new file mode 100644 index 0000000..7e9e66c --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/changeset.go @@ -0,0 +1,47 @@ +package godiff + +type Changeset struct { + FromHash string + ToHash string + Path string + Whitespace string + Diffs []*Diff +} + +func (r Changeset) ForEachComment(callback func(*Diff, *Comment, *Comment)) { + for _, diff := range r.Diffs { + stack := make([]*Comment, 0) + parents := make(map[*Comment]*Comment) + stack = append(stack, diff.FileComments...) + stack = append(stack, diff.LineComments...) + pos := 0 + + for pos < len(stack) { + comment := stack[pos] + + if comment.Comments != nil { + stack = append(stack, comment.Comments...) + for _, c := range comment.Comments { + parents[c] = comment + } + } + + callback(diff, comment, parents[comment]) + + pos++ + } + } +} + +func (r Changeset) ForEachLine( + callback func(*Diff, *Hunk, *Segment, *Line) error, +) error { + for _, diff := range r.Diffs { + err := diff.ForEachLine(callback) + if err != nil { + return err + } + } + + return nil +} diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go b/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go new file mode 100644 index 0000000..c6a4cb6 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go @@ -0,0 +1,455 @@ +package godiff + +import ( + "bufio" + "fmt" + "io" + "regexp" + "strconv" + "strings" + "time" +) + +const ( + stateStartOfDiff = "stateStartOfDiff" + stateDiffHeader = "stateDiffHeader" + stateHunkHeader = "stateHunkHeader" + stateHunkBody = "stateHunkBody" + stateComment = "stateComment" + stateCommentDelim = "stateCommentDelim" + stateCommentHeader = "stateCommentHeader" + stateDiffComment = "stateDiffComment" + stateDiffCommentDelim = "stateDiffCommentDelim" + stateDiffCommentHeader = "stateDiffCommentHeader" +) + +var ( + reDiffHeader = regexp.MustCompile( + `^--- |^\+\+\+ `) + + reGitDiffHeader = regexp.MustCompile( + `^diff |^index `) + + reFromFile = regexp.MustCompile( + `^--- (\S+)(\s+(.*))`) + + reToFile = regexp.MustCompile( + `^\+\+\+ (\S+)(\s+(.*))`) + + reHunk = regexp.MustCompile( + `^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@`) + + reSegmentContext = regexp.MustCompile( + `^ `) + + reSegmentAdded = regexp.MustCompile( + `^\+`) + + reSegmentRemoved = regexp.MustCompile( + `^-`) + + reCommentDelim = regexp.MustCompile( + `^#\s+---`) + + reCommentHeader = regexp.MustCompile( + `^#\s+\[(\d+)@(\d+)\]\s+\|([^|]+)\|(.*)`) + + reCommentText = regexp.MustCompile( + `^#(\s*)(.*)\s*`) + + reIndent = regexp.MustCompile( + `^#(\s+)`) + + reEmptyLine = regexp.MustCompile( + `^\n$`) + + reIgnoredLine = regexp.MustCompile( + `^` + ignorePrefix) +) + +type parser struct { + state string + changeset Changeset + diff *Diff + hunk *Hunk + segment *Segment + comment *Comment + line *Line + lineNumber int + + segmentType string + commentsList []*Comment +} + +type Error struct { + LineNumber int + Message string +} + +func (err Error) Error() string { + return fmt.Sprintf("line %d: %s", err.LineNumber, err.Message) +} + +func ReadChangeset(r io.Reader) (Changeset, error) { + buffer := bufio.NewReader(r) + + current := parser{} + current.state = stateStartOfDiff + + for { + current.lineNumber++ + + line, err := buffer.ReadString('\n') + if err != nil { + break + } + + if reIgnoredLine.MatchString(line) { + continue + } + + err = current.switchState(line) + if err != nil { + return current.changeset, err + } + + err = current.createNodes(line) + if err != nil { + return current.changeset, err + } + + err = current.locateNodes(line) + if err != nil { + return current.changeset, err + } + + err = current.parseLine(line) + if err != nil { + return current.changeset, err + } + } + + for _, comment := range current.commentsList { + comment.Text = strings.TrimSpace(comment.Text) + } + + return current.changeset, nil +} + +func (current *parser) switchState(line string) error { + inComment := false + + switch current.state { + case stateStartOfDiff: + switch { + case reDiffHeader.MatchString(line), reGitDiffHeader.MatchString(line): + current.state = stateDiffHeader + case reCommentText.MatchString(line): + inComment = true + case reEmptyLine.MatchString(line): + // body intentionally left empty + default: + return Error{ + current.lineNumber, + "expected diff header, but none found", + } + } + case stateDiffHeader: + switch { + case reHunk.MatchString(line): + current.state = stateHunkHeader + } + case stateDiffComment, stateDiffCommentDelim, stateDiffCommentHeader: + switch { + case reDiffHeader.MatchString(line), reGitDiffHeader.MatchString(line): + current.state = stateDiffHeader + case reCommentText.MatchString(line): + inComment = true + case reEmptyLine.MatchString(line): + current.state = stateStartOfDiff + } + case stateHunkHeader: + current.state = stateHunkBody + fallthrough + case stateHunkBody, stateComment, stateCommentDelim, stateCommentHeader: + switch { + case reSegmentContext.MatchString(line): + current.state = stateHunkBody + current.segmentType = SegmentTypeContext + case reSegmentRemoved.MatchString(line): + current.state = stateHunkBody + current.segmentType = SegmentTypeRemoved + case reSegmentAdded.MatchString(line): + current.state = stateHunkBody + current.segmentType = SegmentTypeAdded + case reHunk.MatchString(line): + current.state = stateHunkHeader + case reCommentText.MatchString(line): + inComment = true + case reGitDiffHeader.MatchString(line): + current.state = stateDiffHeader + current.diff = nil + current.hunk = nil + current.segment = nil + current.line = nil + case reEmptyLine.MatchString(line): + current.state = stateStartOfDiff + current.diff = nil + current.hunk = nil + current.segment = nil + current.line = nil + } + } + + if !inComment { + current.comment = nil + } else { + switch current.state { + case stateStartOfDiff: + fallthrough + case stateDiffComment, stateDiffCommentDelim, stateDiffCommentHeader: + switch { + case reCommentDelim.MatchString(line): + current.state = stateDiffCommentDelim + case reCommentHeader.MatchString(line): + current.state = stateDiffCommentHeader + case reCommentText.MatchString(line): + current.state = stateDiffComment + } + case stateHunkBody: + fallthrough + case stateComment, stateCommentDelim, stateCommentHeader: + switch { + case reCommentDelim.MatchString(line): + current.state = stateCommentDelim + case reCommentHeader.MatchString(line): + current.state = stateCommentHeader + case reCommentText.MatchString(line): + current.state = stateComment + } + } + } + + // Uncomment for debug state switching + // fmt.Printf("%20s : %#v\n", current.state, line) + + return nil +} + +func (current *parser) createNodes(line string) error { + switch current.state { + case stateDiffComment: + if current.comment != nil { + break + } + fallthrough + case stateDiffCommentDelim, stateDiffCommentHeader: + current.comment = &Comment{} + fallthrough + case stateDiffHeader: + if current.diff == nil { + current.diff = &Diff{} + current.changeset.Diffs = append(current.changeset.Diffs, + current.diff) + } + case stateHunkHeader: + current.hunk = &Hunk{} + current.segment = &Segment{} + case stateCommentDelim, stateCommentHeader: + current.comment = &Comment{} + case stateComment: + if current.comment == nil { + current.comment = &Comment{} + } + case stateHunkBody: + if current.segment.Type != current.segmentType { + current.segment = &Segment{Type: current.segmentType} + current.hunk.Segments = append(current.hunk.Segments, + current.segment) + } + + current.line = &Line{} + current.segment.Lines = append(current.segment.Lines, current.line) + } + + return nil +} + +func (current *parser) locateNodes(line string) error { + switch current.state { + case stateComment, stateDiffComment: + current.locateComment(line) + case stateHunkBody: + current.locateLine(line) + } + + return nil +} + +func (current *parser) locateComment(line string) error { + if current.comment.Parented || strings.TrimSpace(line) == "#" { + return nil + } + + current.commentsList = append(current.commentsList, current.comment) + current.comment.Parented = true + + if current.hunk != nil { + current.comment.Anchor.LineType = current.segment.Type + current.comment.Anchor.Line = current.segment.GetLineNum(current.line) + current.comment.Anchor.Path = current.diff.Destination.ToString + current.comment.Anchor.SrcPath = current.diff.Source.ToString + } + + current.comment.Indent = getIndentSize(line) + + parent := current.findParentComment(current.comment) + if parent != nil { + parent.Comments = append(parent.Comments, current.comment) + } else { + if current.line != nil { + current.diff.LineComments = append(current.diff.LineComments, + current.comment) + current.line.Comments = append(current.line.Comments, + current.comment) + } else { + current.diff.FileComments = append(current.diff.FileComments, + current.comment) + } + } + + return nil +} + +func (current *parser) locateLine(line string) error { + sourceOffset := current.hunk.SourceLine - 1 + destinationOffset := current.hunk.DestinationLine - 1 + if len(current.hunk.Segments) > 1 { + prevSegment := current.hunk.Segments[len(current.hunk.Segments)-2] + lastLine := prevSegment.Lines[len(prevSegment.Lines)-1] + sourceOffset = lastLine.Source + destinationOffset = lastLine.Destination + } + hunkLength := int64(len(current.segment.Lines)) + switch current.segment.Type { + case SegmentTypeContext: + current.line.Source = sourceOffset + hunkLength + current.line.Destination = destinationOffset + hunkLength + case SegmentTypeAdded: + current.line.Source = sourceOffset + current.line.Destination = destinationOffset + hunkLength + case SegmentTypeRemoved: + current.line.Source = sourceOffset + hunkLength + current.line.Destination = destinationOffset + } + + return nil +} + +func (current *parser) parseLine(line string) error { + switch current.state { + case stateDiffHeader: + current.parseDiffHeader(line) + case stateHunkHeader: + current.parseHunkHeader(line) + case stateHunkBody: + current.parseHunkBody(line) + case stateComment, stateDiffComment: + current.parseComment(line) + case stateCommentHeader, stateDiffCommentHeader: + current.parseCommentHeader(line) + } + + return nil +} + +func (current *parser) parseDiffHeader(line string) error { + switch { + case reFromFile.MatchString(line): + matches := reFromFile.FindStringSubmatch(line) + current.changeset.Path = matches[1] + current.diff.Source.ToString = matches[1] + current.changeset.FromHash = matches[3] + current.diff.Attributes.FromHash = []string{matches[3]} + case reToFile.MatchString(line): + matches := reToFile.FindStringSubmatch(line) + current.diff.Destination.ToString = matches[1] + current.changeset.ToHash = matches[3] + current.diff.Attributes.ToHash = []string{matches[3]} + default: + return Error{ + current.lineNumber, + "expected diff header, but not found", + } + } + return nil +} + +func (current *parser) parseHunkHeader(line string) error { + matches := reHunk.FindStringSubmatch(line) + current.hunk.SourceLine, _ = strconv.ParseInt(matches[1], 10, 64) + current.hunk.SourceSpan, _ = strconv.ParseInt(matches[3], 10, 64) + current.hunk.DestinationLine, _ = strconv.ParseInt(matches[4], 10, 64) + current.hunk.DestinationSpan, _ = strconv.ParseInt(matches[6], 10, 64) + current.diff.Hunks = append(current.diff.Hunks, current.hunk) + + return nil +} + +func (current *parser) parseHunkBody(line string) error { + current.line.Line = line[1 : len(line)-1] + return nil +} + +func (current *parser) parseCommentHeader(line string) error { + matches := reCommentHeader.FindStringSubmatch(line) + current.comment.Author.DisplayName = strings.TrimSpace(matches[3]) + current.comment.Id, _ = strconv.ParseInt(matches[1], 10, 64) + updatedDate, _ := time.ParseInLocation(time.ANSIC, + strings.TrimSpace(matches[4]), + time.Local) + current.comment.UpdatedDate = UnixTimestamp(updatedDate.Unix() * 1000) + + version, _ := strconv.ParseInt(matches[2], 10, 64) + current.comment.Version = int(version) + + return nil +} + +func (current *parser) parseComment(line string) error { + matches := reCommentText.FindStringSubmatch(line) + if len(matches[1]) < current.comment.Indent { + return Error{ + LineNumber: current.lineNumber, + Message: fmt.Sprintf( + "unexpected indent, should be at least: %d", + current.comment.Indent, + ), + } + } + + indentedLine := matches[1][current.comment.Indent:] + matches[2] + current.comment.Text += "\n" + indentedLine + + return nil +} + +func (current *parser) findParentComment(comment *Comment) *Comment { + for i := len(current.commentsList) - 1; i >= 0; i-- { + c := current.commentsList[i] + if comment.Indent > c.Indent { + return c + } + } + + return nil +} + +func getIndentSize(line string) int { + matches := reIndent.FindStringSubmatch(line) + if len(matches) == 0 { + return 0 + } + + return len(matches[1]) +} diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go b/vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go new file mode 100644 index 0000000..c47dd55 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go @@ -0,0 +1,54 @@ +package godiff + +import ( + "io" + "regexp" + "strings" + "text/template" + + "github.com/seletskiy/tplutil" +) + +var changesetTpl = template.New("changeset") + +func init() { + commentsTpl := template.New("comment") + + reBeginningOfLine := regexp.MustCompile(`(?m)^`) + reNewLine := regexp.MustCompile(`^|\n`) + reDanglingSpace := regexp.MustCompile(`(?m)\s+$`) + + funcs := template.FuncMap{ + "indent": func(input string) string { + return reBeginningOfLine.ReplaceAllString(input, " ") + }, + "writeComments": func(input CommentsTree) string { + res, _ := tplutil.ExecuteToString(commentsTpl, input) + return res + }, + "writeNote": func(input string) string { + return Note(input) + }, + "trimWhitespace": func(input string) string { + return strings.TrimSpace(input) + }, + "comment": func(input string) string { + //log.Printf("%#v", input) + return reDanglingSpace.ReplaceAllString( + reNewLine.ReplaceAllString(input, `$0# `), + ``, + ) + }, + } + + template.Must( + commentsTpl.Funcs(funcs).Funcs(tplutil.Last).Parse( + tplutil.Strip(commentsTplText))) + template.Must( + changesetTpl.Funcs(funcs).Funcs(tplutil.Last).Parse( + tplutil.Strip(changesetTplText))) +} + +func WriteChangeset(changeset Changeset, to io.Writer) error { + return changesetTpl.Execute(to, changeset) +} diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go b/vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go new file mode 100644 index 0000000..29dda55 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go @@ -0,0 +1,81 @@ +package godiff + +const commentsTplText = ` +{{range $i, $_ := .}} + + {{"\n\n"}} + + {{if gt .Id 0}} + [{{.Id}}@{{.Version}}] | {{.Author.DisplayName}} | {{.UpdatedDate}} + {{"\n\n"}} + {{end}} + + {{.Text | trimWhitespace}} + + {{"\n\n---"}} + + {{writeComments .Comments | indent}} +{{end}}` + +const changesetTplText = ` +{{range $i, $d := .Diffs}} + {{if .Note}} + {{writeNote .Note}} + {{"\n"}} + {{end}} + + {{if .FileComments}} + {{"---" | comment}} + {{"\n"}} + {{writeComments .FileComments | comment}} + {{"\n"}} + {{end}} + + {{if .Hunks}} + ---{{" "}} + {{if .Source.ToString}} + {{.Source.ToString}} + {{else}} + /dev/null + {{end}} + {{"\t"}} + {{$.FromHash}} + {{"\n"}} + + +++{{" "}} + {{if .Destination.ToString}} + {{.Destination.ToString}} + {{else}} + /dev/null + {{end}} + {{"\t"}} + {{$.ToHash}} + {{"\n"}} + + {{range .Hunks}} + @@ -{{.SourceLine}}{{if ne .SourceSpan 0}},{{.SourceSpan}}{{end}} +{{.DestinationLine}}{{if ne .DestinationSpan 0}},{{.DestinationSpan}}{{end}} @@ + {{"\n"}} + + {{range .Segments}} + {{$segment := .}} + {{range .Lines}} + {{$segment.TextPrefix}} + {{.Line}} + + {{"\n"}} + {{if .Comments}} + {{"---" | comment}} + {{"\n"}} + {{writeComments .Comments | comment}} + {{"\n"}} + {{end}} + {{end}} + + {{end}} + {{end}} + {{end}} + + {{if not (last $i $.Diffs)}} + {{"\n\n"}} + {{end}} +{{end}}` diff --git a/vendor/gitea.com/noerw/unidiff-comments/comment.go b/vendor/gitea.com/noerw/unidiff-comments/comment.go new file mode 100644 index 0000000..9b7251e --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/comment.go @@ -0,0 +1,121 @@ +package godiff + +import ( + "regexp" + "time" +) + +type UnixTimestamp int + +func (u UnixTimestamp) String() string { + return time.Unix(int64(u/1000), 0).Format(time.ANSIC) +} + +type Comment struct { + Id int64 + Version int + Text string + CreatedDate UnixTimestamp + UpdatedDate UnixTimestamp + Comments CommentsTree + Author struct { + Name string + EmailAddress string + Id int + DisplayName string + Active bool + Slug string + Type string + } + + Anchor CommentAnchor + + PermittedOperations struct { + Editable bool + Deletable bool + } + + Indent int + Parented bool +} + +type CommentAnchor struct { + FromHash string + ToHash string + Line int64 `json:"line"` + LineType string `json:"lineType"` + Path string `json:"path"` + SrcPath string `json:"srcPath"` + FileType string `json:"fileType"` +} + +type CommentsTree []*Comment + +//const replyIndent = " " + +var begOfLineRe = regexp.MustCompile("(?m)^") + +//func (c Comment) String() string { +// comments, _ := commentTpl.Execute(c) + +// for _, reply := range c.Comments { +// comments += reply.AsReply() +// } + +// return comments +//} + +//func (c Comment) AsReply() string { +// return begOfLineRe.ReplaceAllString( +// commentSpacing+c.String(), +// replyIndent, +// ) +//} + +var reWhiteSpace = regexp.MustCompile(`\s+`) + +func (c Comment) Short(length int) string { + sticked := []rune(reWhiteSpace.ReplaceAllString(c.Text, " ")) + + if len(sticked) > length { + return string(sticked[:length]) + "..." + } else { + return string(sticked) + } +} + +const ignorePrefix = "###" + +var reBeginningOfLine = regexp.MustCompile(`(?m)^`) +var reIgnorePrefixSpace = regexp.MustCompile("(?m)^" + ignorePrefix + " $") + +func Note(String string) string { + return reIgnorePrefixSpace.ReplaceAllString( + reBeginningOfLine.ReplaceAllString(String, ignorePrefix+" "), + ignorePrefix) +} + +//const commentSpacing = "\n\n" +//const commentPrefix = "# " + +//func (comments CommentsTree) String() string { +// res := "" + +// if len(comments) > 0 { +// res = "---" + commentSpacing +// } + +// for i, comment := range comments { +// res += comment.String() +// if i < len(comments)-1 { +// res += commentSpacing +// } +// } + +// if len(comments) > 0 { +// return danglingSpacesRe.ReplaceAllString( +// begOfLineRe.ReplaceAllString(res, "# "), "") +// } else { +// return "" +// } +//} diff --git a/vendor/gitea.com/noerw/unidiff-comments/diff.go b/vendor/gitea.com/noerw/unidiff-comments/diff.go new file mode 100644 index 0000000..339e2eb --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/diff.go @@ -0,0 +1,61 @@ +package godiff + +type Diff struct { + Truncated bool + Source struct { + Parent string + Name string + ToString string + } + Destination struct { + Parent string + Name string + ToString string + } + Hunks []*Hunk + + FileComments CommentsTree + LineComments CommentsTree + + Note string + + // Lists made only for Stash API compatibility. + // TODO: move it to `ash`. + Attributes struct { + FromHash []string + ToHash []string + } +} + +func (d Diff) GetHashFrom() string { + if len(d.Attributes.FromHash) > 0 { + return d.Attributes.FromHash[0] + } else { + return "???" + } +} + +func (d Diff) GetHashTo() string { + if len(d.Attributes.ToHash) > 0 { + return d.Attributes.ToHash[0] + } else { + return "???" + } +} + +func (d Diff) ForEachLine( + callback func(*Diff, *Hunk, *Segment, *Line) error, +) error { + for _, hunk := range d.Hunks { + for _, segment := range hunk.Segments { + for _, line := range segment.Lines { + err := callback(&d, hunk, segment, line) + if err != nil { + return err + } + } + } + } + + return nil +} diff --git a/vendor/gitea.com/noerw/unidiff-comments/hunk.go b/vendor/gitea.com/noerw/unidiff-comments/hunk.go new file mode 100644 index 0000000..8ba514c --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/hunk.go @@ -0,0 +1,10 @@ +package godiff + +type Hunk struct { + SourceLine int64 + SourceSpan int64 + DestinationLine int64 + DestinationSpan int64 + Truncated bool + Segments []*Segment +} diff --git a/vendor/gitea.com/noerw/unidiff-comments/line.go b/vendor/gitea.com/noerw/unidiff-comments/line.go new file mode 100644 index 0000000..a77eae9 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/line.go @@ -0,0 +1,29 @@ +package godiff + +import "regexp" + +type Line struct { + Destination int64 + Source int64 + Line string + Truncated bool + ConflictMarker string + CommentIds []int64 + Comments CommentsTree +} + +var danglingSpacesRe = regexp.MustCompile("(?m) +$") + +//var lineTpl = tplutil.SparseTemplate("line", ` +//{{.Line}} + +//{{if .Comments}} +// {{"\n"}} +// {{.Comments}} +//{{end}} +//`) + +//func (l Line) String() string { +// result, _ := lineTpl.Execute(l) +// return result +//} diff --git a/vendor/gitea.com/noerw/unidiff-comments/segment.go b/vendor/gitea.com/noerw/unidiff-comments/segment.go new file mode 100644 index 0000000..839fe53 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/segment.go @@ -0,0 +1,39 @@ +package godiff + +const ( + SegmentTypeContext = "CONTEXT" + SegmentTypeRemoved = "REMOVED" + SegmentTypeAdded = "ADDED" +) + +type Segment struct { + Type string + Truncated bool + Lines []*Line +} + +func (s Segment) TextPrefix() string { + switch s.Type { + case SegmentTypeAdded: + return "+" + case SegmentTypeRemoved: + return "-" + case SegmentTypeContext: + return " " + default: + return "?" + } +} + +func (s Segment) GetLineNum(l *Line) int64 { + switch s.Type { + case SegmentTypeContext: + fallthrough + case SegmentTypeRemoved: + return l.Source + case SegmentTypeAdded: + return l.Destination + } + + return 0 +} diff --git a/vendor/github.com/seletskiy/tplutil/LICENSE b/vendor/github.com/seletskiy/tplutil/LICENSE new file mode 100644 index 0000000..71df8e6 --- /dev/null +++ b/vendor/github.com/seletskiy/tplutil/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright Stanislav Seletskiy 2014 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/seletskiy/tplutil/README.md b/vendor/github.com/seletskiy/tplutil/README.md new file mode 100644 index 0000000..d820fbf --- /dev/null +++ b/vendor/github.com/seletskiy/tplutil/README.md @@ -0,0 +1 @@ +Please see Godoc at: https://godoc.org/github.com/seletskiy/tplutil diff --git a/vendor/github.com/seletskiy/tplutil/util.go b/vendor/github.com/seletskiy/tplutil/util.go new file mode 100644 index 0000000..e287c1e --- /dev/null +++ b/vendor/github.com/seletskiy/tplutil/util.go @@ -0,0 +1,113 @@ +// Package tplutil provides more convient way to use text/template inside +// the code. +// +// Consider (using text/template): +// +// var myTpl = template.Must(template.New("name").Parse( +// "Some list:\n" + +// "{{range .}}" + +// "# {{.}}\n" + +// "{{end}}")) +// +// `gofmt` will ruin any attempt to format code above. +// +// And with tplutil: +// +// var myTpl = template.Must(template.New("name").Parse(tplutil.Strip(` +// Some list:{{"\n"}} +// +// {{range .}} +// # {{.}}{{"\n"}} +// {{end}} +// `))) +// +// Output will be exactly the same. +// +// Any indenting whitespaces and newlines will be ignored. If must, they +// should be specified by using syntax +// +// `{{" "}}` or `{{"\n"}}`. +// +// It also provide `{{last}}` function to check on last element of pipeline: +// +// var myTpl = template.Must(template.New("asd").Funcs(tplutil.Last).Parse( +// tplutil.Strip(` +// Some list:{{"\n"}} +// +// {{range $i, $_ := .}} +// {{.}} +// {{if not (last $i $)}} +// {{"\n"}}{{/* do not append newline to the last element */}} +// {{end}} +// {{end}} +// `)) +// +package tplutil + +import ( + "bytes" + "fmt" + "io/ioutil" + "path/filepath" + "reflect" + "regexp" + "text/template" +) + +var reInsignificantWhitespace = regexp.MustCompile(`(?m)\n?^\s*`) + +var Last = template.FuncMap{ + "last": func(x int, a interface{}) bool { + return x == reflect.ValueOf(a).Len()-1 + }, +} + +func Strip(text string) string { + return reInsignificantWhitespace.ReplaceAllString(text, ``) +} + +// ExecuteToString applies a parsed template to specified data object and +// returns it output as return value. It can return partial result if +// execution can'tpl be proceed because of error. +func ExecuteToString(tpl *template.Template, v interface{}) (string, error) { + buf := &bytes.Buffer{} + err := tpl.Execute(buf, v) + + return buf.String(), err +} + +// ParseGlob do the same as template.ParseGlob(), but will allow to +// use sparse syntax (like in examples above) in files. +func ParseGlob(tpl *template.Template, pattern string) ( + *template.Template, error, +) { + filenames, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + if len(filenames) == 0 { + return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) + } + for _, filename := range filenames { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + s := Strip(string(b)) + name := filepath.Base(filename) + if tpl == nil { + tpl = template.New(name) + } + var current_tpl *template.Template + if name == tpl.Name() { + current_tpl = tpl + } else { + current_tpl = tpl.New(name) + } + _, err = current_tpl.Parse(s) + if err != nil { + return nil, err + } + } + return tpl, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5c8aa42..9470652 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -3,6 +3,8 @@ code.gitea.io/gitea-vet code.gitea.io/gitea-vet/checks # code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e code.gitea.io/sdk/gitea +# gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 +gitea.com/noerw/unidiff-comments # github.com/AlecAivazis/survey/v2 v2.2.2 github.com/AlecAivazis/survey/v2 github.com/AlecAivazis/survey/v2/core @@ -165,6 +167,8 @@ github.com/olekukonko/tablewriter github.com/pmezard/go-difflib/difflib # github.com/russross/blackfriday/v2 v2.1.0 github.com/russross/blackfriday/v2 +# github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 +github.com/seletskiy/tplutil # github.com/sergi/go-diff v1.1.0 github.com/sergi/go-diff/diffmatchpatch # github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 -- 2.40.1 From 6e736e0972507d56d7cf457e9bc70344a9dfb47f Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Fri, 18 Dec 2020 17:26:33 +0100 Subject: [PATCH 3/8] add `tea pr lgtm|reject` shorthands --- cmd/pulls.go | 2 ++ cmd/pulls/approve.go | 42 ++++++++++++++++++++++++++++++++++++++++++ cmd/pulls/reject.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 cmd/pulls/approve.go create mode 100644 cmd/pulls/reject.go diff --git a/cmd/pulls.go b/cmd/pulls.go index 760a1a8..673e47e 100644 --- a/cmd/pulls.go +++ b/cmd/pulls.go @@ -34,6 +34,8 @@ var CmdPulls = cli.Command{ &pulls.CmdPullsClose, &pulls.CmdPullsReopen, &pulls.CmdPullsReview, + &pulls.CmdPullsApprove, + &pulls.CmdPullsReject, }, } diff --git a/cmd/pulls/approve.go b/cmd/pulls/approve.go new file mode 100644 index 0000000..e8ea096 --- /dev/null +++ b/cmd/pulls/approve.go @@ -0,0 +1,42 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package pulls + +import ( + "fmt" + + "code.gitea.io/tea/cmd/flags" + "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/task" + "code.gitea.io/tea/modules/utils" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdPullsApprove approves a PR +var CmdPullsApprove = cli.Command{ + Name: "approve", + Aliases: []string{"lgtm", "a"}, + Usage: "Approve a pull request", + Description: "Approve a pull request", + ArgsUsage: "", + Action: func(cmd *cli.Context) error { + ctx := context.InitCommand(cmd) + ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) + + if ctx.Args().Len() == 0 { + return fmt.Errorf("Must specify a PR index") + } + + idx, err := utils.ArgToIndex(ctx.Args().First()) + if err != nil { + return err + } + + return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, "", nil) + }, + Flags: flags.AllDefaultFlags, +} diff --git a/cmd/pulls/reject.go b/cmd/pulls/reject.go new file mode 100644 index 0000000..67f80d1 --- /dev/null +++ b/cmd/pulls/reject.go @@ -0,0 +1,44 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package pulls + +import ( + "fmt" + "strings" + + "code.gitea.io/tea/cmd/flags" + "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/task" + "code.gitea.io/tea/modules/utils" + + "code.gitea.io/sdk/gitea" + "github.com/urfave/cli/v2" +) + +// CmdPullsReject requests changes to a PR +var CmdPullsReject = cli.Command{ + Name: "reject", + Usage: "Request changes to a pull request", + Description: "Request changes to a pull request", + ArgsUsage: " ", + Action: func(cmd *cli.Context) error { + ctx := context.InitCommand(cmd) + ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) + + if ctx.Args().Len() < 2 { + return fmt.Errorf("Must specify a PR index and comment") + } + + idx, err := utils.ArgToIndex(ctx.Args().First()) + if err != nil { + return err + } + + comment := strings.Join(ctx.Args().Tail(), " ") + + return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, comment, nil) + }, + Flags: flags.AllDefaultFlags, +} -- 2.40.1 From 2d520128c7f4669c841d2b47f4f75d1f3cde2c4e Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Sat, 19 Dec 2020 09:57:37 +0100 Subject: [PATCH 4/8] vendor slimmed down diff parser --- go.mod | 2 +- go.sum | 4 + .../unidiff-comments/changeset_reader.go | 54 ++--- .../unidiff-comments/changeset_writer.go | 54 ----- .../unidiff-comments/changeset_writer_tpl.go | 81 ------- .../gitea.com/noerw/unidiff-comments/go.mod | 5 + .../gitea.com/noerw/unidiff-comments/go.sum | 2 + .../unidiff-comments/{ => types}/changeset.go | 2 +- .../unidiff-comments/{ => types}/comment.go | 2 +- .../unidiff-comments/{ => types}/diff.go | 2 +- .../unidiff-comments/{ => types}/hunk.go | 2 +- .../unidiff-comments/{ => types}/line.go | 2 +- .../unidiff-comments/{ => types}/segment.go | 2 +- vendor/github.com/seletskiy/tplutil/LICENSE | 201 ------------------ vendor/github.com/seletskiy/tplutil/README.md | 1 - vendor/github.com/seletskiy/tplutil/util.go | 113 ---------- vendor/modules.txt | 5 +- 17 files changed, 49 insertions(+), 485 deletions(-) delete mode 100644 vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go delete mode 100644 vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go create mode 100644 vendor/gitea.com/noerw/unidiff-comments/go.mod create mode 100644 vendor/gitea.com/noerw/unidiff-comments/go.sum rename vendor/gitea.com/noerw/unidiff-comments/{ => types}/changeset.go (98%) rename vendor/gitea.com/noerw/unidiff-comments/{ => types}/comment.go (99%) rename vendor/gitea.com/noerw/unidiff-comments/{ => types}/diff.go (98%) rename vendor/gitea.com/noerw/unidiff-comments/{ => types}/hunk.go (91%) rename vendor/gitea.com/noerw/unidiff-comments/{ => types}/line.go (97%) rename vendor/gitea.com/noerw/unidiff-comments/{ => types}/segment.go (97%) delete mode 100644 vendor/github.com/seletskiy/tplutil/LICENSE delete mode 100644 vendor/github.com/seletskiy/tplutil/README.md delete mode 100644 vendor/github.com/seletskiy/tplutil/util.go diff --git a/go.mod b/go.mod index e1fc7b5..4985aa7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( code.gitea.io/gitea-vet v0.2.1 code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e - gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 + gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b github.com/AlecAivazis/survey/v2 v2.2.2 github.com/Microsoft/go-winio v0.4.15 // indirect github.com/adrg/xdg v0.2.2 diff --git a/go.sum b/go.sum index 5db7cd6..0ae656d 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e h1:oJOoT5TGbSYRNGU code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 h1:1nyuvVnt3q2rXITHxkpXMfOaOQrbK5Bi78m+n2vVXYs= gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9/go.mod h1:1YlCscxedF3xz1q1LjYAi0hlWXbwQxHFaFatUtlJFQ4= +gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE= +gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI= +gitea.com/noerw/unidiff-comments v0.0.0-20201219085322-17e21062c0fa h1:bPh35lXneetZVVmIUlRn67qGoc7lfm+t9p5lIOAIBHQ= +gitea.com/noerw/unidiff-comments v0.0.0-20201219085322-17e21062c0fa/go.mod h1:1YlCscxedF3xz1q1LjYAi0hlWXbwQxHFaFatUtlJFQ4= github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY= github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go b/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go index c6a4cb6..3f556a0 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go +++ b/vendor/gitea.com/noerw/unidiff-comments/changeset_reader.go @@ -1,4 +1,4 @@ -package godiff +package unidiff import ( "bufio" @@ -8,6 +8,8 @@ import ( "strconv" "strings" "time" + + "gitea.com/noerw/unidiff-comments/types" ) const ( @@ -21,6 +23,8 @@ const ( stateDiffComment = "stateDiffComment" stateDiffCommentDelim = "stateDiffCommentDelim" stateDiffCommentHeader = "stateDiffCommentHeader" + + ignorePrefix = "###" ) var ( @@ -69,16 +73,16 @@ var ( type parser struct { state string - changeset Changeset - diff *Diff - hunk *Hunk - segment *Segment - comment *Comment - line *Line + changeset types.Changeset + diff *types.Diff + hunk *types.Hunk + segment *types.Segment + comment *types.Comment + line *types.Line lineNumber int segmentType string - commentsList []*Comment + commentsList []*types.Comment } type Error struct { @@ -90,7 +94,7 @@ func (err Error) Error() string { return fmt.Sprintf("line %d: %s", err.LineNumber, err.Message) } -func ReadChangeset(r io.Reader) (Changeset, error) { +func ReadChangeset(r io.Reader) (types.Changeset, error) { buffer := bufio.NewReader(r) current := parser{} @@ -175,13 +179,13 @@ func (current *parser) switchState(line string) error { switch { case reSegmentContext.MatchString(line): current.state = stateHunkBody - current.segmentType = SegmentTypeContext + current.segmentType = types.SegmentTypeContext case reSegmentRemoved.MatchString(line): current.state = stateHunkBody - current.segmentType = SegmentTypeRemoved + current.segmentType = types.SegmentTypeRemoved case reSegmentAdded.MatchString(line): current.state = stateHunkBody - current.segmentType = SegmentTypeAdded + current.segmentType = types.SegmentTypeAdded case reHunk.MatchString(line): current.state = stateHunkHeader case reCommentText.MatchString(line): @@ -244,31 +248,31 @@ func (current *parser) createNodes(line string) error { } fallthrough case stateDiffCommentDelim, stateDiffCommentHeader: - current.comment = &Comment{} + current.comment = &types.Comment{} fallthrough case stateDiffHeader: if current.diff == nil { - current.diff = &Diff{} + current.diff = &types.Diff{} current.changeset.Diffs = append(current.changeset.Diffs, current.diff) } case stateHunkHeader: - current.hunk = &Hunk{} - current.segment = &Segment{} + current.hunk = &types.Hunk{} + current.segment = &types.Segment{} case stateCommentDelim, stateCommentHeader: - current.comment = &Comment{} + current.comment = &types.Comment{} case stateComment: if current.comment == nil { - current.comment = &Comment{} + current.comment = &types.Comment{} } case stateHunkBody: if current.segment.Type != current.segmentType { - current.segment = &Segment{Type: current.segmentType} + current.segment = &types.Segment{Type: current.segmentType} current.hunk.Segments = append(current.hunk.Segments, current.segment) } - current.line = &Line{} + current.line = &types.Line{} current.segment.Lines = append(current.segment.Lines, current.line) } @@ -332,13 +336,13 @@ func (current *parser) locateLine(line string) error { } hunkLength := int64(len(current.segment.Lines)) switch current.segment.Type { - case SegmentTypeContext: + case types.SegmentTypeContext: current.line.Source = sourceOffset + hunkLength current.line.Destination = destinationOffset + hunkLength - case SegmentTypeAdded: + case types.SegmentTypeAdded: current.line.Source = sourceOffset current.line.Destination = destinationOffset + hunkLength - case SegmentTypeRemoved: + case types.SegmentTypeRemoved: current.line.Source = sourceOffset + hunkLength current.line.Destination = destinationOffset } @@ -408,7 +412,7 @@ func (current *parser) parseCommentHeader(line string) error { updatedDate, _ := time.ParseInLocation(time.ANSIC, strings.TrimSpace(matches[4]), time.Local) - current.comment.UpdatedDate = UnixTimestamp(updatedDate.Unix() * 1000) + current.comment.UpdatedDate = types.UnixTimestamp(updatedDate.Unix() * 1000) version, _ := strconv.ParseInt(matches[2], 10, 64) current.comment.Version = int(version) @@ -434,7 +438,7 @@ func (current *parser) parseComment(line string) error { return nil } -func (current *parser) findParentComment(comment *Comment) *Comment { +func (current *parser) findParentComment(comment *types.Comment) *types.Comment { for i := len(current.commentsList) - 1; i >= 0; i-- { c := current.commentsList[i] if comment.Indent > c.Indent { diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go b/vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go deleted file mode 100644 index c47dd55..0000000 --- a/vendor/gitea.com/noerw/unidiff-comments/changeset_writer.go +++ /dev/null @@ -1,54 +0,0 @@ -package godiff - -import ( - "io" - "regexp" - "strings" - "text/template" - - "github.com/seletskiy/tplutil" -) - -var changesetTpl = template.New("changeset") - -func init() { - commentsTpl := template.New("comment") - - reBeginningOfLine := regexp.MustCompile(`(?m)^`) - reNewLine := regexp.MustCompile(`^|\n`) - reDanglingSpace := regexp.MustCompile(`(?m)\s+$`) - - funcs := template.FuncMap{ - "indent": func(input string) string { - return reBeginningOfLine.ReplaceAllString(input, " ") - }, - "writeComments": func(input CommentsTree) string { - res, _ := tplutil.ExecuteToString(commentsTpl, input) - return res - }, - "writeNote": func(input string) string { - return Note(input) - }, - "trimWhitespace": func(input string) string { - return strings.TrimSpace(input) - }, - "comment": func(input string) string { - //log.Printf("%#v", input) - return reDanglingSpace.ReplaceAllString( - reNewLine.ReplaceAllString(input, `$0# `), - ``, - ) - }, - } - - template.Must( - commentsTpl.Funcs(funcs).Funcs(tplutil.Last).Parse( - tplutil.Strip(commentsTplText))) - template.Must( - changesetTpl.Funcs(funcs).Funcs(tplutil.Last).Parse( - tplutil.Strip(changesetTplText))) -} - -func WriteChangeset(changeset Changeset, to io.Writer) error { - return changesetTpl.Execute(to, changeset) -} diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go b/vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go deleted file mode 100644 index 29dda55..0000000 --- a/vendor/gitea.com/noerw/unidiff-comments/changeset_writer_tpl.go +++ /dev/null @@ -1,81 +0,0 @@ -package godiff - -const commentsTplText = ` -{{range $i, $_ := .}} - - {{"\n\n"}} - - {{if gt .Id 0}} - [{{.Id}}@{{.Version}}] | {{.Author.DisplayName}} | {{.UpdatedDate}} - {{"\n\n"}} - {{end}} - - {{.Text | trimWhitespace}} - - {{"\n\n---"}} - - {{writeComments .Comments | indent}} -{{end}}` - -const changesetTplText = ` -{{range $i, $d := .Diffs}} - {{if .Note}} - {{writeNote .Note}} - {{"\n"}} - {{end}} - - {{if .FileComments}} - {{"---" | comment}} - {{"\n"}} - {{writeComments .FileComments | comment}} - {{"\n"}} - {{end}} - - {{if .Hunks}} - ---{{" "}} - {{if .Source.ToString}} - {{.Source.ToString}} - {{else}} - /dev/null - {{end}} - {{"\t"}} - {{$.FromHash}} - {{"\n"}} - - +++{{" "}} - {{if .Destination.ToString}} - {{.Destination.ToString}} - {{else}} - /dev/null - {{end}} - {{"\t"}} - {{$.ToHash}} - {{"\n"}} - - {{range .Hunks}} - @@ -{{.SourceLine}}{{if ne .SourceSpan 0}},{{.SourceSpan}}{{end}} +{{.DestinationLine}}{{if ne .DestinationSpan 0}},{{.DestinationSpan}}{{end}} @@ - {{"\n"}} - - {{range .Segments}} - {{$segment := .}} - {{range .Lines}} - {{$segment.TextPrefix}} - {{.Line}} - - {{"\n"}} - {{if .Comments}} - {{"---" | comment}} - {{"\n"}} - {{writeComments .Comments | comment}} - {{"\n"}} - {{end}} - {{end}} - - {{end}} - {{end}} - {{end}} - - {{if not (last $i $.Diffs)}} - {{"\n\n"}} - {{end}} -{{end}}` diff --git a/vendor/gitea.com/noerw/unidiff-comments/go.mod b/vendor/gitea.com/noerw/unidiff-comments/go.mod new file mode 100644 index 0000000..044de63 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/go.mod @@ -0,0 +1,5 @@ +module gitea.com/noerw/unidiff-comments + +go 1.15 + +require github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 diff --git a/vendor/gitea.com/noerw/unidiff-comments/go.sum b/vendor/gitea.com/noerw/unidiff-comments/go.sum new file mode 100644 index 0000000..091f1a5 --- /dev/null +++ b/vendor/gitea.com/noerw/unidiff-comments/go.sum @@ -0,0 +1,2 @@ +github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA= +github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw= diff --git a/vendor/gitea.com/noerw/unidiff-comments/changeset.go b/vendor/gitea.com/noerw/unidiff-comments/types/changeset.go similarity index 98% rename from vendor/gitea.com/noerw/unidiff-comments/changeset.go rename to vendor/gitea.com/noerw/unidiff-comments/types/changeset.go index 7e9e66c..2049edc 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/changeset.go +++ b/vendor/gitea.com/noerw/unidiff-comments/types/changeset.go @@ -1,4 +1,4 @@ -package godiff +package types type Changeset struct { FromHash string diff --git a/vendor/gitea.com/noerw/unidiff-comments/comment.go b/vendor/gitea.com/noerw/unidiff-comments/types/comment.go similarity index 99% rename from vendor/gitea.com/noerw/unidiff-comments/comment.go rename to vendor/gitea.com/noerw/unidiff-comments/types/comment.go index 9b7251e..5363ccb 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/comment.go +++ b/vendor/gitea.com/noerw/unidiff-comments/types/comment.go @@ -1,4 +1,4 @@ -package godiff +package types import ( "regexp" diff --git a/vendor/gitea.com/noerw/unidiff-comments/diff.go b/vendor/gitea.com/noerw/unidiff-comments/types/diff.go similarity index 98% rename from vendor/gitea.com/noerw/unidiff-comments/diff.go rename to vendor/gitea.com/noerw/unidiff-comments/types/diff.go index 339e2eb..dbc87b2 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/diff.go +++ b/vendor/gitea.com/noerw/unidiff-comments/types/diff.go @@ -1,4 +1,4 @@ -package godiff +package types type Diff struct { Truncated bool diff --git a/vendor/gitea.com/noerw/unidiff-comments/hunk.go b/vendor/gitea.com/noerw/unidiff-comments/types/hunk.go similarity index 91% rename from vendor/gitea.com/noerw/unidiff-comments/hunk.go rename to vendor/gitea.com/noerw/unidiff-comments/types/hunk.go index 8ba514c..dce2981 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/hunk.go +++ b/vendor/gitea.com/noerw/unidiff-comments/types/hunk.go @@ -1,4 +1,4 @@ -package godiff +package types type Hunk struct { SourceLine int64 diff --git a/vendor/gitea.com/noerw/unidiff-comments/line.go b/vendor/gitea.com/noerw/unidiff-comments/types/line.go similarity index 97% rename from vendor/gitea.com/noerw/unidiff-comments/line.go rename to vendor/gitea.com/noerw/unidiff-comments/types/line.go index a77eae9..d6cd055 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/line.go +++ b/vendor/gitea.com/noerw/unidiff-comments/types/line.go @@ -1,4 +1,4 @@ -package godiff +package types import "regexp" diff --git a/vendor/gitea.com/noerw/unidiff-comments/segment.go b/vendor/gitea.com/noerw/unidiff-comments/types/segment.go similarity index 97% rename from vendor/gitea.com/noerw/unidiff-comments/segment.go rename to vendor/gitea.com/noerw/unidiff-comments/types/segment.go index 839fe53..f0f9761 100644 --- a/vendor/gitea.com/noerw/unidiff-comments/segment.go +++ b/vendor/gitea.com/noerw/unidiff-comments/types/segment.go @@ -1,4 +1,4 @@ -package godiff +package types const ( SegmentTypeContext = "CONTEXT" diff --git a/vendor/github.com/seletskiy/tplutil/LICENSE b/vendor/github.com/seletskiy/tplutil/LICENSE deleted file mode 100644 index 71df8e6..0000000 --- a/vendor/github.com/seletskiy/tplutil/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright Stanislav Seletskiy 2014 - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/seletskiy/tplutil/README.md b/vendor/github.com/seletskiy/tplutil/README.md deleted file mode 100644 index d820fbf..0000000 --- a/vendor/github.com/seletskiy/tplutil/README.md +++ /dev/null @@ -1 +0,0 @@ -Please see Godoc at: https://godoc.org/github.com/seletskiy/tplutil diff --git a/vendor/github.com/seletskiy/tplutil/util.go b/vendor/github.com/seletskiy/tplutil/util.go deleted file mode 100644 index e287c1e..0000000 --- a/vendor/github.com/seletskiy/tplutil/util.go +++ /dev/null @@ -1,113 +0,0 @@ -// Package tplutil provides more convient way to use text/template inside -// the code. -// -// Consider (using text/template): -// -// var myTpl = template.Must(template.New("name").Parse( -// "Some list:\n" + -// "{{range .}}" + -// "# {{.}}\n" + -// "{{end}}")) -// -// `gofmt` will ruin any attempt to format code above. -// -// And with tplutil: -// -// var myTpl = template.Must(template.New("name").Parse(tplutil.Strip(` -// Some list:{{"\n"}} -// -// {{range .}} -// # {{.}}{{"\n"}} -// {{end}} -// `))) -// -// Output will be exactly the same. -// -// Any indenting whitespaces and newlines will be ignored. If must, they -// should be specified by using syntax -// -// `{{" "}}` or `{{"\n"}}`. -// -// It also provide `{{last}}` function to check on last element of pipeline: -// -// var myTpl = template.Must(template.New("asd").Funcs(tplutil.Last).Parse( -// tplutil.Strip(` -// Some list:{{"\n"}} -// -// {{range $i, $_ := .}} -// {{.}} -// {{if not (last $i $)}} -// {{"\n"}}{{/* do not append newline to the last element */}} -// {{end}} -// {{end}} -// `)) -// -package tplutil - -import ( - "bytes" - "fmt" - "io/ioutil" - "path/filepath" - "reflect" - "regexp" - "text/template" -) - -var reInsignificantWhitespace = regexp.MustCompile(`(?m)\n?^\s*`) - -var Last = template.FuncMap{ - "last": func(x int, a interface{}) bool { - return x == reflect.ValueOf(a).Len()-1 - }, -} - -func Strip(text string) string { - return reInsignificantWhitespace.ReplaceAllString(text, ``) -} - -// ExecuteToString applies a parsed template to specified data object and -// returns it output as return value. It can return partial result if -// execution can'tpl be proceed because of error. -func ExecuteToString(tpl *template.Template, v interface{}) (string, error) { - buf := &bytes.Buffer{} - err := tpl.Execute(buf, v) - - return buf.String(), err -} - -// ParseGlob do the same as template.ParseGlob(), but will allow to -// use sparse syntax (like in examples above) in files. -func ParseGlob(tpl *template.Template, pattern string) ( - *template.Template, error, -) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - if len(filenames) == 0 { - return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) - } - for _, filename := range filenames { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - s := Strip(string(b)) - name := filepath.Base(filename) - if tpl == nil { - tpl = template.New(name) - } - var current_tpl *template.Template - if name == tpl.Name() { - current_tpl = tpl - } else { - current_tpl = tpl.New(name) - } - _, err = current_tpl.Parse(s) - if err != nil { - return nil, err - } - } - return tpl, nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9470652..d31580f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -3,8 +3,9 @@ code.gitea.io/gitea-vet code.gitea.io/gitea-vet/checks # code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e code.gitea.io/sdk/gitea -# gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 +# gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b gitea.com/noerw/unidiff-comments +gitea.com/noerw/unidiff-comments/types # github.com/AlecAivazis/survey/v2 v2.2.2 github.com/AlecAivazis/survey/v2 github.com/AlecAivazis/survey/v2/core @@ -167,8 +168,6 @@ github.com/olekukonko/tablewriter github.com/pmezard/go-difflib/difflib # github.com/russross/blackfriday/v2 v2.1.0 github.com/russross/blackfriday/v2 -# github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 -github.com/seletskiy/tplutil # github.com/sergi/go-diff v1.1.0 github.com/sergi/go-diff/diffmatchpatch # github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 -- 2.40.1 From 91a4f43a448df30045bd3fc5950f4e3e9621c2bb Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Sat, 19 Dec 2020 10:29:09 +0100 Subject: [PATCH 5/8] review diff: default to true if users want a shortcut, they can use lgtm or reject subcmds --- modules/interact/pull_review.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/interact/pull_review.go b/modules/interact/pull_review.go index 351a5f4..7a190ef 100644 --- a/modules/interact/pull_review.go +++ b/modules/interact/pull_review.go @@ -31,7 +31,7 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error { // codeComments var reviewDiff bool - promptDiff := &survey.Confirm{Message: "Review & comment the diff?", Default: false} + promptDiff := &survey.Confirm{Message: "Review / comment the diff?", Default: true} if err = survey.AskOne(promptDiff, &reviewDiff); err != nil { return err } -- 2.40.1 From cc9311a1a133570659c066fc3c3aea8d2fc0eab1 Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Sat, 19 Dec 2020 10:33:33 +0100 Subject: [PATCH 6/8] `tea pr approve`: accept optional comment --- cmd/pulls/approve.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/pulls/approve.go b/cmd/pulls/approve.go index e8ea096..355941b 100644 --- a/cmd/pulls/approve.go +++ b/cmd/pulls/approve.go @@ -6,6 +6,7 @@ package pulls import ( "fmt" + "strings" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" @@ -22,7 +23,7 @@ var CmdPullsApprove = cli.Command{ Aliases: []string{"lgtm", "a"}, Usage: "Approve a pull request", Description: "Approve a pull request", - ArgsUsage: "", + ArgsUsage: " []", Action: func(cmd *cli.Context) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) @@ -36,7 +37,9 @@ var CmdPullsApprove = cli.Command{ return err } - return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, "", nil) + comment := strings.Join(ctx.Args().Tail(), " ") + + return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, comment, nil) }, Flags: flags.AllDefaultFlags, } -- 2.40.1 From 7e1479d4f414a4016949c41e9c4701cf727ec1f4 Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Mon, 21 Dec 2020 16:02:46 +0100 Subject: [PATCH 7/8] fix copy paste error --- cmd/pulls/reject.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/pulls/reject.go b/cmd/pulls/reject.go index 67f80d1..f2ce77e 100644 --- a/cmd/pulls/reject.go +++ b/cmd/pulls/reject.go @@ -38,7 +38,7 @@ var CmdPullsReject = cli.Command{ comment := strings.Join(ctx.Args().Tail(), " ") - return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, comment, nil) + return task.CreatePullReview(ctx, idx, gitea.ReviewStateRequestChanges, comment, nil) }, Flags: flags.AllDefaultFlags, } -- 2.40.1 From 19aa421e3e909099f9b73ef72769bc02b57c08d1 Mon Sep 17 00:00:00 2001 From: Norwin Roosen Date: Mon, 21 Dec 2020 16:06:00 +0100 Subject: [PATCH 8/8] clean go.mod --- go.mod | 2 -- go.sum | 6 ------ 2 files changed, 8 deletions(-) diff --git a/go.mod b/go.mod index 4985aa7..be54cb0 100644 --- a/go.mod +++ b/go.mod @@ -24,8 +24,6 @@ require ( github.com/muesli/termenv v0.7.4 github.com/olekukonko/tablewriter v0.0.4 github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/seletskiy/godiff v0.0.0-20160229093234-ad7fb8d1f905 // indirect - github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.6.1 github.com/urfave/cli/v2 v2.3.0 diff --git a/go.sum b/go.sum index 0ae656d..18bd439 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,8 @@ code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e h1:oJOoT5TGbSYRNGUhEiiEz3MqFjU6wELN0/liCZ3RmVg= code.gitea.io/sdk/gitea v0.13.1-0.20201209180822-68eec69f472e/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= -gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9 h1:1nyuvVnt3q2rXITHxkpXMfOaOQrbK5Bi78m+n2vVXYs= -gitea.com/noerw/unidiff-comments v0.0.0-20201218123713-6f546f1fdea9/go.mod h1:1YlCscxedF3xz1q1LjYAi0hlWXbwQxHFaFatUtlJFQ4= gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE= gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI= -gitea.com/noerw/unidiff-comments v0.0.0-20201219085322-17e21062c0fa h1:bPh35lXneetZVVmIUlRn67qGoc7lfm+t9p5lIOAIBHQ= -gitea.com/noerw/unidiff-comments v0.0.0-20201219085322-17e21062c0fa/go.mod h1:1YlCscxedF3xz1q1LjYAi0hlWXbwQxHFaFatUtlJFQ4= github.com/AlecAivazis/survey/v2 v2.2.2 h1:1I4qBrNsHQE+91tQCqVlfrKe9DEL65949d1oKZWVELY= github.com/AlecAivazis/survey/v2 v2.2.2/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -152,8 +148,6 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= -github.com/seletskiy/godiff v0.0.0-20160229093234-ad7fb8d1f905 h1:R9HQcQ3PgW9jmzq1M96mTkRI6QLRKKsB1XU7YwZScus= -github.com/seletskiy/godiff v0.0.0-20160229093234-ad7fb8d1f905/go.mod h1:5RG/Xp1wt6OPVCa/EWs6Hnc7pllTFwE7ZVdkb5O5dso= github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA= github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -- 2.40.1