/// <summary> /// Parses the current file contents to get the AST, tokens, /// and parse errors. /// </summary> private void ParseFileContents() { ParseError[] parseErrors = null; try { this.ScriptAst = Parser.ParseInput( this.Contents, out this.scriptTokens, out parseErrors); } catch (RuntimeException ex) { var parseError = new ParseError( null, ex.ErrorRecord.FullyQualifiedErrorId, ex.Message); parseErrors = new[] { parseError }; this.scriptTokens = new Token[0]; this.ScriptAst = null; } // Translate parse errors into syntax markers this.SyntaxMarkers = parseErrors .Select(ScriptFileMarker.FromParseError) .ToArray(); //Get all dot sourced referenced files and store them this.ReferencedFiles = AstOperations.FindDotSourcedIncludes(this.ScriptAst); }
/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFile( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull("scriptFile", scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle(); CompletionResults completionResults = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, runspaceHandle.Runspace); runspaceHandle.Dispose(); // save state of most recent completion mostRecentCompletions = completionResults; mostRecentRequestFile = scriptFile.Id; mostRecentRequestLine = lineNumber; mostRecentRequestOffest = columnNumber; return(completionResults); }
private SymbolReference FindDeclarationForBuiltinCommand( CommandInfo commandInfo, SymbolReference foundSymbol, Workspace workspace) { SymbolReference foundDefinition = null; if (commandInfo != null) { int index = 0; ScriptFile[] nestedModuleFiles; nestedModuleFiles = GetBuiltinCommandScriptFiles( commandInfo.Module, workspace); while (foundDefinition == null && index < nestedModuleFiles.Length) { foundDefinition = AstOperations.FindDefinitionOfSymbol( nestedModuleFiles[index].ScriptAst, foundSymbol); if (foundDefinition != null) { foundDefinition.FilePath = nestedModuleFiles[index].FilePath; } index++; } } return(foundDefinition); }
/// <summary> /// Finds the details of the symbol at the given script file location. /// </summary> /// <param name="scriptFile">The ScriptFile in which the symbol can be located.</param> /// <param name="lineNumber">The line number at which the symbol can be located.</param> /// <param name="columnNumber">The column number at which the symbol can be located.</param> /// <returns></returns> public async Task <SymbolDetails> FindSymbolDetailsAtLocation( ScriptFile scriptFile, int lineNumber, int columnNumber) { SymbolDetails symbolDetails = null; SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, columnNumber); if (symbolReference != null) { RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle(); symbolReference.FilePath = scriptFile.FilePath; symbolDetails = new SymbolDetails(symbolReference, runspaceHandle.Runspace); runspaceHandle.Dispose(); } else { // TODO #21: Return Result<T> return(null); } return(symbolDetails); }
private SymbolReference FindDeclarationForBuiltinCommand( CommandInfo commandInfo, SymbolReference foundSymbol, Workspace workspace) { if (commandInfo == null) { return(null); } ScriptFile[] nestedModuleFiles = GetBuiltinCommandScriptFiles( commandInfo.Module, workspace); SymbolReference foundDefinition = null; foreach (ScriptFile nestedModuleFile in nestedModuleFiles) { foundDefinition = AstOperations.FindDefinitionOfSymbol( nestedModuleFile.ScriptAst, foundSymbol); if (foundDefinition != null) { foundDefinition.FilePath = nestedModuleFile.FilePath; break; } } return(foundDefinition); }
/// <summary> /// Finds the details of the symbol at the given script file location. /// </summary> /// <param name="scriptFile">The ScriptFile in which the symbol can be located.</param> /// <param name="lineNumber">The line number at which the symbol can be located.</param> /// <param name="columnNumber">The column number at which the symbol can be located.</param> /// <returns></returns> public async Task <SymbolDetails> FindSymbolDetailsAtLocation( ScriptFile scriptFile, int lineNumber, int columnNumber) { SymbolDetails symbolDetails = null; SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, columnNumber); if (symbolReference != null) { symbolReference.FilePath = scriptFile.FilePath; symbolDetails = await SymbolDetails.Create( symbolReference, this.powerShellContext); } else { // TODO #21: Return Result<T> return(null); } return(symbolDetails); }
/// <summary> /// Finds all the occurences of a symbol in the script given a file location /// </summary> /// <param name="file">The details and contents of a open script file</param> /// <param name="lineNumber">The line number of the cursor for the given script</param> /// <param name="columnNumber">The coulumn number of the cursor for the given script</param> /// <returns>FindOccurrencesResult</returns> public FindOccurrencesResult FindOccurrencesInFile( ScriptFile file, int lineNumber, int columnNumber) { SymbolReference foundSymbol = AstOperations.FindSymbolAtPosition( file.ScriptAst, lineNumber, columnNumber); if (foundSymbol != null) { // find all references, and indicate that looking for aliases is not needed IEnumerable <SymbolReference> symbolOccurrences = AstOperations .FindReferencesOfSymbol( file.ScriptAst, foundSymbol, false); return (new FindOccurrencesResult { FoundOccurrences = symbolOccurrences }); } else { return(null); } }
/// <summary> /// Finds the details of the symbol at the given script file location. /// </summary> /// <param name="scriptFile">The ScriptFile in which the symbol can be located.</param> /// <param name="lineNumber">The line number at which the symbol can be located.</param> /// <param name="columnNumber">The column number at which the symbol can be located.</param> /// <returns></returns> public async Task <SymbolDetails> FindSymbolDetailsAtLocation( ScriptFile scriptFile, int lineNumber, int columnNumber) { SymbolDetails symbolDetails = null; SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, columnNumber); if (symbolReference != null) { // Request a runspace handle with a short timeout RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); symbolReference.FilePath = scriptFile.FilePath; symbolDetails = new SymbolDetails(symbolReference, runspaceHandle.Runspace); runspaceHandle.Dispose(); } else { // TODO #21: Return Result<T> return(null); } return(symbolDetails); }
/// <summary> /// Parses the current file contents to get the AST, tokens, /// and parse errors. /// </summary> private void ParseFileContents() { ParseError[] parseErrors = null; try { #if PowerShellv5r2 // This overload appeared with Windows 10 Update 1 if (this.powerShellVersion.Major >= 5 && this.powerShellVersion.Build >= 10586) { // Include the file path so that module relative // paths are evaluated correctly this.ScriptAst = Parser.ParseInput( this.Contents, this.FilePath, out this.scriptTokens, out parseErrors); } else { this.ScriptAst = Parser.ParseInput( this.Contents, out this.scriptTokens, out parseErrors); } #else this.ScriptAst = Parser.ParseInput( this.Contents, out this.scriptTokens, out parseErrors); #endif } catch (RuntimeException ex) { var parseError = new ParseError( null, ex.ErrorRecord.FullyQualifiedErrorId, ex.Message); parseErrors = new[] { parseError }; this.scriptTokens = new Token[0]; this.ScriptAst = null; } // Translate parse errors into syntax markers this.SyntaxMarkers = parseErrors .Select(ScriptFileMarker.FromParseError) .ToArray(); //Get all dot sourced referenced files and store them this.ReferencedFiles = AstOperations.FindDotSourcedIncludes(this.ScriptAst); }
protected override IEnumerable <SymbolReference> GetSymbolsImpl( ScriptFile scriptFile, Version psVersion) { return(AstOperations.FindSymbolsInDocument( scriptFile.ScriptAst, psVersion)); }
/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFile( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull("scriptFile", scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); CommandCompletion commandCompletion = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, runspaceHandle.Runspace); runspaceHandle.Dispose(); if (commandCompletion != null) { try { CompletionResults completionResults = CompletionResults.Create( scriptFile, commandCompletion); // save state of most recent completion mostRecentCompletions = completionResults; mostRecentRequestFile = scriptFile.Id; mostRecentRequestLine = lineNumber; mostRecentRequestOffest = columnNumber; return(completionResults); } catch (ArgumentException e) { // Bad completion results could return an invalid // replacement range, catch that here Logger.Write( LogLevel.Error, $"Caught exception while trying to create CompletionResults:\n\n{e.ToString()}"); } } // If all else fails, return empty results return(new CompletionResults()); }
/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFileAsync( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull(nameof(scriptFile), scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); CommandCompletion commandCompletion = await AstOperations.GetCompletionsAsync( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, _powerShellContext, _logger, new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); if (commandCompletion == null) { return(new CompletionResults()); } try { CompletionResults completionResults = CompletionResults.Create( scriptFile, commandCompletion); // save state of most recent completion _mostRecentCompletions = completionResults; _mostRecentRequestFile = scriptFile.Id; _mostRecentRequestLine = lineNumber; _mostRecentRequestOffest = columnNumber; return(completionResults); } catch (ArgumentException e) { // Bad completion results could return an invalid // replacement range, catch that here _logger.Write( LogLevel.Error, $"Caught exception while trying to create CompletionResults:\n\n{e.ToString()}"); return(new CompletionResults()); } }
/// <summary> /// Finds the parameter set hints of a specific command (determined by a given file location) /// </summary> /// <param name="file">The details and contents of a open script file</param> /// <param name="lineNumber">The line number of the cursor for the given script</param> /// <param name="columnNumber">The coulumn number of the cursor for the given script</param> /// <returns>ParameterSetSignatures</returns> public async Task <ParameterSetSignatures> FindParameterSetsInFile( ScriptFile file, int lineNumber, int columnNumber) { SymbolReference foundSymbol = AstOperations.FindCommandAtPosition( file.ScriptAst, lineNumber, columnNumber); if (foundSymbol != null) { CommandInfo commandInfo = await CommandHelpers.GetCommandInfo( foundSymbol.SymbolName, this.powerShellContext); if (commandInfo != null) { try { IEnumerable <CommandParameterSetInfo> commandParamSets = commandInfo.ParameterSets; return(new ParameterSetSignatures(commandParamSets, foundSymbol)); } catch (RuntimeException e) { // A RuntimeException will be thrown when an invalid attribute is // on a parameter binding block and then that command/script has // its signatures resolved by typing it into a script. this.logger.WriteException("RuntimeException encountered while accessing command parameter sets", e); return(null); } catch (InvalidOperationException) { // For some commands there are no paramsets (like applications). Until // the valid command types are better understood, catch this exception // which gets raised when there are no ParameterSets for the command type. return(null); } } else { return(null); } } else { return(null); } }
/// <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> /// <param name="workspace">The Workspace to which the ScriptFile belongs.</param> /// <returns>The resulting GetDefinitionResult for the symbol's definition.</returns> public async Task <GetDefinitionResult> GetDefinitionOfSymbol( ScriptFile sourceFile, SymbolReference foundSymbol, Workspace workspace) { Validate.IsNotNull("sourceFile", sourceFile); Validate.IsNotNull("foundSymbol", foundSymbol); Validate.IsNotNull("workspace", workspace); ScriptFile[] referencedFiles = workspace.ExpandScriptReferences( sourceFile); // look through the referenced files until definition is found // or there are no more file to look through SymbolReference foundDefinition = null; for (int i = 0; i < referencedFiles.Length; i++) { foundDefinition = AstOperations.FindDefinitionOfSymbol( referencedFiles[i].ScriptAst, foundSymbol); if (foundDefinition != null) { foundDefinition.FilePath = referencedFiles[i].FilePath; break; } } // if definition is not found in referenced files // look for it in the builtin commands if (foundDefinition == null) { CommandInfo cmdInfo = await CommandHelpers.GetCommandInfo( foundSymbol.SymbolName, this.powerShellContext); foundDefinition = await FindDeclarationForBuiltinCommand( cmdInfo, foundSymbol, workspace); } return(foundDefinition != null ? new GetDefinitionResult(foundDefinition) : null); }
/// <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> /// <returns>FindReferencesResult</returns> public async Task <FindReferencesResult> FindReferencesOfSymbol( SymbolReference foundSymbol, ScriptFile[] referencedFiles) { if (foundSymbol != null) { int symbolOffset = referencedFiles[0].GetOffsetAtPosition( foundSymbol.ScriptRegion.StartLineNumber, foundSymbol.ScriptRegion.StartColumnNumber); // Make sure aliases have been loaded await GetAliases(); List <SymbolReference> symbolReferences = new List <SymbolReference>(); foreach (ScriptFile file in referencedFiles) { IEnumerable <SymbolReference> symbolReferencesinFile = AstOperations .FindReferencesOfSymbol( file.ScriptAst, foundSymbol, CmdletToAliasDictionary, AliasToCmdletDictionary) .Select( reference => { reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); reference.FilePath = file.FilePath; return(reference); }); symbolReferences.AddRange(symbolReferencesinFile); } return (new FindReferencesResult { SymbolFileOffset = symbolOffset, SymbolName = foundSymbol.SymbolName, FoundReferences = symbolReferences }); } else { return(null); } }
/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFile( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull("scriptFile", scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); CommandCompletion commandCompletion = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, runspaceHandle.Runspace); runspaceHandle.Dispose(); if (commandCompletion != null) { CompletionResults completionResults = CompletionResults.Create( scriptFile, commandCompletion); // save state of most recent completion mostRecentCompletions = completionResults; mostRecentRequestFile = scriptFile.Id; mostRecentRequestLine = lineNumber; mostRecentRequestOffest = columnNumber; return(completionResults); } else { return(new CompletionResults()); } }
/// <summary> /// Finds the symbol in the script given a file location /// </summary> /// <param name="scriptFile">The details and contents of a open script file</param> /// <param name="lineNumber">The line number of the cursor for the given script</param> /// <param name="columnNumber">The coulumn number of the cursor for the given script</param> /// <returns>A SymbolReference of the symbol found at the given location /// or null if there is no symbol at that location /// </returns> public SymbolReference FindSymbolAtLocation( ScriptFile scriptFile, int lineNumber, int columnNumber) { SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, columnNumber); if (symbolReference != null) { symbolReference.FilePath = scriptFile.FilePath; } return(symbolReference); }
/// <summary> /// Finds the parameter set hints of a specific command (determined by a given file location) /// </summary> /// <param name="file">The details and contents of a open script file</param> /// <param name="lineNumber">The line number of the cursor for the given script</param> /// <param name="columnNumber">The coulumn number of the cursor for the given script</param> /// <returns>ParameterSetSignatures</returns> public async Task <ParameterSetSignatures> FindParameterSetsInFile( ScriptFile file, int lineNumber, int columnNumber) { SymbolReference foundSymbol = AstOperations.FindCommandAtPosition( file.ScriptAst, lineNumber, columnNumber); if (foundSymbol != null) { CommandInfo commandInfo = await CommandHelpers.GetCommandInfo( foundSymbol.SymbolName, this.powerShellContext); if (commandInfo != null) { try { IEnumerable <CommandParameterSetInfo> commandParamSets = commandInfo.ParameterSets; return(new ParameterSetSignatures(commandParamSets, foundSymbol)); } catch (InvalidOperationException) { // For some commands there are no paramsets (like applications). Until // the valid command types are better understood, catch this exception // which gets raised when there are no ParameterSets for the command type. return(null); } } else { return(null); } } else { return(null); } }
/// <summary> /// Finds all the symbols in a file. /// </summary> /// <param name="scriptFile">The ScriptFile in which the symbol can be located.</param> /// <returns></returns> public FindOccurrencesResult FindSymbolsInFile(ScriptFile scriptFile) { Validate.IsNotNull("scriptFile", scriptFile); IEnumerable <SymbolReference> symbolReferencesinFile = AstOperations .FindSymbolsInDocument(scriptFile.ScriptAst) .Select( reference => { reference.SourceLine = scriptFile.GetLine(reference.ScriptRegion.StartLineNumber); reference.FilePath = scriptFile.FilePath; return(reference); }); return (new FindOccurrencesResult { FoundOccurrences = symbolReferencesinFile }); }
private async Task <SymbolReference> FindDeclarationForBuiltinCommand( CommandInfo cmdInfo, SymbolReference foundSymbol, Workspace workspace) { SymbolReference foundDefinition = null; if (cmdInfo != null) { int index = 0; ScriptFile[] nestedModuleFiles; CommandInfo commandInfo = await CommandHelpers.GetCommandInfo( foundSymbol.SymbolName, this.powerShellContext); nestedModuleFiles = GetBuiltinCommandScriptFiles( commandInfo.Module, workspace); while (foundDefinition == null && index < nestedModuleFiles.Length) { foundDefinition = AstOperations.FindDefinitionOfSymbol( nestedModuleFiles[index].ScriptAst, foundSymbol); if (foundDefinition != null) { foundDefinition.FilePath = nestedModuleFiles[index].FilePath; } index++; } } return(foundDefinition); }
protected override bool CanProvideFor(ScriptFile scriptFile) { return((scriptFile.FilePath != null && scriptFile.FilePath.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase)) || AstOperations.IsPowerShellDataFileAst(scriptFile.ScriptAst)); }
/// <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> /// <param name="workspace">The Workspace to which the ScriptFile belongs.</param> /// <returns>The resulting GetDefinitionResult for the symbol's definition.</returns> public async Task <GetDefinitionResult> GetDefinitionOfSymbol( ScriptFile sourceFile, SymbolReference foundSymbol, Workspace workspace) { Validate.IsNotNull("sourceFile", sourceFile); Validate.IsNotNull("foundSymbol", foundSymbol); Validate.IsNotNull("workspace", workspace); ScriptFile[] referencedFiles = workspace.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; for (int i = 0; i < referencedFiles.Length; i++) { foundDefinition = AstOperations.FindDefinitionOfSymbol( referencedFiles[i].ScriptAst, foundSymbol); filesSearched.Add(referencedFiles[i].FilePath); if (foundDefinition != null) { foundDefinition.FilePath = referencedFiles[i].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 var allFiles = workspace.EnumeratePSFiles(); foreach (var file in allFiles) { if (filesSearched.Contains(file)) { continue; } Token[] tokens = null; ParseError[] parseErrors = null; foundDefinition = AstOperations.FindDefinitionOfSymbol( Parser.ParseFile(file, out tokens, out parseErrors), foundSymbol); filesSearched.Add(file); if (foundDefinition != null) { foundDefinition.FilePath = file; break; } } } // if definition is not found in file in the workspace // look for it in the builtin commands if (foundDefinition == null) { CommandInfo cmdInfo = await CommandHelpers.GetCommandInfo( foundSymbol.SymbolName, this.powerShellContext); foundDefinition = FindDeclarationForBuiltinCommand( cmdInfo, foundSymbol, workspace); } return(foundDefinition != null ? new GetDefinitionResult(foundDefinition) : null); }
/// <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 <FindReferencesResult> FindReferencesOfSymbol( SymbolReference foundSymbol, ScriptFile[] referencedFiles, Workspace workspace) { if (foundSymbol != null) { int symbolOffset = referencedFiles[0].GetOffsetAtPosition( foundSymbol.ScriptRegion.StartLineNumber, foundSymbol.ScriptRegion.StartColumnNumber); // Make sure aliases have been loaded await GetAliases(); // We want to look for references first in referenced files, hence we use ordered dictionary var fileMap = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); foreach (ScriptFile file in referencedFiles) { fileMap.Add(file.FilePath, file); } var allFiles = workspace.EnumeratePSFiles(); foreach (var file in allFiles) { if (!fileMap.Contains(file)) { fileMap.Add(file, new ScriptFile(file, null, this.powerShellContext.LocalPowerShellVersion.Version)); } } List <SymbolReference> symbolReferences = new List <SymbolReference>(); foreach (var fileName in fileMap.Keys) { var file = (ScriptFile)fileMap[fileName]; IEnumerable <SymbolReference> symbolReferencesinFile = AstOperations .FindReferencesOfSymbol( file.ScriptAst, foundSymbol, CmdletToAliasDictionary, AliasToCmdletDictionary) .Select( reference => { try { reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); } catch (ArgumentOutOfRangeException e) { reference.SourceLine = string.Empty; this.logger.WriteException("Found reference is out of range in script file", e); } reference.FilePath = file.FilePath; return(reference); }); symbolReferences.AddRange(symbolReferencesinFile); } return (new FindReferencesResult { SymbolFileOffset = symbolOffset, SymbolName = foundSymbol.SymbolName, FoundReferences = symbolReferences }); } else { return(null); } }
/// <summary> /// Parses the current file contents to get the AST, tokens, /// and parse errors. /// </summary> private void ParseFileContents() { ParseError[] parseErrors = null; // First, get the updated file range int lineCount = this.FileLines.Count; if (lineCount > 0) { this.FileRange = new BufferRange( new BufferPosition(1, 1), new BufferPosition( lineCount + 1, this.FileLines[lineCount - 1].Length + 1)); } else { this.FileRange = BufferRange.None; } try { Token[] scriptTokens; // This overload appeared with Windows 10 Update 1 if (this.powerShellVersion.Major >= 6 || (this.powerShellVersion.Major == 5 && this.powerShellVersion.Build >= 10586)) { // Include the file path so that module relative // paths are evaluated correctly this.ScriptAst = Parser.ParseInput( this.Contents, this.FilePath, out scriptTokens, out parseErrors); } else { this.ScriptAst = Parser.ParseInput( this.Contents, out scriptTokens, out parseErrors); } this.ScriptTokens = scriptTokens; } catch (RuntimeException ex) { var parseError = new ParseError( null, ex.ErrorRecord.FullyQualifiedErrorId, ex.Message); parseErrors = new[] { parseError }; this.ScriptTokens = new Token[0]; this.ScriptAst = null; } // Translate parse errors into syntax markers this.SyntaxMarkers = parseErrors .Select(ScriptFileMarker.FromParseError) .ToArray(); // Untitled files have no directory // Discussed in https://github.com/PowerShell/PowerShellEditorServices/pull/815. // Rather than working hard to enable things for untitled files like a phantom directory, // users should save the file. if (IsUntitledPath(this.FilePath)) { // Need to initialize the ReferencedFiles property to an empty array. this.ReferencedFiles = new string[0]; return; } // Get all dot sourced referenced files and store them this.ReferencedFiles = AstOperations.FindDotSourcedIncludes(this.ScriptAst, Path.GetDirectoryName(this.FilePath)); }
/// <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> /// <param name="workspace">The Workspace to which the ScriptFile belongs.</param> /// <returns>The resulting GetDefinitionResult for the symbol's definition.</returns> public async Task <GetDefinitionResult> GetDefinitionOfSymbolAsync( ScriptFile sourceFile, SymbolReference foundSymbol, Workspace workspace) { Validate.IsNotNull(nameof(sourceFile), sourceFile); Validate.IsNotNull(nameof(foundSymbol), foundSymbol); Validate.IsNotNull(nameof(workspace), workspace); ScriptFile[] referencedFiles = workspace.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, workspace, 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 = workspace.EnumeratePSFiles(); foreach (string file in allFiles) { if (filesSearched.Contains(file)) { continue; } Token[] tokens = null; ParseError[] parseErrors = null; foundDefinition = AstOperations.FindDefinitionOfSymbol( Parser.ParseFile(file, out tokens, out parseErrors), foundSymbol); filesSearched.Add(file); if (foundDefinition != null) { foundDefinition.FilePath = file; break; } } } // if definition is not found in file in the workspace // look for it in the builtin commands if (foundDefinition == null) { CommandInfo cmdInfo = await CommandHelpers.GetCommandInfoAsync( foundSymbol.SymbolName, _powerShellContext); foundDefinition = FindDeclarationForBuiltinCommand( cmdInfo, foundSymbol, workspace); } return(foundDefinition != null ? new GetDefinitionResult(foundDefinition) : null); }
/// <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 <FindReferencesResult> FindReferencesOfSymbolAsync( SymbolReference foundSymbol, ScriptFile[] referencedFiles, Workspace workspace) { if (foundSymbol == null) { return(null); } int symbolOffset = referencedFiles[0].GetOffsetAtPosition( foundSymbol.ScriptRegion.StartLineNumber, foundSymbol.ScriptRegion.StartColumnNumber); // Make sure aliases have been loaded await GetAliasesAsync(); // 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]; await _aliasHandle.WaitAsync(); try { IEnumerable <SymbolReference> references = AstOperations.FindReferencesOfSymbol( file.ScriptAst, foundSymbol, _cmdletToAliasDictionary, _aliasToCmdletDictionary); foreach (SymbolReference reference in references) { try { reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); } catch (ArgumentOutOfRangeException e) { reference.SourceLine = string.Empty; _logger.WriteException("Found reference is out of range in script file", e); } reference.FilePath = file.FilePath; symbolReferences.Add(reference); } } finally { _aliasHandle.Release(); } } return(new FindReferencesResult { SymbolFileOffset = symbolOffset, SymbolName = foundSymbol.SymbolName, FoundReferences = 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 <FindReferencesResult> FindReferencesOfSymbol( SymbolReference foundSymbol, ScriptFile[] referencedFiles, Workspace workspace) { if (foundSymbol == null) { return(null); } int symbolOffset = referencedFiles[0].GetOffsetAtPosition( foundSymbol.ScriptRegion.StartLineNumber, foundSymbol.ScriptRegion.StartColumnNumber); // Make sure aliases have been loaded await GetAliases(); // 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 #if CoreCLR // The RuntimeInformation.IsOSPlatform is not supported in .NET Framework var fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? new OrderedDictionary() : new OrderedDictionary(StringComparer.OrdinalIgnoreCase); #else var fileMap = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); #endif foreach (ScriptFile file in referencedFiles) { fileMap.Add(file.FilePath, file); } foreach (string file in workspace.EnumeratePSFiles()) { if (!fileMap.Contains(file)) { ScriptFile scriptFile; try { scriptFile = workspace.GetFile(file); } catch (Exception e) when(e is IOException || e is SecurityException || e is FileNotFoundException || e is DirectoryNotFoundException || e is PathTooLongException || e is UnauthorizedAccessException) { // If we can't access the file for some reason, just ignore it continue; } fileMap.Add(file, 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, _cmdletToAliasDictionary, _aliasToCmdletDictionary); foreach (SymbolReference reference in references) { try { reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); } catch (ArgumentOutOfRangeException e) { reference.SourceLine = string.Empty; _logger.WriteException("Found reference is out of range in script file", e); } reference.FilePath = file.FilePath; symbolReferences.Add(reference); } } return(new FindReferencesResult { SymbolFileOffset = symbolOffset, SymbolName = foundSymbol.SymbolName, FoundReferences = symbolReferences }); }