From c62717fd23e4da71f9013267ad1309990933111f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 18 Jul 2022 11:39:31 +0100 Subject: [PATCH 1/3] settings: repurpose indexer-related options --- docs/SETTINGS.md | 32 +++++++++-------- internal/langserver/handlers/initialize.go | 42 +++++++++++++++------- internal/settings/settings.go | 11 +++--- internal/settings/settings_test.go | 10 +++--- internal/walker/walker.go | 34 +++++++++--------- 5 files changed, 74 insertions(+), 55 deletions(-) diff --git a/docs/SETTINGS.md b/docs/SETTINGS.md index 8d89f67e..e658befc 100644 --- a/docs/SETTINGS.md +++ b/docs/SETTINGS.md @@ -22,26 +22,30 @@ Path to the Terraform binary. This is usually looked up automatically from `$PATH` and should not need to be specified in majority of cases. Use this to override the automatic lookup. -## `rootModulePaths` (`[]string`) +## **DEPRECATED**: `rootModulePaths` (`[]string`) -This allows overriding automatic root module discovery by passing a static list -of absolute or relative paths to root modules (i.e. folders with `*.tf` files -which have been `terraform init`-ed). Conflicts with `ExcludeModulePaths` option. +This option is deprecated and ignored from v0.29+, it was used as an escape hatch +to force indexing of paths in cases where indexer wouldn't index them otherwise. +Indexer in 0.29.0 no longer limited to just initialized modules (folders with `.terraform`) +and instead indexes all directories with `*.tf` files in them. +Therefore this option is no longer relevant. -Relative paths are resolved relative to the directory opened in the editor. +If you previously used it to force indexing of a folder outside of a workspace, +you can just add that folder to the workspace and it will be indexed as usual. -Path separators are converted automatically to the match separators -of the target platform (e.g. `\` on Windows, or `/` on Unix), -symlinks are followed, trailing slashes automatically removed, -and `~` is replaced with your home directory. +## **DEPRECATED**: `excludeModulePaths` (`[]string`) + +Deprecated in favour of `ignorePaths` -## `excludeModulePaths` (`[]string`) +## `ignorePaths` (`[]string`) -This allows exclude root module path when automatic root module discovery by passing a static list -of absolute or relative paths to root modules (i.e. folders with `*.tf` files -which have been `terraform init`-ed). Conflicts with `rootModulePaths` option. +Paths to ignore when indexing the workspace on initialization. This can serve +as an escape hatch in large workspaces. Key side effect of ignoring a path +is that go-to-definition, go-to-references and generally most IntelliSense +related to local `module` blocks will **not** work until the target module code +is explicitly opened. -Relative paths are resolved relative to the directory opened in the editor. +Relative paths are resolved relative to the root (workspace) path opened in the editor. Path separators are converted automatically to the match separators of the target platform (e.g. `\` on Windows, or `/` on Unix), diff --git a/internal/langserver/handlers/initialize.go b/internal/langserver/handlers/initialize.go index 4d78ce79..2419f3bc 100644 --- a/internal/langserver/handlers/initialize.go +++ b/internal/langserver/handlers/initialize.go @@ -183,9 +183,8 @@ func getTelemetryProperties(out *settings.DecodedOptions) map[string]interface{} "lsVersion": "", } - properties["options.rootModulePaths"] = len(out.Options.ModulePaths) > 0 - properties["options.rootModulePaths"] = len(out.Options.ModulePaths) > 0 - properties["options.excludeModulePaths"] = len(out.Options.ExcludeModulePaths) > 0 + properties["options.rootModulePaths"] = len(out.Options.XLegacyModulePaths) > 0 + properties["options.excludeModulePaths"] = len(out.Options.XLegacyExcludeModulePaths) > 0 properties["options.commandPrefix"] = len(out.Options.CommandPrefix) > 0 properties["options.ignoreDirectoryNames"] = len(out.Options.IgnoreDirectoryNames) > 0 properties["options.experimentalFeatures.prefillRequiredFields"] = out.Options.ExperimentalFeatures.PrefillRequiredFields @@ -248,14 +247,33 @@ func (svc *service) setupWalker(ctx context.Context, params lsp.InitializeParams return err } - var excludeModulePaths []string - for _, rawPath := range options.ExcludeModulePaths { + if len(options.XLegacyModulePaths) != 0 { + jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{ + Type: lsp.Warning, + Message: fmt.Sprintf("rootModulePaths (%q) is deprecated (no-op), add a folder to workspace "+ + "instead if you'd like it to be indexed", options.XLegacyModulePaths), + }) + } + if len(options.XLegacyExcludeModulePaths) != 0 { + jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{ + Type: lsp.Warning, + Message: fmt.Sprintf("excludeModulePaths (%q) is deprecated (no-op), use ignorePaths instead", + options.XLegacyExcludeModulePaths), + }) + } + + var ignoredPaths []string + for _, rawPath := range options.IgnorePaths { modPath, err := resolvePath(root.Path(), rawPath) if err != nil { - svc.logger.Printf("Ignoring excluded module path %s: %s", rawPath, err) + jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{ + Type: lsp.Warning, + Message: fmt.Sprintf("Unable to ignore path (unsupported or invalid URI): %s: %s", + rawPath, err), + }) continue } - excludeModulePaths = append(excludeModulePaths, modPath) + ignoredPaths = append(ignoredPaths, modPath) } err = svc.stateStore.WalkerPaths.EnqueueDir(root) @@ -268,7 +286,7 @@ func (svc *service) setupWalker(ctx context.Context, params lsp.InitializeParams if !uri.IsURIValid(folder.URI) { jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{ Type: lsp.Warning, - Message: fmt.Sprintf("Ignoring workspace folder (unsupport or invalid URI) %s."+ + Message: fmt.Sprintf("Ignoring workspace folder (unsupported or invalid URI) %s."+ " This is most likely bug, please report it.", folder.URI), }) continue @@ -288,10 +306,10 @@ func (svc *service) setupWalker(ctx context.Context, params lsp.InitializeParams } } - svc.closedDirWalker.SetIgnoreDirectoryNames(options.IgnoreDirectoryNames) - svc.closedDirWalker.SetExcludeModulePaths(excludeModulePaths) - svc.openDirWalker.SetIgnoreDirectoryNames(options.IgnoreDirectoryNames) - svc.openDirWalker.SetExcludeModulePaths(excludeModulePaths) + svc.closedDirWalker.SetIgnoredDirectoryNames(options.IgnoreDirectoryNames) + svc.closedDirWalker.SetIgnoredPaths(ignoredPaths) + svc.openDirWalker.SetIgnoredDirectoryNames(options.IgnoreDirectoryNames) + svc.openDirWalker.SetIgnoredPaths(ignoredPaths) return nil } diff --git a/internal/settings/settings.go b/internal/settings/settings.go index bc9002d7..28ea21d0 100644 --- a/internal/settings/settings.go +++ b/internal/settings/settings.go @@ -16,11 +16,9 @@ type ExperimentalFeatures struct { } type Options struct { - // ModulePaths describes a list of absolute paths to modules to load - ModulePaths []string `mapstructure:"rootModulePaths"` - ExcludeModulePaths []string `mapstructure:"excludeModulePaths"` CommandPrefix string `mapstructure:"commandPrefix"` IgnoreDirectoryNames []string `mapstructure:"ignoreDirectoryNames"` + IgnorePaths []string `mapstructure:"ignorePaths"` // ExperimentalFeatures encapsulates experimental features users can opt into. ExperimentalFeatures ExperimentalFeatures `mapstructure:"experimentalFeatures"` @@ -30,13 +28,12 @@ type Options struct { TerraformExecPath string `mapstructure:"terraformExecPath"` TerraformExecTimeout string `mapstructure:"terraformExecTimeout"` TerraformLogFilePath string `mapstructure:"terraformLogFilePath"` + + XLegacyModulePaths []string `mapstructure:"rootModulePaths"` + XLegacyExcludeModulePaths []string `mapstructure:"excludeModulePaths"` } func (o *Options) Validate() error { - if len(o.ModulePaths) != 0 && len(o.ExcludeModulePaths) != 0 { - return fmt.Errorf("at most one of `rootModulePaths` and `excludeModulePaths` could be set") - } - if o.TerraformExecPath != "" { path := o.TerraformExecPath if !filepath.IsAbs(path) { diff --git a/internal/settings/settings_test.go b/internal/settings/settings_test.go index 9c267ac8..3b74893d 100644 --- a/internal/settings/settings_test.go +++ b/internal/settings/settings_test.go @@ -16,14 +16,14 @@ func TestDecodeOptions_nil(t *testing.T) { } opts := out.Options - if opts.ModulePaths != nil { - t.Fatalf("expected no options for nil, %#v given", opts.ModulePaths) + if opts.IgnoreDirectoryNames != nil { + t.Fatalf("expected no options for nil, %#v given", opts.IgnoreDirectoryNames) } } func TestDecodeOptions_wrongType(t *testing.T) { _, err := DecodeOptions(map[string]interface{}{ - "rootModulePaths": "/random/path", + "ignorePaths": "/random/path", }) if err == nil { t.Fatal("expected decoding of wrong type to result in error") @@ -32,14 +32,14 @@ func TestDecodeOptions_wrongType(t *testing.T) { func TestDecodeOptions_success(t *testing.T) { out, err := DecodeOptions(map[string]interface{}{ - "rootModulePaths": []string{"/random/path"}, + "ignorePaths": []string{"/random/path"}, }) if err != nil { t.Fatal(err) } opts := out.Options expectedPaths := []string{"/random/path"} - if diff := cmp.Diff(expectedPaths, opts.ModulePaths); diff != "" { + if diff := cmp.Diff(expectedPaths, opts.IgnorePaths); diff != "" { t.Fatalf("options mismatch: %s", diff) } } diff --git a/internal/walker/walker.go b/internal/walker/walker.go index 11f32126..31e3bc6a 100644 --- a/internal/walker/walker.go +++ b/internal/walker/walker.go @@ -44,8 +44,8 @@ type Walker struct { cancelFunc context.CancelFunc - excludeModulePaths map[string]bool - ignoreDirectoryNames map[string]bool + ignoredPaths map[string]bool + ignoredDirectoryNames map[string]bool } type WalkFunc func(ctx context.Context, modHandle document.DirHandle) (job.IDs, error) @@ -62,12 +62,12 @@ type ModuleStore interface { func NewWalker(fs fs.ReadDirFS, pathStore PathStore, modStore ModuleStore, walkFunc WalkFunc) *Walker { return &Walker{ - fs: fs, - pathStore: pathStore, - modStore: modStore, - walkFunc: walkFunc, - logger: discardLogger, - ignoreDirectoryNames: skipDirNames, + fs: fs, + pathStore: pathStore, + modStore: modStore, + walkFunc: walkFunc, + logger: discardLogger, + ignoredDirectoryNames: skipDirNames, } } @@ -75,19 +75,19 @@ func (w *Walker) SetLogger(logger *log.Logger) { w.logger = logger } -func (w *Walker) SetExcludeModulePaths(excludeModulePaths []string) { - w.excludeModulePaths = make(map[string]bool) - for _, path := range excludeModulePaths { - w.excludeModulePaths[path] = true +func (w *Walker) SetIgnoredPaths(ignoredPaths []string) { + w.ignoredPaths = make(map[string]bool) + for _, path := range ignoredPaths { + w.ignoredPaths[path] = true } } -func (w *Walker) SetIgnoreDirectoryNames(ignoreDirectoryNames []string) { +func (w *Walker) SetIgnoredDirectoryNames(ignoredDirectoryNames []string) { if w.cancelFunc != nil { panic("cannot set ignorelist after walking started") } - for _, path := range ignoreDirectoryNames { - w.ignoreDirectoryNames[path] = true + for _, path := range ignoredDirectoryNames { + w.ignoredDirectoryNames[path] = true } } @@ -154,12 +154,12 @@ func (w *Walker) collectJobIds(jobIds job.IDs) { } func (w *Walker) isSkippableDir(dirName string) bool { - _, ok := w.ignoreDirectoryNames[dirName] + _, ok := w.ignoredDirectoryNames[dirName] return ok } func (w *Walker) walk(ctx context.Context, dir document.DirHandle) error { - if _, ok := w.excludeModulePaths[dir.Path()]; ok { + if _, ok := w.ignoredPaths[dir.Path()]; ok { w.logger.Printf("skipping walk due to dir being excluded: %s", dir.Path()) return nil } From f3391038a7a17d887dca70f56d16c876301a9b49 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 20 Jul 2022 15:30:35 +0100 Subject: [PATCH 2/3] track IgnorePaths via telemetry --- internal/langserver/handlers/initialize.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/langserver/handlers/initialize.go b/internal/langserver/handlers/initialize.go index 2419f3bc..f30f23c4 100644 --- a/internal/langserver/handlers/initialize.go +++ b/internal/langserver/handlers/initialize.go @@ -187,6 +187,7 @@ func getTelemetryProperties(out *settings.DecodedOptions) map[string]interface{} properties["options.excludeModulePaths"] = len(out.Options.XLegacyExcludeModulePaths) > 0 properties["options.commandPrefix"] = len(out.Options.CommandPrefix) > 0 properties["options.ignoreDirectoryNames"] = len(out.Options.IgnoreDirectoryNames) > 0 + properties["options.ignorePaths"] = len(out.Options.IgnorePaths) > 0 properties["options.experimentalFeatures.prefillRequiredFields"] = out.Options.ExperimentalFeatures.PrefillRequiredFields properties["options.experimentalFeatures.validateOnSave"] = out.Options.ExperimentalFeatures.ValidateOnSave properties["options.ignoreSingleFileWarning"] = out.Options.IgnoreSingleFileWarning From f8786dfb14beb9dd3696a4b147d3c16840ad9b92 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 20 Jul 2022 15:32:29 +0100 Subject: [PATCH 3/3] settings: prepend options with 'indexing' --- docs/SETTINGS.md | 8 ++++++-- internal/langserver/handlers/initialize.go | 6 +++--- internal/langserver/handlers/initialize_test.go | 2 +- internal/settings/settings.go | 4 ++-- internal/settings/settings_test.go | 8 ++++---- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/SETTINGS.md b/docs/SETTINGS.md index e658befc..3b1e528d 100644 --- a/docs/SETTINGS.md +++ b/docs/SETTINGS.md @@ -37,7 +37,7 @@ you can just add that folder to the workspace and it will be indexed as usual. Deprecated in favour of `ignorePaths` -## `ignorePaths` (`[]string`) +## `indexing.ignorePaths` (`[]string`) Paths to ignore when indexing the workspace on initialization. This can serve as an escape hatch in large workspaces. Key side effect of ignoring a path @@ -72,7 +72,11 @@ Or if left empty This setting should be deprecated once the language server supports multiple workspaces, as this arises in VS code because a server instance is started per VS Code workspace. -## `ignoreDirectoryNames` (`[]string`) +## **DEPRECATED**: `ignoreDirectoryNames` (`[]string`) + +Deprecated in favour of `indexing.ignoreDirectoryNames` + +## `indexing.ignoreDirectoryNames` (`[]string`) This allows excluding directories from being indexed upon initialization by passing a list of directory names. diff --git a/internal/langserver/handlers/initialize.go b/internal/langserver/handlers/initialize.go index f30f23c4..3ae95a59 100644 --- a/internal/langserver/handlers/initialize.go +++ b/internal/langserver/handlers/initialize.go @@ -186,8 +186,8 @@ func getTelemetryProperties(out *settings.DecodedOptions) map[string]interface{} properties["options.rootModulePaths"] = len(out.Options.XLegacyModulePaths) > 0 properties["options.excludeModulePaths"] = len(out.Options.XLegacyExcludeModulePaths) > 0 properties["options.commandPrefix"] = len(out.Options.CommandPrefix) > 0 - properties["options.ignoreDirectoryNames"] = len(out.Options.IgnoreDirectoryNames) > 0 - properties["options.ignorePaths"] = len(out.Options.IgnorePaths) > 0 + properties["options.indexing.ignoreDirectoryNames"] = len(out.Options.IgnoreDirectoryNames) > 0 + properties["options.indexing.ignorePaths"] = len(out.Options.IgnorePaths) > 0 properties["options.experimentalFeatures.prefillRequiredFields"] = out.Options.ExperimentalFeatures.PrefillRequiredFields properties["options.experimentalFeatures.validateOnSave"] = out.Options.ExperimentalFeatures.ValidateOnSave properties["options.ignoreSingleFileWarning"] = out.Options.IgnoreSingleFileWarning @@ -258,7 +258,7 @@ func (svc *service) setupWalker(ctx context.Context, params lsp.InitializeParams if len(options.XLegacyExcludeModulePaths) != 0 { jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{ Type: lsp.Warning, - Message: fmt.Sprintf("excludeModulePaths (%q) is deprecated (no-op), use ignorePaths instead", + Message: fmt.Sprintf("excludeModulePaths (%q) is deprecated (no-op), use indexing.ignorePaths instead", options.XLegacyExcludeModulePaths), }) } diff --git a/internal/langserver/handlers/initialize_test.go b/internal/langserver/handlers/initialize_test.go index 4669bd87..de48418f 100644 --- a/internal/langserver/handlers/initialize_test.go +++ b/internal/langserver/handlers/initialize_test.go @@ -196,7 +196,7 @@ func TestInitialize_ignoreDirectoryNames(t *testing.T) { "rootUri": %q, "processId": 12345, "initializationOptions": { - "ignoreDirectoryNames": [%q] + "indexing.ignoreDirectoryNames": [%q] } }`, tmpDir.URI, "ignore")}) waitForWalkerPath(t, ss, wc, tmpDir) diff --git a/internal/settings/settings.go b/internal/settings/settings.go index 28ea21d0..dec70088 100644 --- a/internal/settings/settings.go +++ b/internal/settings/settings.go @@ -17,8 +17,8 @@ type ExperimentalFeatures struct { type Options struct { CommandPrefix string `mapstructure:"commandPrefix"` - IgnoreDirectoryNames []string `mapstructure:"ignoreDirectoryNames"` - IgnorePaths []string `mapstructure:"ignorePaths"` + IgnoreDirectoryNames []string `mapstructure:"indexing.ignoreDirectoryNames"` + IgnorePaths []string `mapstructure:"indexing.ignorePaths"` // ExperimentalFeatures encapsulates experimental features users can opt into. ExperimentalFeatures ExperimentalFeatures `mapstructure:"experimentalFeatures"` diff --git a/internal/settings/settings_test.go b/internal/settings/settings_test.go index 3b74893d..cbca1730 100644 --- a/internal/settings/settings_test.go +++ b/internal/settings/settings_test.go @@ -23,7 +23,7 @@ func TestDecodeOptions_nil(t *testing.T) { func TestDecodeOptions_wrongType(t *testing.T) { _, err := DecodeOptions(map[string]interface{}{ - "ignorePaths": "/random/path", + "indexing.ignorePaths": "/random/path", }) if err == nil { t.Fatal("expected decoding of wrong type to result in error") @@ -32,7 +32,7 @@ func TestDecodeOptions_wrongType(t *testing.T) { func TestDecodeOptions_success(t *testing.T) { out, err := DecodeOptions(map[string]interface{}{ - "ignorePaths": []string{"/random/path"}, + "indexing.ignorePaths": []string{"/random/path"}, }) if err != nil { t.Fatal(err) @@ -55,7 +55,7 @@ func TestValidate_IgnoreDirectoryNames_error(t *testing.T) { for _, table := range tables { out, err := DecodeOptions(map[string]interface{}{ - "ignoreDirectoryNames": []string{table.input}, + "indexing.ignoreDirectoryNames": []string{table.input}, }) if err != nil { t.Fatal(err) @@ -69,7 +69,7 @@ func TestValidate_IgnoreDirectoryNames_error(t *testing.T) { } func TestValidate_IgnoreDirectoryNames_success(t *testing.T) { out, err := DecodeOptions(map[string]interface{}{ - "ignoreDirectoryNames": []string{"directory"}, + "indexing.ignoreDirectoryNames": []string{"directory"}, }) if err != nil { t.Fatal(err)