Skip to content

Commit

Permalink
Support multiple folders natively
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed May 27, 2021
1 parent 92a2a2c commit ca61baf
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 11 deletions.
74 changes: 74 additions & 0 deletions internal/langserver/handlers/did_change_workspace_folders.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package handlers

import (
"context"
"fmt"

"github.com/creachadair/jrpc2"
lsctx "github.com/hashicorp/terraform-ls/internal/context"
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
"github.com/hashicorp/terraform-ls/internal/uri"
)

func (lh *logHandler) DidChangeWorkspaceFolders(ctx context.Context, params lsp.DidChangeWorkspaceFoldersParams) error {
watcher, err := lsctx.Watcher(ctx)
if err != nil {
return err
}

// walker, err := lsctx.ModuleWalker(ctx)
// if err != nil {
// return err
// }

for _, removed := range params.Event.Removed {
modPath, err := pathFromDocumentURI(removed.URI)
if err != nil {
jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{
Type: lsp.Warning,
Message: fmt.Sprintf("Ignoring removed workspace folder %s: %s."+
" This is most likely bug, please report it.", removed.URI, err),
})
continue
}
err = watcher.RemoveModule(modPath)
if err != nil {
lh.logger.Printf("failed to remove module from watcher: %s", err)
continue
}

// TODO: Remove module from walker's queue
}

for _, added := range params.Event.Added {
modPath, err := pathFromDocumentURI(added.URI)
if err != nil {
jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{
Type: lsp.Warning,
Message: fmt.Sprintf("Ignoring new workspace folder %s: %s."+
" This is most likely bug, please report it.", added.URI, err),
})
continue
}
err = watcher.AddModule(modPath)
if err != nil {
lh.logger.Printf("failed to add module to watcher: %s", err)
continue
}

// TODO: Add module to walker's queue
}

// TODO: Restart walker if it's not walking

return nil
}

func pathFromDocumentURI(docUri string) (string, error) {
rawPath, err := uri.PathFromURI(docUri)
if err != nil {
return "", err
}

return cleanupPath(rawPath)
}
5 changes: 4 additions & 1 deletion internal/langserver/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ func initializeResponse(t *testing.T, commandPrefix string) string {
"full": false
},
"workspace": {
"workspaceFolders": {}
"workspaceFolders": {
"supported": true,
"changeNotifications": "workspace/didChangeWorkspaceFolders"
}
}
},
"serverInfo": {
Expand Down
37 changes: 37 additions & 0 deletions internal/langserver/handlers/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ func (lh *logHandler) Initialize(ctx context.Context, params lsp.InitializeParam
DocumentFormattingProvider: true,
DocumentSymbolProvider: true,
WorkspaceSymbolProvider: true,
Workspace: lsp.WorkspaceGn{
WorkspaceFolders: lsp.WorkspaceFoldersGn{
Supported: true,
ChangeNotifications: "workspace/didChangeWorkspaceFolders",
},
},
},
}

Expand Down Expand Up @@ -114,6 +120,33 @@ func (lh *logHandler) Initialize(ctx context.Context, params lsp.InitializeParam
}
cfgOpts := out.Options

if !clientCaps.Workspace.WorkspaceFolders && len(params.WorkspaceFolders) > 0 {
jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{
Type: lsp.Warning,
Message: "Client sent workspace folders despite not declaring support. " +
"Please report this as a bug.",
})
}

if len(params.WorkspaceFolders) > 0 {
for _, folderPath := range params.WorkspaceFolders {
modPath, err := pathFromDocumentURI(folderPath.URI)
if err != nil {
jrpc2.ServerFromContext(ctx).Notify(ctx, "window/showMessage", &lsp.ShowMessageParams{
Type: lsp.Warning,
Message: fmt.Sprintf("Ignoring workspace folder %s: %s."+
" This is most likely bug, please report it.", folderPath.URI, err),
})
continue
}

err = w.AddModule(modPath)
if err != nil {
return serverCaps, err
}
}
}

