From 19791adf9a04519fb412db21c435b7047300bdd4 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:08:42 +0800 Subject: [PATCH 1/4] support refactor.extract.propToOutput --- internal/langserver/handlers/code_action.go | 15 + .../handlers/extract_prop_to_output.go | 75 +++ .../handlers/extract_prop_to_output_test.go | 483 ++++++++++++++++++ internal/lsp/code_actions.go | 49 +- 4 files changed, 599 insertions(+), 23 deletions(-) create mode 100644 internal/langserver/handlers/extract_prop_to_output.go create mode 100644 internal/langserver/handlers/extract_prop_to_output_test.go diff --git a/internal/langserver/handlers/code_action.go b/internal/langserver/handlers/code_action.go index e062eec7..ae1861e8 100644 --- a/internal/langserver/handlers/code_action.go +++ b/internal/langserver/handlers/code_action.go @@ -74,6 +74,21 @@ func (svc *service) textDocumentCodeAction(ctx context.Context, params lsp.CodeA }, }, }) + case ilsp.ExtractPropertyToOutput: + edits, err := svc.ExtractPropToOutput(ctx, params) + if err != nil { + return ca, err + } + + ca = append(ca, lsp.CodeAction{ + Title: "Extract Property to Output", + Kind: action, + Edit: lsp.WorkspaceEdit{ + Changes: map[lsp.DocumentURI][]lsp.TextEdit{ + lsp.DocumentURI(dh.FullURI()): edits, + }, + }, + }) } } diff --git a/internal/langserver/handlers/extract_prop_to_output.go b/internal/langserver/handlers/extract_prop_to_output.go new file mode 100644 index 00000000..97bd0fa8 --- /dev/null +++ b/internal/langserver/handlers/extract_prop_to_output.go @@ -0,0 +1,75 @@ +package handlers + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/hcl/v2/hclwrite" + ilsp "github.com/hashicorp/terraform-ls/internal/lsp" + lsp "github.com/hashicorp/terraform-ls/internal/protocol" +) + +func (svc *service) ExtractPropToOutput(ctx context.Context, params lsp.CodeActionParams) ([]lsp.TextEdit, error) { + var edits []lsp.TextEdit + + dh := ilsp.HandleFromDocumentURI(params.TextDocument.URI) + + doc, err := svc.stateStore.DocumentStore.GetDocument(dh) + if err != nil { + return edits, err + } + + mod, err := svc.stateStore.Modules.ModuleByPath(dh.Dir.Path()) + if err != nil { + return edits, err + } + + file, ok := mod.ParsedModuleFiles.AsMap()[dh.Filename] + if !ok { + return edits, err + } + + pos, err := ilsp.HCLPositionFromLspPosition(params.Range.Start, doc) + if err != nil { + return edits, err + } + + blocks := file.BlocksAtPos(pos) + if len(blocks) > 1 { + return edits, fmt.Errorf("found more than one block at pos: %v", pos) + } + if len(blocks) == 0 { + return edits, fmt.Errorf("can not find block at position %v", pos) + } + + attr := file.AttributeAtPos(pos) + if attr == nil { + return edits, fmt.Errorf("can not find attribute at position %v", pos) + } + + tfAddr := append(blocks[0].Labels, attr.Name) + + insertPos := lsp.Position{ + Line: uint32(len(doc.Lines)), + Character: uint32(len(doc.Lines)), + } + + edits = append(edits, lsp.TextEdit{ + Range: lsp.Range{ + Start: insertPos, + End: insertPos, + }, + NewText: outputBlock(strings.Join(tfAddr, "_"), strings.Join(tfAddr, ".")), + }) + return edits, nil +} + +func outputBlock(name, tfAddr string) string { + f := hclwrite.NewFile() + b := hclwrite.NewBlock("output", []string{name}) + b.Body().SetAttributeRaw("value", hclwrite.TokensForIdentifier(tfAddr)) + f.Body().AppendNewline() + f.Body().AppendBlock(b) + return string(f.Bytes()) +} diff --git a/internal/langserver/handlers/extract_prop_to_output_test.go b/internal/langserver/handlers/extract_prop_to_output_test.go new file mode 100644 index 00000000..42ef9a1b --- /dev/null +++ b/internal/langserver/handlers/extract_prop_to_output_test.go @@ -0,0 +1,483 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package handlers + +import ( + "errors" + "fmt" + "testing" + + "github.com/creachadair/jrpc2" + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-ls/internal/langserver" + "github.com/hashicorp/terraform-ls/internal/langserver/session" + "github.com/hashicorp/terraform-ls/internal/state" + "github.com/hashicorp/terraform-ls/internal/terraform/exec" + "github.com/hashicorp/terraform-ls/internal/walker" + "github.com/stretchr/testify/mock" +) + +func TestLangServer_extractPropToOutput_withoutInitialization(t *testing.T) { + ls := langserver.NewLangServerMock(t, NewMockSession(nil)) + stop := ls.Start(t) + defer stop() + + ls.CallAndExpectError(t, &langserver.CallRequest{ + Method: "textDocument/formatting", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "version": 0, + "languageId": "terraform", + "text": "provider \"github\" {}", + "uri": "%s/main.tf" + } + }`, TempDir(t).URI), + }, session.SessionNotInitialized.Err()) +} + +func TestLangServer_ExtractPropToOutput_basic(t *testing.T) { + tmpDir := TempDir(t) + + ss, err := state.NewStateStore() + if err != nil { + t.Fatal(err) + } + wc := walker.NewWalkerCollector() + + ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ + StateStore: ss, + WalkerCollector: wc, + TerraformCalls: &exec.TerraformMockCalls{ + PerWorkDir: map[string][]*mock.Call{ + tmpDir.Path(): { + { + Method: "Version", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + }, + ReturnArguments: []interface{}{ + version.Must(version.NewVersion("0.12.0")), + nil, + nil, + }, + }, + { + Method: "GetExecPath", + Repeatability: 1, + ReturnArguments: []interface{}{ + "", + }, + }, + { + Method: "Format", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + []byte("provider \"test\" {\n\n}\n"), + }, + ReturnArguments: []interface{}{ + []byte("provider \"test\" {\n\n}\n"), + nil, + }, + }, + }, + }, + }, + })) + stop := ls.Start(t) + defer stop() + + ls.Call(t, &langserver.CallRequest{ + Method: "initialize", + ReqParams: fmt.Sprintf(`{ + "capabilities": {}, + "rootUri": %q, + "processId": 12345 + }`, tmpDir.URI), + }) + waitForWalkerPath(t, ss, wc, tmpDir) + ls.Notify(t, &langserver.CallRequest{ + Method: "initialized", + ReqParams: "{}", + }) + ls.Call(t, &langserver.CallRequest{ + Method: "textDocument/didOpen", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "version": 0, + "languageId": "terraform", + "text": "provider \"test\" {}\n\nresource \"test_resource\" \"test\" {\n name = \"testname\"\n}", + "uri": "%s/main.tf" + } + }`, tmpDir.URI), + }) + waitForAllJobs(t, ss) + + ls.CallAndExpectResponse(t, &langserver.CallRequest{ + Method: "textDocument/codeAction", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "uri": "%s/main.tf" + }, + "range": { + "start": { + "line": 3, + "character": 17 + }, + "end": { + "line": 3, + "character": 17 + } + }, + "context": { + "only": [ + "refactor.extract.propToOut" + ] + } +}`, tmpDir.URI), + }, `{ + "jsonrpc": "2.0", + "id": 3, + "result": [ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 1, "character": 0 } + }, + "newText": "provider \"test\" {\n" + } + ] + }`) +} + +func TestLangServer_ExtractPropToOutput_oldVersion(t *testing.T) { + tmpDir := TempDir(t) + + ss, err := state.NewStateStore() + if err != nil { + t.Fatal(err) + } + wc := walker.NewWalkerCollector() + + ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ + StateStore: ss, + WalkerCollector: wc, + TerraformCalls: &exec.TerraformMockCalls{ + PerWorkDir: map[string][]*mock.Call{ + tmpDir.Path(): { + { + Method: "Version", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + }, + ReturnArguments: []interface{}{ + version.Must(version.NewVersion("0.7.6")), + nil, + nil, + }, + }, + { + Method: "GetExecPath", + Repeatability: 1, + ReturnArguments: []interface{}{ + "", + }, + }, + { + Method: "Format", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + []byte("provider \"test\" {\n\n}\n"), + }, + ReturnArguments: []interface{}{ + nil, + errors.New("not implemented"), + }, + }, + }, + }, + }, + })) + stop := ls.Start(t) + defer stop() + + ls.Call(t, &langserver.CallRequest{ + Method: "initialize", + ReqParams: fmt.Sprintf(`{ + "capabilities": {}, + "rootUri": %q, + "processId": 12345 + }`, tmpDir.URI), + }) + waitForWalkerPath(t, ss, wc, tmpDir) + + ls.Notify(t, &langserver.CallRequest{ + Method: "initialized", + ReqParams: "{}", + }) + ls.Call(t, &langserver.CallRequest{ + Method: "textDocument/didOpen", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "version": 0, + "languageId": "terraform", + "text": "provider \"test\" {\n\n}\n", + "uri": "%s/main.tf" + } + }`, tmpDir.URI), + }) + waitForAllJobs(t, ss) + + ls.CallAndExpectError(t, &langserver.CallRequest{ + Method: "textDocument/formatting", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "uri": "%s/main.tf" + } + }`, tmpDir.URI), + }, jrpc2.SystemError.Err()) +} + +func TestLangServer_extractPropToOutput_variables(t *testing.T) { + tmpDir := TempDir(t) + + ss, err := state.NewStateStore() + if err != nil { + t.Fatal(err) + } + wc := walker.NewWalkerCollector() + + ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ + StateStore: ss, + WalkerCollector: wc, + TerraformCalls: &exec.TerraformMockCalls{ + PerWorkDir: map[string][]*mock.Call{ + tmpDir.Path(): { + { + Method: "Version", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + }, + ReturnArguments: []interface{}{ + version.Must(version.NewVersion("0.12.0")), + nil, + nil, + }, + }, + { + Method: "GetExecPath", + Repeatability: 1, + ReturnArguments: []interface{}{ + "", + }, + }, + { + Method: "Format", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + []byte("test = \"dev\""), + }, + ReturnArguments: []interface{}{ + []byte("test = \"dev\""), + nil, + }, + }, + }, + }, + }, + })) + stop := ls.Start(t) + defer stop() + + ls.Call(t, &langserver.CallRequest{ + Method: "initialize", + ReqParams: fmt.Sprintf(`{ + "capabilities": {}, + "rootUri": %q, + "processId": 12345 + }`, tmpDir.URI), + }) + waitForWalkerPath(t, ss, wc, tmpDir) + + ls.Notify(t, &langserver.CallRequest{ + Method: "initialized", + ReqParams: "{}", + }) + ls.Call(t, &langserver.CallRequest{ + Method: "textDocument/didOpen", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "version": 0, + "languageId": "terraform-vars", + "text": "test = \"dev\"", + "uri": "%s/terraform.tfvars" + } + }`, tmpDir.URI), + }) + waitForAllJobs(t, ss) + + ls.CallAndExpectResponse(t, &langserver.CallRequest{ + Method: "textDocument/formatting", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "uri": "%s/terraform.tfvars" + } + }`, tmpDir.URI), + }, `{ + "jsonrpc": "2.0", + "id": 3, + "result": [ + { + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 13 } + }, + "newText": "test = \"dev\"" + } + ] + }`) +} + +func TestLangServer_extractPropToOutput_diffBug(t *testing.T) { + tmpDir := TempDir(t) + + cfg := `resource "aws_lambda_function" "f" { + environment { + variables = { + a = "b" + } + } +} +` + formattedCfg := `resource "aws_lambda_function" "f" { + environment { + variables = { + a = "b" + } + } +} +` + + ss, err := state.NewStateStore() + if err != nil { + t.Fatal(err) + } + wc := walker.NewWalkerCollector() + + ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ + StateStore: ss, + WalkerCollector: wc, + TerraformCalls: &exec.TerraformMockCalls{ + PerWorkDir: map[string][]*mock.Call{ + tmpDir.Path(): { + { + Method: "Version", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + }, + ReturnArguments: []interface{}{ + version.Must(version.NewVersion("0.12.0")), + nil, + nil, + }, + }, + { + Method: "GetExecPath", + Repeatability: 1, + ReturnArguments: []interface{}{ + "", + }, + }, + { + Method: "Format", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType("*context.valueCtx"), + []byte(cfg), + }, + ReturnArguments: []interface{}{ + []byte(formattedCfg), + nil, + }, + }, + }, + }, + }, + })) + stop := ls.Start(t) + defer stop() + + ls.Call(t, &langserver.CallRequest{ + Method: "initialize", + ReqParams: fmt.Sprintf(`{ + "capabilities": {}, + "rootUri": %q, + "processId": 12345 + }`, tmpDir.URI), + }) + waitForWalkerPath(t, ss, wc, tmpDir) + + ls.Notify(t, &langserver.CallRequest{ + Method: "initialized", + ReqParams: "{}", + }) + ls.Call(t, &langserver.CallRequest{ + Method: "textDocument/didOpen", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "version": 0, + "languageId": "terraform", + "text": `+fmt.Sprintf("%q", cfg)+`, + "uri": "%s/main.tf" + } + }`, tmpDir.URI), + }) + waitForAllJobs(t, ss) + + ls.CallAndExpectResponse(t, &langserver.CallRequest{ + Method: "textDocument/formatting", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "uri": "%s/main.tf" + } + }`, tmpDir.URI), + }, `{ + "jsonrpc": "2.0", + "id": 3, + "result": [ + { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 5, + "character": 0 + } + }, + "newText": " environment {\n variables = {\n a = \"b\"\n" + }, + { + "range": { + "start": { + "line": 6, + "character": 0 + }, + "end": { + "line": 6, + "character": 0 + } + }, + "newText":" }\n" + } + ] + }`) +} diff --git a/internal/lsp/code_actions.go b/internal/lsp/code_actions.go index b4dc6bf6..a7ae121d 100644 --- a/internal/lsp/code_actions.go +++ b/internal/lsp/code_actions.go @@ -5,6 +5,7 @@ package lsp import ( "sort" + "strings" lsp "github.com/hashicorp/terraform-ls/internal/protocol" ) @@ -12,31 +13,31 @@ import ( const ( // SourceFormatAllTerraform is a Terraform specific format code action. SourceFormatAllTerraform = "source.formatAll.terraform" + ExtractPropertyToOutput = "refactor.extract.propToOut" ) type CodeActions map[lsp.CodeActionKind]bool -var ( - // `source.*`: Source code actions apply to the entire file. They must be explicitly - // requested and will not show in the normal lightbulb menu. Source actions - // can be run on save using editor.codeActionsOnSave and are also shown in - // the source context menu. - // For action definitions, refer to: https://code.visualstudio.com/api/references/vscode-api#CodeActionKind - - // `source.fixAll`: Fix all actions automatically fix errors that have a clear fix that do - // not require user input. They should not suppress errors or perform unsafe - // fixes such as generating new types or classes. - // ** We don't support this as terraform fmt only adjusts style** - // lsp.SourceFixAll: true, - - // `source.formatAll`: Generic format code action. - // We do not register this for terraform to allow fine grained selection of actions. - // A user should be able to set `source.formatAll` to true, and source.formatAll.terraform to false to allow all - // files to be formatted, but not terraform files (or vice versa). - SupportedCodeActions = CodeActions{ - SourceFormatAllTerraform: true, - } -) +// `source.*`: Source code actions apply to the entire file. They must be explicitly +// requested and will not show in the normal lightbulb menu. Source actions +// can be run on save using editor.codeActionsOnSave and are also shown in +// the source context menu. +// For action definitions, refer to: https://code.visualstudio.com/api/references/vscode-api#CodeActionKind + +// `source.fixAll`: Fix all actions automatically fix errors that have a clear fix that do +// not require user input. They should not suppress errors or perform unsafe +// fixes such as generating new types or classes. +// ** We don't support this as terraform fmt only adjusts style** +// lsp.SourceFixAll: true, + +// `source.formatAll`: Generic format code action. +// We do not register this for terraform to allow fine grained selection of actions. +// A user should be able to set `source.formatAll` to true, and source.formatAll.terraform to false to allow all +// files to be formatted, but not terraform files (or vice versa). +var SupportedCodeActions = CodeActions{ + SourceFormatAllTerraform: true, + ExtractPropertyToOutput: true, +} func (c CodeActions) AsSlice() []lsp.CodeActionKind { s := make([]lsp.CodeActionKind, 0) @@ -54,8 +55,10 @@ func (ca CodeActions) Only(only []lsp.CodeActionKind) CodeActions { wanted := make(CodeActions, 0) for _, kind := range only { - if v, ok := ca[kind]; ok { - wanted[kind] = v + for n, v := range ca { + if strings.HasPrefix(string(n), string(kind)) { + wanted[n] = v + } } } From f6a07e6dfa9ba3367db11e45b34ed372e315bb9a Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:11:38 +0800 Subject: [PATCH 2/4] update testcase --- .../handlers/extract_prop_to_output_test.go | 226 ++++-------------- 1 file changed, 52 insertions(+), 174 deletions(-) diff --git a/internal/langserver/handlers/extract_prop_to_output_test.go b/internal/langserver/handlers/extract_prop_to_output_test.go index 42ef9a1b..e4e9e9a4 100644 --- a/internal/langserver/handlers/extract_prop_to_output_test.go +++ b/internal/langserver/handlers/extract_prop_to_output_test.go @@ -24,15 +24,27 @@ func TestLangServer_extractPropToOutput_withoutInitialization(t *testing.T) { defer stop() ls.CallAndExpectError(t, &langserver.CallRequest{ - Method: "textDocument/formatting", + Method: "textDocument/codeAction", ReqParams: fmt.Sprintf(`{ - "textDocument": { - "version": 0, - "languageId": "terraform", - "text": "provider \"github\" {}", - "uri": "%s/main.tf" - } - }`, TempDir(t).URI), + "textDocument": { + "uri": "%s/main.tf" + }, + "range": { + "start": { + "line": 4, + "character": 17 + }, + "end": { + "line": 4, + "character": 17 + } + }, + "context": { + "only": [ + "refactor.extract.propToOut" + ] + } +}`, TempDir(t).URI), }, session.SessionNotInitialized.Err()) } @@ -70,18 +82,6 @@ func TestLangServer_ExtractPropToOutput_basic(t *testing.T) { "", }, }, - { - Method: "Format", - Repeatability: 1, - Arguments: []interface{}{ - mock.AnythingOfType(""), - []byte("provider \"test\" {\n\n}\n"), - }, - ReturnArguments: []interface{}{ - []byte("provider \"test\" {\n\n}\n"), - nil, - }, - }, }, }, }, @@ -108,7 +108,7 @@ func TestLangServer_ExtractPropToOutput_basic(t *testing.T) { "textDocument": { "version": 0, "languageId": "terraform", - "text": "provider \"test\" {}\n\nresource \"test_resource\" \"test\" {\n name = \"testname\"\n}", + "text": "provider \"test\"{}\n\nresource \"test_resource\" \"test\"{\n name = \"test\"\n}", "uri": "%s/main.tf" } }`, tmpDir.URI), @@ -124,11 +124,11 @@ func TestLangServer_ExtractPropToOutput_basic(t *testing.T) { "range": { "start": { "line": 3, - "character": 17 + "character": 14 }, "end": { "line": 3, - "character": 17 + "character": 14 } }, "context": { @@ -137,19 +137,35 @@ func TestLangServer_ExtractPropToOutput_basic(t *testing.T) { ] } }`, tmpDir.URI), - }, `{ - "jsonrpc": "2.0", - "id": 3, - "result": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "newText": "provider \"test\" {\n" - } - ] - }`) + }, fmt.Sprintf(`{ + "jsonrpc": "2.0", + "id": 3, + "result": [ + { + "title": "Extract Property to Output", + "kind": "refactor.extract.propToOut", + "edit": { + "changes": { + "%s/main.tf": [ + { + "range": { + "start": { + "line": 6, + "character": 6 + }, + "end": { + "line": 6, + "character": 6 + } + }, + "newText": "\noutput \"test_resource_test_name\" {\n value = test_resource.test.name\n}\n" + } + ] + } + } + } + ] +}`, tmpDir.URI)) } func TestLangServer_ExtractPropToOutput_oldVersion(t *testing.T) { @@ -343,141 +359,3 @@ func TestLangServer_extractPropToOutput_variables(t *testing.T) { ] }`) } - -func TestLangServer_extractPropToOutput_diffBug(t *testing.T) { - tmpDir := TempDir(t) - - cfg := `resource "aws_lambda_function" "f" { - environment { - variables = { - a = "b" - } - } -} -` - formattedCfg := `resource "aws_lambda_function" "f" { - environment { - variables = { - a = "b" - } - } -} -` - - ss, err := state.NewStateStore() - if err != nil { - t.Fatal(err) - } - wc := walker.NewWalkerCollector() - - ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ - StateStore: ss, - WalkerCollector: wc, - TerraformCalls: &exec.TerraformMockCalls{ - PerWorkDir: map[string][]*mock.Call{ - tmpDir.Path(): { - { - Method: "Version", - Repeatability: 1, - Arguments: []interface{}{ - mock.AnythingOfType(""), - }, - ReturnArguments: []interface{}{ - version.Must(version.NewVersion("0.12.0")), - nil, - nil, - }, - }, - { - Method: "GetExecPath", - Repeatability: 1, - ReturnArguments: []interface{}{ - "", - }, - }, - { - Method: "Format", - Repeatability: 1, - Arguments: []interface{}{ - mock.AnythingOfType("*context.valueCtx"), - []byte(cfg), - }, - ReturnArguments: []interface{}{ - []byte(formattedCfg), - nil, - }, - }, - }, - }, - }, - })) - stop := ls.Start(t) - defer stop() - - ls.Call(t, &langserver.CallRequest{ - Method: "initialize", - ReqParams: fmt.Sprintf(`{ - "capabilities": {}, - "rootUri": %q, - "processId": 12345 - }`, tmpDir.URI), - }) - waitForWalkerPath(t, ss, wc, tmpDir) - - ls.Notify(t, &langserver.CallRequest{ - Method: "initialized", - ReqParams: "{}", - }) - ls.Call(t, &langserver.CallRequest{ - Method: "textDocument/didOpen", - ReqParams: fmt.Sprintf(`{ - "textDocument": { - "version": 0, - "languageId": "terraform", - "text": `+fmt.Sprintf("%q", cfg)+`, - "uri": "%s/main.tf" - } - }`, tmpDir.URI), - }) - waitForAllJobs(t, ss) - - ls.CallAndExpectResponse(t, &langserver.CallRequest{ - Method: "textDocument/formatting", - ReqParams: fmt.Sprintf(`{ - "textDocument": { - "uri": "%s/main.tf" - } - }`, tmpDir.URI), - }, `{ - "jsonrpc": "2.0", - "id": 3, - "result": [ - { - "range": { - "start": { - "line": 1, - "character": 0 - }, - "end": { - "line": 5, - "character": 0 - } - }, - "newText": " environment {\n variables = {\n a = \"b\"\n" - }, - { - "range": { - "start": { - "line": 6, - "character": 0 - }, - "end": { - "line": 6, - "character": 0 - } - }, - "newText":" }\n" - } - ] - }`) -} From 883e5fff9e6015cd71b7daba47535f2feeacd66c Mon Sep 17 00:00:00 2001 From: "hashicorp-copywrite[bot]" <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 06:38:43 +0000 Subject: [PATCH 3/4] [COMPLIANCE] Add required copyright headers Signed-off-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> --- internal/langserver/handlers/extract_prop_to_output.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/langserver/handlers/extract_prop_to_output.go b/internal/langserver/handlers/extract_prop_to_output.go index 97bd0fa8..25c1c820 100644 --- a/internal/langserver/handlers/extract_prop_to_output.go +++ b/internal/langserver/handlers/extract_prop_to_output.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package handlers import ( From a14e85999592979280c9af9ce8104b74facf1de6 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:26:12 +0800 Subject: [PATCH 4/4] revert mistake changes --- internal/lsp/code_actions.go | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/internal/lsp/code_actions.go b/internal/lsp/code_actions.go index a7ae121d..15cdecd2 100644 --- a/internal/lsp/code_actions.go +++ b/internal/lsp/code_actions.go @@ -18,26 +18,27 @@ const ( type CodeActions map[lsp.CodeActionKind]bool -// `source.*`: Source code actions apply to the entire file. They must be explicitly -// requested and will not show in the normal lightbulb menu. Source actions -// can be run on save using editor.codeActionsOnSave and are also shown in -// the source context menu. -// For action definitions, refer to: https://code.visualstudio.com/api/references/vscode-api#CodeActionKind +var ( + // `source.*`: Source code actions apply to the entire file. They must be explicitly + // requested and will not show in the normal lightbulb menu. Source actions + // can be run on save using editor.codeActionsOnSave and are also shown in + // the source context menu. + // For action definitions, refer to: https://code.visualstudio.com/api/references/vscode-api#CodeActionKind -// `source.fixAll`: Fix all actions automatically fix errors that have a clear fix that do -// not require user input. They should not suppress errors or perform unsafe -// fixes such as generating new types or classes. -// ** We don't support this as terraform fmt only adjusts style** -// lsp.SourceFixAll: true, + // `source.fixAll`: Fix all actions automatically fix errors that have a clear fix that do + // not require user input. They should not suppress errors or perform unsafe + // fixes such as generating new types or classes. + // ** We don't support this as terraform fmt only adjusts style** + // lsp.SourceFixAll: true, -// `source.formatAll`: Generic format code action. -// We do not register this for terraform to allow fine grained selection of actions. -// A user should be able to set `source.formatAll` to true, and source.formatAll.terraform to false to allow all -// files to be formatted, but not terraform files (or vice versa). -var SupportedCodeActions = CodeActions{ - SourceFormatAllTerraform: true, - ExtractPropertyToOutput: true, -} + // `source.formatAll`: Generic format code action. + // We do not register this for terraform to allow fine grained selection of actions. + // A user should be able to set `source.formatAll` to true, and source.formatAll.terraform to false to allow all + // files to be formatted, but not terraform files (or vice versa). + SupportedCodeActions = CodeActions{ + SourceFormatAllTerraform: true, + } +) func (c CodeActions) AsSlice() []lsp.CodeActionKind { s := make([]lsp.CodeActionKind, 0)