public static IEnumerable <CompletionItem> GetCompletionForType(FileCompiler fileCompiler, CodeElement codeElement, Token userFilterToken)
        {
            var node = GetMatchingNode(fileCompiler, codeElement);
            IEnumerable <TypeDefinition> types = null;

            if (node?.SymbolTable != null)
            {
                var userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text;
                types =
                    node.SymbolTable.GetTypes(
                        t => t.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)
                        ||
                        (!t.IsFlagSet(Node.Flag.NodeIsIntrinsic) &&
                         t.VisualQualifiedName.ToString()
                         .StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)),
                        new List <SymbolTable.Scope>
                {
                    SymbolTable.Scope.Declarations,
                    SymbolTable.Scope.Global,
                    SymbolTable.Scope.Intrinsic,
                    SymbolTable.Scope.Namespace
                });
            }

            return(CompletionFactoryHelpers.CreateCompletionItemsForType(types, node));
        }
Example #2
0
        public static IEnumerable <CompletionItem> GetCompletionForOfParent(Node node, Token variableNameToken,
                                                                            string userFilterText)
        {
            var completionItems = new List <CompletionItem>();

            if (node == null)
            {
                return(completionItems);
            }

            var currentVariable = node.SymbolTable.GetVariables(
                v => v != null && v.Name.Equals(variableNameToken.Text, StringComparison.InvariantCultureIgnoreCase), SymbolTable.Scope.Global)
                                  .FirstOrDefault();

            if (currentVariable == null)
            {
                return(completionItems);
            }

            var currentParent = currentVariable.Parent as DataDefinition;

            if (currentParent != null)
            {
                completionItems.Add(CompletionFactoryHelpers.CreateCompletionItemForVariable(currentParent));
            }
            return(completionItems);
        }
Example #3
0
        /// <summary>
        /// Get completion Item for a types ADDRESS OF demand.
        /// This method will modify the completionItems ref parameter.
        /// CompletionItems will be filtered on variables declared in LINKAGE SECTION and with LevelNumber equal to 01 or 77.
        /// </summary>
        /// <param name="completionItems">Ref to a list of completion items</param>
        /// <param name="node">Node found on cursor position</param>
        /// <param name="contextToken">ContextToken to select if it's a SET or something else</param>
        /// <param name="userFilterText">Variable Name Filter</param>
        public static IEnumerable <CompletionItem> GetCompletionForAddressOf(Node node, Token contextToken, string userFilterText)
        {
            IEnumerable <DataDefinition> potentialVariable = null;

            if (node == null)
            {
                return(new List <CompletionItem>());
            }

            if (contextToken != null && contextToken.TokenType == TokenType.SET)
            {
                //Get all the variables in Linkage section with Level 01 or 77 and starting by userFilterText.
                potentialVariable = node.SymbolTable.GetVariables(v => v != null &&
                                                                  v.IsFlagSet(Node.Flag.LinkageSectionNode) &&
                                                                  v.CodeElement is DataDefinitionEntry &&
                                                                  (((DataDefinitionEntry)v.CodeElement).LevelNumber.Value == 1 || ((DataDefinitionEntry)v.CodeElement).LevelNumber.Value == 77) &&
                                                                  v.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase),
                                                                  SymbolTable.Scope.GlobalStorage);
            }
            else
            {
                //Get all the variables from any section with level 01 or 77 and starting by userFilterText.
                potentialVariable = node.SymbolTable.GetVariables(
                    v => v != null &&
                    v.CodeElement is DataDefinitionEntry &&
                    (((DataDefinitionEntry)v.CodeElement).LevelNumber.Value == 1 ||
                     ((DataDefinitionEntry)v.CodeElement).LevelNumber.Value == 77) &&
                    v.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase),
                    SymbolTable.Scope.GlobalStorage);
            }

            return(CompletionFactoryHelpers.CreateCompletionItemsForVariables(potentialVariable));
        }
