diff --git a/.gitignore b/.gitignore index ed6903bd0..4e31936dd 100644 --- a/.gitignore +++ b/.gitignore @@ -43,9 +43,10 @@ ClientBin/ *.build.csdef csx/ -# Temporarily exclude files generated by Script Analyzer build -PSLanguageService/Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll -PSLanguageService/Microsoft.Windows.PowerShell.ScriptAnalyzer.dll -PSLanguageService/PSScriptAnalyzer.psd1 -PSLanguageService/ScriptAnalyzer.format.ps1xml -PSLanguageService/ScriptAnalyzer.types.ps1xml +# Don't include ScriptAnalyzer binaries +PowerShellEditorServices/Microsoft.Windows.PowerShell.ScriptAnalyzer.dll +PowerShellEditorServices/Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.dll +PowerShellEditorServices/PSScriptAnalyzer.psd1 +PowerShellEditorServices/ScriptAnalyzer.format.ps1xml +PowerShellEditorServices/ScriptAnalyzer.types.ps1xml + diff --git a/src/PowerShellEditorServices.Transport.Stdio/Message/MessageParser.cs b/src/PowerShellEditorServices.Transport.Stdio/Message/MessageParser.cs index 477e3a39f..6cdc3a616 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/Message/MessageParser.cs +++ b/src/PowerShellEditorServices.Transport.Stdio/Message/MessageParser.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // + using Microsoft.PowerShell.EditorServices.Utility; using Newtonsoft.Json; using Newtonsoft.Json.Linq; diff --git a/src/PowerShellEditorServices.Transport.Stdio/PowerShellEditorServices.Transport.Stdio.csproj b/src/PowerShellEditorServices.Transport.Stdio/PowerShellEditorServices.Transport.Stdio.csproj index 7ca31e292..efe3c8b42 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/PowerShellEditorServices.Transport.Stdio.csproj +++ b/src/PowerShellEditorServices.Transport.Stdio/PowerShellEditorServices.Transport.Stdio.csproj @@ -79,12 +79,23 @@ + + + + + + + + + + + diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionDetailsRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionDetailsRequest.cs new file mode 100644 index 000000000..020b93de0 --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionDetailsRequest.cs @@ -0,0 +1,59 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response; +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Request +{ + [MessageTypeName("completionEntryDetails")] + public class CompletionDetailsRequest : FileRequest + { + public override void ProcessMessage( + EditorSession editorSession, + MessageWriter messageWriter) + { + ScriptFile scriptFile = this.GetScriptFile(editorSession); + + CompletionDetails completionDetails = + editorSession.LanguageService.GetCompletionDetailsInFile( + scriptFile, + this.Arguments.Line, + this.Arguments.Offset, + this.Arguments.EntryNames[0]); + + var details = new List(); + if (completionDetails != null) + { + details.Add( + new CompletionEntryDetails(completionDetails, this.Arguments.EntryNames[0] + )); + messageWriter.WriteMessage( + this.PrepareResponse( + new CompletionDetailsResponse + { + Body = details.ToArray() + })); + } + else + { + messageWriter.WriteMessage( + this.PrepareResponse( + new CompletionDetailsResponse{ + Body = details.ToArray() + })); + } + } + } + + public class CompletionDetailsRequestArgs : FileLocationRequestArgs + { + public string[] EntryNames { get; set; } + } + +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionsRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionsRequest.cs index a829ca545..c7b0f6d89 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionsRequest.cs +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/CompletionsRequest.cs @@ -31,7 +31,6 @@ public override void ProcessMessage( completions))); } } - public class CompletionsRequestArgs : FileLocationRequestArgs { public string Prefix { get; set; } diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/DeclarationRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/DeclarationRequest.cs new file mode 100644 index 000000000..900de6419 --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/DeclarationRequest.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Request +{ + [MessageTypeName("definition")] + public class DeclarationRequest : FileRequest + { + public override void ProcessMessage( + EditorSession editorSession, + MessageWriter messageWriter) + { + ScriptFile scriptFile = this.GetScriptFile(editorSession); + + GetDefinitionResult definition = + editorSession.LanguageService.GetDefinitionInFile( + scriptFile, + this.Arguments.Line, + this.Arguments.Offset); + + if (definition != null) + { + DefinitionResponse defResponse = + DefinitionResponse.Create(definition.FoundDefinition, this.Arguments.File); + + messageWriter.WriteMessage( + this.PrepareResponse(defResponse)); + } + } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/OccurrencesRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/OccurrencesRequest.cs new file mode 100644 index 000000000..d8fe52a8f --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/OccurrencesRequest.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Request +{ + [MessageTypeName("occurrences")] + public class OccurrencesRequest : FileRequest + { + public override void ProcessMessage( + EditorSession editorSession, + MessageWriter messageWriter) + { + ScriptFile scriptFile = this.GetScriptFile(editorSession); + + FindOccurrencesResult occurrencesResult = + editorSession.LanguageService.FindOccurrencesInFile( + scriptFile, + this.Arguments.Line, + this.Arguments.Offset); + + OccurrencesResponse occurrencesResponce = + OccurrencesResponse.Create(occurrencesResult, this.Arguments.File); + + messageWriter.WriteMessage( + this.PrepareResponse( + occurrencesResponce)); + } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/OpenFileRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/OpenFileRequest.cs index 98a40552d..4fd581994 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/Request/OpenFileRequest.cs +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/OpenFileRequest.cs @@ -1,10 +1,10 @@ -using Microsoft.PowerShell.EditorServices.Session; +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Session; using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Request { diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/ReferencesRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/ReferencesRequest.cs new file mode 100644 index 000000000..0f175db7d --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/ReferencesRequest.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Request +{ + [MessageTypeName("references")] + public class ReferencesRequest : FileRequest + { + public override void ProcessMessage( + EditorSession editorSession, + MessageWriter messageWriter) + { + ScriptFile scriptFile = this.GetScriptFile(editorSession); + + FindReferencesResult referencesResult = + editorSession.LanguageService.FindReferencesInFile( + scriptFile, + this.Arguments.Line, + this.Arguments.Offset); + + ReferencesResponse referencesResponse = + ReferencesResponse.Create(referencesResult, this.Arguments.File); + + messageWriter.WriteMessage( + this.PrepareResponse( + referencesResponse)); + } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Request/SignatureHelpRequest.cs b/src/PowerShellEditorServices.Transport.Stdio/Request/SignatureHelpRequest.cs new file mode 100644 index 000000000..7313e5fef --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Request/SignatureHelpRequest.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Request +{ + [MessageTypeName("signatureHelp")] + public class SignatureHelpRequest : FileRequest + { + public override void ProcessMessage( + EditorSession editorSession, + MessageWriter messageWriter) + { + ScriptFile scriptFile = this.GetScriptFile(editorSession); + + ParameterSetSignatures parameterSetSigs = + editorSession.LanguageService.FindParameterSetsInFile( + scriptFile, + this.Arguments.Line, + this.Arguments.Offset); + + SignatureHelpResponse sigHelpResponce = + SignatureHelpResponse.Create(parameterSetSigs); + + messageWriter.WriteMessage( + this.PrepareResponse( + sigHelpResponce)); + } + } + + public class SignatureHelpRequestArgs : FileLocationRequestArgs + { + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionDetailsResponse.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionDetailsResponse.cs new file mode 100644 index 000000000..d64dca7ae --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionDetailsResponse.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using System.Text.RegularExpressions; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response +{ + [MessageTypeName("completionEntryDetails")] + public class CompletionDetailsResponse : ResponseBase + { + } + + public class CompletionEntryDetails + { + public CompletionEntryDetails(CompletionDetails completionResult, string entryName) + { + + Kind = null; + KindModifiers = null; + DisplayParts = null; + Documentation = null; + DocString = null; + + // if the result type is a command return null + if (completionResult != null && + !(completionResult.CompletionType.Equals(CompletionType.Command))) + { + //find matches on square brackets in the the tool tip + var matches = + Regex.Matches(completionResult.ToolTipText, @"^\[(.+)\]"); + string strippedEntryName = + Regex.Replace(entryName, @"^[$_-]", "").Replace("{", "").Replace("}", ""); + + if (matches.Count > 0 && matches[0].Groups.Count > 1) + { + Name = matches[0].Groups[1].Value; + } + // if there are nobracets and the only content is the completion name + else if (completionResult.ToolTipText.Equals(strippedEntryName)) + { + Name = null; + } + else + { + Name = null; + DocString = completionResult.ToolTipText; + } + } + + else { Name = null; } + } + public string Name { get; set; } + + public string Kind { get; set; } + + public string KindModifiers { get; set; } + + public SymbolDisplayPart[] DisplayParts { get; set; } + + public SymbolDisplayPart[] Documentation { get; set; } + + public string DocString { get; set; } + + } + + public class SymbolDisplayPart + { + public string Text { get; set; } + + public string Kind { get; set; } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionsResponse.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionsResponse.cs index 2aa9810df..313faab30 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionsResponse.cs +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/CompletionsResponse.cs @@ -45,4 +45,15 @@ private static string GetCompletionKind(CompletionType completionType) } } } + public class CompletionEntry + { + public string Name { get; set; } + + public string Kind { get; set; } + + public string KindModifiers { get; set; } + + public string SortText { get; set; } + } + } diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/DefinitionResponse.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/DefinitionResponse.cs new file mode 100644 index 000000000..a3b315e2f --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/DefinitionResponse.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response +{ + [MessageTypeName("definition")] + public class DefinitionResponse : ResponseBase + { + public static DefinitionResponse Create(SymbolReference result, string thisFile) + { + if (result != null) + { + //The protocol expects a filespan when there whould only be one definition + List declarResult = new List(); + declarResult.Add( + new FileSpan() + { + Start = new Location + { + Line = result.ScriptRegion.StartLineNumber, + Offset = result.ScriptRegion.StartColumnNumber + }, + End = new Location + { + Line = result.ScriptRegion.EndLineNumber, + Offset = result.ScriptRegion.EndColumnNumber + }, + File = thisFile, + }); + return new DefinitionResponse + { + Body = declarResult.ToArray() + }; + } + else + { + return new DefinitionResponse + { + Body = null + }; + } + } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/LocationResponseElements.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/LocationResponseElements.cs new file mode 100644 index 000000000..deea5b67b --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/LocationResponseElements.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response +{ + public class TextSpan + { + public Location Start { get; set; } + public Location End { get; set; } + } + + public class FileSpan : TextSpan + { + public string File { get; set; } + } + + public class Location + { + public int Line { get; set; } + + public int Offset { get; set; } + } + +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/OccurrencesResponse.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/OccurrencesResponse.cs new file mode 100644 index 000000000..07e57de3e --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/OccurrencesResponse.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response +{ + [MessageTypeName("occurrences")] + public class OccurrencesResponse : ResponseBase + { + public static OccurrencesResponse Create(FindOccurrencesResult occurrencesResult, string thisFile) + { + if (occurrencesResult != null) + { + List occurrenceItems = + new List(); + + foreach (SymbolReference reference in occurrencesResult.FoundOccurrences) + { + occurrenceItems.Add( + new OccurrencesResponseItem() + { + IsWriteAccess = true, + File = thisFile, + Start = new Location + { + Line = reference.ScriptRegion.StartLineNumber, + Offset = reference.ScriptRegion.StartColumnNumber + }, + End = new Location + { + Line = reference.ScriptRegion.EndLineNumber, + Offset = reference.ScriptRegion.EndColumnNumber + }, + }); + } + return new OccurrencesResponse + { + Body = occurrenceItems.ToArray() + }; + } + else + { + return new OccurrencesResponse + { + Body = null + }; + } + } + } + + public class OccurrencesResponseItem : FileSpan + { + public bool IsWriteAccess { get; set; } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/ReferencesResponse.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/ReferencesResponse.cs new file mode 100644 index 000000000..788ced37e --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/ReferencesResponse.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response +{ + [MessageTypeName("references")] + public class ReferencesResponse : ResponseBase + { + public static ReferencesResponse Create(FindReferencesResult referencesResult, string thisFile) + { + if (referencesResult != null && referencesResult.FoundReferences != null) + { + List referenceItems + = new List(); + + foreach (SymbolReference reference in referencesResult.FoundReferences) + { + referenceItems.Add( + new ReferencesResponseItem() + { + Start = new Location + { + Line = reference.ScriptRegion.StartLineNumber, + Offset = reference.ScriptRegion.StartColumnNumber + }, + End = new Location + { + Line = reference.ScriptRegion.EndLineNumber, + Offset = reference.ScriptRegion.EndColumnNumber + }, + IsWriteAccess = true, + File = thisFile, + LineText = reference.SourceLine + }); + } + return new ReferencesResponse + { + Body = new ReferencesResponseBody + { + Refs = referenceItems.ToArray(), + SymbolName = referencesResult.SymbolName, + SymbolDisplayString = referencesResult.SymbolName, + SymbolStartOffest = referencesResult.SymbolFileOffset + } + }; + } + else + { + return new ReferencesResponse + { + Body = null + }; + } + } + } + + public class ReferencesResponseBody + { + public ReferencesResponseItem[] Refs { get; set; } + public string SymbolName { get; set; } + public int SymbolStartOffest { get; set; } + public string SymbolDisplayString { get; set; } + } + + public class ReferencesResponseItem : FileSpan + { + public string LineText { get; set; } + public bool IsWriteAccess { get; set; } + } +} diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/ResponseBase.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/ResponseBase.cs index bcb4c5f29..e646bfc41 100644 --- a/src/PowerShellEditorServices.Transport.Stdio/Response/ResponseBase.cs +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/ResponseBase.cs @@ -3,10 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.PowerShell.EditorServices.Language; using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; using Newtonsoft.Json; -using System.Text.RegularExpressions; namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response { @@ -34,71 +32,4 @@ public ResponseBase() this.Type = MessageType.Response; } } - - public class CompletionEntry - { - public string Name { get; set; } - - public string Kind { get; set; } - - public string KindModifiers { get; set; } - - public string SortText { get; set; } - } - - public class CompletionEntryDetails - { - public CompletionEntryDetails(CompletionDetails completionDetails, string entryName) - { - Kind = null; - KindModifiers = null; - DisplayParts = null; - Documentation = null; - DocString = null; - - // if the result type is a command return null - if (!(completionDetails.CompletionType.Equals(CompletionType.Command))) - { - //find matches on square brackets in the the tool tip - var matches = Regex.Matches(completionDetails.ToolTipText, @"^\[(.+)\]"); - string strippedEntryName = Regex.Replace(entryName, @"^[$_-]","").Replace("{","").Replace("}",""); - - if (matches.Count > 0 && matches[0].Groups.Count > 1) - { - Name = matches[0].Groups[1].Value; - } - // if there are nobracets and the only content is the completion name - else if (completionDetails.ToolTipText.Equals(strippedEntryName)) - { - Name = null; - } - else - { - Name = null; - DocString = completionDetails.ToolTipText; - } - } - - else { Name = null; } - } - public string Name { get; set; } - - public string Kind { get; set; } - - public string KindModifiers { get; set; } - - public SymbolDisplayPart[] DisplayParts { get; set; } - - public SymbolDisplayPart[] Documentation { get; set; } - - public string DocString { get; set; } - - } - - public class SymbolDisplayPart - { - public string Text { get; set; } - - public string Kind { get; set; } - } } diff --git a/src/PowerShellEditorServices.Transport.Stdio/Response/SignatureHelpResponse.cs b/src/PowerShellEditorServices.Transport.Stdio/Response/SignatureHelpResponse.cs new file mode 100644 index 000000000..c3ada431a --- /dev/null +++ b/src/PowerShellEditorServices.Transport.Stdio/Response/SignatureHelpResponse.cs @@ -0,0 +1,135 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Response +{ + [MessageTypeName("signatureHelp")] + public class SignatureHelpResponse : ResponseBase + { + public static SignatureHelpResponse Create(ParameterSetSignatures parameterSets) + { + if (parameterSets != null && parameterSets.Signatures != null) + { + return new SignatureHelpResponse + { + Body = SignatureHelpItems.Create(parameterSets) + }; + } + else + { + return new SignatureHelpResponse + { + Body = null + }; + } + } + } + + public class SignatureHelpItems + { + public IEnumerable Items { get; set; } + public TextSpan ApplicableSpan { get; set; } + public int SelectedItemIndex { get; set; } + public int ArgumentIndex { get; set; } + public int ArgumentCount { get; set; } + public string CommandName { get; set; } + + public static SignatureHelpItems Create(ParameterSetSignatures parameterSets) + { + List itemsList = new List(); + foreach (ParameterSetSignature item in parameterSets.Signatures) + { + itemsList.Add(SignatureHelpItem.Create(item)); + } + + TextSpan textSpan = + new TextSpan + { + Start = new Location + { + Line = parameterSets.ScriptRegion.StartLineNumber, + Offset = parameterSets.ScriptRegion.StartColumnNumber + }, + End = new Location + { + Line = parameterSets.ScriptRegion.EndLineNumber, + Offset = parameterSets.ScriptRegion.EndColumnNumber + } + }; + + return new SignatureHelpItems + { + ArgumentCount = parameterSets.Signatures.Length, + ArgumentIndex = 0, + SelectedItemIndex = 0, + CommandName = parameterSets.CommandName, + ApplicableSpan = textSpan, + Items = itemsList + }; + } + } + + /** + * Represents a single signature to show in signature help. + * */ + public class SignatureHelpItem + { + public bool IsVariadic { get; set; } + public IEnumerable PrefixDisplayParts { get; set; } + public IEnumerable SuffixDisplayParts { get; set; } + public IEnumerable SeparatorDisplayParts { get; set; } + public IEnumerable Parameters { get; set; } + public IEnumerable Documentation { get; set; } + public string SignatureText { get; set; } + + public static SignatureHelpItem Create(ParameterSetSignature paramSetSignature) + { + List parameterList = + new List(); + foreach (ParameterInfo paramInfo in paramSetSignature.Parameters) + { + parameterList.Add(SignatureHelpParameter.Create(paramInfo)); + } + + return new SignatureHelpItem + { + IsVariadic = false, + PrefixDisplayParts = new List(), + SuffixDisplayParts = new List(), + SeparatorDisplayParts = new List(), + Parameters = parameterList, + Documentation = new List(), + SignatureText = paramSetSignature.SignatureText + }; + } + } + + + /** + * Signature help information for a single parameter + * */ + public class SignatureHelpParameter + { + public string Name { get; set; } + public IEnumerable Documentation { get; set; } + public IEnumerable DisplayParts { get; set; } + public bool IsOptional { get; set; } + + public static SignatureHelpParameter Create(ParameterInfo paramInfo) + { + return new SignatureHelpParameter + { + Name = paramInfo.Name, + Documentation = new List(), + DisplayParts = new List(), + IsOptional = false + }; + } + } +} \ No newline at end of file diff --git a/src/PowerShellEditorServices/Language/AstOperations.cs b/src/PowerShellEditorServices/Language/AstOperations.cs index 62996047d..d44100759 100644 --- a/src/PowerShellEditorServices/Language/AstOperations.cs +++ b/src/PowerShellEditorServices/Language/AstOperations.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; @@ -74,5 +75,65 @@ static public CompletionResults GetCompletions( return CompletionResults.Create(commandCompletion); } + + /// + /// Finds the symbol at a given file location + /// + /// The abstract syntax tree of the given script + /// The line number of the cursor for the given script + /// The coulumn number of the cursor for the given script + /// SymbolReference of found symbol + static public SymbolReference FindSymbolAtPosition(Ast scriptAst, int lineNumber, int columnNumber) + { + FindSymbolVisitor symbolVisitor = new FindSymbolVisitor(lineNumber, columnNumber); + scriptAst.Visit(symbolVisitor); + + return symbolVisitor.FoundSymbolReference; + } + + /// + /// Finds the symbol (always Command type) at a given file location + /// + /// The abstract syntax tree of the given script + /// The line number of the cursor for the given script + /// The column number of the cursor for the given script + /// SymbolReference of found command + static public SymbolReference FindCommandAtPosition(Ast scriptAst, int lineNumber, int columnNumber) + { + FindCommandVisitor commandVisitor = new FindCommandVisitor(lineNumber, columnNumber); + scriptAst.Visit(commandVisitor); + + return commandVisitor.FoundCommandReference; + } + + /// + /// Finds all references in a script of the given symbol + /// + /// The abstract syntax tree of the given script + /// The symbol that we are looking for referneces of + /// A collection of SymbolReference objects that are refrences to the symbolRefrence + static public IEnumerable FindReferencesOfSymbol(Ast scriptAst, SymbolReference symbolReference) + { + // find the symbol evaluators for the node types we are handling + FindReferencesVisitor referencesVisitor = new FindReferencesVisitor(symbolReference); + scriptAst.Visit(referencesVisitor); + + return referencesVisitor.FoundReferences; + + } + + /// + /// Finds the definition of the symbol + /// + /// The abstract syntax tree of the given script + /// The symbol that we are looking for the definition of + /// A SymbolReference of the definition of the symbolReference + static public SymbolReference FindDefinitionOfSymbol(Ast scriptAst, SymbolReference symbolReference) + { + FindDeclartionVisitor declarationVisitor = new FindDeclartionVisitor(symbolReference); + scriptAst.Visit(declarationVisitor); + + return declarationVisitor.FoundDeclartion; + } } } diff --git a/src/PowerShellEditorServices/Language/FindCommandVisitor.cs b/src/PowerShellEditorServices/Language/FindCommandVisitor.cs new file mode 100644 index 000000000..349081f15 --- /dev/null +++ b/src/PowerShellEditorServices/Language/FindCommandVisitor.cs @@ -0,0 +1,74 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Management.Automation.Language; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// The vistior used to find the commandAst of a specific location in an AST + /// + internal class FindCommandVisitor : AstVisitor + { + private int lineNumber; + private int columnNumber; + + public SymbolReference FoundCommandReference { get; private set; } + + public FindCommandVisitor(int lineNumber, int columnNumber) + { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + /// + /// Checks to see if this command ast is the symbol we are looking for. + /// Assumes the commandAst will have two elements to be considered the correct command. + /// + /// A CommandAst object in the script's AST + /// A descion to stop searching if the right commandAst was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitCommand(CommandAst commandAst) + { + Ast commandNameAst = commandAst.CommandElements[0]; + if (!(commandAst.CommandElements.Count > 1)) + { + return base.VisitCommand(commandAst); + } + else + { + Ast secondCommandElementAst = commandAst.CommandElements[1]; + + if (this.IsPositionInExtent(commandNameAst.Extent, secondCommandElementAst.Extent)) + { + this.FoundCommandReference = + new SymbolReference( + SymbolType.Function, + commandNameAst.Extent, + string.Empty); + + return AstVisitAction.StopVisit; + } + } + + return base.VisitCommand(commandAst); + } + + /// + /// Is the position of the given location is in the range of the start + /// of the first element to the character before the second element + /// + /// The script extent of the first element of the command ast + /// The script extent of the second element of the command ast + /// True if the given position is in the range of the start of + /// the first element to the character before the second element + private bool IsPositionInExtent(IScriptExtent firstExtent, IScriptExtent secondExtent) + { + return (firstExtent.StartLineNumber == lineNumber && + firstExtent.StartColumnNumber <= columnNumber && + secondExtent.StartColumnNumber >= columnNumber - 1); + } + } +} diff --git a/src/PowerShellEditorServices/Language/FindDeclartionVisitor.cs b/src/PowerShellEditorServices/Language/FindDeclartionVisitor.cs new file mode 100644 index 000000000..af15b0f70 --- /dev/null +++ b/src/PowerShellEditorServices/Language/FindDeclartionVisitor.cs @@ -0,0 +1,86 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Management.Automation.Language; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// The vistor used to find the defintion of a symbol + /// + internal class FindDeclartionVisitor : AstVisitor + { + private SymbolReference symbolRef; + + public SymbolReference FoundDeclartion{ get; private set; } + + public FindDeclartionVisitor(SymbolReference symbolRef) + { + this.symbolRef = symbolRef; + } + + /// + /// Decides if the current function defintion is the right defition + /// for the symbol being searched for. The defintion of the symbol will be a of type + /// SymbolType.Function and have the same name as the symbol + /// + /// A FunctionDefinitionAst in the script's AST + /// A descion to stop searching if the right FunctionDefinitionAst was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) + { + int startColumnNumber = + functionDefinitionAst.Extent.Text.IndexOf( + functionDefinitionAst.Name) + 1; + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = functionDefinitionAst.Name, + StartLineNumber = functionDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length + }; + + if (symbolRef.SymbolType.Equals(SymbolType.Function) && + nameExtent.Text.Equals(symbolRef.ScriptRegion.Text)) + { + this.FoundDeclartion = + new SymbolReference( + SymbolType.Function, + nameExtent, + string.Empty); + + return AstVisitAction.StopVisit; + } + + return base.VisitFunctionDefinition(functionDefinitionAst); + } + + /// + /// Decides if the current variable expression is the right defition for + /// the symbol being searched for. The defintion of the symbol will be a of type + /// SymbolType.Variable and have the same name as the symbol + /// + /// A FunctionDefinitionAst in the script's AST + /// A descion to stop searching if the right VariableExpressionAst was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) + { + if(symbolRef.SymbolType.Equals(SymbolType.Variable) && + variableExpressionAst.Extent.Text.Equals(symbolRef.SymbolName)) + { + this.FoundDeclartion = + new SymbolReference( + SymbolType.Variable, + variableExpressionAst.Extent, + string.Empty); + + return AstVisitAction.StopVisit; + } + + return AstVisitAction.Continue; + } + } +} diff --git a/src/PowerShellEditorServices/Language/FindOccurrencesResult.cs b/src/PowerShellEditorServices/Language/FindOccurrencesResult.cs new file mode 100644 index 000000000..9dc617145 --- /dev/null +++ b/src/PowerShellEditorServices/Language/FindOccurrencesResult.cs @@ -0,0 +1,23 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// A class for the found occurences of a symbol. + /// It contains a collection of symbol references. + /// + public class FindOccurrencesResult + { + #region Properties + /// + /// Gets the collection of SymboleReferences for the all occurences of the symbol + /// + public IEnumerable FoundOccurrences { get; internal set; } + #endregion + } +} diff --git a/src/PowerShellEditorServices/Language/FindReferencesResult.cs b/src/PowerShellEditorServices/Language/FindReferencesResult.cs new file mode 100644 index 000000000..4bff3db5b --- /dev/null +++ b/src/PowerShellEditorServices/Language/FindReferencesResult.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// A class to contain the found references of a symbol. + /// It contains a collection of symbol references, the symbol name, and the symbol's file offset + /// + public class FindReferencesResult + { + #region Properties + /// + /// Gets the name of the symbol + /// + public string SymbolName { get; internal set; } + + /// + /// Gets the file offset (location based on line and column number) of the symbol + /// + public int SymbolFileOffset { get; internal set; } + + /// + /// Gets the collection of SymboleReferences for the all references to the symbol + /// + public IEnumerable FoundReferences { get; internal set; } + #endregion + } +} diff --git a/src/PowerShellEditorServices/Language/FindReferencesVisitor.cs b/src/PowerShellEditorServices/Language/FindReferencesVisitor.cs new file mode 100644 index 000000000..fa1f13b1f --- /dev/null +++ b/src/PowerShellEditorServices/Language/FindReferencesVisitor.cs @@ -0,0 +1,116 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Management.Automation.Language; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// The visitor used to find the references of a symbol in a script's AST + /// + internal class FindReferencesVisitor : AstVisitor + { + private SymbolReference symbolRef; + + public List FoundReferences { get; set; } + + public FindReferencesVisitor(SymbolReference symbolRef) + { + this.symbolRef = symbolRef; + this.FoundReferences = new List(); + } + + /// + /// Decides if the current command is a reference of the symbol being searched for. + /// A reference of the symbol will be a of type SymbolType.Function + /// and have the same name as the symbol + /// + /// A CommandAst in the script's AST + /// A visit action that continues the search for references + public override AstVisitAction VisitCommand(CommandAst commandAst) + { + Ast commandNameAst = commandAst.CommandElements[0]; + if(symbolRef.SymbolType.Equals(SymbolType.Function) && + commandNameAst.Extent.Text.Equals(symbolRef.ScriptRegion.Text)) + { + this.FoundReferences.Add(new SymbolReference( + SymbolType.Function, + commandNameAst.Extent, + string.Empty)); + } + return base.VisitCommand(commandAst); + } + + /// + /// Decides if the current function defintion is a reference of the symbol being searched for. + /// A reference of the symbol will be a of type SymbolType.Function and have the same name as the symbol + /// + /// A functionDefinitionAst in the script's AST + /// A visit action that continues the search for references + public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) + { + int startColumnNumber = + functionDefinitionAst.Extent.Text.IndexOf( + functionDefinitionAst.Name) + 1; + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = functionDefinitionAst.Name, + StartLineNumber = functionDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length + }; + + if (symbolRef.SymbolType.Equals(SymbolType.Function) && + nameExtent.Text.Equals(symbolRef.SymbolName)) + { + this.FoundReferences.Add(new SymbolReference( + SymbolType.Function, + nameExtent, + string.Empty)); + } + return base.VisitFunctionDefinition(functionDefinitionAst); + } + + /// + /// Decides if the current function defintion is a reference of the symbol being searched for. + /// A reference of the symbol will be a of type SymbolType.Parameter and have the same name as the symbol + /// + /// A commandParameterAst in the script's AST + /// A visit action that continues the search for references + public override AstVisitAction VisitCommandParameter(CommandParameterAst commandParameterAst) + { + if (symbolRef.SymbolType.Equals(SymbolType.Parameter) && + commandParameterAst.Extent.Text.Equals(symbolRef.SymbolName)) + { + this.FoundReferences.Add(new SymbolReference( + SymbolType.Parameter, + commandParameterAst.Extent, + string.Empty)); + } + return AstVisitAction.Continue; + } + + /// + /// Decides if the current function defintion is a reference of the symbol being searched for. + /// A reference of the symbol will be a of type SymbolType.Variable and have the same name as the symbol + /// + /// A variableExpressionAst in the script's AST + /// A visit action that continues the search for references + public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) + { + if(symbolRef.SymbolType.Equals(SymbolType.Variable) && + variableExpressionAst.Extent.Text.Equals(symbolRef.SymbolName)) + { + this.FoundReferences.Add(new SymbolReference( + SymbolType.Variable, + variableExpressionAst.Extent, + string.Empty)); + } + return AstVisitAction.Continue; + } + } +} \ No newline at end of file diff --git a/src/PowerShellEditorServices/Language/FindSymbolVisitor.cs b/src/PowerShellEditorServices/Language/FindSymbolVisitor.cs new file mode 100644 index 000000000..b8e003fee --- /dev/null +++ b/src/PowerShellEditorServices/Language/FindSymbolVisitor.cs @@ -0,0 +1,138 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Management.Automation.Language; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// The visitor used to find the the symbol at a specfic location in the AST + /// + internal class FindSymbolVisitor : AstVisitor + { + private int lineNumber; + private int columnNumber; + + public SymbolReference FoundSymbolReference { get; private set; } + + public FindSymbolVisitor(int lineNumber, int columnNumber) + { + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + /// + /// Checks to see if this command ast is the symbol we are looking for. + /// + /// A CommandAst object in the script's AST + /// A descion to stop searching if the right symbol was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitCommand(CommandAst commandAst) + { + Ast commandNameAst = commandAst.CommandElements[0]; + + if (this.IsPositionInExtent(commandNameAst.Extent)) + { + this.FoundSymbolReference = + new SymbolReference( + SymbolType.Function, + commandNameAst.Extent, + string.Empty); + + return AstVisitAction.StopVisit; + } + + return base.VisitCommand(commandAst); + } + + /// + /// Checks to see if this function definition is the symbol we are looking for. + /// + /// A functionDefinitionAst object in the script's AST + /// A descion to stop searching if the right symbol was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) + { + int startColumnNumber = + functionDefinitionAst.Extent.Text.IndexOf( + functionDefinitionAst.Name) + 1; + + IScriptExtent nameExtent = new ScriptExtent() + { + Text = functionDefinitionAst.Name, + StartLineNumber = functionDefinitionAst.Extent.StartLineNumber, + StartColumnNumber = startColumnNumber, + EndColumnNumber = startColumnNumber + functionDefinitionAst.Name.Length + }; + + if (this.IsPositionInExtent(nameExtent)) + { + this.FoundSymbolReference = + new SymbolReference( + SymbolType.Function, + nameExtent, + string.Empty); + + return AstVisitAction.StopVisit; + } + + return base.VisitFunctionDefinition(functionDefinitionAst); + } + + /// + /// Checks to see if this command parameter is the symbol we are looking for. + /// + /// A CommandParameterAst object in the script's AST + /// A descion to stop searching if the right symbol was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitCommandParameter(CommandParameterAst commandParameterAst) + { + if (this.IsPositionInExtent(commandParameterAst.Extent)) + { + this.FoundSymbolReference = + new SymbolReference( + SymbolType.Parameter, + commandParameterAst.Extent, + string.Empty); + return AstVisitAction.StopVisit; + } + return AstVisitAction.Continue; + } + + /// + /// Checks to see if this variable expression is the symbol we are looking for. + /// + /// A VariableExpressionAst object in the script's AST + /// A descion to stop searching if the right symbol was found, + /// or a decision to continue if it wasn't found + public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) + { + if (this.IsPositionInExtent(variableExpressionAst.Extent)) + { + this.FoundSymbolReference = + new SymbolReference( + SymbolType.Variable, + variableExpressionAst.Extent, + string.Empty); + + return AstVisitAction.StopVisit; + } + + return AstVisitAction.Continue; + } + + /// + /// Is the position of the given location is in the ast's extent + /// + /// The script extent of the element + /// True if the given position is in the range of the element's extent + private bool IsPositionInExtent(IScriptExtent extent) + { + return (extent.StartLineNumber == lineNumber && + extent.StartColumnNumber <= columnNumber && + extent.EndColumnNumber >= columnNumber); + } + } +} diff --git a/src/PowerShellEditorServices/Language/GetDefinitionResult.cs b/src/PowerShellEditorServices/Language/GetDefinitionResult.cs new file mode 100644 index 000000000..6feb831c2 --- /dev/null +++ b/src/PowerShellEditorServices/Language/GetDefinitionResult.cs @@ -0,0 +1,30 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// A class to contain the found defintion of a symbol. + /// It contains the symbol reference of the defintion + /// + public class GetDefinitionResult + { + #region Properties + /// + /// Gets the symbolReference of the found definition + /// + public SymbolReference FoundDefinition { get; internal set; } + #endregion + + /// + /// Constructs an instance of a GetDefinitionResut + /// + /// The symbolRefernece for the found definition + public GetDefinitionResult(SymbolReference symRef) + { + FoundDefinition = symRef; + } + } +} diff --git a/src/PowerShellEditorServices/Language/LanguageService.cs b/src/PowerShellEditorServices/Language/LanguageService.cs index 3b4d57125..f53ea1d6f 100644 --- a/src/PowerShellEditorServices/Language/LanguageService.cs +++ b/src/PowerShellEditorServices/Language/LanguageService.cs @@ -22,6 +22,10 @@ public class LanguageService #region Private Fields private Runspace runspace; + private CompletionResults mostRecentCompletions; + private int mostRecentRequestLine; + private int mostRecentRequestOffest; + private string mostRecentRequestFile; #endregion @@ -75,14 +79,205 @@ public CompletionResults GetCompletionsInFile( lineNumber, columnNumber); - return + CompletionResults completionResults = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, this.runspace); + + // save state of most recent completion + mostRecentCompletions = completionResults; + mostRecentRequestFile = scriptFile.FilePath; + mostRecentRequestLine = lineNumber; + mostRecentRequestOffest = columnNumber; + + return completionResults; + } + + /// + /// Finds command completion details for the script given a file location + /// + /// The details and contents of a open script file + /// The line number of the cursor for the given script + /// The coulumn number of the cursor for the given script + /// The name of the suggestion that needs details + /// CompletionResult object (contains information about the command completion) + public CompletionDetails GetCompletionDetailsInFile( + ScriptFile file, + int lineNumber, + int columnNumber, + string entryName) + { + if (file.FilePath.Equals(mostRecentRequestFile) && + lineNumber == mostRecentRequestLine && + columnNumber == mostRecentRequestOffest) + { + CompletionDetails completionResult = + mostRecentCompletions.Completions.First( + result => result.CompletionText.Equals(entryName)); + return completionResult; + } + else { return null; } + } + + /// + /// Finds all the references of a symbol in the script given a file location + /// + /// The details and contents of a open script file + /// The line number of the cursor for the given script + /// The coulumn number of the cursor for the given script + /// FindReferencesResult + public FindReferencesResult FindReferencesInFile( + ScriptFile file, + int lineNumber, + int columnNumber) + { + SymbolReference foundSymbol = + AstOperations.FindSymbolAtPosition( + file.ScriptAst, + lineNumber, + columnNumber); + + if (foundSymbol != null) + { + IEnumerable symbolReferences = + AstOperations + .FindReferencesOfSymbol( + file.ScriptAst, + foundSymbol) + .Select( + reference => + { + reference.SourceLine = + file.GetLine(reference.ScriptRegion.StartLineNumber); + return reference; + }); + + return + new FindReferencesResult + { + SymbolFileOffset = file.GetOffsetAtPosition(lineNumber, columnNumber), + SymbolName = foundSymbol.SymbolName, + FoundReferences = symbolReferences + }; + } + else { return null; } + } + + /// + /// Finds the definition of a symbol in the script given a file location + /// + /// The details and contents of a open script file + /// The line number of the cursor for the given script + /// The coulumn number of the cursor for the given script + /// GetDefinitionResult + public GetDefinitionResult GetDefinitionInFile( + ScriptFile file, + int lineNumber, + int columnNumber) + { + SymbolReference foundSymbol = + AstOperations.FindSymbolAtPosition( + file.ScriptAst, + lineNumber, + columnNumber); + + if (foundSymbol != null) + { + SymbolReference foundDefinition = + AstOperations.FindDefinitionOfSymbol( + file.ScriptAst, + foundSymbol); + + return new GetDefinitionResult(foundDefinition); + } + else { return null; } + } + + /// + /// Finds all the occurences of a symbol in the script given a file location + /// + /// The details and contents of a open script file + /// The line number of the cursor for the given script + /// The coulumn number of the cursor for the given script + /// FindOccurrencesResult + public FindOccurrencesResult FindOccurrencesInFile( + ScriptFile file, + int lineNumber, + int columnNumber) + { + SymbolReference foundSymbol = + AstOperations.FindSymbolAtPosition( + file.ScriptAst, + lineNumber, + columnNumber); + if (foundSymbol != null) + { + IEnumerable symbolOccurrences = + AstOperations + .FindReferencesOfSymbol( + file.ScriptAst, + foundSymbol); + + return + new FindOccurrencesResult + { + FoundOccurrences = symbolOccurrences + }; + } + else { return null; } } + /// + /// Finds the parameter set hints of a specific command (determined by a given file location) + /// + /// The details and contents of a open script file + /// The line number of the cursor for the given script + /// The coulumn number of the cursor for the given script + /// ParameterSetSignatures + public ParameterSetSignatures FindParameterSetsInFile( + ScriptFile file, + int lineNumber, + int columnNumber) + { + SymbolReference foundSymbol = + AstOperations.FindCommandAtPosition( + file.ScriptAst, + lineNumber, + columnNumber); + + if (foundSymbol != null) + { + if (GetCommandInfo(foundSymbol.SymbolName) != null) + { + IEnumerable commandInfo = + GetCommandInfo(foundSymbol.SymbolName).ParameterSets; + List commandInfoSet = + new List(commandInfo); + + return new ParameterSetSignatures(commandInfoSet, foundSymbol); + } + else { return null; } + } + else { return null; } + } + #endregion + + private CommandInfo GetCommandInfo(string commandName) + { + CommandInfo commandInfo = null; + + using (PowerShell powerShell = PowerShell.Create()) + { + powerShell.Runspace = this.runspace; + powerShell.AddCommand("Get-Command"); + powerShell.AddArgument(commandName); + commandInfo = powerShell.Invoke().FirstOrDefault(); + } + + return commandInfo; + } } } diff --git a/src/PowerShellEditorServices/Language/ParameterSetSignatures.cs b/src/PowerShellEditorServices/Language/ParameterSetSignatures.cs new file mode 100644 index 000000000..a7c7b7acc --- /dev/null +++ b/src/PowerShellEditorServices/Language/ParameterSetSignatures.cs @@ -0,0 +1,131 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Session; +using System.Collections.Generic; +using System.Management.Automation; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// A class for containing the commandName, the command's + /// possible signatures, and the script extent of the command + /// + public class ParameterSetSignatures + { + #region Properties + /// + /// Gets the name of the command + /// + public string CommandName { get; internal set; } + + /// + /// Gets the collection of signatures for the command + /// + public ParameterSetSignature[] Signatures { get; internal set; } + + /// + /// Gets the script extent of the command + /// + public ScriptRegion ScriptRegion { get; internal set; } + #endregion + + /// + /// Constructs an instance of a ParameterSetSignatures object + /// + /// Collection of parameter set info + /// The SymbolReference of the command + public ParameterSetSignatures(List commandInfoSet, SymbolReference foundSymbol) + { + List paramSetSignatures = new List(); + foreach (CommandParameterSetInfo setInfo in commandInfoSet) + { + paramSetSignatures.Add(new ParameterSetSignature(setInfo)); + } + Signatures = paramSetSignatures.ToArray(); + CommandName = foundSymbol.ScriptRegion.Text; + ScriptRegion = foundSymbol.ScriptRegion; + } + } + + /// + /// A class for containing the signature text and the collection of parameters for a signature + /// + public class ParameterSetSignature + { + #region Properties + /// + /// Gets the signature text + /// + public string SignatureText { get; internal set; } + + /// + /// Gets the collection of parameters for the signature + /// + public IEnumerable Parameters { get; internal set; } + #endregion + + /// + /// Constructs an instance of a ParameterSetSignature + /// + /// Collection of parameter info + public ParameterSetSignature(CommandParameterSetInfo commandParamInfoSet) + { + List parameterInfo = new List(); + foreach (CommandParameterInfo commandParameterInfo in commandParamInfoSet.Parameters) + { + parameterInfo.Add(new ParameterInfo(commandParameterInfo)); + } + SignatureText = commandParamInfoSet.ToString(); + Parameters = parameterInfo.ToArray(); + } + } + + /// + /// A class for containing the parameter info of a parameter + /// + public class ParameterInfo + { + #region Properties + /// + /// Gets the name of the parameter + /// + public string Name { get; internal set; } + + /// + /// Gets the type of the parameter + /// + public string ParameterType { get; internal set; } + + /// + /// Gets the position of the parameter + /// + public int Position { get; internal set; } + + /// + /// Gets a boolean for whetheer or not the parameter is required + /// + public bool IsMandatory { get; internal set; } + + /// + /// Gets the help message of the parameter + /// + public string HelpMessage { get; internal set; } + #endregion + + /// + /// Constructs an instance of a ParameterInfo object + /// + /// Parameter info of the parameter + public ParameterInfo(CommandParameterInfo parameterInfo) + { + this.Name = parameterInfo.Name; + this.ParameterType = parameterInfo.ParameterType.FullName; + this.Position = parameterInfo.Position; + this.IsMandatory = parameterInfo.IsMandatory; + this.HelpMessage = parameterInfo.HelpMessage; + } + } +} diff --git a/src/PowerShellEditorServices/Language/ScriptExtent.cs b/src/PowerShellEditorServices/Language/ScriptExtent.cs index f7395b012..d166952b2 100644 --- a/src/PowerShellEditorServices/Language/ScriptExtent.cs +++ b/src/PowerShellEditorServices/Language/ScriptExtent.cs @@ -4,11 +4,7 @@ // using System; -using System.Collections.Generic; -using System.Linq; using System.Management.Automation.Language; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.PowerShell.EditorServices.Language { @@ -26,7 +22,8 @@ public class ScriptExtent : IScriptExtent /// public string File { - get { throw new NotImplementedException(); } + get; + set; } /// diff --git a/src/PowerShellEditorServices/Language/SymbolReference.cs b/src/PowerShellEditorServices/Language/SymbolReference.cs new file mode 100644 index 000000000..84e1609b3 --- /dev/null +++ b/src/PowerShellEditorServices/Language/SymbolReference.cs @@ -0,0 +1,81 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Session; +using System.Management.Automation.Language; + +namespace Microsoft.PowerShell.EditorServices.Language +{ + /// + /// A way to define symbols on a higher level + /// + public enum SymbolType + { + /// + /// The symbol type is unknown + /// + Unknown = 0, + + /// + /// The symbol is a vairable + /// + Variable, + + /// + /// The symbol is a function + /// + Function, + + /// + /// The symbol is a parameter + /// + Parameter + } + + /// + /// A class that holds the type, name, script extent, and source line of a symbol + /// + public class SymbolReference + { + #region Properties + /// + /// Gets the symbol's type + /// + public SymbolType SymbolType { get; private set; } + + /// + /// Gets the name of the symbol + /// + public string SymbolName { get; private set; } + + /// + /// Gets the script extent of the symbol + /// + public ScriptRegion ScriptRegion { get; private set; } + + /// + /// Gets the contents of the line the given symbol is on + /// + public string SourceLine { get; internal set; } + #endregion + + /// + /// Constructs and instance of a SymbolReference + /// + /// The higher level type of the symbol + /// The script extent of the symbol + /// The line contents of the given symbol (defaults to empty string) + public SymbolReference(SymbolType symbolType, IScriptExtent scriptExtent, string sourceLine = "") + { + // TODO: Verify params + this.SymbolType = symbolType; + this.SymbolName = scriptExtent.Text; + this.ScriptRegion = ScriptRegion.Create(scriptExtent); + this.SourceLine = sourceLine; + + // TODO: Make sure end column number usage is correct + } + } +} diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index e3a2af44f..7771791c2 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -70,8 +70,17 @@ + + + + + + + + + diff --git a/submodules/PSScriptAnalyzer b/submodules/PSScriptAnalyzer index 96652e225..297140f0d 160000 --- a/submodules/PSScriptAnalyzer +++ b/submodules/PSScriptAnalyzer @@ -1 +1 @@ -Subproject commit 96652e2259048336a68d93db72a10a10165bd39d +Subproject commit 297140f0d3f1205da010073cd2ad8ab8cd43b665 diff --git a/test/PowerShellEditorServices.Test.Host/ScenarioTests.cs b/test/PowerShellEditorServices.Test.Host/ScenarioTests.cs index e722ee159..976cf7d22 100644 --- a/test/PowerShellEditorServices.Test.Host/ScenarioTests.cs +++ b/test/PowerShellEditorServices.Test.Host/ScenarioTests.cs @@ -3,11 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Transport.Stdio; using Microsoft.PowerShell.EditorServices.Transport.Stdio.Event; using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message; using Microsoft.PowerShell.EditorServices.Transport.Stdio.Request; using Microsoft.PowerShell.EditorServices.Transport.Stdio.Response; using System; +using System.Collections.Generic; using Xunit; namespace Microsoft.PowerShell.EditorServices.Test.Host @@ -81,6 +84,291 @@ public void ServiceCompletesFunctionName() // TODO: Add more asserts } + [Fact] + public void CompletesDetailOnVariableSuggestion() + { + this.SendOpenFileRequest("TestFiles\\CompleteFunctionName.ps1"); + this.MessageWriter.WriteMessage( + new CompletionsRequest + { + Arguments = new CompletionsRequestArgs + { + File = "TestFiles\\CompleteFunctionName.ps1", + Line = 4, + Offset = 6, + Prefix = "" + } + }); + CompletionsResponse completion = this.WaitForMessage(); + List entryName = new List(); + entryName.Add("$ConsoleFileName"); + this.MessageWriter.WriteMessage( + new CompletionDetailsRequest + { + Arguments = new CompletionDetailsRequestArgs + { + File = "TestFiles\\CompleteFunctionName.ps1", + Line = 4, + Offset = 6, + EntryNames = entryName.ToArray() + } + }); + CompletionDetailsResponse completionDetail = this.WaitForMessage(); + Assert.NotNull(completionDetail.Body[0]); + Assert.Equal("string", completionDetail.Body[0].Name); + } + + [Fact] + public void CompletesDetailOnVariableDocSuggestion() + { + this.SendOpenFileRequest("TestFiles\\CompleteFunctionName.ps1"); + this.MessageWriter.WriteMessage( + new CompletionsRequest + { + Arguments = new CompletionsRequestArgs + { + File = "TestFiles\\CompleteFunctionName.ps1", + Line = 7, + Offset = 5, + Prefix = "" + } + }); + CompletionsResponse completion = this.WaitForMessage(); + List entryName = new List(); + entryName.Add("$HKCU:"); + this.MessageWriter.WriteMessage( + new CompletionDetailsRequest + { + Arguments = new CompletionDetailsRequestArgs + { + File = "TestFiles\\CompleteFunctionName.ps1", + Line = 7, + Offset = 5, + EntryNames = entryName.ToArray() + } + }); + CompletionDetailsResponse completionDetail = this.WaitForMessage(); + Assert.NotNull(completionDetail.Body[0]); + Assert.Equal("The software settings for the current user", completionDetail.Body[0].DocString); + } + + [Fact] + public void CompletesDetailOnCommandSuggestion() + { + this.SendOpenFileRequest("TestFiles\\CompleteFunctionName.ps1"); + this.MessageWriter.WriteMessage( + new CompletionsRequest + { + Arguments = new CompletionsRequestArgs + { + File = "TestFiles\\CompleteFunctionName.ps1", + Line = 6, + Offset = 9, + Prefix = "" + } + }); + CompletionsResponse completion = this.WaitForMessage(); + List entryName = new List(); + entryName.Add("Get-Process"); + this.MessageWriter.WriteMessage( + new CompletionDetailsRequest + { + Arguments = new CompletionDetailsRequestArgs + { + File = "TestFiles\\CompleteFunctionName.ps1", + Line = 6, + Offset = 9, + EntryNames = entryName.ToArray() + } + }); + CompletionDetailsResponse completionDetail = this.WaitForMessage(); + Assert.Null(completionDetail.Body[0].Name); + } + + [Fact] + public void FindsReferencesOfVariable() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new ReferencesRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 8, + Offset = 5, + } + }); + + ReferencesResponse references = this.WaitForMessage(); + Assert.NotNull(references); + Assert.Equal(references.Body.Refs.Length, 3); + Assert.Equal(references.Body.Refs[0].LineText, "$things = 4"); + } + + [Fact] + public void FindsNoReferencesOfEmptyLine() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new ReferencesRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 9, + Offset = 1, + } + }); + + ReferencesResponse references = this.WaitForMessage(); + Assert.Null(references.Body); + } + + [Fact] + public void FindsReferencesOnFunctionDefinition() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new ReferencesRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 1, + Offset = 18, + } + }); + + ReferencesResponse references = this.WaitForMessage(); + Assert.NotNull(references); + Assert.Equal(references.Body.Refs.Length, 3); + Assert.Equal(references.Body.SymbolName, "My-Function"); + } + + [Fact] + public void FindsReferencesOnCommand() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new ReferencesRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 10, + Offset = 2, + } + }); + + ReferencesResponse references = this.WaitForMessage(); + Assert.NotNull(references); + Assert.Equal(references.Body.Refs.Length, 3); + Assert.Equal(references.Body.SymbolName, "My-Function"); + } + + [Fact] + public void FindsDefinitionOfCommand() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new DeclarationRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 3, + Offset = 12, + } + }); + DefinitionResponse definition = this.WaitForMessage(); + Assert.NotNull(definition); + Assert.Equal(1, definition.Body[0].Start.Line); + Assert.Equal(10, definition.Body[0].Start.Offset); + } + + [Fact] + public void FindsNoDefinitionOfBuiltinCommand() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new DeclarationRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 12, + Offset = 10, + } + }); + DefinitionResponse definition = this.WaitForMessage(); + Assert.Null(definition.Body); + } + + [Fact] + public void FindsDefintionOfVariable() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new DeclarationRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 10, + Offset = 14, + } + }); + DefinitionResponse definition = this.WaitForMessage(); + Assert.NotNull(definition); + Assert.Equal(6, definition.Body[0].Start.Line); + Assert.Equal(1, definition.Body[0].Start.Offset); + Assert.Equal(6, definition.Body[0].End.Line); + Assert.Equal(8, definition.Body[0].End.Offset); + } + + [Fact] + public void FindsOccurencesOnFunctionDefinition() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new OccurrencesRequest + { + Arguments = new FileLocationRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 1, + Offset = 18, + } + }); + + OccurrencesResponse occurences = this.WaitForMessage(); + Assert.NotNull(occurences); + Assert.Equal(occurences.Body.Length, 3); + Assert.Equal(occurences.Body[1].Start.Line, 3); + } + + [Fact] + public void GetsParameterHintsOnCommand() + { + this.SendOpenFileRequest("TestFiles\\FindReferences.ps1"); + this.MessageWriter.WriteMessage( + new SignatureHelpRequest + { + Arguments = new SignatureHelpRequestArgs + { + File = "TestFiles\\FindReferences.ps1", + Line = 14, + Offset = 15, + } + }); + + SignatureHelpResponse sigHelp = this.WaitForMessage(); + Assert.NotNull(sigHelp); + Assert.Equal(sigHelp.Body.CommandName, "Write-Output"); + Assert.Equal(sigHelp.Body.ArgumentCount, 1); + } + [Fact] public void ServiceExecutesReplCommandAndReceivesOutput() { diff --git a/test/PowerShellEditorServices.Test.Host/TestFiles/CompleteFunctionName.ps1 b/test/PowerShellEditorServices.Test.Host/TestFiles/CompleteFunctionName.ps1 index d7c7e7c8f..a049b2845 100644 --- a/test/PowerShellEditorServices.Test.Host/TestFiles/CompleteFunctionName.ps1 +++ b/test/PowerShellEditorServices.Test.Host/TestFiles/CompleteFunctionName.ps1 @@ -1,5 +1,7 @@ function My-Function { } - -My- \ No newline at end of file +$Cons +My- +Get-Proc +$HKC \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Host/TestFiles/FindReferences.ps1 b/test/PowerShellEditorServices.Test.Host/TestFiles/FindReferences.ps1 index 0468c9f27..9373c10f4 100644 --- a/test/PowerShellEditorServices.Test.Host/TestFiles/FindReferences.ps1 +++ b/test/PowerShellEditorServices.Test.Host/TestFiles/FindReferences.ps1 @@ -8,3 +8,7 @@ $things = 4 $things My-Function $things + +Write-Output "Hi"; + +Write-Output "" \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs new file mode 100644 index 000000000..877145fae --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsFunctionDefinition.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared.Definition +{ + public class FindsFunctionDefinition + { + public static readonly ScriptRegion SourceDetails = + new ScriptRegion + { + File = @"References\SimpleFile.ps1", + StartLineNumber = 3, + StartColumnNumber = 12 + }; + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs b/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs new file mode 100644 index 000000000..de337e6b7 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Definition/FindsVariableDefinition.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared.Definition +{ + public class FindsVariableDefinition + { + public static readonly ScriptRegion SourceDetails = + new ScriptRegion + { + File = @"References\SimpleFile.ps1", + StartLineNumber = 8, + StartColumnNumber = 3 + }; + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs new file mode 100644 index 000000000..28bbefd52 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindOccurrencesOnParameter.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared.Occurrences +{ + public class FindOccurrencesOnParameter + { + public static readonly ScriptRegion SourceDetails = + new ScriptRegion + { + File = @"References\SimpleFile.ps1", + StartLineNumber = 1, + StartColumnNumber = 31 + }; + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs new file mode 100644 index 000000000..b5658603b --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/Occurrences/FindsOccurrencesOnFunction.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared.Occurrences +{ + public class FindsOccurrencesOnFunction + { + public static readonly ScriptRegion SourceDetails = + new ScriptRegion + { + File = @"References\SimpleFile.ps1", + StartLineNumber = 1, + StartColumnNumber = 17 + }; + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs new file mode 100644 index 000000000..24c805238 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommand.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared.ParameterHint +{ + public class FindsParameterSetsOnCommand + { + public static readonly ScriptRegion SourceDetails = + new ScriptRegion + { + File = @"ParameterHints\ParamHints.ps1", + StartLineNumber = 1, + StartColumnNumber = 14 + }; + } +} \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs new file mode 100644 index 000000000..0ebbc19aa --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/ParameterHints/FindsParameterSetsOnCommandWithSpaces.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using Microsoft.PowerShell.EditorServices.Language; +using Microsoft.PowerShell.EditorServices.Session; + +namespace Microsoft.PowerShell.EditorServices.Test.Shared.ParameterHint +{ + public class FindsParameterSetsOnCommandWithSpaces + { + public static readonly ScriptRegion SourceDetails = + new ScriptRegion + { + File = @"ParameterHints\ParamHints.ps1", + StartLineNumber = 9, + StartColumnNumber = 31 + }; + } +} diff --git a/test/PowerShellEditorServices.Test.Shared/ParameterHints/ParamHints.ps1 b/test/PowerShellEditorServices.Test.Shared/ParameterHints/ParamHints.ps1 new file mode 100644 index 000000000..b04da2374 --- /dev/null +++ b/test/PowerShellEditorServices.Test.Shared/ParameterHints/ParamHints.ps1 @@ -0,0 +1,9 @@ +Get-Process - + +function Do-Stuff($things){ + +} + +Write-Output "" + +Write-Host () \ No newline at end of file diff --git a/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj b/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj index c24baf51b..ae967bbcd 100644 --- a/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj +++ b/test/PowerShellEditorServices.Test.Shared/PowerShellEditorServices.Test.Shared.csproj @@ -42,7 +42,15 @@ + + + + + + + + @@ -54,6 +62,17 @@ PowerShellEditorServices + + + PreserveNewest + + + + + PreserveNewest + + +