github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/work/hover.go (about) 1 // Copyright 2022 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 5 package work 6 7 import ( 8 "bytes" 9 "context" 10 "go/token" 11 12 "golang.org/x/mod/modfile" 13 "github.com/powerman/golang-tools/internal/event" 14 "github.com/powerman/golang-tools/internal/lsp/protocol" 15 "github.com/powerman/golang-tools/internal/lsp/source" 16 errors "golang.org/x/xerrors" 17 ) 18 19 func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.Hover, error) { 20 // We only provide hover information for the view's go.work file. 21 if fh.URI() != snapshot.WorkFile() { 22 return nil, nil 23 } 24 25 ctx, done := event.Start(ctx, "work.Hover") 26 defer done() 27 28 // Get the position of the cursor. 29 pw, err := snapshot.ParseWork(ctx, fh) 30 if err != nil { 31 return nil, errors.Errorf("getting go.work file handle: %w", err) 32 } 33 spn, err := pw.Mapper.PointSpan(position) 34 if err != nil { 35 return nil, errors.Errorf("computing cursor position: %w", err) 36 } 37 hoverRng, err := spn.Range(pw.Mapper.Converter) 38 if err != nil { 39 return nil, errors.Errorf("computing hover range: %w", err) 40 } 41 42 // Confirm that the cursor is inside a use statement, and then find 43 // the position of the use statement's directory path. 44 use, pathStart, pathEnd := usePath(pw, hoverRng.Start) 45 46 // The cursor position is not on a use statement. 47 if use == nil { 48 return nil, nil 49 } 50 51 // Get the mod file denoted by the use. 52 modfh, err := snapshot.GetFile(ctx, modFileURI(pw, use)) 53 if err != nil { 54 return nil, errors.Errorf("getting modfile handle: %w", err) 55 } 56 pm, err := snapshot.ParseMod(ctx, modfh) 57 if err != nil { 58 return nil, errors.Errorf("getting modfile handle: %w", err) 59 } 60 mod := pm.File.Module.Mod 61 62 // Get the range to highlight for the hover. 63 rng, err := source.ByteOffsetsToRange(pw.Mapper, fh.URI(), pathStart, pathEnd) 64 if err != nil { 65 return nil, err 66 } 67 options := snapshot.View().Options() 68 return &protocol.Hover{ 69 Contents: protocol.MarkupContent{ 70 Kind: options.PreferredContentFormat, 71 Value: mod.Path, 72 }, 73 Range: rng, 74 }, nil 75 } 76 77 func usePath(pw *source.ParsedWorkFile, pos token.Pos) (use *modfile.Use, pathStart, pathEnd int) { 78 for _, u := range pw.File.Use { 79 path := []byte(u.Path) 80 s, e := u.Syntax.Start.Byte, u.Syntax.End.Byte 81 i := bytes.Index(pw.Mapper.Content[s:e], path) 82 if i == -1 { 83 // This should not happen. 84 continue 85 } 86 // Shift the start position to the location of the 87 // module directory within the use statement. 88 pathStart, pathEnd = s+i, s+i+len(path) 89 if token.Pos(pathStart) <= pos && pos <= token.Pos(pathEnd) { 90 return u, pathStart, pathEnd 91 } 92 } 93 return nil, 0, 0 94 }