golang.org/x/tools/gopls@v0.15.3/internal/server/selection_range.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 server 6 7 import ( 8 "context" 9 "fmt" 10 11 "golang.org/x/tools/go/ast/astutil" 12 "golang.org/x/tools/gopls/internal/cache/parsego" 13 "golang.org/x/tools/gopls/internal/file" 14 "golang.org/x/tools/gopls/internal/protocol" 15 "golang.org/x/tools/internal/event" 16 ) 17 18 // selectionRange defines the textDocument/selectionRange feature, 19 // which, given a list of positions within a file, 20 // reports a linked list of enclosing syntactic blocks, innermost first. 21 // 22 // See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange. 23 // 24 // This feature can be used by a client to implement "expand selection" in a 25 // language-aware fashion. Multiple input positions are supported to allow 26 // for multiple cursors, and the entire path up to the whole document is 27 // returned for each cursor to avoid multiple round-trips when the user is 28 // likely to issue this command multiple times in quick succession. 29 func (s *server) SelectionRange(ctx context.Context, params *protocol.SelectionRangeParams) ([]protocol.SelectionRange, error) { 30 ctx, done := event.Start(ctx, "lsp.Server.selectionRange") 31 defer done() 32 33 fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI) 34 if err != nil { 35 return nil, err 36 } 37 defer release() 38 39 if kind := snapshot.FileKind(fh); kind != file.Go { 40 return nil, fmt.Errorf("SelectionRange not supported for file of type %s", kind) 41 } 42 43 pgf, err := snapshot.ParseGo(ctx, fh, parsego.ParseFull) 44 if err != nil { 45 return nil, err 46 } 47 48 result := make([]protocol.SelectionRange, len(params.Positions)) 49 for i, protocolPos := range params.Positions { 50 pos, err := pgf.PositionPos(protocolPos) 51 if err != nil { 52 return nil, err 53 } 54 55 path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos) 56 57 tail := &result[i] // tail of the Parent linked list, built head first 58 59 for j, node := range path { 60 rng, err := pgf.NodeRange(node) 61 if err != nil { 62 return nil, err 63 } 64 65 // Add node to tail. 66 if j > 0 { 67 tail.Parent = &protocol.SelectionRange{} 68 tail = tail.Parent 69 } 70 tail.Range = rng 71 } 72 } 73 74 return result, nil 75 }