Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial update #103

Merged
merged 10 commits into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dist/
.idea/
39 changes: 35 additions & 4 deletions internal/filesystem/file.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package filesystem

import (
"bytes"
"path/filepath"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/source"
encunicode "golang.org/x/text/encoding/unicode"
)
Expand All @@ -16,8 +18,9 @@ type file struct {
content []byte
open bool

ls source.Lines
errs bool
version int
ls source.Lines
errs bool
}

func NewFile(fullPath string, content []byte) *file {
Expand All @@ -40,6 +43,14 @@ func (f *file) URI() string {
return URIFromPath(f.fullPath)
}

func (f *file) Version() int {
return f.version
}

func (f *file) SetVersion(version int) {
f.version = version
}

func (f *file) Lines() source.Lines {
if f.ls == nil {
f.ls = source.MakeSourceLines(f.Filename(), f.content)
Expand All @@ -52,13 +63,33 @@ func (f *file) Text() []byte {
}

func (f *file) applyChange(change FileChange) error {
newBytes := []byte(change.Text())
f.change(newBytes)
// if the range is regarded as nil, we regard it as full content change
if rangeIsNil(change.Range()) {
f.change([]byte(change.Text()))
return nil
}
b := &bytes.Buffer{}
b.Grow(len(f.content) + diffLen(change))
b.Write(f.content[:change.Range().Start.Byte])
b.WriteString(change.Text())
b.Write(f.content[change.Range().End.Byte:])

f.change(b.Bytes())
return nil
}

func (f *file) change(s []byte) {
njuCZ marked this conversation as resolved.
Show resolved Hide resolved
f.content = s
f.ls = nil
}

// HCL column and line indexes start from 1, therefore if the any index
// contains 0, we assume it is an undefined range
func rangeIsNil(r hcl.Range) bool {
return r.End.Column == 0 && r.End.Line == 0
radeksimko marked this conversation as resolved.
Show resolved Hide resolved
}

func diffLen(change FileChange) int {
rangeLen := change.Range().End.Byte - change.Range().Start.Byte
return len(change.Text()) - rangeLen
}
129 changes: 126 additions & 3 deletions internal/filesystem/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,139 @@ func TestFile_ApplyChange_fullUpdate(t *testing.T) {
}
}

func TestFile_ApplyChange_partialUpdate(t *testing.T) {
testData := []struct {
Name string
Content string
FileChange *fileChange
Expect string
}{
{
Name: "length grow: 4",
Content: "hello world",
FileChange: &fileChange{
text: "terraform",
rng: hcl.Range{
Start: hcl.Pos{
Line: 1,
Column: 7,
Byte: 6,
},
End: hcl.Pos{
Line: 1,
Column: 12,
Byte: 11,
},
},
},
Expect: "hello terraform",
},
{
Name: "length the same",
Content: "hello world",
FileChange: &fileChange{
text: "earth",
rng: hcl.Range{
Start: hcl.Pos{
Line: 1,
Column: 7,
Byte: 6,
},
End: hcl.Pos{
Line: 1,
Column: 12,
Byte: 11,
},
},
},
Expect: "hello earth",
},
{
Name: "length grow: -2",
Content: "hello world",
FileChange: &fileChange{
text: "HCL",
rng: hcl.Range{
Start: hcl.Pos{
Line: 1,
Column: 7,
Byte: 6,
},
End: hcl.Pos{
Line: 1,
Column: 12,
Byte: 11,
},
},
},
Expect: "hello HCL",
},
{
Name: "add utf-18 character",
Content: "hello world",
FileChange: &fileChange{
text: "𐐀𐐀 ",
rng: hcl.Range{
Start: hcl.Pos{
Line: 1,
Column: 7,
Byte: 6,
},
End: hcl.Pos{
Line: 1,
Column: 7,
Byte: 6,
},
},
},
Expect: "hello 𐐀𐐀 world",
},
{
Name: "modify when containing utf-18 character",
Content: "hello 𐐀𐐀 world",
FileChange: &fileChange{
text: "aa𐐀",
rng: hcl.Range{
Start: hcl.Pos{
Line: 1,
Column: 9,
Byte: 10,
},
End: hcl.Pos{
Line: 1,
Column: 11,
Byte: 14,
},
},
},
Expect: "hello 𐐀aa𐐀 world",
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Name)

f := NewFile("file:///test.tf", []byte(v.Content))
err := f.applyChange(v.FileChange)
if err != nil {
t.Fatal(err)
}

if string(f.content) != v.Expect {
t.Fatalf("expected: %q but actually: %q", v.Expect, string(f.content))
}
}
}

njuCZ marked this conversation as resolved.
Show resolved Hide resolved
type fileChange struct {
text string
rng hcl.Range
}

func (fc *fileChange) Text() string {
return fc.text
}

func (fc *fileChange) Range() hcl.Range {
return hcl.Range{
// TODO: Implement partial updates
}
return fc.rng
}
2 changes: 2 additions & 0 deletions internal/filesystem/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func (fs *fsystem) Open(file File) error {
f = NewFile(file.FullPath(), file.Text())
}
f.open = true
f.version = file.Version()
d.files[file.Filename()] = f
return nil
}
Expand All @@ -54,6 +55,7 @@ func (fs *fsystem) Change(fh VersionedFileHandler, changes FileChanges) error {
for _, change := range changes {
f.applyChange(change)
}
f.SetVersion(fh.Version())
return nil
}

Expand Down
27 changes: 6 additions & 21 deletions internal/filesystem/filesystem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/source"
)

