golang.org/x/tools/gopls@v0.15.3/internal/golang/change_quote.go (about)

     1  // Copyright 2023 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 golang
     6  
     7  import (
     8  	"go/ast"
     9  	"go/token"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"golang.org/x/tools/go/ast/astutil"
    14  	"golang.org/x/tools/gopls/internal/file"
    15  	"golang.org/x/tools/gopls/internal/protocol"
    16  	"golang.org/x/tools/gopls/internal/util/bug"
    17  	"golang.org/x/tools/gopls/internal/util/safetoken"
    18  	"golang.org/x/tools/internal/diff"
    19  )
    20  
    21  // ConvertStringLiteral reports whether we can convert between raw and interpreted
    22  // string literals in the [start, end), along with a CodeAction containing the edits.
    23  //
    24  // Only the following conditions are true, the action in result is valid
    25  //   - [start, end) is enclosed by a string literal
    26  //   - if the string is interpreted string, need check whether the convert is allowed
    27  func ConvertStringLiteral(pgf *ParsedGoFile, fh file.Handle, rng protocol.Range) (protocol.CodeAction, bool) {
    28  	startPos, endPos, err := pgf.RangePos(rng)
    29  	if err != nil {
    30  		return protocol.CodeAction{}, false // e.g. invalid range
    31  	}
    32  	path, _ := astutil.PathEnclosingInterval(pgf.File, startPos, endPos)
    33  	lit, ok := path[0].(*ast.BasicLit)
    34  	if !ok || lit.Kind != token.STRING {
    35  		return protocol.CodeAction{}, false
    36  	}
    37  
    38  	str, err := strconv.Unquote(lit.Value)
    39  	if err != nil {
    40  		return protocol.CodeAction{}, false
    41  	}
    42  
    43  	interpreted := lit.Value[0] == '"'
    44  	// Not all "..." strings can be represented as `...` strings.
    45  	if interpreted && !strconv.CanBackquote(strings.ReplaceAll(str, "\n", "")) {
    46  		return protocol.CodeAction{}, false
    47  	}
    48  
    49  	var (
    50  		title   string
    51  		newText string
    52  	)
    53  	if interpreted {
    54  		title = "Convert to raw string literal"
    55  		newText = "`" + str + "`"
    56  	} else {
    57  		title = "Convert to interpreted string literal"
    58  		newText = strconv.Quote(str)
    59  	}
    60  
    61  	start, end, err := safetoken.Offsets(pgf.Tok, lit.Pos(), lit.End())
    62  	if err != nil {
    63  		bug.Reportf("failed to get string literal offset by token.Pos:%v", err)
    64  		return protocol.CodeAction{}, false
    65  	}
    66  	edits := []diff.Edit{{
    67  		Start: start,
    68  		End:   end,
    69  		New:   newText,
    70  	}}
    71  	pedits, err := protocol.EditsFromDiffEdits(pgf.Mapper, edits)
    72  	if err != nil {
    73  		bug.Reportf("failed to convert diff.Edit to protocol.TextEdit:%v", err)
    74  		return protocol.CodeAction{}, false
    75  	}
    76  
    77  	return protocol.CodeAction{
    78  		Title: title,
    79  		Kind:  protocol.RefactorRewrite,
    80  		Edit: &protocol.WorkspaceEdit{
    81  			DocumentChanges: documentChanges(fh, pedits),
    82  		},
    83  	}, true
    84  }