diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index 222dfdce58..b0cbeb0fad 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -290,17 +290,21 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { } // Fold all ranges. - got, err := foldRanges(m, string(m.Content), ranges) - if err != nil { - t.Error(err) - continue - } - want := string(r.data.Golden("foldingRange", spn.URI().Filename(), func() ([]byte, error) { - return []byte(got), nil - })) + nonOverlapping := nonOverlappingRanges(ranges) + for i, rngs := range nonOverlapping { + got, err := foldRanges(m, string(m.Content), rngs) + if err != nil { + t.Error(err) + continue + } + tag := fmt.Sprintf("foldingRange-%d", i) + want := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) { + return []byte(got), nil + })) - if want != got { - t.Errorf("foldingRanges failed for %s, expected:\n%v\ngot:\n%v", filename, want, got) + if want != got { + t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, filename, want, got) + } } // Filter by kind. @@ -313,17 +317,21 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { } } - got, err := foldRanges(m, string(m.Content), kindOnly) - if err != nil { - t.Error(err) - continue - } - want := string(r.data.Golden("foldingRange-"+string(kind), spn.URI().Filename(), func() ([]byte, error) { - return []byte(got), nil - })) + nonOverlapping := nonOverlappingRanges(kindOnly) + for i, rngs := range nonOverlapping { + got, err := foldRanges(m, string(m.Content), rngs) + if err != nil { + t.Error(err) + continue + } + tag := fmt.Sprintf("foldingRange-%s-%d", kind, i) + want := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) { + return []byte(got), nil + })) - if want != got { - t.Errorf("foldingRanges-%s failed for %s, expected:\n%v\ngot:\n%v", string(kind), filename, want, got) + if want != got { + t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, filename, want, got) + } } } @@ -331,9 +339,37 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { } } +func nonOverlappingRanges(ranges []protocol.FoldingRange) (res [][]protocol.FoldingRange) { + for _, fRng := range ranges { + setNum := len(res) + for i := 0; i < len(res); i++ { + canInsert := true + for _, rng := range res[i] { + if conflict(rng, fRng) { + canInsert = false + break + } + } + if canInsert { + setNum = i + break + } + } + if setNum == len(res) { + res = append(res, []protocol.FoldingRange{}) + } + res[setNum] = append(res[setNum], fRng) + } + return res +} + +func conflict(a, b protocol.FoldingRange) bool { + // a start position is <= b start positions + return (a.StartLine < b.StartLine || (a.StartLine == b.StartLine && a.StartCharacter <= b.StartCharacter)) && + (a.EndLine > b.StartLine || (a.EndLine == b.StartLine && a.EndCharacter > b.StartCharacter)) +} + func foldRanges(m *protocol.ColumnMapper, contents string, ranges []protocol.FoldingRange) (string, error) { - // TODO(suzmue): Allow folding ranges to intersect for these tests, do a folding by level, - // or per individual fold. foldedText := "<>" res := contents // Apply the edits from the end of the file forward diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go index 2243db2498..00e71b0b85 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -288,18 +288,23 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { t.Error(err) continue } - // Fold all ranges. - got, err := foldRanges(string(data), ranges) - if err != nil { - t.Error(err) - continue - } - want := string(r.data.Golden("foldingRange", spn.URI().Filename(), func() ([]byte, error) { - return []byte(got), nil - })) - if want != got { - t.Errorf("foldingRanges failed for %s, expected:\n%v\ngot:\n%v", filename, want, got) + // Fold all ranges. + nonOverlapping := nonOverlappingRanges(ranges) + for i, rngs := range nonOverlapping { + got, err := foldRanges(string(data), rngs) + if err != nil { + t.Error(err) + continue + } + tag := fmt.Sprintf("foldingRange-%d", i) + want := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) { + return []byte(got), nil + })) + + if want != got { + t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, filename, want, got) + } } // Filter by kind. @@ -312,17 +317,21 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { } } - got, err := foldRanges(string(data), kindOnly) - if err != nil { - t.Error(err) - continue - } - want := string(r.data.Golden("foldingRange-"+string(kind), spn.URI().Filename(), func() ([]byte, error) { - return []byte(got), nil - })) + nonOverlapping := nonOverlappingRanges(kindOnly) + for i, rngs := range nonOverlapping { + got, err := foldRanges(string(data), rngs) + if err != nil { + t.Error(err) + continue + } + tag := fmt.Sprintf("foldingRange-%s-%d", kind, i) + want := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) { + return []byte(got), nil + })) - if want != got { - t.Errorf("foldingRanges-%s failed for %s, expected:\n%v\ngot:\n%v", string(kind), filename, want, got) + if want != got { + t.Errorf("%s: failed for %s, expected:\n%v\ngot:\n%v", tag, filename, want, got) + } } } @@ -330,8 +339,36 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) { } } +func nonOverlappingRanges(ranges []source.FoldingRangeInfo) (res [][]source.FoldingRangeInfo) { + for _, fRng := range ranges { + setNum := len(res) + for i := 0; i < len(res); i++ { + canInsert := true + for _, rng := range res[i] { + if conflict(rng, fRng) { + canInsert = false + break + } + } + if canInsert { + setNum = i + break + } + } + if setNum == len(res) { + res = append(res, []source.FoldingRangeInfo{}) + } + res[setNum] = append(res[setNum], fRng) + } + return res +} + +func conflict(a, b source.FoldingRangeInfo) bool { + // a start position is <= b start positions + return a.Range.Start <= b.Range.Start && a.Range.End > b.Range.Start +} + func foldRanges(contents string, ranges []source.FoldingRangeInfo) (string, error) { - // TODO(suzmue): Allow folding ranges to intersect for these tests. foldedText := "<>" res := contents // Apply the folds from the end of the file forward diff --git a/internal/lsp/testdata/folding/a.go b/internal/lsp/testdata/folding/a.go index 03ee153614..e691ee00e0 100644 --- a/internal/lsp/testdata/folding/a.go +++ b/internal/lsp/testdata/folding/a.go @@ -1,7 +1,7 @@ package folding //@fold("package") import ( - _ "fmt" + "fmt" _ "log" ) @@ -10,6 +10,17 @@ import _ "os" // bar is a function. // With a multiline doc comment. func bar() string { + switch { + case true: + if true { + fmt.Println("true") + } + case false: + fmt.Println("false") + default: + fmt.Println("default") + } + return ` this string is not indented` diff --git a/internal/lsp/testdata/folding/a.go.golden b/internal/lsp/testdata/folding/a.go.golden index 4ffe9b7aa7..2c09f381f3 100644 --- a/internal/lsp/testdata/folding/a.go.golden +++ b/internal/lsp/testdata/folding/a.go.golden @@ -1,4 +1,4 @@ --- foldingRange -- +-- foldingRange-0 -- package folding //@fold("package") import (<>) @@ -8,11 +8,115 @@ import _ "os" // bar is a function.<> func bar(<>) string {<>} --- foldingRange-comment -- +-- foldingRange-1 -- package folding //@fold("package") import ( - _ "fmt" + "fmt" + _ "log" +) + +import _ "os" + +// bar is a function. +// With a multiline doc comment. +func bar() string { + switch {<>} + + return ` +this string +is not indented` + +} + +-- foldingRange-2 -- +package folding //@fold("package") + +import ( + "fmt" + _ "log" +) + +import _ "os" + +// bar is a function. +// With a multiline doc comment. +func bar() string { + switch { + case true:<> + case false:<> + default:<> + } + + return ` +this string +is not indented` + +} + +-- foldingRange-3 -- +package folding //@fold("package") + +import ( + "fmt" + _ "log" +) + +import _ "os" + +// bar is a function. +// With a multiline doc comment. +func bar() string { + switch { + case true: + if true {<>} + case false: + fmt.Println(<>) + default: + fmt.Println(<>) + } + + return ` +this string +is not indented` + +} + +-- foldingRange-4 -- +package folding //@fold("package") + +import ( + "fmt" + _ "log" +) + +import _ "os" + +// bar is a function. +// With a multiline doc comment. +func bar() string { + switch { + case true: + if true { + fmt.Println(<>) + } + case false: + fmt.Println("false") + default: + fmt.Println("default") + } + + return ` +this string +is not indented` + +} + +-- foldingRange-comment-0 -- +package folding //@fold("package") + +import ( + "fmt" _ "log" ) @@ -20,13 +124,24 @@ import _ "os" // bar is a function.<> func bar() string { + switch { + case true: + if true { + fmt.Println("true") + } + case false: + fmt.Println("false") + default: + fmt.Println("default") + } + return ` this string is not indented` } --- foldingRange-imports -- +-- foldingRange-imports-0 -- package folding //@fold("package") import (<>) @@ -36,6 +151,17 @@ import _ "os" // bar is a function. // With a multiline doc comment. func bar() string { + switch { + case true: + if true { + fmt.Println("true") + } + case false: + fmt.Println("false") + default: + fmt.Println("default") + } + return ` this string is not indented`