Skip to content

Commit

Permalink
replace_all
Browse files Browse the repository at this point in the history
new test

zz

reflect

diable in lhs
  • Loading branch information
xzbdmw committed Dec 11, 2024
1 parent ab5f969 commit 34d0e27
Show file tree
Hide file tree
Showing 21 changed files with 1,067 additions and 208 deletions.
Binary file added gopls/doc/assets/extract-val-all-before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added gopls/doc/assets/extract-var-all-after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 10 additions & 3 deletions gopls/doc/features/transformation.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Gopls supports the following code actions:
- [`refactor.extract.method`](#extract)
- [`refactor.extract.toNewFile`](#extract.toNewFile)
- [`refactor.extract.variable`](#extract)
- [`refactor.extract.variable-all`](#extract)
- [`refactor.inline.call`](#refactor.inline.call)
- [`refactor.rewrite.changeQuote`](#refactor.rewrite.changeQuote)
- [`refactor.rewrite.fillStruct`](#refactor.rewrite.fillStruct)
Expand Down Expand Up @@ -364,14 +365,22 @@ newly created declaration that contains the selected code:
will be a method of the same receiver type.

- **`refactor.extract.variable`** replaces an expression by a reference to a new
local variable named `x` initialized by the expression:
local variable named `newVar` initialized by the expression:

![Before extracting a var](../assets/extract-var-before.png)
![After extracting a var](../assets/extract-var-after.png)

- **`refactor.extract.constant** does the same thing for a constant
expression, introducing a local const declaration.
- **`refactor.extract.variable-all`** replaces all occurrences of the selected expression
within the function with a reference to a new local variable named `newVar`.
This extracts the expression once and reuses it wherever it appears in the function.

![Before extracting all occurrences of EXPR](../assets/extract-var-all-before.png)
![After extracting all occurrences of EXPR](../assets/extract-var-all-after.png)

- **`refactor.extract.constant-all** does the same thing for a constant
expression, introducing a local const declaration.
If the default name for the new declaration is already in use, gopls
generates a fresh name.

Expand All @@ -387,10 +396,8 @@ number of cases where it falls short, including:

- https://github.com/golang/go/issues/66289
- https://github.com/golang/go/issues/65944
- https://github.com/golang/go/issues/64821
- https://github.com/golang/go/issues/63394
- https://github.com/golang/go/issues/61496
- https://github.com/golang/go/issues/50851

The following Extract features are planned for 2024 but not yet supported:

Expand Down
5 changes: 5 additions & 0 deletions gopls/doc/release/v0.18.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ func (C[T]) Pop() (T, bool) { ... }
var _ Stack[int] = C[int]{}
```

## Extract all occurrences of the same expression under selection

When you have multiple instances of the same expression in a function,
you can use this code action to extract it into a variable.
All occurrences of the expression will be replaced with a reference to the new variable.
36 changes: 34 additions & 2 deletions gopls/internal/golang/codeaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ var codeActionProducers = [...]codeActionProducer{
{kind: settings.RefactorExtractToNewFile, fn: refactorExtractToNewFile},
{kind: settings.RefactorExtractConstant, fn: refactorExtractVariable, needPkg: true},
{kind: settings.RefactorExtractVariable, fn: refactorExtractVariable, needPkg: true},
{kind: settings.RefactorExtractConstantAll, fn: refactorExtractVariableAll, needPkg: true},
{kind: settings.RefactorExtractVariableAll, fn: refactorExtractVariableAll, needPkg: true},
{kind: settings.RefactorInlineCall, fn: refactorInlineCall, needPkg: true},
{kind: settings.RefactorRewriteChangeQuote, fn: refactorRewriteChangeQuote},
{kind: settings.RefactorRewriteFillStruct, fn: refactorRewriteFillStruct, needPkg: true},
Expand Down Expand Up @@ -467,14 +469,15 @@ func refactorExtractMethod(ctx context.Context, req *codeActionsRequest) error {
// See [extractVariable] for command implementation.
func refactorExtractVariable(ctx context.Context, req *codeActionsRequest) error {
info := req.pkg.TypesInfo()
if expr, _, err := canExtractVariable(info, req.pgf.File, req.start, req.end); err == nil {
if exprs, err := canExtractVariable(info, req.pgf.File, req.start, req.end, false); err == nil {
// Offer one of refactor.extract.{constant,variable}
// based on the constness of the expression; this is a
// limitation of the codeActionProducers mechanism.
// Beware that future evolutions of the refactorings
// may make them diverge to become non-complementary,
// for example because "if const x = ...; y {" is illegal.
constant := info.Types[expr].Value != nil
// Same as [refactorExtractVariableAll].
constant := info.Types[exprs[0]].Value != nil
if (req.kind == settings.RefactorExtractConstant) == constant {
title := "Extract variable"
if constant {
Expand All @@ -486,6 +489,35 @@ func refactorExtractVariable(ctx context.Context, req *codeActionsRequest) error
return nil
}

// refactorExtractVariableAll produces "Extract N occurrences of EXPR" code action.
// See [extractAllOccursOfExpr] for command implementation.
func refactorExtractVariableAll(ctx context.Context, req *codeActionsRequest) error {
info := req.pkg.TypesInfo()
// Don't suggest if only one expr is found,
// otherwise it will duplicate with [refactorExtractVariable]
if exprs, err := canExtractVariable(info, req.pgf.File, req.start, req.end, true); err == nil && len(exprs) > 1 {
start, end, err := req.pgf.NodeOffsets(exprs[0])
if err != nil {
return err
}
desc := string(req.pgf.Src[start:end])
if len(desc) >= 40 || strings.Contains(desc, "\n") {
desc = astutil.NodeDescription(exprs[0])
}
constant := info.Types[exprs[0]].Value != nil
if (req.kind == settings.RefactorExtractConstantAll) == constant {
var title string
if constant {
title = fmt.Sprintf("Extract %d occurrences of const expression: %s", len(exprs), desc)
} else {
title = fmt.Sprintf("Extract %d occurrences of %s", len(exprs), desc)
}
req.addApplyFixAction(title, fixExtractVariableAll, req.loc)
}
}
return nil
}

// refactorExtractToNewFile produces "Extract declarations to new file" code actions.
// See [server.commandHandler.ExtractToNewFile] for command implementation.
func refactorExtractToNewFile(ctx context.Context, req *codeActionsRequest) error {
Expand Down
Loading

0 comments on commit 34d0e27

Please sign in to comment.