Skip to content
This repository has been archived by the owner on Aug 21, 2022. It is now read-only.

Commit

Permalink
Merge branch 'master' of github.com:golang/tools into bingo
Browse files Browse the repository at this point in the history
  • Loading branch information
saibing committed Sep 4, 2019
2 parents 137bc65 + be0da05 commit 9c5d923
Show file tree
Hide file tree
Showing 9 changed files with 439 additions and 54 deletions.
4 changes: 2 additions & 2 deletions gopls/doc/design.md
Expand Up @@ -263,7 +263,7 @@ Previous | [impl]
| | This feature is hard to scale up to large code bases, and is going to take thought to get right. It may be feasible to implemented a more limited form in the meantime.

---
Document symbols | Provides the set of top level symbols in teh current file.
Document symbols | Provides the set of top level symbols in the current file.
---------------- | ---
Requires | AST of the current file only
LSP | [`textDocument/documentSymbol`]
Expand Down Expand Up @@ -397,4 +397,4 @@ Previous | N/A
[`textDocument/selectionRange`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md#textDocument_selectionRange
[`textDocument/signatureHelp`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md#textDocument_signatureHelp
[`textDocument/typeDefinition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md#textDocument_typeDefinition
[`workspace/didChangeWatchedFiles`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md#workspace_didChangeWatchedFiles
[`workspace/didChangeWatchedFiles`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/specification.md#workspace_didChangeWatchedFiles
2 changes: 1 addition & 1 deletion internal/lsp/folding_range.go
Expand Up @@ -20,7 +20,7 @@ func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRange
return nil, err
}

ranges, err := source.FoldingRange(ctx, view, f)
ranges, err := source.FoldingRange(ctx, view, f, s.lineFoldingOnly)
if err != nil {
return nil, err
}
Expand Down
2 changes: 2 additions & 0 deletions internal/lsp/general.go
Expand Up @@ -160,6 +160,8 @@ func (s *Server) setClientCapabilities(caps protocol.ClientCapabilities) {
if len(caps.TextDocument.Hover.ContentFormat) > 0 {
s.preferredContentFormat = caps.TextDocument.Hover.ContentFormat[0]
}
// Check if the client supports only line folding.
s.lineFoldingOnly = caps.TextDocument.FoldingRange.LineFoldingOnly
}

func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
Expand Down
103 changes: 78 additions & 25 deletions internal/lsp/lsp_test.go
Expand Up @@ -269,8 +269,9 @@ func summarizeCompletionItems(i int, want []source.CompletionItem, got []protoco
func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
for _, spn := range data {
uri := spn.URI()
filename := uri.Filename()

// Test all folding ranges.
r.server.lineFoldingOnly = false
ranges, err := r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: protocol.NewURI(uri),
Expand All @@ -280,61 +281,113 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
t.Error(err)
continue
}
r.foldingRanges(t, "foldingRange", uri, ranges)

f, err := getGoFile(r.ctx, r.server.session.ViewOf(uri), uri)
if err != nil {
t.Fatal(err)
}
m, err := getMapper(r.ctx, f)
// Test folding ranges with lineFoldingOnly = true.
r.server.lineFoldingOnly = true
ranges, err = r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: protocol.NewURI(uri),
},
})
if err != nil {
t.Fatal(err)
t.Error(err)
continue
}
r.foldingRanges(t, "foldingRange-lineFolding", uri, ranges)

}
}

// Fold all ranges.
got, err := foldRanges(m, string(m.Content), ranges)
func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, ranges []protocol.FoldingRange) {
f, err := getGoFile(r.ctx, r.server.session.ViewOf(uri), uri)
if err != nil {
t.Fatal(err)
}
m, err := getMapper(r.ctx, f)
if err != nil {
t.Fatal(err)
}

// Fold all ranges.
nonOverlapping := nonOverlappingRanges(ranges)
for i, rngs := range nonOverlapping {
got, err := foldRanges(m, string(m.Content), rngs)
if err != nil {
t.Error(err)
continue
}
want := string(r.data.Golden("foldingRange", spn.URI().Filename(), func() ([]byte, error) {
tag := fmt.Sprintf("%s-%d", prefix, i)
want := string(r.data.Golden(tag, 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)
t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
}
}

// Filter by kind.
kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
for _, kind := range kinds {
var kindOnly []protocol.FoldingRange
for _, fRng := range ranges {
if fRng.Kind == string(kind) {
kindOnly = append(kindOnly, fRng)
}
// Filter by kind.
kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
for _, kind := range kinds {
var kindOnly []protocol.FoldingRange
for _, fRng := range ranges {
if fRng.Kind == string(kind) {
kindOnly = append(kindOnly, fRng)
}
}

got, err := foldRanges(m, string(m.Content), kindOnly)
nonOverlapping := nonOverlappingRanges(kindOnly)
for i, rngs := range nonOverlapping {
got, err := foldRanges(m, string(m.Content), rngs)
if err != nil {
t.Error(err)
continue
}
want := string(r.data.Golden("foldingRange-"+string(kind), spn.URI().Filename(), func() ([]byte, error) {
tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i)
want := string(r.data.Golden(tag, 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)
t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
}

}

}
}

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
Expand Down
1 change: 1 addition & 0 deletions internal/lsp/server.go
Expand Up @@ -92,6 +92,7 @@ type Server struct {
preferredContentFormat protocol.MarkupKind
disabledAnalyses map[string]struct{}
wantSuggestedFixes bool
lineFoldingOnly bool

supportedCodeActions map[source.FileKind]map[protocol.CodeActionKind]bool

Expand Down
5 changes: 4 additions & 1 deletion internal/lsp/source/folding_range.go
Expand Up @@ -16,7 +16,7 @@ type FoldingRangeInfo struct {
}

// FoldingRange gets all of the folding range for f.
func FoldingRange(ctx context.Context, view View, f GoFile) (ranges []FoldingRangeInfo, err error) {
func FoldingRange(ctx context.Context, view View, f GoFile, lineFoldingOnly bool) (ranges []FoldingRangeInfo, err error) {
// TODO(suzmue): consider limiting the number of folding ranges returned, and
// implement a way to prioritize folding ranges in that case.
file, err := f.GetAST(ctx, ParseFull)
Expand Down Expand Up @@ -54,6 +54,9 @@ func FoldingRange(ctx context.Context, view View, f GoFile) (ranges []FoldingRan
}

if start.IsValid() && end.IsValid() {
if lineFoldingOnly && f.FileSet().Position(start).Line == f.FileSet().Position(end).Line {
return true
}
ranges = append(ranges, FoldingRangeInfo{
Range: span.NewRange(f.FileSet(), start, end),
Kind: kind,
Expand Down
89 changes: 69 additions & 20 deletions internal/lsp/source/source_test.go
Expand Up @@ -276,67 +276,116 @@ func summarizeCompletionItems(i int, want []source.CompletionItem, got []source.
func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
for _, spn := range data {
uri := spn.URI()
filename := uri.Filename()

f, err := r.view.GetFile(r.ctx, uri)
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
data, _, err := f.Handle(r.ctx).Read(r.ctx)
if err != nil {
t.Error(err)
continue
}

ranges, err := source.FoldingRange(r.ctx, r.view, f.(source.GoFile))
// Test all folding ranges.
ranges, err := source.FoldingRange(r.ctx, r.view, f.(source.GoFile), false)
if err != nil {
t.Error(err)
continue
}
data, _, err := f.Handle(r.ctx).Read(r.ctx)
r.foldingRanges(t, "foldingRange", uri, string(data), ranges)

// Test folding ranges with lineFoldingOnly
ranges, err = source.FoldingRange(r.ctx, r.view, f.(source.GoFile), true)
if err != nil {
t.Error(err)
continue
}
// Fold all ranges.
got, err := foldRanges(string(data), ranges)
r.foldingRanges(t, "foldingRange-lineFolding", uri, string(data), ranges)

}
}

func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data string, ranges []source.FoldingRangeInfo) {
t.Helper()
// Fold all ranges.
nonOverlapping := nonOverlappingRanges(ranges)
for i, rngs := range nonOverlapping {
got, err := foldRanges(string(data), rngs)
if err != nil {
t.Error(err)
continue
}
want := string(r.data.Golden("foldingRange", spn.URI().Filename(), func() ([]byte, error) {
tag := fmt.Sprintf("%s-%d", prefix, i)
want := string(r.data.Golden(tag, 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)
t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
}
}

// Filter by kind.
kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
for _, kind := range kinds {
var kindOnly []source.FoldingRangeInfo
for _, fRng := range ranges {
if fRng.Kind == kind {
kindOnly = append(kindOnly, fRng)
}
// Filter by kind.
kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
for _, kind := range kinds {
var kindOnly []source.FoldingRangeInfo
for _, fRng := range ranges {
if fRng.Kind == kind {
kindOnly = append(kindOnly, fRng)
}
}

got, err := foldRanges(string(data), kindOnly)
nonOverlapping := nonOverlappingRanges(kindOnly)
for i, rngs := range nonOverlapping {
got, err := foldRanges(string(data), rngs)
if err != nil {
t.Error(err)
continue
}
want := string(r.data.Golden("foldingRange-"+string(kind), spn.URI().Filename(), func() ([]byte, error) {
tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i)
want := string(r.data.Golden(tag, 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)
t.Errorf("%s: failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
}

}

}
}

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
Expand Down
13 changes: 12 additions & 1 deletion internal/lsp/testdata/folding/a.go
@@ -1,7 +1,7 @@
package folding //@fold("package")

import (
_ "fmt"
"fmt"
_ "log"
)

Expand All @@ -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`
Expand Down

1 comment on commit 9c5d923

@arichiardi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there @saibing, thanks for doing this port, is the plan for it to be merged into tools proper?

Please sign in to comment.