github.com/getgauge/gauge@v1.6.9/api/lang/codeLens.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  	"fmt"
    11  
    12  	"encoding/json"
    13  	"strconv"
    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  const (
    23  	executeCommand    = "gauge.execute"
    24  	debugCommand      = "gauge.debug"
    25  	inParallelCommand = "gauge.execute.inParallel"
    26  	referencesCommand = "gauge.showReferences"
    27  
    28  	runSpecCodeLens       = "Run Spec"
    29  	debugSpecCodeLens     = "Debug Spec"
    30  	runInParallelCodeLens = "Run in parallel"
    31  	runScenarioCodeLens   = "Run Scenario"
    32  	debugScenarioCodeLens = "Debug Scenario"
    33  	referenceCodeLens     = "%s reference(s)"
    34  )
    35  
    36  func codeLenses(req *jsonrpc2.Request) (interface{}, error) {
    37  	var params lsp.CodeLensParams
    38  	if err := json.Unmarshal(*req.Params, &params); err != nil {
    39  		return nil, fmt.Errorf("failed to parse request %v", err)
    40  	}
    41  	if util.IsSpec(string(params.TextDocument.URI)) {
    42  		return getExecutionCodeLenses(params)
    43  	}
    44  	if util.IsConcept(string(params.TextDocument.URI)) {
    45  		return getConceptReferenceCodeLenses(params)
    46  	}
    47  	return getImplementationReferenceCodeLenses(params)
    48  }
    49  
    50  func getExecutionCodeLenses(params lsp.CodeLensParams) (interface{}, error) {
    51  	uri := params.TextDocument.URI
    52  	file := util.ConvertURItoFilePath(uri)
    53  	spec, res, err := new(parser.SpecParser).Parse(getContent(uri), gauge.NewConceptDictionary(), file)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	if !res.Ok {
    58  		return nil, concatenateErrors(res, file)
    59  	}
    60  	var codeLenses []lsp.CodeLens
    61  	runCodeLens := createCodeLens(spec.Heading.LineNo-1, runSpecCodeLens, executeCommand, getExecutionArgs(spec.FileName))
    62  	codeLenses = append(codeLenses, runCodeLens)
    63  	if lRunner.lspID != "" {
    64  		debugCodeLens := createCodeLens(spec.Heading.LineNo-1, debugSpecCodeLens, debugCommand, getExecutionArgs(spec.FileName))
    65  		codeLenses = append(codeLenses, debugCodeLens)
    66  	}
    67  	if spec.DataTable.IsInitialized() {
    68  		codeLenses = append(codeLenses, getDataTableLenses(spec)...)
    69  	}
    70  	return append(getScenarioCodeLenses(spec), codeLenses...), nil
    71  }
    72  
    73  func getConceptReferenceCodeLenses(params lsp.CodeLensParams) (interface{}, error) {
    74  	uri := params.TextDocument.URI
    75  	file := util.ConvertURItoFilePath(uri)
    76  	concepts, _ := new(parser.ConceptParser).Parse(getContent(uri), file)
    77  	allSteps := provider.AllSteps(false)
    78  	var lenses []lsp.CodeLens
    79  	for _, concept := range concepts {
    80  		lenses = append(lenses, createReferenceCodeLens(allSteps, uri, concept.Value, int(concept.LineNo)))
    81  	}
    82  	return lenses, nil
    83  }
    84  
    85  func getImplementationReferenceCodeLenses(params lsp.CodeLensParams) (interface{}, error) {
    86  	if lRunner.runner == nil {
    87  		return nil, nil
    88  	}
    89  	uri := params.TextDocument.URI
    90  	stepPositionsResponse, err := getStepPositionResponse(uri)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	allSteps := provider.AllSteps(true)
    95  	var lenses []lsp.CodeLens
    96  	for _, stepPosition := range stepPositionsResponse.GetStepPositions() {
    97  		lenses = append(lenses, createReferenceCodeLens(allSteps, uri, stepPosition.GetStepValue(), int(stepPosition.GetSpan().GetStart())))
    98  	}
    99  	return lenses, nil
   100  }
   101  
   102  func createReferenceCodeLens(allSteps []*gauge.Step, uri lsp.DocumentURI, stepValue string, startPosition int) lsp.CodeLens {
   103  	var count int
   104  	for _, step := range allSteps {
   105  		if stepValue == step.Value {
   106  			count++
   107  		}
   108  	}
   109  	lensTitle := fmt.Sprintf(referenceCodeLens, strconv.Itoa(count))
   110  	lensPosition := lsp.Position{Line: startPosition - 1, Character: 0}
   111  	lineNo := startPosition - 1
   112  	args := []interface{}{uri, lensPosition, stepValue}
   113  
   114  	return createCodeLens(lineNo, lensTitle, referencesCommand, args)
   115  }
   116  
   117  func getDataTableLenses(spec *gauge.Specification) []lsp.CodeLens {
   118  	var lenses []lsp.CodeLens
   119  	lenses = append(lenses, createCodeLens(spec.Heading.LineNo-1, runInParallelCodeLens, inParallelCommand, getExecutionArgs(spec.FileName)))
   120  	return lenses
   121  }
   122  
   123  func getScenarioCodeLenses(spec *gauge.Specification) []lsp.CodeLens {
   124  	var lenses []lsp.CodeLens
   125  	for _, sce := range spec.Scenarios {
   126  		args := getExecutionArgs(fmt.Sprintf("%s:%d", spec.FileName, sce.Heading.LineNo))
   127  		lens := createCodeLens(sce.Heading.LineNo-1, runScenarioCodeLens, executeCommand, args)
   128  		lenses = append(lenses, lens)
   129  		if lRunner.lspID != "" {
   130  			debugCodeLens := createCodeLens(sce.Heading.LineNo-1, debugScenarioCodeLens, debugCommand, args)
   131  			lenses = append(lenses, debugCodeLens)
   132  		}
   133  	}
   134  	return lenses
   135  }
   136  
   137  func createCodeLens(lineNo int, lensTitle, command string, args []interface{}) lsp.CodeLens {
   138  	return lsp.CodeLens{
   139  		Range: lsp.Range{
   140  			Start: lsp.Position{Line: lineNo, Character: 0},
   141  			End:   lsp.Position{Line: lineNo, Character: len(lensTitle)},
   142  		},
   143  		Command: lsp.Command{
   144  			Command:   command,
   145  			Title:     lensTitle,
   146  			Arguments: args,
   147  		},
   148  	}
   149  }
   150  
   151  func getExecutionArgs(id string) []interface{} {
   152  	var args []interface{}
   153  	return append(args, id)
   154  }
   155  
   156  func concatenateErrors(res *parser.ParseResult, file string) error {
   157  	errs := ""
   158  	for _, e := range res.ParseErrors {
   159  		errs = fmt.Sprintf("%s%s:%d %s\n", errs, e.FileName, e.LineNo, e.Message)
   160  	}
   161  	return fmt.Errorf("failed to parse %s\n%s", file, errs)
   162  }