mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 15:46:26 -04:00
Resolve a diff line's identity from a content snapshot, not the live view
The diff-line primitive (recover a rendered row's patch-space identity) is about to gain a second, inverse consumer: the escape restore scans a focused main view's rows as it re-renders, looking for the row that matches a target patch identity. That scan runs over the *loading* off-screen buffer, not the displayed view, so the resolver can't be tied to the displayed view's per-view-line readers. Pull the three backends (OSC metadata, buffer parse, lazygit-edit hyperlink) onto a single buffer-agnostic resolver that takes a snapshot of a diff's per-line content — text, metadata and hyperlink per unwrapped buffer line — and the line to resolve. The forward consumers (click/enter/edit/PR) feed it a snapshot of the displayed buffer (gocui.DiffLineContents) after mapping the wrapped view line to its buffer line; the upcoming scan will feed it the off-screen buffer's loaded rows. Behavior is unchanged.
This commit is contained in:
parent
239426d46b
commit
cfe356b08d
|
|
@ -1832,6 +1832,48 @@ func (v *View) DiffLineMetadataInLine(y int) (string, bool) {
|
|||
return "", false
|
||||
}
|
||||
|
||||
// DiffLineContent is the raw per-line material the diff-line backends parse to
|
||||
// recover a rendered row's patch-space identity (see diff-line-metadata-notes.md):
|
||||
// the decolorized text (for host-side parsing, mechanism #1), the OSC-456
|
||||
// metadata payload a pager emitted (#2), and the line's hyperlink (delta's
|
||||
// lazygit-edit fallback). It is indexed by unwrapped buffer line, so one entry
|
||||
// covers all the (wrapped) view lines that line maps to.
|
||||
type DiffLineContent struct {
|
||||
Text string
|
||||
Metadata string
|
||||
Hyperlink string
|
||||
}
|
||||
|
||||
// DiffLineContents returns the per-line diff material (see DiffLineContent) for
|
||||
// every line of the displayed buffer. It is the snapshot the diff-line backends
|
||||
// scan: taken under the write lock in one call, so the text, metadata and
|
||||
// hyperlink of a given line stay consistent with each other even if a concurrent
|
||||
// re-render is rebuilding the buffer.
|
||||
func (v *View) DiffLineContents() []DiffLineContent {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
return diffLineContents(v.buf)
|
||||
}
|
||||
|
||||
func diffLineContents(buf *viewBuffer) []DiffLineContent {
|
||||
contents := make([]DiffLineContent, len(buf.lines))
|
||||
for i, line := range buf.lines {
|
||||
text := strings.ReplaceAll(line.cells.String(), "\x00", "")
|
||||
var metadata, hyperlink string
|
||||
for _, c := range line.cells {
|
||||
if metadata == "" {
|
||||
metadata = c.metadata
|
||||
}
|
||||
if hyperlink == "" {
|
||||
hyperlink = c.hyperlink
|
||||
}
|
||||
}
|
||||
contents[i] = DiffLineContent{Text: text, Metadata: metadata, Hyperlink: hyperlink}
|
||||
}
|
||||
return contents
|
||||
}
|
||||
|
||||
// BufferLineForViewLine maps a view line index (which counts wrapped lines) to
|
||||
// the index of the corresponding line in the unwrapped internal buffer (as
|
||||
// returned by BufferLines). Several view lines can map to the same buffer line
|
||||
|
|
|
|||
|
|
@ -160,22 +160,50 @@ func (self *StagingHelper) GetDiffLineInfo(windowName string, viewLineIdx int) (
|
|||
return types.DiffLineInfo{}, false
|
||||
}
|
||||
|
||||
if info, ok := self.diffLineInfoFromMetadata(v, viewLineIdx); ok {
|
||||
// A click/cursor lands on a (wrapped) view line; resolve it to the unwrapped
|
||||
// buffer line all three backends key off, then read that buffer line's content.
|
||||
bufferLineIdx, ok := v.BufferLineForViewLine(viewLineIdx)
|
||||
if !ok {
|
||||
return types.DiffLineInfo{}, false
|
||||
}
|
||||
return self.diffLineInfoFromContents(v.DiffLineContents(), bufferLineIdx)
|
||||
}
|
||||
|
||||
// diffLineInfoFromContents recovers the patch-space identity of the buffer line
|
||||
// at idx within a snapshot of a diff's per-line content (see gocui.DiffLineContent).
|
||||
// It is the single resolver behind both directions of the diff-line primitive —
|
||||
// the forward consumers (click/enter/edit/PR, via GetDiffLineInfo on the displayed
|
||||
// view) and the inverse identity scan that finds a target line in a focused main
|
||||
// view as it re-renders (escape restore, via the loading off-screen content). It
|
||||
// tries three backends in order of fidelity:
|
||||
//
|
||||
// - mechanism #2: per-line OSC metadata emitted by a patched pager (delta),
|
||||
// which carries the side directly and so serves the renderings #1 can't parse
|
||||
// (delta's default mode, --line-numbers, diff-so-fancy);
|
||||
// - mechanism #1: parsing the decolorized buffer, which serves structure-
|
||||
// preserving renderings (no pager, git diff --color, delta --color-only);
|
||||
// - delta's lazygit-edit:// hyperlinks; the hyperlink can't convey the side, so
|
||||
// its result is reported as a non-deletion content line.
|
||||
func (self *StagingHelper) diffLineInfoFromContents(contents []gocui.DiffLineContent, idx int) (types.DiffLineInfo, bool) {
|
||||
if idx < 0 || idx >= len(contents) {
|
||||
return types.DiffLineInfo{}, false
|
||||
}
|
||||
|
||||
if info, ok := self.diffLineInfoFromMetadata(contents[idx].Metadata); ok {
|
||||
return info, true
|
||||
}
|
||||
if info, ok := self.diffLineInfoFromBuffer(v, viewLineIdx); ok {
|
||||
if info, ok := self.diffLineInfoFromBuffer(contents, idx); ok {
|
||||
return info, true
|
||||
}
|
||||
return self.diffLineInfoFromHyperlink(v, viewLineIdx)
|
||||
return self.diffLineInfoFromHyperlink(contents[idx].Hyperlink)
|
||||
}
|
||||
|
||||
// diffLineInfoFromMetadata reads mechanism #2's per-line OSC metadata. The
|
||||
// payload is positional and ';'-delimited — version;type;new-line;old-line;file
|
||||
// — with the file last so it may itself contain ';', and old-line empty unless
|
||||
// the line is a deletion. See diff-line-metadata-notes.md §9.2.
|
||||
func (self *StagingHelper) diffLineInfoFromMetadata(v *gocui.View, viewLineIdx int) (types.DiffLineInfo, bool) {
|
||||
payload, ok := v.DiffLineMetadataInLine(viewLineIdx)
|
||||
if !ok {
|
||||
func (self *StagingHelper) diffLineInfoFromMetadata(payload string) (types.DiffLineInfo, bool) {
|
||||
if payload == "" {
|
||||
return types.DiffLineInfo{}, false
|
||||
}
|
||||
|
||||
|
|
@ -199,13 +227,13 @@ func (self *StagingHelper) diffLineInfoFromMetadata(v *gocui.View, viewLineIdx i
|
|||
}, true
|
||||
}
|
||||
|
||||
func (self *StagingHelper) diffLineInfoFromBuffer(v *gocui.View, viewLineIdx int) (types.DiffLineInfo, bool) {
|
||||
bufferLineIdx, ok := v.BufferLineForViewLine(viewLineIdx)
|
||||
if !ok {
|
||||
return types.DiffLineInfo{}, false
|
||||
func (self *StagingHelper) diffLineInfoFromBuffer(contents []gocui.DiffLineContent, idx int) (types.DiffLineInfo, bool) {
|
||||
texts := make([]string, len(contents))
|
||||
for i, c := range contents {
|
||||
texts[i] = c.Text
|
||||
}
|
||||
|
||||
parsed, ok := parseDiffLineFromBuffer(v.BufferLines(), bufferLineIdx)
|
||||
parsed, ok := parseDiffLineFromBuffer(texts, idx)
|
||||
if !ok {
|
||||
return types.DiffLineInfo{}, false
|
||||
}
|
||||
|
|
@ -218,12 +246,7 @@ func (self *StagingHelper) diffLineInfoFromBuffer(v *gocui.View, viewLineIdx int
|
|||
}, true
|
||||
}
|
||||
|
||||
func (self *StagingHelper) diffLineInfoFromHyperlink(v *gocui.View, viewLineIdx int) (types.DiffLineInfo, bool) {
|
||||
hyperlink, ok := v.HyperLinkInLine(viewLineIdx, "lazygit-edit:")
|
||||
if !ok {
|
||||
return types.DiffLineInfo{}, false
|
||||
}
|
||||
|
||||
func (self *StagingHelper) diffLineInfoFromHyperlink(hyperlink string) (types.DiffLineInfo, bool) {
|
||||
matches := lazygitEditURLRegexp.FindStringSubmatch(hyperlink)
|
||||
if matches == nil {
|
||||
return types.DiffLineInfo{}, false
|
||||
|
|
|
|||
Loading…
Reference in a new issue