github.com/getgauge/gauge@v1.6.9/api/lang/completion.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 "strings" 12 13 "github.com/sourcegraph/go-langserver/pkg/lsp" 14 "github.com/sourcegraph/jsonrpc2" 15 ) 16 17 type insertTextFormat int 18 19 const ( 20 text insertTextFormat = 1 21 snippet insertTextFormat = 2 22 concept = "Concept" 23 step = "Step" 24 tag = "Tag" 25 tagIdentifier = "tags:" 26 emptyString = "" 27 colon = ":" 28 comma = "," 29 ) 30 31 type completionItem struct { 32 lsp.CompletionItem 33 InsertTextFormat insertTextFormat `json:"insertTextFormat,omitempty"` 34 } 35 36 type completionList struct { 37 IsIncomplete bool `json:"isIncomplete"` 38 Items []completionItem `json:"items"` 39 } 40 41 func completion(req *jsonrpc2.Request) (interface{}, error) { 42 var params lsp.TextDocumentPositionParams 43 if err := json.Unmarshal(*req.Params, ¶ms); err != nil { 44 return nil, err 45 } 46 line := getLine(params.TextDocument.URI, params.Position.Line) 47 pLine := line 48 if len(line) > params.Position.Character { 49 pLine = line[:params.Position.Character] 50 } 51 if isInTagsContext(params.Position.Line, params.TextDocument.URI) { 52 return tagsCompletion(line, pLine, params) 53 } 54 if !isStepCompletion(pLine, params.Position.Character) { 55 return completionList{IsIncomplete: false, Items: []completionItem{}}, nil 56 } 57 if inParameterContext(line, params.Position.Character) { 58 return paramCompletion(line, pLine, params) 59 } 60 v, err := stepCompletion(line, pLine, params) 61 if err != nil && v != nil { 62 // there were errors, but gauge will return completions on a best effort promise. 63 logError(req, err.Error()) 64 return v, nil 65 } 66 return v, err 67 } 68 69 func isInTagsContext(line int, uri lsp.DocumentURI) bool { 70 if strings.HasPrefix(strings.ToLower(strings.Join(strings.Fields(getLine(uri, line)), "")), tagIdentifier) { 71 return true 72 } else if line != 0 && (endsWithComma(getLine(uri, line-1)) && isInTagsContext(line-1, uri)) { 73 return true 74 } 75 return false 76 } 77 78 func endsWithComma(line string) bool { 79 return strings.HasSuffix(strings.TrimSpace(line), comma) 80 } 81 82 func isStepCompletion(line string, character int) bool { 83 if character == 0 { 84 return false 85 } 86 if !strings.HasPrefix(strings.TrimSpace(line), "*") { 87 return false 88 } 89 return true 90 } 91 92 func inParameterContext(line string, charPos int) bool { 93 pl := line 94 if len(line) > charPos { 95 pl = line[:charPos] 96 } 97 lineAfterCharPos := strings.SplitAfter(pl, "*") 98 if len(lineAfterCharPos) == 1 { 99 return false 100 } 101 l := strings.TrimPrefix(strings.SplitAfter(pl, "*")[1], " ") 102 var stack string 103 for _, value := range l { 104 if string(value) == "<" { 105 stack = stack + string(value) 106 } 107 if string(value) == ">" && len(stack) != 0 && stack[len(stack)-1:] == "<" { 108 stack = stack[:len(stack)-1] 109 } 110 if string(value) == "\"" { 111 if len(stack) != 0 && stack[len(stack)-1:] == "\"" { 112 stack = stack[:len(stack)-1] 113 } else { 114 stack = stack + string(value) 115 } 116 } 117 } 118 return len(stack) != 0 119 } 120 121 func resolveCompletion(req *jsonrpc2.Request) (interface{}, error) { 122 var params completionItem 123 if err := json.Unmarshal(*req.Params, ¶ms); err != nil { 124 return nil, err 125 } 126 return params, nil 127 } 128 129 func getEditRange(index int, position lsp.Position, pLine, line string, endSeparator string) lsp.Range { 130 start := lsp.Position{Line: position.Line, Character: index + 1} 131 endIndex := start.Character 132 if len(line) >= position.Character { 133 lineAfterCursor := line[position.Character:] 134 endIndex = strings.Index(lineAfterCursor, endSeparator) 135 } 136 if endIndex == -1 { 137 endIndex = len(line) 138 } else { 139 endIndex = len(pLine) + endIndex + 1 140 } 141 end := lsp.Position{Line: position.Line, Character: endIndex} 142 return lsp.Range{Start: start, End: end} 143 }