/// <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>
        internal async Task <IEnumerable <CompletionItem> > GetCompletionsInFileAsync(
            ScriptFile scriptFile,
            int lineNumber,
            int columnNumber,
            CancellationToken cancellationToken)
        {
            Validate.IsNotNull(nameof(scriptFile), scriptFile);

            CommandCompletion result = await AstOperations.GetCompletionsAsync(
                scriptFile.ScriptAst,
                scriptFile.ScriptTokens,
                scriptFile.GetOffsetAtPosition(lineNumber, columnNumber),
                _executionService,
                _logger,
                cancellationToken).ConfigureAwait(false);

            if (result.CompletionMatches.Count == 0)
            {
                return(Array.Empty <CompletionItem>());
            }

            BufferRange replacedRange = scriptFile.GetRangeBetweenOffsets(
                result.ReplacementIndex,
                result.ReplacementIndex + result.ReplacementLength);

            // Create OmniSharp CompletionItems from PowerShell CompletionResults. We use a for loop
            // because the index is used for sorting.
            CompletionItem[] completionItems = new CompletionItem[result.CompletionMatches.Count];
            for (int i = 0; i < result.CompletionMatches.Count; i++)
            {
                completionItems[i] = CreateCompletionItem(result.CompletionMatches[i], replacedRange, i + 1);
            }
            return(completionItems);
        }
Beispiel #2
0
        public void CanFindSymbolAtPostion(int lineNumber, int columnNumber, string expectedName)
        {
            SymbolReference reference = AstOperations.FindSymbolAtPosition(s_ast, lineNumber, columnNumber);

            Assert.NotNull(reference);
            Assert.Equal(expectedName, reference.SymbolName);
        }
Beispiel #3
0
        /// <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>
        /// 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 = null;

            using (var cts = new CancellationTokenSource(DefaultWaitTimeoutMilliseconds))
            {
                commandCompletion =
                    await AstOperations.GetCompletionsAsync(
                        scriptFile.ScriptAst,
                        scriptFile.ScriptTokens,
                        fileOffset,
                        _powerShellContextService,
                        _logger,
                        cts.Token).ConfigureAwait(false);
            }

            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.LogError(
                    $"Caught exception while trying to create CompletionResults:\n\n{e.ToString()}");

                return(new CompletionResults());
            }
        }
Beispiel #5
0
        /// <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 column number of the cursor for the given script</param>
        /// <returns>ParameterSetSignatures</returns>
        public async Task <ParameterSetSignatures> FindParameterSetsInFileAsync(
            ScriptFile file,
            int lineNumber,
            int columnNumber)
        {
            SymbolReference foundSymbol =
                AstOperations.FindCommandAtPosition(
                    file.ScriptAst,
                    lineNumber,
                    columnNumber);

            // If we are not possibly looking at a Function, we don't
            // need to continue because we won't be able to get the
            // CommandInfo object.
            if (foundSymbol?.SymbolType is not SymbolType.Function
                and not SymbolType.Unknown)
            {
                return(null);
            }

            CommandInfo commandInfo =
                await CommandHelpers.GetCommandInfoAsync(
                    foundSymbol.SymbolName,
                    _runspaceContext.CurrentRunspace,
                    _executionService).ConfigureAwait(false);

            if (commandInfo == null)
            {
                return(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.
                _logger.LogException("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);
            }
        }
        IEnumerable <SymbolReference> IDocumentSymbolProvider.ProvideDocumentSymbols(
            ScriptFile scriptFile)
        {
            if ((scriptFile.FilePath != null &&
                 scriptFile.FilePath.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase)) ||
                AstOperations.IsPowerShellDataFileAst(scriptFile.ScriptAst))
            {
                var findHashtableSymbolsVisitor = new FindHashtableSymbolsVisitor();
                scriptFile.ScriptAst.Visit(findHashtableSymbolsVisitor);
                return(findHashtableSymbolsVisitor.SymbolReferences);
            }

            return(Enumerable.Empty <SymbolReference>());
        }
Beispiel #7
0
        /// <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> FindParameterSetsInFileAsync(
            ScriptFile file,
            int lineNumber,
            int columnNumber,
            PowerShellContextService powerShellContext)
        {
            SymbolReference foundSymbol =
                AstOperations.FindCommandAtPosition(
                    file.ScriptAst,
                    lineNumber,
                    columnNumber);

            if (foundSymbol == null)
            {
                return(null);
            }

            CommandInfo commandInfo =
                await CommandHelpers.GetCommandInfoAsync(
                    foundSymbol.SymbolName,
                    powerShellContext).ConfigureAwait(false);

            if (commandInfo == null)
            {
                return(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.
                _logger.LogException("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);
            }
        }
Beispiel #8
0
        IEnumerable <SymbolReference> IDocumentSymbolProvider.ProvideDocumentSymbols(
            ScriptFile scriptFile)
        {
            if (scriptFile != null &&
                scriptFile.FilePath != null &&
                (scriptFile.FilePath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase) ||
                 scriptFile.FilePath.EndsWith(".psm1", StringComparison.OrdinalIgnoreCase)))
            {
                return
                    (AstOperations.FindSymbolsInDocument(
                         scriptFile.ScriptAst,
                         this.powerShellVersion));
            }

            return(Enumerable.Empty <SymbolReference>());
        }
Beispiel #9
0
        public void CanFindReferencesOfSymbolAtPostion(int lineNumber, int columnNumber, Position[] positions)
        {
            SymbolReference symbol = AstOperations.FindSymbolAtPosition(s_ast, lineNumber, columnNumber);

            IEnumerable <SymbolReference> references = AstOperations.FindReferencesOfSymbol(s_ast, symbol, needsAliases: false);

            int positionsIndex = 0;

            foreach (SymbolReference reference in references)
            {
                Assert.Equal(positions[positionsIndex].Line, reference.ScriptRegion.StartLineNumber);
                Assert.Equal(positions[positionsIndex].Character, reference.ScriptRegion.StartColumnNumber);

                positionsIndex++;
            }
        }
Beispiel #10
0
        /// <summary>
        /// Finds all the occurrences 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="symbolLineNumber">The line number of the cursor for the given script</param>
        /// <param name="symbolColumnNumber">The column number of the cursor for the given script</param>
        /// <returns>FindOccurrencesResult</returns>
        public static IReadOnlyList <SymbolReference> FindOccurrencesInFile(
            ScriptFile file,
            int symbolLineNumber,
            int symbolColumnNumber)
        {
            SymbolReference foundSymbol = AstOperations.FindSymbolAtPosition(
                file.ScriptAst,
                symbolLineNumber,
                symbolColumnNumber);

            if (foundSymbol == null)
            {
                return(null);
            }

            return(AstOperations.FindReferencesOfSymbol(file.ScriptAst, foundSymbol).ToArray());
        }
Beispiel #11
0
        /// <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);
        }
