Skip to content

Commit

Permalink
Gate formatting capability on v0.7.7+
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Jul 9, 2020
1 parent 3121f31 commit 1377ff5
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 38 deletions.
36 changes: 18 additions & 18 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ func (k *contextKey) String() string {
}

var (
ctxFs = &contextKey{"filesystem"}
ctxClientCapsSetter = &contextKey{"client capabilities setter"}
ctxClientCaps = &contextKey{"client capabilities"}
ctxTfExecPath = &contextKey{"terraform executable path"}
ctxTfExecLogPath = &contextKey{"terraform executor log path"}
ctxTfExecTimeout = &contextKey{"terraform execution timeout"}
ctxWatcher = &contextKey{"watcher"}
ctxRootModuleMngr = &contextKey{"root module manager"}
ctxParserFinder = &contextKey{"parser finder"}
ctxTfExecFinder = &contextKey{"terraform exec finder"}
ctxRootModuleCaFi = &contextKey{"root module candidate finder"}
ctxRootModuleWalker = &contextKey{"root module walker"}
ctxRootDir = &contextKey{"root directory"}
ctxFs = &contextKey{"filesystem"}
ctxClientCapsSetter = &contextKey{"client capabilities setter"}
ctxClientCaps = &contextKey{"client capabilities"}
ctxTfExecPath = &contextKey{"terraform executable path"}
ctxTfExecLogPath = &contextKey{"terraform executor log path"}
ctxTfExecTimeout = &contextKey{"terraform execution timeout"}
ctxWatcher = &contextKey{"watcher"}
ctxRootModuleMngr = &contextKey{"root module manager"}
ctxParserFinder = &contextKey{"parser finder"}
ctxTfFormatterFinder = &contextKey{"terraform formatter finder"}
ctxRootModuleCaFi = &contextKey{"root module candidate finder"}
ctxRootModuleWalker = &contextKey{"root module walker"}
ctxRootDir = &contextKey{"root directory"}
)

