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, &params); 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, &params); 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  }