// Static user-provided paths take precedence over dynamic discovery
if len(cfgOpts.ModulePaths) > 0 {
lh.logger.Printf("Attempting to add %d static module paths", len(cfgOpts.ModulePaths))
Expand Down Expand Up @@ -171,6 +204,10 @@ func resolvePath(rootDir, rawPath string) (string, error) {
path = filepath.Join(rootDir, rawPath)
}

return cleanupPath(path)
}

func cleanupPath(path string) (string, error) {
absPath, err := filepath.EvalSymlinks(path)
return toLowerVolumePath(absPath), err
}
Expand Down
11 changes: 11 additions & 0 deletions internal/langserver/handlers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,17 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {

return handle(ctx, req, lh.TextDocumentDidSave)
},
"workspace/didChangeWorkspaceFolders": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
return nil, err
}

ctx = lsctx.WithModuleWalker(ctx, svc.walker)
ctx = lsctx.WithWatcher(ctx, svc.watcher)

return handle(ctx, req, lh.DidChangeWorkspaceFolders)
},
"workspace/executeCommand": func(ctx context.Context, req *jrpc2.Request) (interface{}, error) {
err := session.CheckInitializationIsConfirmed()
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions internal/state/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,11 @@ func (s *ModuleStore) Add(modPath string) error {
return err
}
if obj != nil {
return &AlreadyExistsError{
Idx: modPath,
}
// Ignore already existing module because
// user may have hierarchically overlapping
// folders in their workspace, so hitting
// the module twice is possible.
return nil
}

err = txn.Insert(s.tableName, newModule(modPath))
Expand Down
9 changes: 2 additions & 7 deletions internal/state/module_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package state

import (
"errors"
"path/filepath"
"testing"

Expand Down Expand Up @@ -30,12 +29,8 @@ func TestModuleStore_Add_duplicate(t *testing.T) {
}

err = s.Modules.Add(modPath)
if err == nil {
t.Fatal("expected error for duplicate entry")
}
existsError := &AlreadyExistsError{}
if !errors.As(err, &existsError) {
t.Fatalf("unexpected error: %s", err)
if err != nil {
t.Fatal(err)
}
}

Expand Down
1 change: 1 addition & 0 deletions internal/terraform/module/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ type Watcher interface {
Stop() error
SetLogger(*log.Logger)
AddModule(string) error
RemoveModule(string) error
IsModuleWatched(string) bool
}
5 changes: 5 additions & 0 deletions internal/terraform/module/walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func (w *Walker) setWalking(isWalking bool) {
w.walking = isWalking
}

// TODO: EnqueuePath(path string)

// TODO: RemovePathFromQueue(path string)

// TODO: Refactor into StartWalking(ctx context.Context) error
func (w *Walker) StartWalking(ctx context.Context, path string) error {
if w.IsWalking() {
return fmt.Errorf("walker is already running")
Expand Down
25 changes: 25 additions & 0 deletions internal/terraform/module/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,31 @@ func (w *watcher) AddModule(modPath string) error {
return nil
}

func (w *watcher) RemoveModule(modPath string) error {
modPath = filepath.Clean(modPath)

w.logger.Printf("removing module from watching: %s", modPath)

for modI, mod := range w.modules {
if pathcmp.PathEquals(mod.Path, modPath) {
for _, wPath := range mod.Watched {
w.fw.Remove(wPath)
}
w.fw.Remove(mod.Path)
w.modules = append(w.modules[:modI], w.modules[modI+1:]...)
}

for i, wp := range mod.Watched {
if pathcmp.PathEquals(wp, modPath) {
w.fw.Remove(wp)
mod.Watched = append(mod.Watched[:i], mod.Watched[i+1:]...)
}
}
}

return nil
}

func (w *watcher) run(ctx context.Context) {
for {
select {
Expand Down
4 changes: 4 additions & 0 deletions internal/terraform/module/watcher_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func (w *mockWatcher) AddModule(string) error {
return nil
}

func (w *mockWatcher) RemoveModule(string) error {
return nil
}

func (w *mockWatcher) IsModuleWatched(string) bool {
return false
}

0 comments on commit ca61baf

Please sign in to comment.