github.com/getgauge/gauge@v1.6.9/api/lang/symbols.go (about)

     1  /*----------------------------------------------------------------
     2   *  Copyright (c) ThoughtWorks, Inc.
     3   *  Licensed under the Apache License, Version 2.0
     4   *  See LICENSE in the project root for license information.
     5   *----------------------------------------------------------------*/
     6  
     7  package lang
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"sort"
    13  	"strings"
    14  
    15  	"github.com/getgauge/gauge/gauge"
    16  	"github.com/getgauge/gauge/parser"
    17  	"github.com/getgauge/gauge/util"
    18  	"github.com/sourcegraph/go-langserver/pkg/lsp"
    19  	"github.com/sourcegraph/jsonrpc2"
    20  )
    21  
    22  func documentSymbols(req *jsonrpc2.Request) (interface{}, error) {
    23  	var params lsp.DocumentSymbolParams
    24  	var err error
    25  	if err = json.Unmarshal(*req.Params, &params); err != nil {
    26  		return nil, fmt.Errorf("failed to parse request %v", err)
    27  	}
    28  	file := util.ConvertURItoFilePath(params.TextDocument.URI)
    29  	content := getContent(params.TextDocument.URI)
    30  	if util.IsConcept(file) {
    31  		return getConceptSymbols(content, file), nil
    32  	}
    33  	spec, parseResult, err := new(parser.SpecParser).Parse(content, gauge.NewConceptDictionary(), file)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	if !parseResult.Ok {
    38  		return nil, fmt.Errorf("parsing failed for %s. %s", file, parseResult.Errors())
    39  	}
    40  	var symbols = make([]*lsp.SymbolInformation, 0)
    41  	symbols = append(symbols, getSpecSymbol(spec))
    42  	for _, scn := range spec.Scenarios {
    43  		symbols = append(symbols, getScenarioSymbol(scn, file))
    44  	}
    45  	return symbols, nil
    46  }
    47  
    48  func workspaceSymbols(req *jsonrpc2.Request) (interface{}, error) {
    49  	var params lsp.WorkspaceSymbolParams
    50  	var err error
    51  	if err = json.Unmarshal(*req.Params, &params); err != nil {
    52  		return nil, fmt.Errorf("failed to parse request %v", err)
    53  	}
    54  
    55  	if len(params.Query) < 2 {
    56  		return nil, nil
    57  	}
    58  
    59  	var specSymbols = make([]*lsp.SymbolInformation, 0)
    60  	var scnSymbols = make([]*lsp.SymbolInformation, 0)
    61  	specDetails := provider.GetAvailableSpecDetails([]string{})
    62  	for _, specDetail := range specDetails {
    63  		if !specDetail.HasSpec() {
    64  			continue
    65  		}
    66  		spec := specDetail.Spec
    67  		if strings.Contains(strings.ToLower(specDetail.Spec.Heading.Value), strings.ToLower(params.Query)) {
    68  			specSymbols = append(specSymbols, getSpecSymbol(spec))
    69  		}
    70  		for _, scn := range spec.Scenarios {
    71  			if strings.Contains(strings.ToLower(scn.Heading.Value), strings.ToLower(params.Query)) {
    72  				scnSymbols = append(scnSymbols, getScenarioSymbol(scn, spec.FileName))
    73  			}
    74  		}
    75  	}
    76  	sort.Sort(byName(specSymbols))
    77  	sort.Sort(byName(scnSymbols))
    78  	return append(specSymbols, scnSymbols...), nil
    79  }
    80  
    81  func getSpecSymbol(s *gauge.Specification) *lsp.SymbolInformation {
    82  	return &lsp.SymbolInformation{
    83  		Name: fmt.Sprintf("# %s", s.Heading.Value),
    84  		Kind: lsp.SKNamespace,
    85  		Location: lsp.Location{
    86  			URI: util.ConvertPathToURI(s.FileName),
    87  			Range: lsp.Range{
    88  				Start: lsp.Position{Line: s.Heading.LineNo - 1, Character: 0},
    89  				End:   lsp.Position{Line: s.Heading.LineNo - 1, Character: len(s.Heading.Value)},
    90  			},
    91  		},
    92  	}
    93  }
    94  
    95  func getScenarioSymbol(s *gauge.Scenario, path string) *lsp.SymbolInformation {
    96  	return &lsp.SymbolInformation{
    97  		Name: fmt.Sprintf("## %s", s.Heading.Value),
    98  		Kind: lsp.SKNamespace,
    99  		Location: lsp.Location{
   100  			URI: util.ConvertPathToURI(path),
   101  			Range: lsp.Range{
   102  				Start: lsp.Position{Line: s.Heading.LineNo - 1, Character: 0},
   103  				End:   lsp.Position{Line: s.Heading.LineNo - 1, Character: len(s.Heading.Value)},
   104  			},
   105  		},
   106  	}
   107  }
   108  
   109  func getConceptSymbols(content, file string) []*lsp.SymbolInformation {
   110  	concepts, _ := new(parser.ConceptParser).Parse(content, file)
   111  	var symbols = make([]*lsp.SymbolInformation, 0)
   112  	for _, cpt := range concepts {
   113  		symbols = append(symbols, &lsp.SymbolInformation{
   114  			Name: fmt.Sprintf("# %s", cpt.LineText),
   115  			Kind: lsp.SKNamespace,
   116  			Location: lsp.Location{
   117  				URI: util.ConvertPathToURI(file),
   118  				Range: lsp.Range{
   119  					Start: lsp.Position{Line: cpt.LineNo - 1, Character: 0},
   120  					End:   lsp.Position{Line: cpt.LineNo - 1, Character: len(cpt.LineText)},
   121  				},
   122  			},
   123  		})
   124  	}
   125  	return symbols
   126  }
   127  
   128  type byName []*lsp.SymbolInformation
   129  
   130  func (s byName) Len() int {
   131  	return len(s)
   132  }
   133  
   134  func (s byName) Swap(i, j int) {
   135  	s[i], s[j] = s[j], s[i]
   136  }
   137  
   138  func (s byName) Less(i, j int) bool {
   139  	return strings.Compare(s[i].Name, s[j].Name) < 0
   140  }