/// <summary> /// Finds the definition of a symbol in the script file or any of the /// files that it references. /// </summary> /// <param name="sourceFile">The initial script file to be searched for the symbol's definition.</param> /// <param name="foundSymbol">The symbol for which a definition will be found.</param> /// <returns>The resulting GetDefinitionResult for the symbol's definition.</returns> public async Task <SymbolReference> GetDefinitionOfSymbolAsync( ScriptFile sourceFile, SymbolReference foundSymbol) { Validate.IsNotNull(nameof(sourceFile), sourceFile); Validate.IsNotNull(nameof(foundSymbol), foundSymbol); ScriptFile[] referencedFiles = _workspaceService.ExpandScriptReferences( sourceFile); var filesSearched = new HashSet <string>(StringComparer.OrdinalIgnoreCase); // look through the referenced files until definition is found // or there are no more file to look through SymbolReference foundDefinition = null; foreach (ScriptFile scriptFile in referencedFiles) { foundDefinition = AstOperations.FindDefinitionOfSymbol( scriptFile.ScriptAst, foundSymbol); filesSearched.Add(scriptFile.FilePath); if (foundDefinition != null) { foundDefinition.FilePath = scriptFile.FilePath; break; } if (foundSymbol.SymbolType == SymbolType.Function) { // Dot-sourcing is parsed as a "Function" Symbol. string dotSourcedPath = GetDotSourcedPath(foundSymbol, scriptFile); if (scriptFile.FilePath == dotSourcedPath) { foundDefinition = new SymbolReference(SymbolType.Function, foundSymbol.SymbolName, scriptFile.ScriptAst.Extent, scriptFile.FilePath); break; } } } // if the definition the not found in referenced files // look for it in all the files in the workspace if (foundDefinition == null) { // Get a list of all powershell files in the workspace path IEnumerable <string> allFiles = _workspaceService.EnumeratePSFiles(); foreach (string file in allFiles) { if (filesSearched.Contains(file)) { continue; } foundDefinition = AstOperations.FindDefinitionOfSymbol( Parser.ParseFile(file, out Token[] tokens, out ParseError[] parseErrors),
/// <summary> /// Finds the definition of a symbol in the script file or any of the /// files that it references. /// </summary> /// <param name="sourceFile">The initial script file to be searched for the symbol's definition.</param> /// <param name="foundSymbol">The symbol for which a definition will be found.</param> /// <returns>The resulting GetDefinitionResult for the symbol's definition.</returns> public async Task <SymbolReference> GetDefinitionOfSymbolAsync( ScriptFile sourceFile, SymbolReference foundSymbol) { Validate.IsNotNull(nameof(sourceFile), sourceFile); Validate.IsNotNull(nameof(foundSymbol), foundSymbol); // If symbol is an alias, resolve it. (Dictionary <string, List <string> > _, Dictionary <string, string> aliasToCmdlets) = await CommandHelpers.GetAliasesAsync(_executionService).ConfigureAwait(false); if (aliasToCmdlets.ContainsKey(foundSymbol.SymbolName)) { foundSymbol = new SymbolReference( foundSymbol.SymbolType, aliasToCmdlets[foundSymbol.SymbolName], foundSymbol.ScriptRegion, foundSymbol.FilePath, foundSymbol.SourceLine); } ScriptFile[] referencedFiles = _workspaceService.ExpandScriptReferences( sourceFile); HashSet <string> filesSearched = new(StringComparer.OrdinalIgnoreCase); // look through the referenced files until definition is found // or there are no more file to look through SymbolReference foundDefinition = null; foreach (ScriptFile scriptFile in referencedFiles) { foundDefinition = AstOperations.FindDefinitionOfSymbol( scriptFile.ScriptAst, foundSymbol); filesSearched.Add(scriptFile.FilePath); if (foundDefinition != null) { foundDefinition.FilePath = scriptFile.FilePath; break; } if (foundSymbol.SymbolType == SymbolType.Function) { // Dot-sourcing is parsed as a "Function" Symbol. string dotSourcedPath = GetDotSourcedPath(foundSymbol, scriptFile); if (scriptFile.FilePath == dotSourcedPath) { foundDefinition = new SymbolReference(SymbolType.Function, foundSymbol.SymbolName, scriptFile.ScriptAst.Extent, scriptFile.FilePath); break; } } } // if the definition the not found in referenced files // look for it in all the files in the workspace if (foundDefinition == null) { // Get a list of all powershell files in the workspace path foreach (string file in _workspaceService.EnumeratePSFiles()) { if (filesSearched.Contains(file)) { continue; } foundDefinition = AstOperations.FindDefinitionOfSymbol( Parser.ParseFile(file, out Token[] tokens, out ParseError[] parseErrors),
/// <summary> /// Finds all the references of a symbol /// </summary> /// <param name="foundSymbol">The symbol to find all references for</param> /// <param name="referencedFiles">An array of scriptFiles too search for references in</param> /// <param name="workspace">The workspace that will be searched for symbols</param> /// <returns>FindReferencesResult</returns> public List <SymbolReference> FindReferencesOfSymbol( SymbolReference foundSymbol, ScriptFile[] referencedFiles, WorkspaceService workspace) { if (foundSymbol == null) { return(null); } // NOTE: we use to make sure aliases were loaded but took it out because we needed the pipeline thread. // We want to look for references first in referenced files, hence we use ordered dictionary // TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic var fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? new OrderedDictionary() : new OrderedDictionary(StringComparer.OrdinalIgnoreCase); foreach (ScriptFile scriptFile in referencedFiles) { fileMap[scriptFile.FilePath] = scriptFile; } foreach (string filePath in workspace.EnumeratePSFiles()) { if (!fileMap.Contains(filePath)) { if (!workspace.TryGetFile(filePath, out ScriptFile scriptFile)) { // If we can't access the file for some reason, just ignore it continue; } fileMap[filePath] = scriptFile; } } var symbolReferences = new List <SymbolReference>(); foreach (object fileName in fileMap.Keys) { var file = (ScriptFile)fileMap[fileName]; IEnumerable <SymbolReference> references = AstOperations.FindReferencesOfSymbol( file.ScriptAst, foundSymbol, needsAliases: false); foreach (SymbolReference reference in references) { try { reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); } catch (ArgumentOutOfRangeException e) { reference.SourceLine = string.Empty; _logger.LogException("Found reference is out of range in script file", e); } reference.FilePath = file.FilePath; symbolReferences.Add(reference); } } return(symbolReferences); }
/// <summary> /// Finds all the references of a symbol /// </summary> /// <param name="foundSymbol">The symbol to find all references for</param> /// <param name="referencedFiles">An array of scriptFiles too search for references in</param> /// <param name="workspace">The workspace that will be searched for symbols</param> /// <returns>FindReferencesResult</returns> public async Task <List <SymbolReference> > FindReferencesOfSymbol( SymbolReference foundSymbol, ScriptFile[] referencedFiles, WorkspaceService workspace) { if (foundSymbol == null) { return(null); } (Dictionary <string, List <string> > cmdletToAliases, Dictionary <string, string> aliasToCmdlets) = await CommandHelpers.GetAliasesAsync(_executionService).ConfigureAwait(false); // We want to look for references first in referenced files, hence we use ordered dictionary // TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic OrderedDictionary fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? new OrderedDictionary() : new OrderedDictionary(StringComparer.OrdinalIgnoreCase); foreach (ScriptFile scriptFile in referencedFiles) { fileMap[scriptFile.FilePath] = scriptFile; } foreach (string filePath in workspace.EnumeratePSFiles()) { if (!fileMap.Contains(filePath)) { if (!workspace.TryGetFile(filePath, out ScriptFile scriptFile)) { // If we can't access the file for some reason, just ignore it continue; } fileMap[filePath] = scriptFile; } } List <SymbolReference> symbolReferences = new(); foreach (object fileName in fileMap.Keys) { ScriptFile file = (ScriptFile)fileMap[fileName]; IEnumerable <SymbolReference> references = AstOperations.FindReferencesOfSymbol( file.ScriptAst, foundSymbol, cmdletToAliases, aliasToCmdlets); foreach (SymbolReference reference in references) { try { reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); } catch (ArgumentOutOfRangeException e) { reference.SourceLine = string.Empty; _logger.LogException("Found reference is out of range in script file", e); } reference.FilePath = file.FilePath; symbolReferences.Add(reference); } } return(symbolReferences); }