diff --git a/internal/langserver/handlers/initialize.go b/internal/langserver/handlers/initialize.go index bb19995f8..db0ea9aef 100644 --- a/internal/langserver/handlers/initialize.go +++ b/internal/langserver/handlers/initialize.go @@ -138,6 +138,30 @@ func (lh *logHandler) Initialize(ctx context.Context, params lsp.InitializeParam } walker.SetLogger(lh.logger) + var excludeModulePaths []string + for _, rawPath := range cfgOpts.ExcludeModulePaths { + modPath, err := resolvePath(rootDir, rawPath) + if err != nil { + lh.logger.Printf("Ignoring excluded module path %s: %s", rawPath, err) + continue + } + excludeModulePaths = append(excludeModulePaths, modPath) + } + + walker.SetExcludeModulePaths(excludeModulePaths) + walker.EnqueuePath(fh.Dir()) + + // Walker runs asynchronously so we're intentionally *not* + // passing the request context here + walkerCtx := context.Background() + + // Walker is also started early to allow gradual consumption + // and avoid overfilling the queue + err = walker.StartWalking(walkerCtx) + if err != nil { + return serverCaps, err + } + if len(params.WorkspaceFolders) > 0 { for _, folderPath := range params.WorkspaceFolders { modPath, err := pathFromDocumentURI(folderPath.URI) @@ -176,24 +200,7 @@ func (lh *logHandler) Initialize(ctx context.Context, params lsp.InitializeParam return serverCaps, nil } - var excludeModulePaths []string - for _, rawPath := range cfgOpts.ExcludeModulePaths { - modPath, err := resolvePath(rootDir, rawPath) - if err != nil { - lh.logger.Printf("Ignoring excluded module path %s: %s", rawPath, err) - continue - } - excludeModulePaths = append(excludeModulePaths, modPath) - } - - walker.SetExcludeModulePaths(excludeModulePaths) - walker.EnqueuePath(fh.Dir()) - - // Walker runs asynchronously so we're intentionally *not* - // passing the request context here - err = walker.StartWalking(context.Background()) - - return serverCaps, err + return serverCaps, nil } func resolvePath(rootDir, rawPath string) (string, error) { diff --git a/internal/langserver/handlers/initialize_test.go b/internal/langserver/handlers/initialize_test.go index 3ba93ceb6..7c661c5e5 100644 --- a/internal/langserver/handlers/initialize_test.go +++ b/internal/langserver/handlers/initialize_test.go @@ -92,3 +92,30 @@ func TestInitialize_withInvalidRootURI(t *testing.T) { "rootUri": "meh" }`}, code.SystemError.Err()) } + +func TestInitialize_multipleFolders(t *testing.T) { + rootDir := TempDir(t) + ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ + TerraformCalls: &exec.TerraformMockCalls{ + PerWorkDir: map[string][]*mock.Call{ + rootDir.Dir(): validTfMockCalls(), + }, + }, + })) + stop := ls.Start(t) + defer stop() + + ls.Call(t, &langserver.CallRequest{ + Method: "initialize", + ReqParams: fmt.Sprintf(`{ + "capabilities": {}, + "rootUri": %q, + "processId": 12345, + "workspaceFolders": [ + { + "uri": %q, + "name": "root" + } + ] + }`, rootDir.URI(), rootDir.URI())}) +} diff --git a/internal/terraform/module/walker.go b/internal/terraform/module/walker.go index 2e30c66eb..3def6072d 100644 --- a/internal/terraform/module/walker.go +++ b/internal/terraform/module/walker.go @@ -50,6 +50,11 @@ type Walker struct { excludeModulePaths map[string]bool } +// queueCap represents channel buffer size +// which when reached causes EnqueuePath to block +// until a path is consumed +const queueCap = 50 + func NewWalker(fs filesystem.Filesystem, modMgr ModuleManager) *Walker { return &Walker{ fs: fs, @@ -58,7 +63,7 @@ func NewWalker(fs filesystem.Filesystem, modMgr ModuleManager) *Walker { walkingMu: &sync.RWMutex{}, queue: newWalkerQueue(fs), queueMu: &sync.Mutex{}, - pushChan: make(chan struct{}, 1), + pushChan: make(chan struct{}, queueCap), doneCh: make(chan struct{}, 0), } } @@ -100,6 +105,10 @@ func (w *Walker) EnqueuePath(path string) { defer w.queueMu.Unlock() heap.Push(w.queue, path) + w.triggerConsumption() +} + +func (w *Walker) triggerConsumption() { w.pushChan <- struct{}{} }