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 }