Beispiel #12
0
        /// <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 Task <SymbolDetails> FindSymbolDetailsAtLocationAsync(
            ScriptFile scriptFile,
            int lineNumber,
            int columnNumber)
        {
            SymbolReference symbolReference =
                AstOperations.FindSymbolAtPosition(
                    scriptFile.ScriptAst,
                    lineNumber,
                    columnNumber);

            if (symbolReference == null)
            {
                return(Task.FromResult <SymbolDetails>(null));
            }

            symbolReference.FilePath = scriptFile.FilePath;
            return(SymbolDetails.CreateAsync(
                       symbolReference,
                       _runspaceContext.CurrentRunspace,
                       _executionService));
        }
Beispiel #13
0
        /// <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> FindSymbolDetailsAtLocationAsync(
            ScriptFile scriptFile,
            int lineNumber,
            int columnNumber)
        {
            SymbolReference symbolReference =
                AstOperations.FindSymbolAtPosition(
                    scriptFile.ScriptAst,
                    lineNumber,
                    columnNumber);

            if (symbolReference == null)
            {
                return(null);
            }

            symbolReference.FilePath = scriptFile.FilePath;
            SymbolDetails symbolDetails = await SymbolDetails.CreateAsync(
                symbolReference,
                _powerShellContextService).ConfigureAwait(false);

            return(symbolDetails);
        }
Beispiel #14
0
        /// <summary>
        /// Parses the current file contents to get the AST, tokens,
        /// and parse errors.
        /// </summary>
        private void ParseFileContents()
        {
#if false
            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
            {
#if SqlToolsv5r2
                // This overload appeared with Windows 10 Update 1
                if (this.SqlToolsVersion.Major >= 5 &&
                    this.SqlToolsVersion.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);
#endif
        }
Beispiel #15
0
        /// <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 = Array.Empty <Token>();
                this.ScriptAst    = null;
            }

            // Translate parse errors into syntax markers
            this.DiagnosticMarkers =
                parseErrors
                .Select(ScriptFileMarker.FromParseError)
                .ToList();

            // 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 = Array.Empty <string>();
                return;
            }

            // Get all dot sourced referenced files and store them
            this.ReferencedFiles = AstOperations.FindDotSourcedIncludes(this.ScriptAst, Path.GetDirectoryName(this.FilePath));
        }
Beispiel #16
0
        /// <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);
        }
Beispiel #17
0
        /// <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);
        }
Beispiel #18
0
        /// <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),