diff options
Diffstat (limited to 'internal/lsp/source/folding_range.go')
-rw-r--r-- | internal/lsp/source/folding_range.go | 185 |
1 files changed, 0 insertions, 185 deletions
diff --git a/internal/lsp/source/folding_range.go b/internal/lsp/source/folding_range.go deleted file mode 100644 index 576308f99..000000000 --- a/internal/lsp/source/folding_range.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package source - -import ( - "context" - "go/ast" - "go/token" - "sort" - "strings" - - "golang.org/x/tools/internal/lsp/protocol" -) - -// FoldingRangeInfo holds range and kind info of folding for an ast.Node -type FoldingRangeInfo struct { - MappedRange - Kind protocol.FoldingRangeKind -} - -// FoldingRange gets all of the folding range for f. -func FoldingRange(ctx context.Context, snapshot Snapshot, fh FileHandle, 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. - pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) - if err != nil { - return nil, err - } - - // With parse errors, we wouldn't be able to produce accurate folding info. - // LSP protocol (3.16) currently does not have a way to handle this case - // (https://github.com/microsoft/language-server-protocol/issues/1200). - // We cannot return an error either because we are afraid some editors - // may not handle errors nicely. As a workaround, we now return an empty - // result and let the client handle this case by double check the file - // contents (i.e. if the file is not empty and the folding range result - // is empty, raise an internal error). - if pgf.ParseErr != nil { - return nil, nil - } - - fset := snapshot.FileSet() - - // Get folding ranges for comments separately as they are not walked by ast.Inspect. - ranges = append(ranges, commentsFoldingRange(fset, pgf.Mapper, pgf.File)...) - - visit := func(n ast.Node) bool { - rng := foldingRangeFunc(fset, pgf.Mapper, n, lineFoldingOnly) - if rng != nil { - ranges = append(ranges, rng) - } - return true - } - // Walk the ast and collect folding ranges. - ast.Inspect(pgf.File, visit) - - sort.Slice(ranges, func(i, j int) bool { - irng, _ := ranges[i].Range() - jrng, _ := ranges[j].Range() - return protocol.CompareRange(irng, jrng) < 0 - }) - - return ranges, nil -} - -// foldingRangeFunc calculates the line folding range for ast.Node n -func foldingRangeFunc(fset *token.FileSet, m *protocol.ColumnMapper, n ast.Node, lineFoldingOnly bool) *FoldingRangeInfo { - // TODO(suzmue): include trailing empty lines before the closing - // parenthesis/brace. - var kind protocol.FoldingRangeKind - var start, end token.Pos - switch n := n.(type) { - case *ast.BlockStmt: - // Fold between positions of or lines between "{" and "}". - var startList, endList token.Pos - if num := len(n.List); num != 0 { - startList, endList = n.List[0].Pos(), n.List[num-1].End() - } - start, end = validLineFoldingRange(fset, n.Lbrace, n.Rbrace, startList, endList, lineFoldingOnly) - case *ast.CaseClause: - // Fold from position of ":" to end. - start, end = n.Colon+1, n.End() - case *ast.CommClause: - // Fold from position of ":" to end. - start, end = n.Colon+1, n.End() - case *ast.CallExpr: - // Fold from position of "(" to position of ")". - start, end = n.Lparen+1, n.Rparen - case *ast.FieldList: - // Fold between positions of or lines between opening parenthesis/brace and closing parenthesis/brace. - var startList, endList token.Pos - if num := len(n.List); num != 0 { - startList, endList = n.List[0].Pos(), n.List[num-1].End() - } - start, end = validLineFoldingRange(fset, n.Opening, n.Closing, startList, endList, lineFoldingOnly) - case *ast.GenDecl: - // If this is an import declaration, set the kind to be protocol.Imports. - if n.Tok == token.IMPORT { - kind = protocol.Imports - } - // Fold between positions of or lines between "(" and ")". - var startSpecs, endSpecs token.Pos - if num := len(n.Specs); num != 0 { - startSpecs, endSpecs = n.Specs[0].Pos(), n.Specs[num-1].End() - } - start, end = validLineFoldingRange(fset, n.Lparen, n.Rparen, startSpecs, endSpecs, lineFoldingOnly) - case *ast.BasicLit: - // Fold raw string literals from position of "`" to position of "`". - if n.Kind == token.STRING && len(n.Value) >= 2 && n.Value[0] == '`' && n.Value[len(n.Value)-1] == '`' { - start, end = n.Pos(), n.End() - } - case *ast.CompositeLit: - // Fold between positions of or lines between "{" and "}". - var startElts, endElts token.Pos - if num := len(n.Elts); num != 0 { - startElts, endElts = n.Elts[0].Pos(), n.Elts[num-1].End() - } - start, end = validLineFoldingRange(fset, n.Lbrace, n.Rbrace, startElts, endElts, lineFoldingOnly) - } - - // Check that folding positions are valid. - if !start.IsValid() || !end.IsValid() { - return nil - } - // in line folding mode, do not fold if the start and end lines are the same. - if lineFoldingOnly && fset.Position(start).Line == fset.Position(end).Line { - return nil - } - return &FoldingRangeInfo{ - MappedRange: NewMappedRange(fset, m, start, end), - Kind: kind, - } -} - -// validLineFoldingRange returns start and end token.Pos for folding range if the range is valid. -// returns token.NoPos otherwise, which fails token.IsValid check -func validLineFoldingRange(fset *token.FileSet, open, close, start, end token.Pos, lineFoldingOnly bool) (token.Pos, token.Pos) { - if lineFoldingOnly { - if !open.IsValid() || !close.IsValid() { - return token.NoPos, token.NoPos - } - - // Don't want to fold if the start/end is on the same line as the open/close - // as an example, the example below should *not* fold: - // var x = [2]string{"d", - // "e" } - if fset.Position(open).Line == fset.Position(start).Line || - fset.Position(close).Line == fset.Position(end).Line { - return token.NoPos, token.NoPos - } - - return open + 1, end - } - return open + 1, close -} - -// commentsFoldingRange returns the folding ranges for all comment blocks in file. -// The folding range starts at the end of the first line of the comment block, and ends at the end of the -// comment block and has kind protocol.Comment. -func commentsFoldingRange(fset *token.FileSet, m *protocol.ColumnMapper, file *ast.File) (comments []*FoldingRangeInfo) { - for _, commentGrp := range file.Comments { - startGrp, endGrp := fset.Position(commentGrp.Pos()), fset.Position(commentGrp.End()) - if startGrp.Line == endGrp.Line { - // Don't fold single line comments. - continue - } - - firstComment := commentGrp.List[0] - startPos, endLinePos := firstComment.Pos(), firstComment.End() - startCmmnt, endCmmnt := fset.Position(startPos), fset.Position(endLinePos) - if startCmmnt.Line != endCmmnt.Line { - // If the first comment spans multiple lines, then we want to have the - // folding range start at the end of the first line. - endLinePos = token.Pos(int(startPos) + len(strings.Split(firstComment.Text, "\n")[0])) - } - comments = append(comments, &FoldingRangeInfo{ - // Fold from the end of the first line comment to the end of the comment block. - MappedRange: NewMappedRange(fset, m, endLinePos, commentGrp.End()), - Kind: protocol.Comment, - }) - } - return comments -} |