internal/lsp: add tests for nested folding ranges

FoldingRanges may be nested. Test nested folding ranges by separating
out the folding ranges by nested level and checking each level.

Change-Id: I12c72daa3e6c6b9d4959209b3a41b27e2b59866f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/192398
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Suzy Mueller 2019-08-29 15:28:51 -04:00
parent a56fc290d6
commit afe7f8212f
4 changed files with 259 additions and 49 deletions

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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`