github.com/getgauge/gauge@v1.6.9/api/lang/completionStep.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  	"regexp"
    12  	"strings"
    13  
    14  	"github.com/getgauge/gauge/gauge"
    15  	"github.com/getgauge/gauge/parser"
    16  	"github.com/sourcegraph/go-langserver/pkg/lsp"
    17  )
    18  
    19  func stepCompletion(line, pLine string, params lsp.TextDocumentPositionParams) (interface{}, error) {
    20  	list := completionList{IsIncomplete: false, Items: make([]completionItem, 0)}
    21  	editRange := getStepEditRange(line, params.Position)
    22  	prefix := getPrefix(pLine)
    23  	givenArgs, err := getStepArgs(strings.TrimSpace(pLine))
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	for _, c := range provider.Concepts() {
    28  		fText := prefix + getStepFilterText(c.StepValue.StepValue, c.StepValue.Parameters, givenArgs)
    29  		cText := prefix + addPlaceHolders(c.StepValue.StepValue, c.StepValue.Parameters)
    30  		list.Items = append(list.Items, newStepCompletionItem(c.StepValue.ParameterizedStepValue, cText, concept, fText, editRange))
    31  	}
    32  	s, err := allImplementedStepValues()
    33  	allSteps := append(allUsedStepValues(), s...)
    34  	for _, sv := range removeDuplicates(allSteps) {
    35  		fText := prefix + getStepFilterText(sv.StepValue, sv.Args, givenArgs)
    36  		cText := prefix + addPlaceHolders(sv.StepValue, sv.Args)
    37  		list.Items = append(list.Items, newStepCompletionItem(sv.ParameterizedStepValue, cText, step, fText, editRange))
    38  	}
    39  	return list, err
    40  }
    41  
    42  func removeDuplicates(steps []gauge.StepValue) []gauge.StepValue {
    43  	encountered := map[string]bool{}
    44  	result := []gauge.StepValue{}
    45  	for _, v := range steps {
    46  		if !encountered[v.StepValue] {
    47  			encountered[v.StepValue] = true
    48  			result = append(result, v)
    49  		}
    50  	}
    51  	return result
    52  }
    53  
    54  func allUsedStepValues() []gauge.StepValue {
    55  	var stepValues []gauge.StepValue
    56  	for _, s := range provider.Steps(true) {
    57  		stepValues = append(stepValues, parser.CreateStepValue(s))
    58  	}
    59  	return stepValues
    60  }
    61  func allImplementedStepValues() ([]gauge.StepValue, error) {
    62  	var stepValues []gauge.StepValue
    63  	res, err := getAllStepsResponse()
    64  	if err != nil {
    65  		return stepValues, fmt.Errorf("failed to get steps from runner. %s", err.Error())
    66  	}
    67  	for _, stepText := range res.GetSteps() {
    68  		stepValue, _ := parser.ExtractStepValueAndParams(stepText, false)
    69  		stepValues = append(stepValues, *stepValue)
    70  	}
    71  	return stepValues, nil
    72  }
    73  
    74  func getStepArgs(line string) ([]gauge.StepArg, error) {
    75  	givenArgs := make([]gauge.StepArg, 0)
    76  	if line != "" && strings.TrimSpace(line) != "*" {
    77  		specParser := new(parser.SpecParser)
    78  		tokens, errs := specParser.GenerateTokens(line, "")
    79  		if len(errs) > 0 {
    80  			return nil, fmt.Errorf("Unable to parse text entered")
    81  		}
    82  		var err error
    83  		givenArgs, err = parser.ExtractStepArgsFromToken(tokens[0])
    84  		if err != nil {
    85  			return nil, fmt.Errorf("Unable to parse text entered")
    86  		}
    87  	}
    88  	return givenArgs, nil
    89  
    90  }
    91  
    92  func getStepFilterText(text string, params []string, givenArgs []gauge.StepArg) string {
    93  	if len(params) > 0 {
    94  		for i, p := range params {
    95  			if len(givenArgs) > i {
    96  				if givenArgs[i].ArgType == gauge.Static {
    97  					text = strings.Replace(text, "{}", fmt.Sprintf("\"%s\"", givenArgs[i].ArgValue()), 1)
    98  				} else {
    99  					text = strings.Replace(text, "{}", fmt.Sprintf("<%s>", givenArgs[i].ArgValue()), 1)
   100  				}
   101  			} else {
   102  				text = strings.Replace(text, "{}", fmt.Sprintf("<%s>", p), 1)
   103  			}
   104  		}
   105  	}
   106  	return text
   107  }
   108  
   109  func getStepEditRange(line string, cursorPos lsp.Position) lsp.Range {
   110  	start := 1
   111  	loc := regexp.MustCompile(`^\s*\*(\s*)`).FindIndex([]byte(line))
   112  	if loc != nil {
   113  		start = loc[1]
   114  	}
   115  	if start > cursorPos.Character {
   116  		start = cursorPos.Character
   117  	}
   118  	end := len(line)
   119  	if end < 2 {
   120  		end = 1
   121  	}
   122  	if end < cursorPos.Character {
   123  		end = cursorPos.Character
   124  	}
   125  	startPos := lsp.Position{Line: cursorPos.Line, Character: start}
   126  	endPos := lsp.Position{Line: cursorPos.Line, Character: end}
   127  	return lsp.Range{Start: startPos, End: endPos}
   128  }
   129  
   130  func getPrefix(line string) string {
   131  	if strings.HasPrefix(strings.TrimPrefix(line, " "), "* ") {
   132  		return ""
   133  	}
   134  	return " "
   135  }
   136  
   137  func addPlaceHolders(text string, args []string) string {
   138  	text = strings.Replace(text, "{}", "\"{}\"", -1)
   139  	for i, v := range args {
   140  		value := i + 1
   141  		if value == len(args) {
   142  			value = 0
   143  		}
   144  		text = strings.Replace(text, "{}", fmt.Sprintf("${%d:%s}", value, v), 1)
   145  	}
   146  	return text
   147  }
   148  
   149  func newStepCompletionItem(stepText, text, kind, fText string, editRange lsp.Range) completionItem {
   150  	return completionItem{
   151  		CompletionItem: lsp.CompletionItem{
   152  			Label:         stepText,
   153  			Detail:        kind,
   154  			Kind:          lsp.CIKFunction,
   155  			TextEdit:      &lsp.TextEdit{Range: editRange, NewText: text},
   156  			FilterText:    fText,
   157  			Documentation: stepText,
   158  		},
   159  		InsertTextFormat: snippet,
   160  	}
   161  }