mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-06 05:57:38 -04:00
Reading a change in lazygit and saying something about it on GitHub means finding the line again in the browser: open the pull request, find the commit, find the file, scroll to the line. The line is already under the cursor here. Bind G in the focused main view, the key the commits panel opens the pull request with, to open it at the line the selection is on. The URL names the commits whose diff is on screen, so that the line numbers of the diff are the ones the page shows, the file by the SHA-256 of its repo-relative path, and the line by the side of the diff it is on: R for the new version of the file, L for the old one, where a deleted line is. GitHub documents none of that; the form was read off the URLs its own pages carry. One commit is named by its hash. A range of them is named by the commit the range starts after and the commit it ends at, the form the chooser above a pull request's files uses. The commit a range starts after is the parent of its oldest commit; where the range starts where the pull request itself does, that parent is none of the pull request's own commits, and the keyword BASE stands for it. Which branch's pull request that is depends on the panel beneath: the checked-out branch below the commits panel, the branch drilled into below the sub-commits panel, and whichever of those the commit files panel was entered from. Panels showing a diff that no pull request has a view of don't answer, and the command isn't offered over their diffs at all. Neither is it offered over a diff that is not the commit's own, where the line numbers on screen are not the ones the page shows: a diff against another ref in diffing mode, and the custom patch, whose lines sit at the numbers the patch gives them. A pull request holds only the commits of its branch that are pushed, and its pages say they can't find any other commit. So the command refuses where a commit of the diff is not one of the pull request's. Amend a commit in the middle of the branch, and the diffs of the commits below it still open; the ones above it sit on hashes the remote doesn't have. A commit from before the branch, in a main branch already, is refused too. Only GitHub pull requests are known, since that is where the pull request data comes from. The whole path can't be exercised headlessly: no pull request reaches the model without a GitHub token, so the test covers where the command is offered and the three reasons it refuses. The URL is unit-tested instead, both the anchor of a line and the way the commits are named. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
package context
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type CommitFilesContext struct {
|
|
*filetree.CommitFileTreeViewModel
|
|
*ListContextTrait
|
|
*DynamicTitleBuilder
|
|
}
|
|
|
|
var (
|
|
_ types.IListContext = (*CommitFilesContext)(nil)
|
|
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
|
_ types.IFilterableContext = (*CommitFilesContext)(nil)
|
|
_ types.DiffMainViewContext = (*CommitFilesContext)(nil)
|
|
_ types.PullRequestDiffContext = (*CommitFilesContext)(nil)
|
|
)
|
|
|
|
func (self *CommitFilesContext) GetDiffMainViewType() types.DiffMainViewType {
|
|
return types.DiffMainViewTypePatchBuilding
|
|
}
|
|
|
|
// BranchForPullRequest asks the panel this one was entered from: the files are a
|
|
// commit's, and which branch's pull request that commit is up for review in is known
|
|
// there rather than here.
|
|
func (self *CommitFilesContext) BranchForPullRequest() string {
|
|
if parent, ok := self.GetParentContext().(types.PullRequestDiffContext); ok {
|
|
return parent.BranchForPullRequest()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// CommitsForPullRequest asks the panel this one was entered from as well: the files
|
|
// listed here are those of the commits selected there.
|
|
func (self *CommitFilesContext) CommitsForPullRequest() ([]*models.Commit, string) {
|
|
if parent, ok := self.GetParentContext().(types.PullRequestDiffContext); ok {
|
|
return parent.CommitsForPullRequest()
|
|
}
|
|
return nil, ""
|
|
}
|
|
|
|
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
|
viewModel := filetree.NewCommitFileTreeViewModel(
|
|
func() []*models.CommitFile { return c.Model().CommitFiles },
|
|
c.Common,
|
|
c.UserConfig().Gui.ShowFileTree,
|
|
)
|
|
|
|
getDisplayStrings := func(_ int, _ int) [][]string {
|
|
if viewModel.Len() == 0 {
|
|
return [][]string{{style.FgRed.Sprint("(none)")}}
|
|
}
|
|
|
|
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
|
lines := presentation.RenderCommitFileTree(viewModel, c.Git().Patch.PatchBuilder, showFileIcons, &c.UserConfig().Gui.CustomIcons)
|
|
return lo.Map(lines, func(line string, _ int) []string {
|
|
return []string{line}
|
|
})
|
|
}
|
|
|
|
ctx := &CommitFilesContext{
|
|
CommitFileTreeViewModel: viewModel,
|
|
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.CommitFilesDynamicTitle),
|
|
ListContextTrait: &ListContextTrait{
|
|
Context: NewSimpleContext(
|
|
NewBaseContext(NewBaseContextOpts{
|
|
View: c.Views().CommitFiles,
|
|
WindowName: "commits",
|
|
Key: COMMIT_FILES_CONTEXT_KEY,
|
|
Kind: types.SIDE_CONTEXT,
|
|
Focusable: true,
|
|
Transient: true,
|
|
}),
|
|
),
|
|
ListRenderer: ListRenderer{
|
|
list: viewModel,
|
|
getDisplayStrings: getDisplayStrings,
|
|
},
|
|
c: c,
|
|
},
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
func (self *CommitFilesContext) GetDiffTerminals() []string {
|
|
return []string{self.GetRef().RefName()}
|
|
}
|
|
|
|
func (self *CommitFilesContext) RefForAdjustingLineNumberInDiff() string {
|
|
if refs := self.GetRefRange(); refs != nil {
|
|
return refs.To.RefName()
|
|
}
|
|
return self.GetRef().RefName()
|
|
}
|
|
|
|
func (self *CommitFilesContext) GetFromAndToForDiff() (string, string) {
|
|
return FromAndToForDiff(self.GetRef(), self.GetRefRange())
|
|
}
|
|
|
|
// FromAndToForDiff gives the two ends to diff for a ref, or for a range of them: a
|
|
// range runs from the parent of its first ref to its last, a single ref from its own
|
|
// parent to itself.
|
|
func FromAndToForDiff(ref models.Ref, refRange *types.RefRange) (string, string) {
|
|
if refRange != nil {
|
|
return refRange.From.ParentRefName(), refRange.To.RefName()
|
|
}
|
|
return ref.ParentRefName(), ref.RefName()
|
|
}
|
|
|
|
func (self *CommitFilesContext) ReInit(ref models.Ref, refRange *types.RefRange) {
|
|
self.SetRef(ref)
|
|
self.SetRefRange(refRange)
|
|
if refRange != nil {
|
|
self.SetTitleRef(fmt.Sprintf("%s-%s", refRange.From.ShortRefName(), refRange.To.ShortRefName()))
|
|
} else {
|
|
self.SetTitleRef(ref.Description())
|
|
}
|
|
self.GetView().Title = self.Title()
|
|
}
|