diff --git a/internal/lsp/folding_range.go b/internal/lsp/folding_range.go index a81961915d..f8913159a5 100644 --- a/internal/lsp/folding_range.go +++ b/internal/lsp/folding_range.go @@ -15,14 +15,27 @@ func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRange if err != nil { return nil, err } - m, err := getMapper(ctx, f) - if err != nil { - return nil, err - } - ranges, err := source.FoldingRange(ctx, view, f, s.session.Options().LineFoldingOnly) if err != nil { return nil, err } - return source.ToProtocolFoldingRanges(m, ranges) + return toProtocolFoldingRanges(ranges) +} + +func toProtocolFoldingRanges(ranges []*source.FoldingRangeInfo) ([]protocol.FoldingRange, error) { + result := make([]protocol.FoldingRange, 0, len(ranges)) + for _, info := range ranges { + rng, err := info.Range() + if err != nil { + return nil, err + } + result = append(result, protocol.FoldingRange{ + StartLine: rng.Start.Line, + StartCharacter: rng.Start.Character, + EndLine: rng.End.Line, + EndCharacter: rng.End.Character, + Kind: string(info.Kind), + }) + } + return result, nil } diff --git a/internal/lsp/link.go b/internal/lsp/link.go index 67869191ca..954aea2db4 100644 --- a/internal/lsp/link.go +++ b/internal/lsp/link.go @@ -27,7 +27,7 @@ func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLink if err != nil { return nil, err } - m, err := getMapper(ctx, f) + data, _, err := f.Handle(ctx).Read(ctx) if err != nil { return nil, err } @@ -35,6 +35,8 @@ func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLink if file == nil { return nil, err } + tok := view.Session().Cache().FileSet().File(file.Pos()) + m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), f.FileSet(), tok, data) var links []protocol.DocumentLink ast.Inspect(file, func(node ast.Node) bool { diff --git a/internal/lsp/source/folding_range.go b/internal/lsp/source/folding_range.go index 96158e1700..4ffd612c87 100644 --- a/internal/lsp/source/folding_range.go +++ b/internal/lsp/source/folding_range.go @@ -11,8 +11,8 @@ import ( ) type FoldingRangeInfo struct { - Range span.Range - Kind protocol.FoldingRangeKind + mappedRange + Kind protocol.FoldingRangeKind } // FoldingRange gets all of the folding range for f. @@ -24,9 +24,14 @@ func FoldingRange(ctx context.Context, view View, f GoFile, lineFoldingOnly bool if err != nil { return nil, err } + data, _, err := f.Handle(ctx).Read(ctx) + if err != nil { + return nil, err + } + m := protocol.NewColumnMapper(f.URI(), f.URI().Filename(), fset, fset.File(file.Pos()), data) // Get folding ranges for comments separately as they are not walked by ast.Inspect. - ranges = append(ranges, commentsFoldingRange(f.FileSet(), file)...) + ranges = append(ranges, commentsFoldingRange(view, m, file)...) foldingFunc := foldingRange if lineFoldingOnly { @@ -34,29 +39,26 @@ func FoldingRange(ctx context.Context, view View, f GoFile, lineFoldingOnly bool } visit := func(n ast.Node) bool { - rng := foldingFunc(fset, n) + rng := foldingFunc(view, m, n) if rng != nil { ranges = append(ranges, rng) } return true } - // Walk the ast and collect folding ranges. ast.Inspect(file, visit) sort.Slice(ranges, func(i, j int) bool { - if ranges[i].Range.Start < ranges[j].Range.Start { - return true - } else if ranges[i].Range.Start > ranges[j].Range.Start { - return false - } - return ranges[i].Range.End < ranges[j].Range.End + irng, _ := ranges[i].Range() + jrng, _ := ranges[j].Range() + return protocol.CompareRange(irng, jrng) < 0 }) + return ranges, nil } // foldingRange calculates the folding range for n. -func foldingRange(fset *token.FileSet, n ast.Node) *FoldingRangeInfo { +func foldingRange(view View, m *protocol.ColumnMapper, n ast.Node) *FoldingRangeInfo { var kind protocol.FoldingRangeKind var start, end token.Pos switch n := n.(type) { @@ -80,18 +82,22 @@ func foldingRange(fset *token.FileSet, n ast.Node) *FoldingRangeInfo { } start, end = n.Lparen+1, n.Rparen } - if !start.IsValid() || !end.IsValid() { return nil } return &FoldingRangeInfo{ - Range: span.NewRange(fset, start, end), - Kind: kind, + mappedRange: mappedRange{ + m: m, + spanRange: span.NewRange(view.Session().Cache().FileSet(), start, end), + }, + Kind: kind, } } // lineFoldingRange calculates the line folding range for n. -func lineFoldingRange(fset *token.FileSet, n ast.Node) *FoldingRangeInfo { +func lineFoldingRange(view View, m *protocol.ColumnMapper, n ast.Node) *FoldingRangeInfo { + fset := view.Session().Cache().FileSet() + // TODO(suzmue): include trailing empty lines before the closing // parenthesis/brace. var kind protocol.FoldingRangeKind @@ -169,47 +175,31 @@ func lineFoldingRange(fset *token.FileSet, n ast.Node) *FoldingRangeInfo { return nil } return &FoldingRangeInfo{ - Range: span.NewRange(fset, start, end), - Kind: kind, + mappedRange: mappedRange{ + m: m, + spanRange: span.NewRange(fset, start, end), + }, + Kind: kind, } } // commentsFoldingRange returns the folding ranges for all comment blocks in file. // The folding range starts at the end of the first comment, and ends at the end of the // comment block and has kind protocol.Comment. -func commentsFoldingRange(fset *token.FileSet, file *ast.File) (comments []*FoldingRangeInfo) { +func commentsFoldingRange(view View, m *protocol.ColumnMapper, file *ast.File) (comments []*FoldingRangeInfo) { for _, commentGrp := range file.Comments { // Don't fold single comments. if len(commentGrp.List) <= 1 { continue } comments = append(comments, &FoldingRangeInfo{ - // Fold from the end of the first line comment to the end of the comment block. - Range: span.NewRange(fset, commentGrp.List[0].End(), commentGrp.End()), - Kind: protocol.Comment, + mappedRange: mappedRange{ + m: m, + // Fold from the end of the first line comment to the end of the comment block. + spanRange: span.NewRange(view.Session().Cache().FileSet(), commentGrp.List[0].End(), commentGrp.End()), + }, + Kind: protocol.Comment, }) } return comments } - -func ToProtocolFoldingRanges(m *protocol.ColumnMapper, ranges []*FoldingRangeInfo) ([]protocol.FoldingRange, error) { - var res []protocol.FoldingRange - for _, r := range ranges { - spn, err := r.Range.Span() - if err != nil { - return nil, err - } - rng, err := m.Range(spn) - if err != nil { - return nil, err - } - res = append(res, protocol.FoldingRange{ - StartLine: rng.Start.Line, - StartCharacter: rng.Start.Character, - EndLine: rng.End.Line, - EndCharacter: rng.End.Character, - Kind: string(r.Kind), - }) - } - return res, nil -} diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go index d307d0152d..d41062f2ec 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -305,7 +305,7 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data string, ranges []*source.FoldingRangeInfo) { t.Helper() // Fold all ranges. - nonOverlapping := nonOverlappingRanges(ranges) + nonOverlapping := nonOverlappingRanges(t, ranges) for i, rngs := range nonOverlapping { got, err := foldRanges(string(data), rngs) if err != nil { @@ -332,7 +332,7 @@ func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data s } } - nonOverlapping := nonOverlappingRanges(kindOnly) + nonOverlapping := nonOverlappingRanges(t, kindOnly) for i, rngs := range nonOverlapping { got, err := foldRanges(string(data), rngs) if err != nil { @@ -352,13 +352,13 @@ func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data s } } -func nonOverlappingRanges(ranges []*source.FoldingRangeInfo) (res [][]*source.FoldingRangeInfo) { +func nonOverlappingRanges(t *testing.T, ranges []*source.FoldingRangeInfo) (res [][]*source.FoldingRangeInfo) { for _, fRng := range ranges { setNum := len(res) for i := 0; i < len(res); i++ { canInsert := true for _, rng := range res[i] { - if conflict(rng, fRng) { + if conflict(t, rng, fRng) { canInsert = false break } @@ -376,9 +376,17 @@ func nonOverlappingRanges(ranges []*source.FoldingRangeInfo) (res [][]*source.Fo return res } -func conflict(a, b *source.FoldingRangeInfo) bool { +func conflict(t *testing.T, a, b *source.FoldingRangeInfo) bool { + arng, err := a.Range() + if err != nil { + t.Fatal(err) + } + brng, err := b.Range() + if err != nil { + t.Fatal(err) + } // a start position is <= b start positions - return a.Range.Start <= b.Range.Start && a.Range.End > b.Range.Start + return protocol.ComparePosition(arng.Start, brng.Start) <= 0 && protocol.ComparePosition(arng.End, brng.Start) > 0 } func foldRanges(contents string, ranges []*source.FoldingRangeInfo) (string, error) { @@ -388,7 +396,7 @@ func foldRanges(contents string, ranges []*source.FoldingRangeInfo) (string, err // to preserve the offsets. for i := len(ranges) - 1; i >= 0; i-- { fRange := ranges[i] - spn, err := fRange.Range.Span() + spn, err := fRange.Span() if err != nil { return "", err }