Example #4
0
        private static void SearchVariableInTypesAndLevels(Node node, DataDefinition variable, ref List <CompletionItem> completionItems)
        {
            var symbolTable = node.SymbolTable;

            if (!variable.IsPartOfATypeDef)                                         //Variable is not comming from a type.
            {
                if (symbolTable.GetVariablesExplicit(new URI(variable.Name)).Any()) //Check if this variable is present locally.
                {
                    completionItems.Add(CompletionFactoryHelpers.CreateCompletionItemForVariable(variable));
                }
            }
            else
            {
                if (symbolTable.TypesReferences != null) //We are in a typedef, get references of this type
                {
                    var type = variable.ParentTypeDefinition;
                    IEnumerable <DataDefinition> references = null;
                    references = symbolTable.TypesReferences.Where(t => t.Key == type).SelectMany(r => r.Value);

                    foreach (var reference in references)
                    {
                        if (symbolTable.GetVariablesExplicit(new URI(reference.Name)).Any())                                                                                             //Check if this variable is present locally. If not just ignore it
                        {
                            if (reference.ParentTypeDefinition == null)                                                                                                                  //Check if the variable is inside a typedef or not, if not it's a final varaible
                            {
                                var referenceArrangedQualifiedName = string.Join("::", reference.VisualQualifiedName.ToString().Split(reference.VisualQualifiedName.Separator).Skip(1)); //Skip Program Name
                                var finalQualifiedName             = string.Format("{0}::{1}", referenceArrangedQualifiedName, variable.VisualQualifiedName.Head);
                                var variableDisplay = string.Format("{0} ({1}) ({2})", variable.Name, variable.DataType.Name, finalQualifiedName);
                                completionItems.Add(new CompletionItem(variableDisplay)
                                {
                                    insertText = finalQualifiedName, kind = CompletionItemKind.Variable
                                });
                            }
                            else //If the reference is always in a typedef, let's loop and ride up until we are in a final variable
                            {
                                var tempCompletionItems = new List <CompletionItem>();
                                SearchVariableInTypesAndLevels(node, reference, ref tempCompletionItems);

                                if (tempCompletionItems.Count > 0)
                                {
                                    foreach (var tempComp in tempCompletionItems)
                                    {
                                        tempComp.insertText += "::" + variable.VisualQualifiedName.Head;
                                        tempComp.label       = string.Format("{0} ({1}) ({2})", variable.Name, variable.DataType.Name, tempComp.insertText);
                                        completionItems.Add(tempComp);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        public static IEnumerable <CompletionItem> GetCompletionForProcedure(FileCompiler fileCompiler, CodeElement codeElement, Token userFilterToken, Dictionary <SignatureInformation, FunctionDeclaration> functionDeclarationSignatureDictionary)
        {
            var node = GetMatchingNode(fileCompiler, codeElement);
            IEnumerable <FunctionDeclaration> procedures = null;
            IEnumerable <DataDefinition>      variables  = null;
            var completionItems = new List <CompletionItem>();

            if (node != null)
            {
                if (node.SymbolTable != null)
                {
                    var userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text;
                    procedures =
                        node.SymbolTable.GetFunctions(
                            f =>
                            f.VisualQualifiedName.ToString()
                            .StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase) ||
                            f.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase),
                            new List <SymbolTable.Scope>
                    {
                        SymbolTable.Scope.Declarations,
                        SymbolTable.Scope.Intrinsic,
                        SymbolTable.Scope.Namespace
                    });
                    variables = node.SymbolTable.GetVariables(da => da.Picture != null &&
                                                              da.DataType ==
                                                              Compiler.CodeElements.DataType.Alphanumeric &&
                                                              da.Name.StartsWith(userFilterText,
                                                                                 StringComparison.InvariantCultureIgnoreCase),
                                                              new List <SymbolTable.Scope> {
                        SymbolTable.Scope.Declarations, SymbolTable.Scope.Global
                    });
                }
            }

            completionItems.AddRange(CompletionFactoryHelpers.CreateCompletionItemsForProcedures(procedures, node, functionDeclarationSignatureDictionary));

            foreach (var variable in variables)
            {
                var completionItem = new CompletionItem(string.Format("{0}", variable.Name));
                completionItem.insertText = variable.Name;
                completionItem.kind       = CompletionItemKind.Variable;
                completionItems.Add(completionItem);
            }


            return(completionItems);
        }
Example #6
0
        public static IEnumerable <CompletionItem> GetCompletionForVariable(FileCompiler fileCompiler, CodeElement codeElement, Expression <Func <DataDefinition, bool> > predicate)
        {
            var completionItems = new List <CompletionItem>();
            var node            = GetMatchingNode(fileCompiler, codeElement);

            if (node == null)
            {
                return(completionItems);
            }

            var variables = node.SymbolTable.GetVariables(predicate, SymbolTable.Scope.GlobalStorage);

            completionItems.AddRange(CompletionFactoryHelpers.CreateCompletionItemsForVariables(variables));

            return(completionItems);
        }
        public static IEnumerable <CompletionItem> GetCompletionForQualifiedName(Position position, FileCompiler fileCompiler, CodeElement codeElement, Token qualifiedNameSeparatorToken, Token userFilterToken, Dictionary <SignatureInformation, FunctionDeclaration> functionDeclarationSignatureDictionary)
        {
            var completionItems     = new List <CompletionItem>();
            var arrangedCodeElement = codeElement as CodeElementWrapper;
            var node           = GetMatchingNode(fileCompiler, codeElement);
            var userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text;

            //Get the token before MatchingToken
            var userTokenToSeek =
                arrangedCodeElement?.ArrangedConsumedTokens.ElementAt(
                    arrangedCodeElement.ArrangedConsumedTokens.IndexOf(qualifiedNameSeparatorToken) - 1);
            var qualifiedNameTokens = new List <Token>();

            if (arrangedCodeElement != null)
            {
                qualifiedNameTokens.AddRange(
                    arrangedCodeElement.ArrangedConsumedTokens?.Where(
                        t => (t?.TokenType == TokenType.UserDefinedWord || t?.TokenType == TokenType.QualifiedNameSeparator) && (t.EndColumn <= position.character && t.Line == position.line + 1) || t.Line < position.line + 1));
                //Remove all the userdefinedword token and also QualifiedNameToken
                arrangedCodeElement.ArrangedConsumedTokens = arrangedCodeElement.ArrangedConsumedTokens.Except(qualifiedNameTokens).ToList();
                //We only wants the token that in front of any QualifiedName
                //Get the first significant token (i.e CALL/TYPE/...)
                Token firstSignificantToken, tempUserFilterToken;
                CodeElementMatcher.MatchCompletionCodeElement(position, new List <CodeElementWrapper> {
                    arrangedCodeElement
                }, out tempUserFilterToken,
                                                              out firstSignificantToken);


                //Select the qualifiedName chain closest to cursor
                Token previousToken = null;
                qualifiedNameTokens.Reverse();
                var filteredQualifiedNameTokens = new List <Token>(); //Will contains all the tokens forming the qualifiedName chain.
                foreach (var token in qualifiedNameTokens)
                {
                    if (previousToken == null ||
                        ((previousToken.TokenType == TokenType.QualifiedNameSeparator && token.TokenType == TokenType.UserDefinedWord) ||
                         (token.TokenType == TokenType.QualifiedNameSeparator && previousToken.TokenType == TokenType.UserDefinedWord)))
                    {
                        filteredQualifiedNameTokens.Add(token);
                    }
                    else
                    {
                        break;
                    }

                    previousToken = token;
                }
                filteredQualifiedNameTokens.Reverse();

                //For MOVE INPUT OUTPUT variables etc.. , get all the children of a variable that are accessible
                //Try to find corresponding variables
                var qualifiedName = string.Join(".",
                                                filteredQualifiedNameTokens.Where(
                                                    t =>
                                                    t.TokenType == TokenType.UserDefinedWord &&
                                                    !(t.Text == userFilterText && userFilterToken != null &&
                                                      t.StartIndex == userFilterToken.StartIndex && t.EndColumn == userFilterToken.EndColumn) &&
                                                    ((firstSignificantToken != null && ((t.StartIndex >= firstSignificantToken.EndColumn && t.Line == firstSignificantToken.Line) || t.Line > firstSignificantToken.Line)) ||
                                                     firstSignificantToken == null) &&
                                                    ((t.EndColumn <= position.character && t.Line == position.line + 1) || t.Line < position.line + 1))
                                                .Select(t => t.Text));
                var possibleVariables = node.SymbolTable.GetVariablesExplicit(new URI(qualifiedName));

                if (possibleVariables != null && possibleVariables.Any())
                {
                    //Get children of a type to get completion possibilities
                    foreach (var variable in possibleVariables)
                    {
                        var children = new List <Node>();
                        if (variable.Children != null && variable.Children.Count > 0) //It's a variable with levels inside
                        {
                            children.AddRange(variable.Children);
                        }
                        else //It's a typed variable, we have to search for children in the type
                        {
                            var typeChildren = GetTypeChildrens(node.SymbolTable, variable);
                            if (typeChildren != null)
                            {
                                children.AddRange(typeChildren.Where(t => t.Name != null));
                            }
                        }

                        var computedChildrenList = new List <Node>();
                        foreach (var child in children)
                        {
                            GetNextRelevantChildren(child, computedChildrenList);
                        }

                        completionItems.AddRange(CompletionFactoryHelpers.CreateCompletionItemsForVariables(
                                                     computedChildrenList.Where(
                                                         c => c.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)) //Filter on user text
                                                     .Select(child => child as DataDefinition), false));
                    }
                }
                else
                { //If no variables found, it's could be a children declared in a typedef..
                    var children       = new List <Node>();
                    var potentialTypes =
                        node.SymbolTable.GetTypes(
                            t =>
                            t.Children != null &&
                            t.Children.Any(
                                tc => tc.Name != null && tc.Name.Equals(userTokenToSeek.Text, StringComparison.InvariantCultureIgnoreCase)),
                            new List <SymbolTable.Scope>
                    {
                        SymbolTable.Scope.Declarations,
                        SymbolTable.Scope.Global,
                        SymbolTable.Scope.Intrinsic,
                        SymbolTable.Scope.Namespace
                    });

                    foreach (var nodeType in potentialTypes.SelectMany(t => t.Children).Where(c => c != null && c.Name != null && c.Name.Equals(userTokenToSeek.Text, StringComparison.InvariantCultureIgnoreCase)))
                    {
                        var nodeDataDef = nodeType as DataDefinition;
                        if (nodeDataDef == null)
                        {
                            continue;
                        }

                        var typeChildrens = GetTypeChildrens(node.SymbolTable, nodeDataDef);
                        if (typeChildrens != null)
                        {
                            children.AddRange(typeChildrens);
                        }
                    }

                    completionItems.AddRange(CompletionFactoryHelpers.CreateCompletionItemsForVariables(
                                                 children.Where(
                                                     c => c.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)) //Filter on user text
                                                 .Select(child => child as DataDefinition), false));
                }

                if (firstSignificantToken != null)
                {
                    switch (firstSignificantToken.TokenType)
                    {
                    case TokenType.CALL:
                    {
                        functionDeclarationSignatureDictionary.Clear();     //Clear to avoid key collision
                        //On CALL get possible procedures and functions in the seeked program
                        var programs = node.SymbolTable.GetPrograms(userTokenToSeek.Text);
                        if (programs != null && programs.Any())
                        {
                            var procedures =
                                programs.First()
                                .SymbolTable.GetFunctions(
                                    f =>
                                    f.Name.StartsWith(userFilterText,
                                                      StringComparison.InvariantCultureIgnoreCase) ||
                                    f.VisualQualifiedName.ToString()
                                    .StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase),
                                    new List <SymbolTable.Scope>
                                {
                                    SymbolTable.Scope.Declarations
                                });
                            completionItems.AddRange(CompletionFactoryHelpers.CreateCompletionItemsForProcedures(procedures, node, functionDeclarationSignatureDictionary, false));
                        }
                        break;
                    }

                    case TokenType.TYPE:
                    {
                        //On TYPE get possible public types in the seeked program
                        var programs = node.SymbolTable.GetPrograms(userTokenToSeek.Text);
                        if (programs != null && programs.Any())
                        {
                            var types =
                                programs.First()
                                .SymbolTable.GetTypes(
                                    t =>
                                    t.Name.StartsWith(userFilterText,
                                                      StringComparison.InvariantCultureIgnoreCase),
                                    new List <SymbolTable.Scope>
                                {
                                    SymbolTable.Scope.Declarations,
                                    SymbolTable.Scope.Global
                                });
                            completionItems.AddRange(CompletionFactoryHelpers.CreateCompletionItemsForType(types, node, false));
                        }
                        break;
                    }
                    }
                }
            }

            return(completionItems.Distinct());
        }
        public static IEnumerable <CompletionItem> GetCompletionForProcedureParameter(Position position, FileCompiler fileCompiler, CodeElement codeElement, Token userFilterToken, Token lastSignificantToken, FunctionDeclaration procedureSignatureContext)
        {
            var completionItems     = new List <CompletionItem>();
            var arrangedCodeElement = codeElement as CodeElementWrapper;
            var node           = GetMatchingNode(fileCompiler, codeElement);
            var userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text;

            //Get procedure name or qualified name
            string procedureName = CompletionFactoryHelpers.GetProcedureNameFromTokens(arrangedCodeElement?.ArrangedConsumedTokens);
            IEnumerable <FunctionDeclaration> calledProcedures = null;

            if (procedureSignatureContext == null ||
                !(procedureSignatureContext.QualifiedName.ToString().Equals(procedureName, StringComparison.InvariantCultureIgnoreCase) ||
                  procedureSignatureContext.Name.Equals(procedureName, StringComparison.InvariantCultureIgnoreCase)))
            {
                //Try to get procedure by its name
                calledProcedures =
                    node.SymbolTable.GetFunctions(
                        p =>
                        p.Name.Equals(procedureName) ||
                        p.VisualQualifiedName.ToString().Equals(procedureName), new List <SymbolTable.Scope>
                {
                    SymbolTable.Scope.Declarations,
                    SymbolTable.Scope.Intrinsic,
                    SymbolTable.Scope.Namespace
                });
            }
            else
            {
                //If the procedure name is equivalent to the signature selected by signature help, we can assume the user is always on the same procedure.
                calledProcedures = new List <FunctionDeclaration> {
                    procedureSignatureContext
                };
            }

            var alreadyGivenTokens = arrangedCodeElement?.ArrangedConsumedTokens
                                     .SkipWhile(t => t != lastSignificantToken).Skip(1)
                                     .TakeWhile(t => t.TokenType != TokenType.OUTPUT && t.TokenType != TokenType.IN_OUT)
                                     .Except(new List <Token>()
            {
                userFilterToken
            })
                                     .Where(t => (t.StartIndex < position.character && t.Line == position.line + 1) || t.Line < position.line + 1);

            int       alreadyGivenParametersCount = 0;
            TokenType?previousTokenType           = null;

            //Loop that allows to ignore qualified name parameters.
            if (alreadyGivenTokens != null)
            {
                foreach (var givenToken in alreadyGivenTokens)
                {
                    if (givenToken.TokenType == TokenType.UserDefinedWord && (previousTokenType == null || previousTokenType.Value == TokenType.UserDefinedWord))
                    {
                        alreadyGivenParametersCount++;
                    }
                    previousTokenType = givenToken.TokenType;
                }
            }


            IEnumerable <DataDefinition> potentialVariablesForCompletion = null;

            foreach (var procedure in calledProcedures)
            {
                IEnumerable <ParameterDescription> procParams = null;
                //Switch to select the correct parameters profile
                #region Selective parameters Switch
                switch (lastSignificantToken.TokenType)
                {
                case TokenType.INPUT:
                {
                    procParams = procedure.Profile.InputParameters;
                    break;
                }

                case TokenType.OUTPUT:
                {
                    procParams = procedure.Profile.OutputParameters;
                    break;
                }

                case TokenType.IN_OUT:
                {
                    procParams = procedure.Profile.InoutParameters;
                    break;
                }

                default:
                    procParams = new List <ParameterDescription>();
                    break;
                }

                #endregion

                //If the user already written all or more parameters than required let's check for an other proc signature
                if (alreadyGivenParametersCount >= procParams.Count())
                {
                    continue;
                }

                //Else see which parameter could be filled
                var parameterToFill = procParams.ToArray()[alreadyGivenParametersCount];
                //Get local/global variable that could correspond to the parameter

                potentialVariablesForCompletion = node.SymbolTable.GetVariablesByType(parameterToFill.DataType, potentialVariablesForCompletion, new List <SymbolTable.Scope> {
                    SymbolTable.Scope.Declarations, SymbolTable.Scope.Global
                });
            }

            if (potentialVariablesForCompletion == null)
            {
                return(completionItems);
            }

            foreach (var potentialVariable in potentialVariablesForCompletion.Where(v => v.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)).Distinct())
            {
                SearchVariableInTypesAndLevels(node, potentialVariable, ref completionItems); //Add potential variables to completionItems
            }
            return(completionItems);
        }
        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);
        }
