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  }