public override Definition OnDefinition(TextDocumentPosition parameters) { AnalyticsWrapper.Telemetry.TrackEvent("[Definition]", EventType.Completion); //Send event to analytics var defaultDefinition = new Definition(parameters.uri, new Range()); Uri objUri = new Uri(parameters.uri); if (objUri.IsFile) { var fileCompiler = typeCobolWorkspace.OpenedFileCompiler[objUri]; if (fileCompiler.CompilationResultsForProgram != null && fileCompiler.CompilationResultsForProgram.ProcessedTokensDocumentSnapshot != null) { var matchingCodeElement = fileCompiler.CompilationResultsForProgram.ProgramClassDocumentSnapshot.NodeCodeElementLinkers .Keys.FirstOrDefault(c => c.ConsumedTokens.Any( t => t.Line == parameters.position.line + 1 && parameters.position.character >= t.StartIndex && parameters.position.character <= t.StopIndex + 1)); if (matchingCodeElement == null) { return(defaultDefinition); } var matchingNode = fileCompiler.CompilationResultsForProgram.ProgramClassDocumentSnapshot.NodeCodeElementLinkers[matchingCodeElement]; if (matchingNode == null) { return(defaultDefinition); } var matchingToken = matchingCodeElement.ConsumedTokens.FirstOrDefault(t => t.Line == parameters.position.line + 1 && parameters.position.character >= t.StartIndex && parameters.position.character <= t.StopIndex + 1); if (matchingToken == null) { return(defaultDefinition); } Token userFilterToken = null; Token lastSignificantToken = null; var potentialDefinitionNodes = new List <Node>(); CodeElementMatcher.MatchCompletionCodeElement(parameters.position, new List <CodeElementWrapper>() { new CodeElementWrapper(matchingCodeElement) }, out userFilterToken, out lastSignificantToken); //Magic happens here if (lastSignificantToken != null) { switch (lastSignificantToken.TokenType) { case TokenType.PERFORM: { potentialDefinitionNodes.AddRange( matchingNode.SymbolTable.GetParagraphs( p => p.Name.Equals(matchingToken.Text, StringComparison.InvariantCultureIgnoreCase))); break; } case TokenType.CALL: { potentialDefinitionNodes.AddRange(matchingNode.SymbolTable.GetFunctions( f => f.Name.Equals(matchingToken.Text, StringComparison.InvariantCultureIgnoreCase), new List <SymbolTable.Scope>() { SymbolTable.Scope.Declarations, SymbolTable.Scope.Global })); break; } case TokenType.TYPE: { potentialDefinitionNodes.AddRange(matchingNode.SymbolTable.GetTypes( t => t.Name.Equals(matchingToken.Text, StringComparison.InvariantCultureIgnoreCase), new List <SymbolTable.Scope>() { SymbolTable.Scope.Declarations, SymbolTable.Scope.Global })); break; } case TokenType.INPUT: case TokenType.OUTPUT: case TokenType.IN_OUT: case TokenType.MOVE: case TokenType.TO: default: { potentialDefinitionNodes.AddRange(matchingNode.SymbolTable.GetVariables( v => v.Name.Equals(matchingToken.Text, StringComparison.InvariantCultureIgnoreCase), new List <SymbolTable.Scope>() { SymbolTable.Scope.Declarations, SymbolTable.Scope.Global })); break; } } } if (potentialDefinitionNodes.Count > 0) { var nodeDefinition = potentialDefinitionNodes.FirstOrDefault(); if (nodeDefinition != null) { return(new Definition(parameters.uri, new Range() { start = new Position(nodeDefinition.CodeElement.Line - 1, 0) })); } } } } return(defaultDefinition); }
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()); }
/// <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); }