Esempio n. 1
0
        public override SignatureHelp OnSignatureHelp(TextDocumentPosition parameters)
        {
            AnalyticsWrapper.Telemetry.TrackEvent("[SignatureHelp]", EventType.Completion); //Send event to analytics
            var fileCompiler = GetFileCompilerFromStringUri(parameters.uri);

            if (fileCompiler?.CompilationResultsForProgram?.ProcessedTokensDocumentSnapshot == null) //Semantic snapshot is not available
            {
                return(null);
            }

            var wrappedCodeElement = CodeElementFinder(fileCompiler, parameters.position).FirstOrDefault();

            if (wrappedCodeElement == null) //No codeelements found
            {
                return(null);
            }

            var node = CompletionFactory.GetMatchingNode(fileCompiler, wrappedCodeElement);

            //Get procedure name or qualified name
            string procedureName = CompletionFactoryHelpers.GetProcedureNameFromTokens(wrappedCodeElement.ArrangedConsumedTokens);

            //Try to get procedure by its name
            var calledProcedures =
                node.SymbolTable.GetFunctions(
                    p =>
                    p.Name.Equals(procedureName) ||
                    p.QualifiedName.ToString().Equals(procedureName), new List <SymbolTable.Scope>
            {
                SymbolTable.Scope.Declarations,
                SymbolTable.Scope.Intrinsic,
                SymbolTable.Scope.Namespace
            });
            var signatureHelp = new SignatureHelp();

            if (calledProcedures == null)
            {
                return(null);
            }

            if (calledProcedures.Count() == 1)
            {
                var calledProcedure = calledProcedures.First();
                //Create and return SignatureHelp object
                signatureHelp.signatures      = new SignatureInformation[1];
                signatureHelp.signatures[0]   = ProcedureSignatureHelper.SignatureHelperSignatureFormatter(calledProcedure);
                signatureHelp.activeSignature = 0; //Set the active signature as the one just created
                //Select the current parameter the user is expecting
                signatureHelp.activeParameter = ProcedureSignatureHelper.SignatureHelperParameterSelecter(calledProcedure, wrappedCodeElement, parameters.position);

                //There is only one possibility so the context can be set right now
                _SignatureCompletionContext = calledProcedure;

                return(signatureHelp);
            }

            //Else try to find the best matching signature

            //Get all given INPUT
            var givenInputParameters = CompletionFactoryHelpers.AggregateTokens(
                wrappedCodeElement.ArrangedConsumedTokens.SkipWhile(t => t.TokenType != TokenType.INPUT)
                .Skip(1)     //Ignore the INPUT Token
                .TakeWhile(t => !(t.TokenType == TokenType.OUTPUT || t.TokenType == TokenType.IN_OUT))).ToList();
            //Get all given OUTPUT
            var givenOutputParameters = CompletionFactoryHelpers.AggregateTokens(
                wrappedCodeElement.ArrangedConsumedTokens.SkipWhile(t => t.TokenType != TokenType.OUTPUT)
                .Skip(1)    //Ignore the INPUT Token
                .TakeWhile(t => !(t.TokenType == TokenType.INPUT || t.TokenType == TokenType.IN_OUT))).ToList();
            //Get all given INOUT
            var givenInoutParameters = CompletionFactoryHelpers.AggregateTokens(
                wrappedCodeElement.ArrangedConsumedTokens.SkipWhile(t => t.TokenType != TokenType.IN_OUT)
                .Skip(1)   //Ignore the INPUT Token
                .TakeWhile(t => !(t.TokenType == TokenType.OUTPUT || t.TokenType == TokenType.INPUT))).ToList();
            var totalGivenParameters = givenInputParameters.Count + givenInoutParameters.Count + givenOutputParameters.Count;

            var signatureInformation = new List <SignatureInformation>();

            _FunctionDeclarationSignatureDictionary.Clear();
            FunctionDeclaration bestmatchingProcedure = null;
            int previousMatchingWeight = 0, selectedSignatureIndex = 0;

            if (totalGivenParameters == 0)
            {
                foreach (var procedure in calledProcedures) //No parameters given, return all possibilities
                {
                    var formattedSignatureInformation = ProcedureSignatureHelper.SignatureHelperSignatureFormatter(procedure);
                    signatureInformation.Add(formattedSignatureInformation);
                    _FunctionDeclarationSignatureDictionary.Add(formattedSignatureInformation, procedure);
                }
            }
            else
            {
                foreach (var procedure in calledProcedures)
                {
                    //The commented parts allow to restrict the potential compatible signature to return to the client
                    int matchingWeight = 0;
                    //Test INPUT parameters
                    matchingWeight = matchingWeight + ProcedureSignatureHelper.ParametersTester(procedure.Profile.InputParameters, givenInputParameters, node);

                    //Test OUTPUT parameters
                    matchingWeight = matchingWeight + ProcedureSignatureHelper.ParametersTester(procedure.Profile.OutputParameters, givenOutputParameters, node);

                    //Test INOUT parameters
                    matchingWeight = matchingWeight + ProcedureSignatureHelper.ParametersTester(procedure.Profile.InoutParameters, givenInoutParameters, node);

                    if (matchingWeight > 0 && matchingWeight >= previousMatchingWeight && totalGivenParameters > 0)
                    {
                        if (matchingWeight > previousMatchingWeight)
                        {
                            signatureInformation.Clear();  //If the matchingWeight is superior than previous, it means that the previous signature is not precise enough.
                        }
                        var formattedSignatureInformation = ProcedureSignatureHelper.SignatureHelperSignatureFormatter(procedure);
                        signatureInformation.Add(formattedSignatureInformation);
                        _FunctionDeclarationSignatureDictionary.Add(formattedSignatureInformation, procedure);

                        previousMatchingWeight = matchingWeight;
                        bestmatchingProcedure  = procedure;
                    }
                    selectedSignatureIndex++;
                }
            }

            signatureHelp.signatures = signatureInformation.ToArray();

            if (signatureInformation.Count == 1)
            {
                _SignatureCompletionContext   = bestmatchingProcedure;                                                                                                     //Set the completion context
                signatureHelp.activeSignature = 0;                                                                                                                         //Select the only signature for the client
                signatureHelp.activeParameter = ProcedureSignatureHelper.SignatureHelperParameterSelecter(bestmatchingProcedure, wrappedCodeElement, parameters.position); //Select the current parameter
            }

            return(signatureHelp);
        }
