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 }