Example #10
0
        public static IEnumerable <CompletionItem> GetCompletionForProcedureParameter(Position position, FileCompiler fileCompiler, CodeElement codeElement, Token userFilterToken, Token lastSignificantToken, FunctionDeclaration procedureSignatureContext)
        {
            var completionItems     = new List <CompletionItem>();
            var arrangedCodeElement = codeElement as CodeElementWrapper;
            var node = GetMatchingNode(fileCompiler, codeElement);

            if (node == null)
            {
                return(completionItems);
            }

            var userFilterText = userFilterToken == null ? string.Empty : userFilterToken.Text;

            //Get procedure name or qualified name
            string procedureName = CompletionFactoryHelpers.GetProcedureNameFromTokens(arrangedCodeElement?.ArrangedConsumedTokens);
            IEnumerable <FunctionDeclaration> calledProcedures = null;

            if (procedureSignatureContext == null ||
                !(procedureSignatureContext.QualifiedName.ToString().Equals(procedureName, StringComparison.InvariantCultureIgnoreCase) ||
                  procedureSignatureContext.Name.Equals(procedureName, StringComparison.InvariantCultureIgnoreCase)))
            {
                //Try to get procedure by its name
                calledProcedures =
                    node.SymbolTable.GetFunctions(
                        p =>
                        p.Name.Equals(procedureName, StringComparison.InvariantCultureIgnoreCase) ||
                        p.VisualQualifiedName.ToString().Equals(procedureName, StringComparison.InvariantCultureIgnoreCase),
                        SymbolTable.Scope.Intrinsic
                        );
            }
            else
            {
                //If the procedure name is equivalent to the signature selected by signature help, we can assume the user is always on the same procedure.
                calledProcedures = new List <FunctionDeclaration> {
                    procedureSignatureContext
                };
            }

            var alreadyGivenTokens = arrangedCodeElement?.ArrangedConsumedTokens
                                     .SkipWhile(t => t != lastSignificantToken).Skip(1)
                                     .TakeWhile(t => t.TokenType != TokenType.OUTPUT && t.TokenType != TokenType.IN_OUT)
                                     .Except(new List <Token>()
            {
                userFilterToken
            })
                                     .Where(t => (t.StartIndex < position.character && t.Line == position.line + 1) || t.Line < position.line + 1);

            int       alreadyGivenParametersCount = 0;
            TokenType?previousTokenType           = null;

            //Loop that allows to ignore qualified name parameters.
            if (alreadyGivenTokens != null)
            {
                foreach (var givenToken in alreadyGivenTokens)
                {
                    if (givenToken.TokenType == TokenType.UserDefinedWord && (previousTokenType == null || previousTokenType.Value == TokenType.UserDefinedWord))
                    {
                        alreadyGivenParametersCount++;
                    }
                    previousTokenType = givenToken.TokenType;
                }
            }


            IEnumerable <DataDefinition> potentialVariablesForCompletion = null;
            int maxInput = -1;

            int  maxInOut = -1;
            bool allProcsHaveInOutParams = true;
            bool noProcsHaveInOutParams  = true;

            bool allProcsHaveOutputParams = true;

            foreach (var procedure in calledProcedures)
            {
                IEnumerable <ParameterDescription> procParams;

                //Switch to select the correct parameters profile
                #region Selective parameters Switch
                switch (lastSignificantToken.TokenType)
                {
                case TokenType.INPUT:
                {
                    procParams = procedure.Profile.InputParameters;
                    //Get number of input parameters left per procedure
                    break;
                }

                case TokenType.OUTPUT:
                {
                    procParams = procedure.Profile.OutputParameters;
                    break;
                }

                case TokenType.IN_OUT:
                {
                    procParams = procedure.Profile.InoutParameters;
                    //Get number of inout parameters left per procedure
                    break;
                }

                default:
                    procParams = new List <ParameterDescription>();
                    break;
                }
                #endregion

                if (procedure.Profile.InputParameters.Count > maxInput)
                {
                    maxInput = procedure.Profile.InputParameters.Count;
                }
                if (procedure.Profile.InoutParameters.Count > maxInOut)
                {
                    maxInOut = procedure.Profile.InoutParameters.Count;
                }

                if (procedure.Profile.InoutParameters.Count == 0)
                {
                    allProcsHaveInOutParams = false;
                }
                else
                {
                    noProcsHaveInOutParams = false;
                }
                if (procedure.Profile.OutputParameters.Count == 0)
                {
                    allProcsHaveOutputParams = false;
                }


                //If the user already written all or more parameters than required let's check for an other proc signature
                if (alreadyGivenParametersCount >= procParams.Count())
                {
                    continue;
                }

                //Else see which parameter could be filled
                var parameterToFill = procParams.ToArray()[alreadyGivenParametersCount];
                //Get local/global variable that could correspond to the parameter

                potentialVariablesForCompletion = node.SymbolTable.GetVariablesByType(parameterToFill.DataType, potentialVariablesForCompletion, SymbolTable.Scope.GlobalStorage);
            }

            if (potentialVariablesForCompletion == null)
            {
                return(completionItems);
            }

            foreach (var potentialVariable in potentialVariablesForCompletion.Where(v => v.Name.StartsWith(userFilterText, StringComparison.InvariantCultureIgnoreCase)).Distinct())
            {
                SearchVariableInTypesAndLevels(node, potentialVariable, ref completionItems); //Add potential variables to completionItems*
            }
            //If signature of procedure is available
            if (procedureSignatureContext != null)
            {
                //Add IN-OUT or OUTPUT after INPUT ?
                if (lastSignificantToken.TokenType == TokenType.INPUT &&
                    alreadyGivenParametersCount == (procedureSignatureContext.Profile.InputParameters.Count - 1))
                {
                    if (procedureSignatureContext.Profile.InoutParameters.Count != 0)
                    {
                        AddIn_OutSuffixToCompletionItems(lastSignificantToken, completionItems);
                    }
                    else if (procedureSignatureContext.Profile.OutputParameters.Count != 0)
                    {
                        AddOutputSuffixToCompletionItems(lastSignificantToken, completionItems);
                    }
                }

                //Add OUTPUT after IN-OUT ?
                else if (lastSignificantToken.TokenType == TokenType.IN_OUT &&
                         alreadyGivenParametersCount == (procedureSignatureContext.Profile.InoutParameters.Count - 1) &&
                         procedureSignatureContext.Profile.OutputParameters.Count != 0)
                {
                    AddOutputSuffixToCompletionItems(lastSignificantToken, completionItems);
                }
            }
            else
            {
                //Add IN-OUT or OUTPUT after INPUT ?
                //If we reach the last INPUT parameter
                if (lastSignificantToken.TokenType == TokenType.INPUT && alreadyGivenParametersCount == maxInput - 1)
                {
                    //If all procs have IN-OUT params
                    if (allProcsHaveInOutParams)
                    {
                        AddIn_OutSuffixToCompletionItems(lastSignificantToken, completionItems);
                    }
                    //If no procedures have IN-OUT params and all have OUTPUT params
                    else if (noProcsHaveInOutParams && allProcsHaveOutputParams)
                    {
                        AddOutputSuffixToCompletionItems(lastSignificantToken, completionItems);
                    }
                    //Otherwise we cannot choose between IN-OUT, OUTPUT and nothing, so we choose nothing and let the user add the good keyword manually.
                    //#908 will change this behavior by asking for the signature context
                }

                //Add OUTPUT after IN-OUT ?
                else if (lastSignificantToken.TokenType == TokenType.IN_OUT && alreadyGivenParametersCount == (maxInOut - 1))
                {
                    //If all procedures have OUTPUT parameter
                    if (allProcsHaveOutputParams)
                    {
                        AddOutputSuffixToCompletionItems(lastSignificantToken, completionItems);
                    }
                }
            }

            return(completionItems);
        }