func missingContextErr(ctxKey *contextKey) *MissingContextErr {
Expand Down Expand Up @@ -132,14 +132,14 @@ func ParserFinder(ctx context.Context) (rootmodule.ParserFinder, error) {
return pf, nil
}

func WithTerraformExecFinder(tef rootmodule.TerraformExecFinder, ctx context.Context) context.Context {
return context.WithValue(ctx, ctxTfExecFinder, tef)
func WithTerraformFormatterFinder(tef rootmodule.TerraformFormatterFinder, ctx context.Context) context.Context {
return context.WithValue(ctx, ctxTfFormatterFinder, tef)
}

func TerraformExecutorFinder(ctx context.Context) (rootmodule.TerraformExecFinder, error) {
pf, ok := ctx.Value(ctxTfExecFinder).(rootmodule.TerraformExecFinder)
func TerraformFormatterFinder(ctx context.Context) (rootmodule.TerraformFormatterFinder, error) {
pf, ok := ctx.Value(ctxTfFormatterFinder).(rootmodule.TerraformFormatterFinder)
if !ok {
return nil, missingContextErr(ctxTfExecFinder)
return nil, missingContextErr(ctxTfFormatterFinder)
}
return pf, nil
}
Expand Down
25 changes: 25 additions & 0 deletions internal/terraform/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,31 @@ func (e *Executor) run(ctx context.Context, args ...string) ([]byte, error) {
return e.runCmd(cmd)
}

type Formatter func(ctx context.Context, input []byte) ([]byte, error)

func (e *Executor) FormatterForVersion(v string) (Formatter, error) {
if v == "" {
return nil, fmt.Errorf("unknown version - unable to provide formatter")
}

ver, err := version.NewVersion(v)
if err != nil {
return nil, err
}

// "fmt" command was first introduced in v0.7.7
fmtCapableVersion, err := version.NewVersion("0.7.7")
if err != nil {
return nil, err
}

if ver.GreaterThanOrEqual(fmtCapableVersion) {
return e.Format, nil
}

return nil, fmt.Errorf("no formatter available for %s", v)
}

func (e *Executor) Format(ctx context.Context, input []byte) ([]byte, error) {
cmd, err := e.cmd(ctx, "fmt", "-")
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/terraform/rootmodule/root_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ func (rm *rootModule) ReferencesModulePath(path string) bool {
return false
}

func (rm *rootModule) TerraformExecutor() (*exec.Executor, error) {
func (rm *rootModule) TerraformFormatter() (exec.Formatter, error) {
if !rm.IsTerraformLoaded() {
return nil, fmt.Errorf("terraform executor is not loaded yet")
}
Expand All @@ -363,7 +363,7 @@ func (rm *rootModule) TerraformExecutor() (*exec.Executor, error) {
return nil, fmt.Errorf("no terraform executor available")
}

return rm.tfExec, nil
return rm.tfExec.FormatterForVersion(rm.tfVersion)
}

func (rm *rootModule) IsTerraformLoaded() bool {
Expand Down
18 changes: 11 additions & 7 deletions internal/terraform/rootmodule/root_module_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,21 +181,19 @@ func (rmm *rootModuleManager) IsSchemaLoaded(path string) (bool, error) {
return rm.IsSchemaLoaded(), nil
}

func (rmm *rootModuleManager) TerraformExecutorForDir(ctx context.Context, path string) (*exec.Executor, error) {
func (rmm *rootModuleManager) TerraformFormatterForDir(ctx context.Context, path string) (exec.Formatter, error) {
rm, err := rmm.RootModuleByPath(path)
if err != nil {
if IsRootModuleNotFound(err) {
// TODO: obtain TF version and return "formatter" instead of executor
// gated by particular version which introduced "fmt"
return rmm.discoverTerraformExecutor(ctx, path)
return rmm.newTerraformFormatter(ctx, path)
}
return nil, err
}

return rm.TerraformExecutor()
return rm.TerraformFormatter()
}

func (rmm *rootModuleManager) discoverTerraformExecutor(ctx context.Context, path string) (*exec.Executor, error) {
func (rmm *rootModuleManager) newTerraformFormatter(ctx context.Context, path string) (exec.Formatter, error) {
tfPath := rmm.tfExecPath
if tfPath == "" {
var err error
Expand All @@ -218,7 +216,13 @@ func (rmm *rootModuleManager) discoverTerraformExecutor(ctx context.Context, pat
tf.SetTimeout(rmm.tfExecTimeout)
}

return tf, nil
version, err := tf.Version(ctx)
if err != nil {
return nil, err
}
rmm.logger.Printf("Terraform version %s found at %s (alternative)", version, tf.GetExecPath())

return tf.FormatterForVersion(version)
}

func (rmm *rootModuleManager) IsTerraformLoaded(path string) (bool, error) {
Expand Down
8 changes: 4 additions & 4 deletions internal/terraform/rootmodule/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type ParserFinder interface {
IsSchemaLoaded(path string) (bool, error)
}

type TerraformExecFinder interface {
TerraformExecutorForDir(ctx context.Context, path string) (*exec.Executor, error)
type TerraformFormatterFinder interface {
TerraformFormatterForDir(ctx context.Context, path string) (exec.Formatter, error)
IsTerraformLoaded(path string) (bool, error)
}

Expand All @@ -30,7 +30,7 @@ type RootModuleCandidateFinder interface {

type RootModuleManager interface {
ParserFinder
TerraformExecFinder
TerraformFormatterFinder
RootModuleCandidateFinder

SetLogger(logger *log.Logger)
Expand Down Expand Up @@ -68,7 +68,7 @@ type RootModule interface {
UpdateModuleManifest(manifestFile File) error
Parser() (lang.Parser, error)
IsParserLoaded() bool
TerraformExecutor() (*exec.Executor, error)
TerraformFormatter() (exec.Formatter, error)
IsTerraformLoaded() bool
}

Expand Down
12 changes: 6 additions & 6 deletions langserver/handlers/formatting.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (h *logHandler) TextDocumentFormatting(ctx context.Context, params lsp.Docu
return edits, err
}

tff, err := lsctx.TerraformExecutorFinder(ctx)
tff, err := lsctx.TerraformFormatterFinder(ctx)
if err != nil {
return edits, err
}
Expand All @@ -31,12 +31,12 @@ func (h *logHandler) TextDocumentFormatting(ctx context.Context, params lsp.Docu
return edits, err
}

tf, err := findTerraformExecutor(ctx, tff, file.Dir())
format, err := findTerraformFormatter(ctx, tff, file.Dir())
if err != nil {
return edits, err
}

formatted, err := tf.Format(ctx, file.Text())
formatted, err := format(ctx, file.Text())
if err != nil {
return edits, err
}
Expand All @@ -46,11 +46,11 @@ func (h *logHandler) TextDocumentFormatting(ctx context.Context, params lsp.Docu
return ilsp.TextEdits(changes), nil
}

func findTerraformExecutor(ctx context.Context, tff rootmodule.TerraformExecFinder, dir string) (*exec.Executor, error) {
func findTerraformFormatter(ctx context.Context, tff rootmodule.TerraformFormatterFinder, dir string) (exec.Formatter, error) {
isLoaded, err := tff.IsTerraformLoaded(dir)
if err != nil {
if rootmodule.IsRootModuleNotFound(err) {
return tff.TerraformExecutorForDir(ctx, dir)
return tff.TerraformFormatterForDir(ctx, dir)
}
return nil, err
} else {
Expand All @@ -60,5 +60,5 @@ func findTerraformExecutor(ctx context.Context, tff rootmodule.TerraformExecFind
}
}

return tff.TerraformExecutorForDir(ctx, dir)
return tff.TerraformFormatterForDir(ctx, dir)
}
49 changes: 49 additions & 0 deletions langserver/handlers/formatting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"testing"

"github.com/creachadair/jrpc2/code"
"github.com/hashicorp/terraform-ls/internal/terraform/exec"
"github.com/hashicorp/terraform-ls/langserver"
"github.com/hashicorp/terraform-ls/langserver/session"
Expand All @@ -30,6 +31,10 @@ func TestLangServer_formatting_basic(t *testing.T) {
ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{
ManagerTfExecQueue: &exec.MockQueue{
Q: []*exec.MockItem{
{
Args: []string{"version"},
Stdout: "Terraform v0.12.0\n",
},
{
Args: []string{"fmt", "-"},
Stdout: "provider \"test\" {\n\n}\n",
Expand Down Expand Up @@ -81,3 +86,47 @@ func TestLangServer_formatting_basic(t *testing.T) {
]
}`)
}

func TestLangServer_formatting_oldVersion(t *testing.T) {
ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{
ManagerTfExecQueue: &exec.MockQueue{
Q: []*exec.MockItem{
{
Args: []string{"version"},
Stdout: "Terraform v0.7.6\n",
},
},
},
}))
stop := ls.Start(t)
defer stop()

ls.Call(t, &langserver.CallRequest{
Method: "initialize",
ReqParams: fmt.Sprintf(`{
"capabilities": {},
"rootUri": %q,
"processId": 12345
}`, TempDir(t).URI())})
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"
}
}`, TempDir(t).URI())})
ls.CallAndExpectError(t, &langserver.CallRequest{
Method: "textDocument/formatting",
ReqParams: fmt.Sprintf(`{
"textDocument": {
"uri": "%s/main.tf"
}
}`, TempDir(t).URI())}, code.SystemError.Err())
}
2 changes: 1 addition & 1 deletion langserver/handlers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {
}

ctx = lsctx.WithFilesystem(fs, ctx)
ctx = lsctx.WithTerraformExecFinder(svc.modMgr, ctx)
ctx = lsctx.WithTerraformFormatterFinder(svc.modMgr, ctx)

return handle(ctx, req, lh.TextDocumentFormatting)
},
Expand Down

0 comments on commit 1377ff5

Please sign in to comment.