func TestFilesystem_Change_notOpen(t *testing.T) {
fs := NewFilesystem()

var changes FileChanges
changes = append(changes, &testChange{})
changes = append(changes, &fileChange{})
h := &testHandler{"file:///doesnotexist"}

err := fs.Change(h, changes)
Expand Down Expand Up @@ -41,7 +40,7 @@ func TestFilesystem_Change_closed(t *testing.T) {
}

var changes FileChanges
changes = append(changes, &testChange{})
changes = append(changes, &fileChange{})
err = fs.Change(fh, changes)

expectedErr := &FileNotOpenErr{fh}
Expand Down Expand Up @@ -105,10 +104,10 @@ func TestFilesystem_Change_multipleChanges(t *testing.T) {
})

var changes FileChanges
changes = append(changes, &testChange{text: "ahoy"})
changes = append(changes, &testChange{text: ""})
changes = append(changes, &testChange{text: "quick brown fox jumped over\nthe lazy dog"})
changes = append(changes, &testChange{text: "bye"})
changes = append(changes, &fileChange{text: "ahoy"})
changes = append(changes, &fileChange{text: ""})
changes = append(changes, &fileChange{text: "quick brown fox jumped over\nthe lazy dog"})
changes = append(changes, &fileChange{text: "bye"})

err := fs.Change(fh, changes)
if err != nil {
Expand Down Expand Up @@ -196,17 +195,3 @@ func (fh *testHandler) Filename() string {
func (fh *testHandler) Version() int {
return 0
}

type testChange struct {
text string
}

func (ch *testChange) Text() string {
return ch.text
}

func (ch *testChange) Range() hcl.Range {
return hcl.Range{
// TODO: Implement partial updates
}
}
radeksimko marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions internal/filesystem/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type File interface {
FileHandler
Text() []byte
Lines() source.Lines
Version() int
}

type FilePosition interface {
Expand Down
16 changes: 11 additions & 5 deletions internal/lsp/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ type File interface {
}

type file struct {
fh FileHandler
ls source.Lines
text []byte
fh FileHandler
ls source.Lines
text []byte
version int
}

func (f *file) URI() string {
Expand Down Expand Up @@ -50,9 +51,14 @@ func (f *file) lines() source.Lines {
return f.ls
}

func (f *file) Version() int {
return f.version
}

func FileFromDocumentItem(doc lsp.TextDocumentItem) *file {
return &file{
fh: FileHandler(doc.URI),
text: []byte(doc.Text),
fh: FileHandler(doc.URI),
text: []byte(doc.Text),
version: doc.Version,
}
}
30 changes: 27 additions & 3 deletions internal/lsp/file_change.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package lsp

import (
"fmt"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/filesystem"
"github.com/sourcegraph/go-lsp"
Expand All @@ -15,7 +13,15 @@ type fileChange struct {

func FileChange(chEvent lsp.TextDocumentContentChangeEvent, f File) (*fileChange, error) {
if chEvent.Range != nil {
return nil, fmt.Errorf("Partial updates are not supported (yet)")
rng, err := lspRangeToHCL(*chEvent.Range, f)
if err != nil {
return nil, err
}

return &fileChange{
text: chEvent.Text,
rng: *rng,
}, nil
}

return &fileChange{
Expand Down Expand Up @@ -61,6 +67,24 @@ func hclRangeToLSP(hclRng hcl.Range) lsp.Range {
}
}

func lspRangeToHCL(lspRng lsp.Range, f File) (*hcl.Range, error) {
startPos, err := lspPositionToHCL(f.Lines(), lspRng.Start)
if err != nil {
return nil, err
}

endPos, err := lspPositionToHCL(f.Lines(), lspRng.End)
if err != nil {
return nil, err
}

return &hcl.Range{
Filename: f.Filename(),
Start: startPos,
End: endPos,
}, nil
}

func (fc *fileChange) Text() string {
return fc.text
}
Expand Down
Loading