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)); }
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); }
/// <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)); }
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); }
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); }
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); }