blob: 6fa56d42615cb5c7d310745b202113478f625822 [file] [log] [blame]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package golang
import (
"go/ast"
"go/token"
"strconv"
"strings"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/gopls/internal/cache/parsego"
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/settings"
"golang.org/x/tools/gopls/internal/util/bug"
"golang.org/x/tools/gopls/internal/util/safetoken"
"golang.org/x/tools/internal/diff"
)
// convertStringLiteral reports whether we can convert between raw and interpreted
// string literals in the [start, end) range, along with a CodeAction containing the edits.
//
// Only the following conditions are true, the action in result is valid
// - [start, end) is enclosed by a string literal
// - if the string is interpreted string, need check whether the convert is allowed
func convertStringLiteral(pgf *parsego.File, fh file.Handle, startPos, endPos token.Pos) (protocol.CodeAction, bool) {
path, _ := astutil.PathEnclosingInterval(pgf.File, startPos, endPos)
lit, ok := path[0].(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
return protocol.CodeAction{}, false
}
str, err := strconv.Unquote(lit.Value)
if err != nil {
return protocol.CodeAction{}, false
}
interpreted := lit.Value[0] == '"'
// Not all "..." strings can be represented as `...` strings.
if interpreted && !strconv.CanBackquote(strings.ReplaceAll(str, "\n", "")) {
return protocol.CodeAction{}, false
}
var (
title string
newText string
)
if interpreted {
title = "Convert to raw string literal"
newText = "`" + str + "`"
} else {
title = "Convert to interpreted string literal"
newText = strconv.Quote(str)
}
start, end, err := safetoken.Offsets(pgf.Tok, lit.Pos(), lit.End())
if err != nil {
bug.Reportf("failed to get string literal offset by token.Pos:%v", err)
return protocol.CodeAction{}, false
}
edits := []diff.Edit{{
Start: start,
End: end,
New: newText,
}}
textedits, err := protocol.EditsFromDiffEdits(pgf.Mapper, edits)
if err != nil {
bug.Reportf("failed to convert diff.Edit to protocol.TextEdit:%v", err)
return protocol.CodeAction{}, false
}
return protocol.CodeAction{
Title: title,
Kind: settings.RefactorRewriteChangeQuote,
Edit: protocol.NewWorkspaceEdit(protocol.DocumentChangeEdit(fh, textedits)),
}, true
}