Skip to content

Commit 9906c49

Browse files
authored
Store documents in a memdb-backed table (hashicorp#771)
* internal/document: Decouple document types * internal/state: Decouple document state into memdb * internal/filesystem: reduce impl to simple OS FS + interface for DocumentStore Previously filesystem package had two major use cases, to offer a unified io/fs.FS interface for e.g. parsing *.tf or *.tfvars, which was implemented mostly via external library (spf13/afero). Secondly it also provided a direct full access to the "in-memory layer" of the filesystem for RPC handlers (e.g. didOpen, didChange, didClose, ...). These use cases rarely overlap throughout the codebase and so this lead to unnecessary imports of the `filesystem` package in places where we only needed either the OS-level FS or in-mem FS, but almost never both. This decoupling allows us to import `filesystem` or `state.DocumentStore` separately. Also, as we no longer need the in-mem backend of afero, it makes more sense to just reimplement the small part of the 3rd party library instead. * internal/hcl: Update references * internal/lsp: Update references * internal/source: Update references * internal/terraform: update references * internal/*: Update RPC handlers + custom cmds * replace last occurence of afero with osFs * internal/document: add tests and comments to Handle+DirHandle * internal/uri: refactor & add comments * internal/uri: introduce MustParseURI * internal/uri: account for VSCode's over-escaping of colon * internal/document: clean up handle logic & normalize URIs before saving * internal/uri: account for drive-letter normalization in VSCode
1 parent 6659c80 commit 9906c49

File tree

111 files changed

+2514
-2415
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+2514
-2415
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ require (
3030
github.com/pmezard/go-difflib v1.0.0
3131
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
3232
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
33-
github.com/spf13/afero v1.8.1
33+
github.com/spf13/afero v1.8.1 // indirect
3434
github.com/stretchr/testify v1.7.0
3535
github.com/vektra/mockery/v2 v2.10.0
3636
github.com/zclconf/go-cty v1.10.0

internal/cmd/completion_command.go

+21-21
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/hashicorp/hcl-lang/lang"
1414
"github.com/hashicorp/terraform-ls/internal/decoder"
15+
"github.com/hashicorp/terraform-ls/internal/document"
1516
"github.com/hashicorp/terraform-ls/internal/filesystem"
1617
"github.com/hashicorp/terraform-ls/internal/logging"
1718
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
@@ -63,8 +64,6 @@ func (c *CompletionCommand) Run(args []string) int {
6364
return 1
6465
}
6566

66-
fh := ilsp.FileHandlerFromPath(path)
67-
6867
parts := strings.Split(c.atPos, ":")
6968
if len(parts) != 2 {
7069
c.Ui.Error(fmt.Sprintf("Error parsing at-pos argument: %q (expected line:col format)", c.atPos))
@@ -84,56 +83,57 @@ func (c *CompletionCommand) Run(args []string) int {
8483

8584
logger := logging.NewLogger(os.Stderr)
8685

87-
fs := filesystem.NewFilesystem()
88-
fs.SetLogger(logger)
89-
fs.CreateAndOpenDocument(fh, "terraform", content)
90-
91-
doc, err := fs.GetDocument(fh)
86+
ss, err := state.NewStateStore()
9287
if err != nil {
9388
c.Ui.Error(err.Error())
9489
return 1
9590
}
9691

97-
fPos, err := ilsp.FilePositionFromDocumentPosition(lsp.TextDocumentPositionParams{
98-
TextDocument: lsp.TextDocumentIdentifier{
99-
URI: fh.DocumentURI(),
100-
},
101-
Position: lspPos,
102-
}, doc)
92+
dh := document.HandleFromPath(path)
93+
err = ss.DocumentStore.OpenDocument(dh, "terraform", 0, content)
10394
if err != nil {
10495
c.Ui.Error(err.Error())
10596
return 1
10697
}
10798

108-
ctx := context.Background()
109-
ss, err := state.NewStateStore()
99+
fs := filesystem.NewFilesystem(ss.DocumentStore)
100+
fs.SetLogger(logger)
101+
102+
doc, err := ss.DocumentStore.GetDocument(dh)
110103
if err != nil {
111104
c.Ui.Error(err.Error())
112105
return 1
113106
}
114-
modMgr := module.NewSyncModuleManager(ctx, fs, ss.Modules, ss.ProviderSchemas)
115107

116-
_, err = modMgr.AddModule(fh.Dir())
108+
pos, err := ilsp.HCLPositionFromLspPosition(lspPos, doc)
117109
if err != nil {
118110
c.Ui.Error(err.Error())
119111
return 1
120112
}
121113

122-
pos := fPos.Position()
114+
ctx := context.Background()
115+
116+
modMgr := module.NewSyncModuleManager(ctx, fs, ss.DocumentStore, ss.Modules, ss.ProviderSchemas)
117+
118+
_, err = modMgr.AddModule(dh.Dir.Path())
119+
if err != nil {
120+
c.Ui.Error(err.Error())
121+
return 1
122+
}
123123

124124
d, err := decoder.NewDecoder(ctx, &decoder.PathReader{
125125
ModuleReader: ss.Modules,
126126
SchemaReader: ss.ProviderSchemas,
127127
}).Path(lang.Path{
128-
Path: doc.Dir(),
129-
LanguageID: doc.LanguageID(),
128+
Path: doc.Dir.Path(),
129+
LanguageID: doc.LanguageID,
130130
})
131131
if err != nil {
132132
c.Ui.Error(err.Error())
133133
return 1
134134
}
135135

136-
candidates, err := d.CandidatesAtPos(doc.Filename(), pos)
136+
candidates, err := d.CandidatesAtPos(doc.Filename, pos)
137137
if err != nil {
138138
c.Ui.Error(fmt.Sprintf("failed to find candidates: %s", err.Error()))
139139
return 1

internal/cmd/inspect_module_command.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,19 @@ func (c *InspectModuleCommand) inspect(rootPath string) error {
8484
return fmt.Errorf("expected %s to be a directory", rootPath)
8585
}
8686

87-
fs := filesystem.NewFilesystem()
88-
89-
ctx := context.Background()
9087
ss, err := state.NewStateStore()
9188
if err != nil {
9289
return err
9390
}
94-
modMgr := module.NewSyncModuleManager(ctx, fs, ss.Modules, ss.ProviderSchemas)
91+
92+
fs := filesystem.NewFilesystem(ss.DocumentStore)
93+
94+
ctx := context.Background()
95+
96+
modMgr := module.NewSyncModuleManager(ctx, fs, ss.DocumentStore, ss.Modules, ss.ProviderSchemas)
9597
modMgr.SetLogger(c.logger)
9698

97-
walker := module.SyncWalker(fs, modMgr)
99+
walker := module.SyncWalker(fs, ss.DocumentStore, modMgr)
98100
walker.SetLogger(c.logger)
99101

100102
ctx, cancel := ictx.WithSignalCancel(context.Background(),

internal/context/context.go

-15
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"time"
66

7-
"github.com/hashicorp/terraform-ls/internal/filesystem"
87
"github.com/hashicorp/terraform-ls/internal/langserver/diagnostics"
98
lsp "github.com/hashicorp/terraform-ls/internal/protocol"
109
"github.com/hashicorp/terraform-ls/internal/settings"
@@ -20,7 +19,6 @@ func (k *contextKey) String() string {
2019
}
2120

2221
var (
23-
ctxDs = &contextKey{"document storage"}
2422
ctxTfExecPath = &contextKey{"terraform executable path"}
2523
ctxTfExecLogPath = &contextKey{"terraform executor log path"}
2624
ctxTfExecTimeout = &contextKey{"terraform execution timeout"}
@@ -40,19 +38,6 @@ func missingContextErr(ctxKey *contextKey) *MissingContextErr {
4038
return &MissingContextErr{ctxKey}
4139
}
4240

43-
func WithDocumentStorage(ctx context.Context, fs filesystem.DocumentStorage) context.Context {
44-
return context.WithValue(ctx, ctxDs, fs)
45-
}
46-
47-
func DocumentStorage(ctx context.Context) (filesystem.DocumentStorage, error) {
48-
fs, ok := ctx.Value(ctxDs).(filesystem.DocumentStorage)
49-
if !ok {
50-
return nil, missingContextErr(ctxDs)
51-
}
52-
53-
return fs, nil
54-
}
55-
5641
func WithTerraformExecLogPath(ctx context.Context, path string) context.Context {
5742
return context.WithValue(ctx, ctxTfExecLogPath, path)
5843
}

internal/document/change.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package document
2+
3+
import (
4+
"bytes"
5+
6+
"github.com/hashicorp/terraform-ls/internal/source"
7+
)
8+
9+
type Change interface {
10+
Text() string
11+
Range() *Range
12+
}
13+
14+
type Changes []Change
15+
16+
func ApplyChanges(original []byte, changes Changes) ([]byte, error) {
17+
if len(changes) == 0 {
18+
return original, nil
19+
}
20+
21+
var buf bytes.Buffer
22+
_, err := buf.Write(original)
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
for _, ch := range changes {
28+
err := applyDocumentChange(&buf, ch)
29+
if err != nil {
30+
return nil, err
31+
}
32+
}
33+
34+
return buf.Bytes(), nil
35+
}
36+
37+
func applyDocumentChange(buf *bytes.Buffer, change Change) error {
38+
// if the range is nil, we assume it is full content change
39+
if change.Range() == nil {
40+
buf.Reset()
41+
_, err := buf.WriteString(change.Text())
42+
return err
43+
}
44+
45+
lines := source.MakeSourceLines("", buf.Bytes())
46+
47+
startByte, err := ByteOffsetForPos(lines, change.Range().Start)
48+
if err != nil {
49+
return err
50+
}
51+
endByte, err := ByteOffsetForPos(lines, change.Range().End)
52+
if err != nil {
53+
return err
54+
}
55+
56+
diff := endByte - startByte
57+
if diff > 0 {
58+
buf.Grow(diff)
59+
}
60+
61+
beforeChange := make([]byte, startByte, startByte)
62+
copy(beforeChange, buf.Bytes())
63+
afterBytes := buf.Bytes()[endByte:]
64+
afterChange := make([]byte, len(afterBytes), len(afterBytes))
65+
copy(afterChange, afterBytes)
66+
67+
buf.Reset()
68+
69+
_, err = buf.Write(beforeChange)
70+
if err != nil {
71+
return err
72+
}
73+
_, err = buf.WriteString(change.Text())
74+
if err != nil {
75+
return err
76+
}
77+
_, err = buf.Write(afterChange)
78+
if err != nil {
79+
return err
80+
}
81+
82+
return nil
83+
}

0 commit comments

Comments
 (0)