1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package lsp
6
7import (
8	"context"
9	"fmt"
10	"sort"
11
12	"golang.org/x/tools/internal/event"
13	"golang.org/x/tools/internal/lsp/command"
14	"golang.org/x/tools/internal/lsp/mod"
15	"golang.org/x/tools/internal/lsp/protocol"
16	"golang.org/x/tools/internal/lsp/source"
17)
18
19func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
20	snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
21	defer release()
22	if !ok {
23		return nil, err
24	}
25	var lenses map[command.Command]source.LensFunc
26	switch fh.Kind() {
27	case source.Mod:
28		lenses = mod.LensFuncs()
29	case source.Go:
30		lenses = source.LensFuncs()
31	default:
32		// Unsupported file kind for a code lens.
33		return nil, nil
34	}
35	var result []protocol.CodeLens
36	for cmd, lf := range lenses {
37		if !snapshot.View().Options().Codelenses[string(cmd)] {
38			continue
39		}
40		added, err := lf(ctx, snapshot, fh)
41		// Code lens is called on every keystroke, so we should just operate in
42		// a best-effort mode, ignoring errors.
43		if err != nil {
44			event.Error(ctx, fmt.Sprintf("code lens %s failed", cmd), err)
45			continue
46		}
47		result = append(result, added...)
48	}
49	sort.Slice(result, func(i, j int) bool {
50		a, b := result[i], result[j]
51		if protocol.CompareRange(a.Range, b.Range) == 0 {
52			return a.Command.Command < b.Command.Command
53		}
54		return protocol.CompareRange(a.Range, b.Range) < 0
55	})
56	return result, nil
57}
58