diff --git a/dap/adapter.go b/dap/adapter.go index 7ced02dce..c9b823dbe 100644 --- a/dap/adapter.go +++ b/dap/adapter.go @@ -9,6 +9,7 @@ import ( "path" "path/filepath" "slices" + "strings" "sync" "sync/atomic" @@ -573,7 +574,9 @@ func (b *breakpointMap) Set(fname string, sbps []dap.SourceBreakpoint) (breakpoi // null back in the JSON if there are no breakpoints breakpoints = []dap.Breakpoint{} - prev := b.byPath[fname] + // Use lowercase paths to normalize the case. We can only know the correct casing after + // we intersect the breakpoint map. When we report the pending breakpoint to the editor, + prev := b.getByPath(fname) for _, sbp := range sbps { index := slices.IndexFunc(prev, func(e dap.Breakpoint) bool { return sbp.Line >= e.Line && sbp.Line <= e.EndLine && sbp.Column >= e.Column && sbp.Column <= e.EndColumn @@ -589,12 +592,16 @@ func (b *breakpointMap) Set(fname string, sbps []dap.SourceBreakpoint) (breakpoi EndLine: sbp.Line, Column: sbp.Column, EndColumn: sbp.Column, - Reason: "pending", + Source: &dap.Source{ + Name: path.Base(fname), + Path: fname, + }, + Reason: "pending", } } breakpoints = append(breakpoints, bp) } - b.byPath[fname] = breakpoints + b.setByPath(fname, breakpoints) return breakpoints } @@ -615,7 +622,7 @@ func (b *breakpointMap) Intersect(ctx Context, src *pb.Source, ws string) map[di for _, info := range src.Infos { fname := filepath.Join(ws, info.Filename) - bps := b.byPath[fname] + bps := b.getByPath(fname) for _, bp := range bps { if !bp.Verified && bp.Reason != "failed" { bp.Reason = "failed" @@ -656,7 +663,7 @@ func (b *breakpointMap) intersect(ctx Context, src *pb.Source, locs *pb.Location info := src.Infos[loc.SourceIndex] fname := filepath.Join(ws, info.Filename) - bps := b.byPath[fname] + bps := b.getByPath(fname) if len(bps) == 0 { // No breakpoints for this file. continue @@ -675,6 +682,13 @@ func (b *breakpointMap) intersect(ctx Context, src *pb.Source, locs *pb.Location bp.Verified = true bp.Reason = "" + // The path from the source might be different than the path + // the editor sent to us just because of different casing. + // Prefer the version given to us from buildkit and tell + // the editor what the proper casing for this should be. + bp.Source.Name = path.Base(fname) + bp.Source.Path = fname + ctx.C() <- &dap.BreakpointEvent{ Event: dap.Event{Event: "breakpoint"}, Body: dap.BreakpointEventBody{ @@ -689,3 +703,11 @@ func (b *breakpointMap) intersect(ctx Context, src *pb.Source, locs *pb.Location } return 0 } + +func (b *breakpointMap) setByPath(fname string, bps []dap.Breakpoint) { + b.byPath[strings.ToLower(fname)] = bps +} + +func (b *breakpointMap) getByPath(fname string) []dap.Breakpoint { + return b.byPath[strings.ToLower(fname)] +} diff --git a/dap/adapter_test.go b/dap/adapter_test.go index cf2f1db15..21760ccb5 100644 --- a/dap/adapter_test.go +++ b/dap/adapter_test.go @@ -202,7 +202,7 @@ func TestBreakpointMapIntersectVerified(t *testing.T) { assert.Len(t, digests, wantMatches) expectedEvents := make(map[int]struct{}) - for i, bp := range bm.byPath[fpath] { + for i, bp := range bm.getByPath(fpath) { if breakpointCases[i].expectVerified { expectedEvents[bp.Id] = struct{}{} } @@ -226,7 +226,7 @@ func TestBreakpointMapIntersectVerified(t *testing.T) { } } - stored := bm.byPath[fpath] + stored := bm.getByPath(fpath) if assert.Len(t, stored, len(breakpointCases)) { for i, bc := range breakpointCases { assert.Equal(t, bc.expectVerified, stored[i].Verified, "breakpoint %d (%s) mismatch", i, bc.desc) diff --git a/tests/dap_build.go b/tests/dap_build.go index ce7f169b6..32816f150 100644 --- a/tests/dap_build.go +++ b/tests/dap_build.go @@ -228,18 +228,26 @@ func testDapBuildVerifiedBreakpoints(t *testing.T, sb integration.Sandbox) { { Reason: "changed", Breakpoint: dap.Breakpoint{ - Id: 1, - Line: 2, - EndLine: 2, + Id: 1, + Line: 2, + EndLine: 2, + Source: &dap.Source{ + Name: "Dockerfile", + Path: path.Join(dir, "Dockerfile"), + }, Verified: true, }, }, { Reason: "changed", Breakpoint: dap.Breakpoint{ - Id: 2, - Line: 10, - EndLine: 10, + Id: 2, + Line: 10, + EndLine: 10, + Source: &dap.Source{ + Name: "Dockerfile", + Path: path.Join(dir, "Dockerfile"), + }, Verified: false, Reason: "failed", },