Esempio n. 2
0
        /// <summary>
        /// Request to request completion at a given text document position. The request's
        /// parameter is of type[TextDocumentPosition](#TextDocumentPosition) the response
        /// is of type[CompletionItem[]](#CompletionItem) or a Thenable that resolves to such.
        /// </summary>
        public override List <CompletionItem> OnCompletion(TextDocumentPosition parameters)
        {
            var fileCompiler = GetFileCompilerFromStringUri(parameters.uri);

            if (fileCompiler == null)
            {
                return(null);
            }

            List <CompletionItem> items = new List <CompletionItem>();

            if (fileCompiler.CompilationResultsForProgram != null &&
                fileCompiler.CompilationResultsForProgram.ProcessedTokensDocumentSnapshot != null)
            {
                var wrappedCodeElements = CodeElementFinder(fileCompiler, parameters.position);
                if (wrappedCodeElements == null)
                {
                    return(new List <CompletionItem>());
                }

                Token userFilterToken      = null;
                Token lastSignificantToken = null;
                //Try to get a significant token for competion and return the codeelement containing the matching token.
                CodeElement matchingCodeElement = CodeElementMatcher.MatchCompletionCodeElement(parameters.position,
                                                                                                wrappedCodeElements,
                                                                                                out userFilterToken, out lastSignificantToken); //Magic happens here
                var userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text;

                if (lastSignificantToken != null)
                {
                    AnalyticsWrapper.Telemetry.TrackEvent("[Completion] " + lastSignificantToken.TokenType, EventType.Completion);
                    switch (lastSignificantToken.TokenType)
                    {
                    case TokenType.PERFORM:
                    {
                        items.AddRange(CompletionFactory.GetCompletionPerformParagraph(fileCompiler,
                                                                                       matchingCodeElement, userFilterToken));
                        break;
                    }

                    case TokenType.CALL:
                    {
                        _FunctionDeclarationSignatureDictionary.Clear();     //Clear to avoid key collision
                        items.AddRange(CompletionFactory.GetCompletionForProcedure(fileCompiler, matchingCodeElement,
                                                                                   userFilterToken, _FunctionDeclarationSignatureDictionary));
                        items.AddRange(CompletionFactory.GetCompletionForLibrary(fileCompiler, matchingCodeElement,
                                                                                 userFilterToken));
                        break;
                    }

                    case TokenType.TYPE:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForType(fileCompiler, matchingCodeElement,
                                                                              userFilterToken));
                        items.AddRange(CompletionFactory.GetCompletionForLibrary(fileCompiler, matchingCodeElement,
                                                                                 userFilterToken));
                        break;
                    }

                    case TokenType.QualifiedNameSeparator:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForQualifiedName(parameters.position,
                                                                                       fileCompiler, matchingCodeElement, lastSignificantToken, userFilterToken, _FunctionDeclarationSignatureDictionary));
                        break;
                    }

                    case TokenType.INPUT:
                    case TokenType.OUTPUT:
                    case TokenType.IN_OUT:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForProcedureParameter(parameters.position,
                                                                                            fileCompiler, matchingCodeElement, userFilterToken, lastSignificantToken, _SignatureCompletionContext));
                        break;
                    }

                    case TokenType.MOVE:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForVariable(fileCompiler, matchingCodeElement,
                                                                                  da =>
                                                                                  da.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase) &&
                                                                                  ((da.CodeElement != null &&
                                                                                    (da.CodeElement as DataDefinitionEntry).LevelNumber.Value < 88) ||
                                                                                   (da.CodeElement == null && da is IndexDefinition))));
                        //Ignore 88 level variable
                        break;
                    }

                    case TokenType.TO:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForTo(fileCompiler, matchingCodeElement,
                                                                            userFilterToken, lastSignificantToken));
                        break;
                    }

                    case TokenType.INTO:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForVariable(fileCompiler, matchingCodeElement,
                                                                                  v => v.Name.StartsWith(userFilterText, StringComparison.CurrentCultureIgnoreCase) &&
                                                                                  ((v.CodeElement as DataDefinitionEntry) != null &&
                                                                                   v.DataType == DataType.Alphabetic ||
                                                                                   v.DataType == DataType.Alphanumeric ||
                                                                                   v.DataType == DataType.AlphanumericEdited)
                                                                                  ));
                        break;
                    }

                    case TokenType.SET:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForVariable(fileCompiler, matchingCodeElement,
                                                                                  v => v.Name.StartsWith(userFilterText, StringComparison.CurrentCultureIgnoreCase)
                                                                                  &&
                                                                                  (((v.CodeElement as DataDefinitionEntry) != null &&
                                                                                    (v.CodeElement as DataDefinitionEntry).LevelNumber.Value == 88)
                                                                                                                 //Level 88 Variable
                                                                                   || v.DataType == DataType.Numeric || //Numeric Integer Variable
                                                                                   v.Usage == DataUsage.Pointer) //Or usage is pointer
                                                                                  ));
                        break;
                    }

                    case TokenType.OF:
                    {
                        items.AddRange(CompletionFactory.GetCompletionForOf(fileCompiler, matchingCodeElement,
                                                                            userFilterToken, parameters.position));
                        break;
                    }

                    default:
                        break;
                    }
                }
                else
                {
                    //If no known keyword has been found, let's try to get the context and return available variables.
                    if (matchingCodeElement == null && wrappedCodeElements.Any())
                    {
                        userFilterToken =
                            wrappedCodeElements.First().ArrangedConsumedTokens.FirstOrDefault(
                                t =>
                                parameters.position.character <= t.StopIndex + 1 && parameters.position.character > t.StartIndex &&
                                t.Line == parameters.position.line + 1 &&
                                t.TokenType == TokenType.UserDefinedWord);                              //Get the userFilterToken to filter the results

                        userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text; //Convert token to text

                        items.AddRange(CompletionFactory.GetCompletionForVariable(fileCompiler,
                                                                                  wrappedCodeElements.First(), da => da.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)));
                    }
                    else
                    {
                        //Return a default text to inform the user that completion is not available after the given token
                        items.Add(new CompletionItem("Completion is not available in this context")
                        {
                            insertText = ""
                        });
                    }
                }

                if (userFilterToken != null)
                {
                    //Add the range object to let the client know the position of the user filter token
                    var range = new Range(userFilterToken.Line - 1, userFilterToken.StartIndex,
                                          userFilterToken.Line - 1, userFilterToken.StopIndex + 1);
                    //-1 on lne to 0 based / +1 on stop index to include the last character
                    items = items.Select(c =>
                    {
                        if (c.data != null && c.data.GetType().IsArray)
                        {
                            ((object[])c.data)[0] = range;
                        }
                        else
                        {
                            c.data = range;
                        }
                        return(c);
                    }).ToList();
                }
            }


            return(items);
        }