├── src ├── EditorServicesCommandSuite │ ├── GlobalSuppressions.cs │ ├── Language │ │ ├── TokenFinderDirection.cs │ │ ├── TokenFilters.cs │ │ ├── CaseType.cs │ │ ├── SelectionVariableAnalysisResult.cs │ │ ├── TokenFinder.After.cs │ │ ├── TokenFinder.Before.cs │ │ ├── SimplePosition.cs │ │ ├── TokenFinder.ContainingOrAfter.cs │ │ ├── UsingDescription.cs │ │ ├── TokenFinder.Containing.cs │ │ ├── TokenFinder.ContainingOrBefore.cs │ │ ├── AssignmentVariableTargetVisitor.cs │ │ ├── TokenFinder.Where.cs │ │ ├── TokenCollection.cs │ │ ├── SimpleRange.cs │ │ ├── TokenFinder.ClosestTo.cs │ │ ├── NodeNotFoundException.cs │ │ └── UsingUtilities.cs │ ├── AssemblyInfo.cs │ ├── Utility │ │ ├── StringLiterals.cs │ │ ├── SettingsScope.cs │ │ ├── DocumentHelpers.cs │ │ ├── Validate.cs │ │ ├── ManifestInfo.cs │ │ └── CommandSuiteSettingInfo.cs │ ├── CodeGeneration │ │ ├── WorkspaceChangeType.cs │ │ ├── Refactors │ │ │ ├── ChangeStringEnclosureConfiguration.cs │ │ │ ├── SurroundSelectedLinesSettings.cs │ │ │ ├── DefaultFromSettingAttribute.cs │ │ │ ├── ExpandMemberExpressionSettings.cs │ │ │ ├── RefactorConfiguration.cs │ │ │ ├── SettingAttribute.cs │ │ │ ├── ExtractFunctionSettings.cs │ │ │ ├── RefactorConfigurationAttribute.cs │ │ │ ├── RefactorAttribute.cs │ │ │ ├── AdditionalParameterTypes.cs │ │ │ ├── RefactorKind.cs │ │ │ ├── RefactorInfo.cs │ │ │ ├── ExtractFunctionDestination.cs │ │ │ ├── Parameter.cs │ │ │ ├── DocumentContext.cs │ │ │ ├── CommandSplatRefactorSettings.cs │ │ │ ├── StringEnclosureType.cs │ │ │ ├── IRefactorInfo.cs │ │ │ ├── ExpressionSurroundType.cs │ │ │ ├── RefactorProviderInfo.cs │ │ │ ├── RefactorService.cs │ │ │ ├── IDocumentRefactorProvider.cs │ │ │ ├── NameUnnamedBlockRefactor.cs │ │ │ ├── ChangeNamedBlockKindRefactor.cs │ │ │ ├── ConfiguredDocumentContext.cs │ │ │ ├── RefactorProvider.cs │ │ │ ├── ConfigureRefactorBinder.cs │ │ │ └── InternalNavigationService.cs │ │ ├── Command.cs │ │ ├── CodeActionIds.cs │ │ └── DocumentEditWriterExtensions.cs │ ├── Reflection │ │ ├── ParameterDescription.cs │ │ ├── ReflectionExtensions.cs │ │ ├── AstMemberDescription.cs │ │ ├── ReflectedMemberDescription.cs │ │ └── ReflectionCache.cs │ ├── Inference │ │ ├── Utils.cs │ │ ├── TypeInferenceRuntimePermissions.cs │ │ ├── ReflectionFieldInfo.cs │ │ ├── ReflectionPropertyInfo.cs │ │ └── ReflectionMethodInfo.cs │ ├── EditorServicesCommandSuite.csproj │ ├── Internal │ │ ├── PSTokenRefactorAttribute.cs │ │ ├── PSSelectionRefactorAttribute.cs │ │ ├── PSAstRefactorAttribute.cs │ │ ├── IDocumentEditProcessor.cs │ │ ├── TaskCmdletAdapter.cs │ │ ├── DiagnosticMarker.cs │ │ ├── DocumentEdit.cs │ │ ├── ScriptBasedRefactorProviderAttribute.cs │ │ ├── NoCommandSuiteInstanceException.cs │ │ ├── INavigationSupportsOpenDocument.cs │ │ └── IRefactorAnalysisContext.cs │ ├── stylecop.json │ ├── ConcurrentCollection.cs │ ├── EditorServicesCommandSuite.ruleset │ └── Commands │ │ └── CommandSuiteSettingCompleter.cs ├── EditorServicesCommandSuite.EditorServices │ ├── GlobalSuppressions.cs │ ├── LspPosition.cs │ ├── EditorServicesCommandSuite.EditorServices.csproj │ ├── LspRange.cs │ ├── ContextService.cs │ ├── Internal │ │ └── NullLogger.cs │ ├── ILanguageServerServiceExtensions.cs │ ├── IEditorScriptFileExtensions.cs │ ├── EditorServicesNavigationService.cs │ └── DiagnosticsService.cs ├── EditorServicesCommandSuite.PSReadLine │ ├── InputPromptMenu.cs │ ├── EditorServicesCommandSuite.PSReadLine.csproj │ ├── NullDiagnosticService.cs │ ├── DocumentService.cs │ ├── Menus.cs │ ├── ContextService.cs │ ├── PSReadLineNavigationService.cs │ └── Internal │ │ └── CommandSuite.cs └── EditorServicesCommandSuite.Common.props ├── test └── EditorServicesCommandSuite.Tests │ ├── GlobalSuppressions.cs │ ├── EditorServicesCommandSuite.Tests.csproj │ ├── DropNamespaceTests.cs │ └── NameUnnamedBlockTests.cs ├── .vscode ├── extensions.json ├── settings.json ├── launch.json └── tasks.json ├── ESCSSettings.psd1 ├── debugHarness.ps1 ├── azure-pipelines.yml ├── docs ├── en-US │ ├── Remove-Semicolon.md │ ├── Set-UsingStatementOrder.md │ ├── Add-CommandToManifest.md │ ├── Import-CommandSuite.md │ ├── Add-ModuleQualification.md │ ├── Expand-TypeImplementation.md │ ├── New-ESCSSettingsFile.md │ ├── ConvertTo-SplatExpression.md │ ├── Expand-Expression.md │ ├── Set-RuleSuppression.md │ ├── ConvertTo-LocalizationString.md │ ├── ConvertTo-MarkdownHelp.md │ └── Add-PinvokeMethod.md └── CODE_OF_CONDUCT.md ├── tools ├── AssertRequiredModule.ps1 └── AssertPSRL.ps1 ├── ScriptAnalyzerSettings.psd1 ├── LICENSE ├── ThirdPartyNotices.txt └── .gitattributes /src/EditorServicesCommandSuite/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: SuppressMessage("Usage", "RCS1146", Justification = "Hurts readability.")] 4 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: SuppressMessage("Usage", "RCS1146", Justification = "Hurts readability.")] 4 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinderDirection.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.Language 2 | { 3 | internal enum TokenFinderDirection 4 | { 5 | None = 0, 6 | 7 | Previous = 1, 8 | 9 | Next = 2, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/EditorServicesCommandSuite.Tests/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: SuppressMessage("Design", "RCS1090")] 4 | [assembly: SuppressMessage("Readability", "RCS1192", Justification = "Used consistently to align lines in test builder.")] 5 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("EditorServicesCommandSuite.Tests")] 4 | [assembly: InternalsVisibleTo("EditorServicesCommandSuite.EditorServices")] 5 | [assembly: InternalsVisibleTo("EditorServicesCommandSuite.PSReadLine")] 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-dotnettools.csharp", 6 | "ms-vscode.powershell", 7 | "DavidAnson.vscode-markdownlint", 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Utility/StringLiterals.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.Utility 2 | { 3 | internal static class StringLiterals 4 | { 5 | public const string InternalUseOnly = "do not use this method"; 6 | 7 | public const string ScriptFileExtension = ".ps1"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFilters.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.Language 2 | { 3 | internal delegate void TokenSearchStopper(in TokenNode node, ref bool stopSearch); 4 | 5 | internal delegate bool TokenFilter(in TokenNode node); 6 | 7 | internal delegate bool TokenFilter(in TokenNode node, TState state); 8 | } 9 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/WorkspaceChangeType.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration 2 | { 3 | internal enum WorkspaceChangeType 4 | { 5 | Delete, 6 | 7 | New, 8 | 9 | Rename, 10 | 11 | Move, 12 | 13 | Edit, 14 | 15 | Context, 16 | 17 | Command, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ChangeStringEnclosureConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | internal class ChangeStringEnclosureConfiguration : RefactorConfiguration 6 | { 7 | [Parameter] 8 | public StringEnclosureType Type { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Command.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration 2 | { 3 | internal sealed class Command 4 | { 5 | public Command(string name, object arguments = null) 6 | { 7 | Name = name; 8 | Arguments = arguments; 9 | } 10 | 11 | public string Name { get; } 12 | 13 | public object Arguments { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/SurroundSelectedLinesSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | internal class SurroundSelectedLinesSettings : RefactorConfiguration 6 | { 7 | [Parameter(Position = 0)] 8 | [ValidateNotNull] 9 | public ExpressionSurroundType SurroundType { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/DefaultFromSettingAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 6 | internal class DefaultFromSettingAttribute : SettingAttribute 7 | { 8 | internal DefaultFromSettingAttribute(string key) 9 | : base(key) 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/InputPromptMenu.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.PSReadLine 2 | { 3 | internal class InputPromptMenu : ConsoleBufferMenu 4 | { 5 | internal InputPromptMenu(string caption, string message) 6 | : base(caption, message) 7 | { 8 | } 9 | 10 | protected override string GetResult() 11 | { 12 | return _input.ToString(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ExpandMemberExpressionSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | internal class ExpandMemberExpressionSettings : RefactorConfiguration 6 | { 7 | [Parameter] 8 | [DefaultFromSetting("ExpandMemberExpression.AllowNonPublicMembers", Default = "$false")] 9 | public SwitchParameter AllowNonPublicMembers { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/LspPosition.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.PowerShell.EditorServices.Extensions; 2 | 3 | namespace EditorServicesCommandSuite.EditorServices 4 | { 5 | internal class LspPosition : ILspFilePosition 6 | { 7 | public LspPosition(int line, int character) 8 | { 9 | Line = line; 10 | Character = character; 11 | } 12 | 13 | public int Line { get; } 14 | 15 | public int Character { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 5 | { 6 | internal class RefactorConfiguration 7 | { 8 | internal RefactorConfiguration() 9 | { 10 | CallSite> callSite = ConfigureRefactorBinder.Get(this); 11 | callSite.Target(callSite, this); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/SettingAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 6 | internal class SettingAttribute : Attribute 7 | { 8 | internal SettingAttribute(string key) 9 | { 10 | Key = key; 11 | } 12 | 13 | public string Key { get; } 14 | 15 | public string Default { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Reflection/ParameterDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace EditorServicesCommandSuite.Reflection 4 | { 5 | internal class ParameterDescription 6 | { 7 | internal ParameterDescription(string name, PSTypeName parameterType) 8 | { 9 | Name = name; 10 | ParameterType = parameterType; 11 | } 12 | 13 | public string Name { get; } 14 | 15 | public PSTypeName ParameterType { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ExtractFunctionSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | internal class ExtractFunctionSettings : RefactorConfiguration 6 | { 7 | [Parameter] 8 | public string FunctionName { get; set; } 9 | 10 | [Parameter] 11 | public ExtractFunctionDestination Type { get; set; } 12 | 13 | [Parameter] 14 | public string FilePath { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorConfigurationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | internal class RefactorConfigurationAttribute : Attribute 7 | { 8 | internal RefactorConfigurationAttribute(Type configurationType) 9 | { 10 | ConfigurationType = configurationType; 11 | } 12 | 13 | internal Type ConfigurationType { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ESCSSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # The relative path from the current workspace to the root directory of the module. 3 | MainModuleDirectory = '.\module' 4 | 5 | # The relative path from the current workspace to the main module manifest file. 6 | SourceManifestPath = '.\module\*.psd1' 7 | 8 | # The relative path from the current workspace to the string localization psd1 file. 9 | StringLocalizationManifest = '.\module\en-US\Strings.psd1' 10 | 11 | # The relative path from the current workspace to the directory where markdown files are stored. 12 | MarkdownDocsPath = '.\docs' 13 | } 14 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | internal class RefactorAttribute : Attribute 7 | { 8 | internal RefactorAttribute(string verb, string noun) 9 | { 10 | Verb = verb; 11 | Noun = noun; 12 | } 13 | 14 | public string Verb { get; } 15 | 16 | public string Noun { get; } 17 | 18 | public Type Parameters { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Inference/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using System.Management.Automation.Internal; 3 | 4 | namespace EditorServicesCommandSuite.Inference 5 | { 6 | internal static class Utils 7 | { 8 | internal static object Base(object obj) 9 | { 10 | if (!(obj is PSObject pso)) 11 | { 12 | return obj; 13 | } 14 | 15 | if (pso == AutomationNull.Value) 16 | { 17 | return null; 18 | } 19 | 20 | return pso.BaseObject; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/EditorServicesCommandSuite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/EditorServicesCommandSuite.PSReadLine.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ..\..\lib\PSReadLine\Microsoft.PowerShell.PSReadLine2.dll 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/EditorServicesCommandSuite.EditorServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ..\..\lib\PowerShellEditorServices\bin\Microsoft.PowerShell.EditorServices.dll 8 | false 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/PSTokenRefactorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// Represents a function based refactor provider that targets PowerShell token. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | public sealed class PSTokenRefactorAttribute : ScriptBasedRefactorProviderAttribute 13 | { 14 | internal override RefactorKind Kind { get; } = RefactorKind.Token; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/PSSelectionRefactorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// Represents a function based refactor provider that targets a script extent. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | public sealed class PSSelectionRefactorAttribute : ScriptBasedRefactorProviderAttribute 13 | { 14 | internal override RefactorKind Kind { get; } = RefactorKind.Selection; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | //-------- Files configuration -------- 3 | 4 | // When enabled, will trim trailing whitespace when you save a file. 5 | "files.trimTrailingWhitespace": true, 6 | 7 | // When enabled, insert a final new line at the end of the file when saving it. 8 | "files.insertFinalNewline": true, 9 | 10 | "search.exclude": { 11 | "Release": true 12 | }, 13 | 14 | //-------- PowerShell Configuration -------- 15 | 16 | // Use a custom PowerShell Script Analyzer settings file for this workspace. 17 | // Relative paths for this setting are always relative to the workspace root dir. 18 | "powershell.scriptAnalysis.settingsPath": "ScriptAnalyzerSettings.psd1", 19 | } 20 | -------------------------------------------------------------------------------- /debugHarness.ps1: -------------------------------------------------------------------------------- 1 | # Use this file to debug the module. 2 | Import-Module -Name $PSScriptRoot\module\EditorServicesCommandSuite.psd1 -Force 3 | 4 | # Uncomment this to break on exceptions. 5 | #Set-PSBreakpoint -Variable StackTrace -Mode Write 6 | 7 | # Uncomment and change these to prep editor context. 8 | #$psEditor.Workspace.OpenFile((Join-Path $psEditor.Workspace.Path 'module\Public\Get-Token.ps1')) 9 | #$psEditor.Workspace.OpenFile($MyInvocation.MyCommand.Path) 10 | #$psEditor.GetEditorContext().SetSelection(17, 12, 17, 12) 11 | #Start-Sleep -Milliseconds 50 12 | 13 | # Place editor command to debug below (before the return). 14 | 15 | return 16 | 17 | 18 | # To debug against a specific AST, place the expression here (under the return) and use the commands 19 | # above to prepare context and invoke the test command. 20 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/CaseType.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.Language 2 | { 3 | /// 4 | /// Represents a style of capitalization for identifiers. 5 | /// 6 | public enum CaseType 7 | { 8 | /// 9 | /// Case style will default based on the action taken. 10 | /// 11 | Default = 0, 12 | 13 | /// 14 | /// The first letter of each word in the identifier will be capitalized. 15 | /// 16 | PascalCase = 1, 17 | 18 | /// 19 | /// The first letter of each word in the identifier will be capitalized, 20 | /// except for the first word which will be all lowercase. 21 | /// 22 | CamelCase = 2, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | strategy: 2 | matrix: 3 | linux: 4 | imageName: 'ubuntu-16.04' 5 | mac: 6 | imageName: 'macos-10.13' 7 | windows: 8 | imageName: 'vs2017-win2016' 9 | 10 | trigger: 11 | - 0.5.0 12 | 13 | pool: 14 | vmImage: $(imageName) 15 | 16 | steps: 17 | - task: PowerShell@2 18 | displayName: 'Build Script' 19 | inputs: 20 | targetType: filePath 21 | filePath: build.ps1 22 | arguments: '-Force' 23 | 24 | - task: PublishPipelineArtifact@1 25 | displayName: 'Publish Pipeline Artifact' 26 | inputs: 27 | artifactName: EditorServicesCommandSuite-$(imageName) 28 | targetPath: 'Release' 29 | 30 | - task: PublishTestResults@2 31 | displayName: 'Publish Test Results' 32 | inputs: 33 | testResultsFormat: VSTest 34 | testResultsFiles: 'TestResults/results.trx' 35 | 36 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Inference/TypeInferenceRuntimePermissions.cs: -------------------------------------------------------------------------------- 1 | // This file is based on a file from https://github.com/PowerShell/PowerShell, and 2 | // edited to use public API's where possible. While not generated, the comment below 3 | // is included to exclude it from StyleCop analysis. 4 | // 5 | 6 | namespace EditorServicesCommandSuite.Inference 7 | { 8 | /// 9 | /// Enum describing permissions to use runtime evaluation during type inference. 10 | /// 11 | internal enum TypeInferenceRuntimePermissions 12 | { 13 | /// 14 | /// No runtime use is allowed. 15 | /// 16 | None = 0, 17 | 18 | /// 19 | /// Use of SafeExprEvaluator visitor is allowed. 20 | /// 21 | AllowSafeEval = 1, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/AdditionalParameterTypes.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 2 | { 3 | /// 4 | /// Represents what additional parameter types should be included. 5 | /// 6 | public enum AdditionalParameterTypes 7 | { 8 | /// 9 | /// Indicates that only bound parameters should be added to the splat expression. 10 | /// 11 | None = 0, 12 | 13 | /// 14 | /// Indicates that unbound mandatory parameters should be included in the splat expression. 15 | /// 16 | Mandatory = 1, 17 | 18 | /// 19 | /// Indicates that all resolved parameters should be included in the splat expression. 20 | /// 21 | All = 2, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Utility/SettingsScope.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.Utility 2 | { 3 | /// 4 | /// Represents a scope in which CommandSuite settings are stored. 5 | /// 6 | public enum SettingsScope 7 | { 8 | /// 9 | /// Scope for settings local to the current process. 10 | /// 11 | Process, 12 | 13 | /// 14 | /// Scope for settings local to the current workspace. 15 | /// 16 | Workspace, 17 | 18 | /// 19 | /// Scope for settings local to the current user. 20 | /// 21 | User, 22 | 23 | /// 24 | /// Scope for settings that apply to all users on the current machine. 25 | /// 26 | Machine, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/en-US/Remove-Semicolon.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: 4 | schema: 2.0.0 5 | --- 6 | 7 | # Remove-Semicolon 8 | 9 | ## SYNOPSIS 10 | 11 | Remove useless semicolons from the current file. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Remove-Semicolon 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Remove-Semicolon function will delete any semicolon in the current file that is not followed by a new line or is within a class property definition. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | Remove-Semicolon 29 | ``` 30 | 31 | Removes all useless semicolons from the current file. 32 | 33 | ## PARAMETERS 34 | 35 | ## INPUTS 36 | 37 | ### None 38 | 39 | ## OUTPUTS 40 | 41 | ### None 42 | 43 | ## NOTES 44 | 45 | ## RELATED LINKS 46 | 47 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorKind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 4 | { 5 | /// 6 | /// Represents the type of language element that a refactor can target. 7 | /// 8 | [Flags] 9 | public enum RefactorKind 10 | { 11 | /// 12 | /// Indicates that the refactor provider can target AST nodes. 13 | /// 14 | Ast = 1, 15 | 16 | /// 17 | /// Indicates that the refactor provider can target PowerShell language tokens. 18 | /// 19 | Token = 2, 20 | 21 | /// 22 | /// Indicates that the refactor provider can target the text selected within the 23 | /// host editor. 24 | /// 25 | Selection = 4, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/SelectionVariableAnalysisResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | 5 | namespace EditorServicesCommandSuite.Language 6 | { 7 | internal class SelectionVariableAnalysisResult 8 | { 9 | public SelectionVariableAnalysisResult( 10 | VariableExpressionAst firstOccurrence, 11 | PSTypeName inferredType) 12 | { 13 | VariableName = firstOccurrence.VariablePath.UserPath; 14 | InferredType = inferredType; 15 | Occurrences.Add(firstOccurrence); 16 | } 17 | 18 | public string VariableName { get; } 19 | 20 | public List Occurrences { get; } = new List(); 21 | 22 | public PSTypeName InferredType { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/EditorServicesCommandSuite.Tests/EditorServicesCommandSuite.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | preview 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/PSAstRefactorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// Represents a function based refactor provider that targets an AST. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | public sealed class PSAstRefactorAttribute : ScriptBasedRefactorProviderAttribute 13 | { 14 | /// 15 | /// Gets or sets the AST types that the function can accept. 16 | /// 17 | [EditorBrowsable(EditorBrowsableState.Never)] 18 | public Type[] Targets { get; set; } = Type.EmptyTypes; 19 | 20 | internal override RefactorKind Kind { get; } = RefactorKind.Ast; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using EditorServicesCommandSuite.Internal; 5 | 6 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 7 | { 8 | internal class RefactorInfo 9 | where TTarget : class 10 | { 11 | internal RefactorInfo(IDocumentRefactorProvider provider, DocumentContextBase request, TTarget target) 12 | { 13 | Request = request; 14 | Provider = provider; 15 | Target = target; 16 | } 17 | 18 | public IDocumentRefactorProvider Provider { get; } 19 | 20 | public string Name => Provider.Name; 21 | 22 | public string Description => Provider.Description; 23 | 24 | internal DocumentContextBase Request { get; } 25 | 26 | internal TTarget Target { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ExtractFunctionDestination.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 2 | { 3 | /// 4 | /// Represents the placement of a new generated function. 5 | /// 6 | public enum ExtractFunctionDestination 7 | { 8 | /// 9 | /// Indicates that the caller should prompt for a destination. 10 | /// 11 | Prompt, 12 | 13 | /// 14 | /// Indicates that the function should be placed above the extracted text. 15 | /// 16 | Inline, 17 | 18 | /// 19 | /// Indicates that the function should be placed in the closest Begin block. 20 | /// 21 | Begin, 22 | 23 | /// 24 | /// Indicates that the function should be placed in a new file. 25 | /// 26 | NewFile, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/Parameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | 5 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 6 | { 7 | internal class Parameter 8 | { 9 | internal Parameter( 10 | string name, 11 | ParameterBindingResult value) 12 | { 13 | Name = name; 14 | Value = value; 15 | } 16 | 17 | internal Parameter(ParameterMetadata metadata, ParameterSetMetadata setMetadata) 18 | { 19 | Name = metadata.Name; 20 | IsMandatory = setMetadata.IsMandatory; 21 | Type = metadata.ParameterType; 22 | } 23 | 24 | public string Name { get; } 25 | 26 | public ParameterBindingResult Value { get; } 27 | 28 | public bool IsMandatory { get; } 29 | 30 | public Type Type { get; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/en-US/Set-UsingStatementOrder.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Set-UsingStatementOrder.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Set-UsingStatementOrder 8 | 9 | ## SYNOPSIS 10 | 11 | Sort using statements in the current file. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Set-UsingStatementOrder 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Set-UsingStatementOrder function will sort using statements by type (e.g. Assembly \> Module \> Namespace) and then alphabetically. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | Set-UsingStatementOrder 29 | ``` 30 | 31 | Sort using statements in the current file. 32 | 33 | ## PARAMETERS 34 | 35 | ## INPUTS 36 | 37 | ### None 38 | 39 | ## OUTPUTS 40 | 41 | ### None 42 | 43 | ## NOTES 44 | 45 | ## RELATED LINKS 46 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "indentation": { 5 | "indentationSize": 4, 6 | "tabSize": 4, 7 | "useTabs": false 8 | }, 9 | "orderingRules": { 10 | "elementOrder": [ 11 | "kind", 12 | "accessibility", 13 | "constant", 14 | "static", 15 | "readonly" 16 | ], 17 | "systemUsingDirectivesFirst": true, 18 | "usingDirectivesPlacement": "outsideNamespace", 19 | "blankLinesBetweenUsingGroups": "allow" 20 | }, 21 | "layoutRules": { 22 | "newlineAtEndOfFile": "require" 23 | }, 24 | "documentationRules": { 25 | "xmlHeader": false, 26 | "documentationCulture": "en-US", 27 | "documentExposedElements": true, 28 | "documentInternalElements": false 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/IDocumentEditProcessor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// Provides the ability to process objects. 9 | /// 10 | internal interface IDocumentEditProcessor 11 | { 12 | /// 13 | /// Apply document edits to the current document. 14 | /// 15 | /// The edits to apply. 16 | /// 17 | /// The cancellation token that will be checked prior to completing the returned task. 18 | /// 19 | /// 20 | /// A object representing the asynchronus operation. 21 | /// 22 | Task WriteDocumentEditsAsync(IEnumerable edits, CancellationToken cancellationToken); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.After.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | 3 | namespace EditorServicesCommandSuite.Language 4 | { 5 | internal partial class TokenFinder 6 | { 7 | public static readonly TokenFilter AfterFilter 8 | = (in TokenNode node, SimplePosition position) 9 | => node.Value?.Extent.StartOffset > position.Offset; 10 | 11 | public TokenFinder After(IScriptPosition position) => After(new SimplePosition(position)); 12 | 13 | public TokenFinder After(SimplePosition position) 14 | { 15 | return Where(AfterFilter, position); 16 | } 17 | 18 | public TokenFinder AfterStartOf(IScriptExtent extent) => After(extent.StartOffset); 19 | 20 | public TokenFinder AfterStartOf(SimpleRange range) => After(range.Start); 21 | 22 | public TokenFinder AfterEndOf(IScriptExtent extent) => After(extent.EndOffset); 23 | 24 | public TokenFinder AfterEndOf(SimpleRange range) => After(range.End); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.Before.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | 3 | namespace EditorServicesCommandSuite.Language 4 | { 5 | internal partial class TokenFinder 6 | { 7 | public static readonly TokenFilter BeforeFilter 8 | = (in TokenNode node, SimplePosition position) 9 | => node.Value?.Extent.EndOffset < position.Offset; 10 | 11 | public TokenFinder Before(IScriptPosition position) => Before(new SimplePosition(position)); 12 | 13 | public TokenFinder Before(SimplePosition position) 14 | { 15 | return Where(BeforeFilter, position); 16 | } 17 | 18 | public TokenFinder BeforeStartOf(IScriptExtent extent) => Before(extent.StartOffset); 19 | 20 | public TokenFinder BeforeStartOf(SimpleRange range) => Before(range.Start); 21 | 22 | public TokenFinder BeforeEndOf(IScriptExtent extent) => Before(extent.EndOffset); 23 | 24 | public TokenFinder BeforeEndOf(SimpleRange range) => Before(range.End); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/NullDiagnosticService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using EditorServicesCommandSuite.Internal; 6 | using EditorServicesCommandSuite.Utility; 7 | 8 | namespace EditorServicesCommandSuite.PSReadLine 9 | { 10 | internal class NullDiagnosticService : IRefactorAnalysisContext 11 | { 12 | public Task> GetDiagnosticsFromPathAsync( 13 | string path, 14 | ThreadController pipelineThread, 15 | CancellationToken cancellationToken) 16 | { 17 | return Task.FromResult(Enumerable.Empty()); 18 | } 19 | 20 | public Task> GetDiagnosticsFromContentsAsync( 21 | string contents, 22 | ThreadController pipelineThread, 23 | CancellationToken cancellationToken) 24 | { 25 | return Task.FromResult(Enumerable.Empty()); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/en-US/Add-CommandToManifest.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/Add-CommandToManifest.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Add-CommandToManifest 8 | 9 | ## SYNOPSIS 10 | 11 | Add a function to the workspace module manifest. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Add-CommandToManifest [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Add-CommandToManifest function finds the closest function definition in the current file and uses it to update manifest fields. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | Add-CommandToManifest 29 | ``` 30 | 31 | Adds the closest function to the workspace module manifest. 32 | 33 | ## PARAMETERS 34 | 35 | ## INPUTS 36 | 37 | ### None 38 | 39 | This function does not accept input from the pipeline. 40 | 41 | ## OUTPUTS 42 | 43 | ### None 44 | 45 | This function does not return objects to the pipeline. 46 | 47 | ## NOTES 48 | 49 | ## RELATED LINKS 50 | 51 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/DocumentContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Threading; 5 | using EditorServicesCommandSuite.Internal; 6 | using EditorServicesCommandSuite.Language; 7 | using EditorServicesCommandSuite.Utility; 8 | 9 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 10 | { 11 | internal class DocumentContext : DocumentContextBase 12 | { 13 | internal DocumentContext( 14 | ScriptBlockAst rootAst, 15 | Ast currentAst, 16 | TokenNode currentToken, 17 | IScriptExtent selectionExtent, 18 | PSCmdlet cmdlet, 19 | CancellationToken cancellationToken, 20 | ThreadController threadController) 21 | : base(rootAst, currentAst, currentToken, selectionExtent, cmdlet, cancellationToken, threadController) 22 | { 23 | } 24 | 25 | internal override TConfiguration GetConfiguration() 26 | { 27 | return new TConfiguration(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/SimplePosition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation.Language; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal readonly struct SimplePosition : IEquatable 7 | { 8 | public readonly int Offset; 9 | 10 | public SimplePosition(int offset) => Offset = offset; 11 | 12 | public SimplePosition(IScriptPosition position) => Offset = position.Offset; 13 | 14 | public static implicit operator SimplePosition(int offset) => new SimplePosition(offset); 15 | 16 | public static implicit operator int(SimplePosition position) => position.Offset; 17 | 18 | public bool Equals(SimplePosition other) => other.Offset == Offset; 19 | 20 | public override bool Equals(object obj) => obj is SimplePosition position && Equals(position); 21 | 22 | public override int GetHashCode() => Offset.GetHashCode(); 23 | 24 | public override string ToString() => string.Format( 25 | System.Globalization.CultureInfo.CurrentCulture, 26 | LanguageStrings.SimplePositionFormat, 27 | Offset); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/LspRange.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Microsoft.PowerShell.EditorServices.Extensions; 3 | 4 | namespace EditorServicesCommandSuite.EditorServices 5 | { 6 | internal class LspRange : ILspFileRange 7 | { 8 | public LspRange(ILspFilePosition start, ILspFilePosition end) 9 | { 10 | Start = start; 11 | End = end; 12 | } 13 | 14 | public ILspFilePosition Start { get; } 15 | 16 | public ILspFilePosition End { get; } 17 | 18 | [EditorBrowsable(EditorBrowsableState.Advanced)] 19 | public void Deconstruct(out ILspFilePosition start, out ILspFilePosition end) 20 | { 21 | start = Start; 22 | end = End; 23 | } 24 | 25 | [EditorBrowsable(EditorBrowsableState.Advanced)] 26 | public void Deconstruct( 27 | out long startLine, 28 | out long startCharacter, 29 | out long endLine, 30 | out long endCharacter) 31 | { 32 | startLine = Start.Line; 33 | startCharacter = Start.Character; 34 | endLine = End.Line; 35 | endCharacter = End.Character; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Inference/ReflectionFieldInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | 4 | namespace EditorServicesCommandSuite.Inference 5 | { 6 | internal class ReflectionFieldInfo : PSPropertyInfo 7 | { 8 | private readonly System.Reflection.FieldInfo _field; 9 | 10 | internal ReflectionFieldInfo(System.Reflection.FieldInfo field) 11 | { 12 | _field = field; 13 | SetMemberName(field.Name); 14 | } 15 | 16 | public override bool IsGettable { get; } = true; 17 | 18 | public override bool IsSettable => !(_field.IsLiteral || _field.IsInitOnly); 19 | 20 | public override PSMemberTypes MemberType => PSMemberTypes.Property; 21 | 22 | public override string TypeNameOfValue => _field.FieldType.FullName; 23 | 24 | public override object Value 25 | { 26 | get => throw new System.NotSupportedException(); 27 | set => throw new System.NotSupportedException(); 28 | } 29 | 30 | internal Type ReturnType => _field.FieldType; 31 | 32 | public override PSMemberInfo Copy() 33 | { 34 | return new ReflectionFieldInfo(_field); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.ContainingOrAfter.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | 3 | namespace EditorServicesCommandSuite.Language 4 | { 5 | internal partial class TokenFinder 6 | { 7 | public static readonly TokenFilter ContainingOrAfterFilter 8 | = (in TokenNode node, SimplePosition position) 9 | => AfterFilter(in node, position) || ContainingFilter(in node, position); 10 | 11 | public TokenFinder ContainingOrAfter(IScriptPosition position) => ContainingOrAfter(new SimplePosition(position)); 12 | 13 | public TokenFinder ContainingOrAfter(SimplePosition position) 14 | { 15 | return Where(ContainingOrAfterFilter, position); 16 | } 17 | 18 | public TokenFinder ContainingOrAfterStartOf(IScriptExtent extent) => ContainingOrAfter(extent.StartOffset); 19 | 20 | public TokenFinder ContainingOrAfterStartOf(SimpleRange range) => ContainingOrAfter(range.Start); 21 | 22 | public TokenFinder ContainingOrAfterEndOf(IScriptExtent extent) => ContainingOrAfter(extent.EndOffset); 23 | 24 | public TokenFinder ContainingOrAfterEndOf(SimpleRange range) => ContainingOrAfter(range.End); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/UsingDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management.Automation.Language; 3 | using EditorServicesCommandSuite.Internal; 4 | 5 | namespace EditorServicesCommandSuite.Language 6 | { 7 | internal class UsingDescription 8 | { 9 | private static readonly Dictionary s_kindMap = 10 | new Dictionary() 11 | { 12 | { UsingStatementKind.Assembly, TokenKind.Assembly }, 13 | { UsingStatementKind.Module, TokenKind.Module }, 14 | { UsingStatementKind.Namespace, TokenKind.Namespace }, 15 | { UsingStatementKind.Command, TokenKind.Command }, 16 | { UsingStatementKind.Type, TokenKind.Type }, 17 | }; 18 | 19 | public string Text { get; set; } 20 | 21 | public UsingStatementKind Kind { get; set; } 22 | 23 | public override string ToString() 24 | { 25 | return TokenTraits.Text(TokenKind.Using) 26 | + Symbols.Space 27 | + TokenTraits.Text(s_kindMap[Kind]) 28 | + Symbols.Space 29 | + Text; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/TaskCmdletAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management.Automation; 3 | using System.Threading; 4 | using EditorServicesCommandSuite.Utility; 5 | using Microsoft.PowerShell.Cmdletization; 6 | 7 | namespace EditorServicesCommandSuite.Internal 8 | { 9 | internal class TaskCmdletAdapter : CmdletAdapter 10 | { 11 | private static readonly Dictionary s_emptyPrivateData = 12 | new Dictionary(); 13 | 14 | private readonly CancellationTokenSource _isStopping = new CancellationTokenSource(); 15 | 16 | public static CancellationToken GetStoppingCancellationToken(PSCmdlet cmdlet) 17 | { 18 | Validate.IsNotNull(nameof(cmdlet), cmdlet); 19 | var adapter = new TaskCmdletAdapter(); 20 | adapter.Initialize( 21 | cmdlet, 22 | "unused", 23 | "1.0.0", 24 | new System.Version(1, 0, 0, 0), 25 | s_emptyPrivateData); 26 | 27 | return adapter._isStopping.Token; 28 | } 29 | 30 | public override void StopProcessing() 31 | { 32 | _isStopping.Cancel(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/AssertRequiredModule.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [Parameter(Mandatory)] 4 | [ValidateNotNullOrEmpty()] 5 | [string] $Name, 6 | 7 | [Parameter(Mandatory)] 8 | [version] $RequiredVersion, 9 | 10 | [Parameter()] 11 | [ValidateSet('CurrentUser', 'AllUsers')] 12 | [string] $Scope = 'CurrentUser', 13 | 14 | [Parameter()] 15 | [switch] $Force 16 | ) 17 | end { 18 | if (Get-Module $Name -ErrorAction Ignore) { 19 | Remove-Module $Name -Force 20 | } 21 | 22 | $importModuleSplat = @{ 23 | RequiredVersion = $RequiredVersion 24 | Name = $Name 25 | ErrorAction = 'Stop' 26 | } 27 | 28 | # TODO: Install required versions into the tools folder 29 | try { 30 | Import-Module @importModuleSplat -Force 31 | } catch [System.IO.FileLoadException] { 32 | # Hope this is the FileList manifest bug and move on if the module 33 | # seems like it loaded. 34 | if (-not (Get-Module $Name -ErrorAction Ignore)) { 35 | throw $PSItem 36 | } 37 | } catch [System.IO.FileNotFoundException] { 38 | Install-Module @importModuleSplat -Force:$Force.IsPresent -Scope $Scope 39 | Import-Module @importModuleSplat -Force 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/DocumentService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using EditorServicesCommandSuite.Internal; 6 | using Microsoft.PowerShell; 7 | 8 | namespace EditorServicesCommandSuite.PSReadLine 9 | { 10 | internal class DocumentService : IDocumentEditProcessor 11 | { 12 | public Task WriteDocumentEditsAsync( 13 | IEnumerable edits, 14 | CancellationToken cancellationToken) 15 | { 16 | // Add the current buffer to history in case PSRL can't undo successfully. 17 | PSConsoleReadLine.GetBufferState(out string input, out _); 18 | PSConsoleReadLine.AddToHistory(input); 19 | foreach (var edit in edits.OrderByDescending(edit => edit.StartOffset)) 20 | { 21 | cancellationToken.ThrowIfCancellationRequested(); 22 | PSConsoleReadLine.Replace( 23 | (int)edit.StartOffset, 24 | (int)(edit.EndOffset - edit.StartOffset), 25 | edit.NewValue.Replace("\r", string.Empty)); 26 | } 27 | 28 | return Task.CompletedTask; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.Containing.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | using EditorServicesCommandSuite.Utility; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal partial class TokenFinder 7 | { 8 | public TokenFinder Containing(IScriptPosition position) => Containing(new SimplePosition(position)); 9 | 10 | public TokenFinder Containing(SimplePosition position) 11 | { 12 | if (_searchFailed) return this; 13 | if (TryFind(ContainingFilter, position, out TokenNode node)) 14 | { 15 | _node = node; 16 | return this; 17 | } 18 | 19 | _searchFailed = true; 20 | _reason = Error.TokenPositionNotFound(position); 21 | return this; 22 | } 23 | 24 | public TokenFinder ContainingStartOf(IScriptExtent extent) => Containing(extent.StartOffset); 25 | 26 | public TokenFinder ContainingStartOf(SimpleRange range) => Containing(range.Start); 27 | 28 | public TokenFinder ContainingEndOf(IScriptExtent extent) => Containing(extent.EndOffset); 29 | 30 | public TokenFinder ContainingEndOf(SimpleRange range) => Containing(range.End); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.ContainingOrBefore.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | 3 | namespace EditorServicesCommandSuite.Language 4 | { 5 | internal partial class TokenFinder 6 | { 7 | public static readonly TokenFilter ContainingOrBeforeFilter 8 | = (in TokenNode node, SimplePosition position) 9 | => BeforeFilter(in node, position) || ContainingFilter(in node, position); 10 | 11 | public TokenFinder ContainingOrBefore(IScriptPosition position) 12 | => ContainingOrBefore(new SimplePosition(position)); 13 | 14 | public TokenFinder ContainingOrBefore(SimplePosition position) 15 | { 16 | return Where(ContainingOrBeforeFilter, position); 17 | } 18 | 19 | public TokenFinder ContainingOrBeforeStartOf(IScriptExtent extent) => ContainingOrBefore(extent.StartOffset); 20 | 21 | public TokenFinder ContainingOrBeforeStartOf(SimpleRange range) => ContainingOrBefore(range.Start); 22 | 23 | public TokenFinder ContainingOrBeforeEndOf(IScriptExtent extent) => ContainingOrBefore(extent.EndOffset); 24 | 25 | public TokenFinder ContainingOrBeforeEndOf(SimpleRange range) => ContainingOrBefore(range.End); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/CommandSplatRefactorSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using EditorServicesCommandSuite.Language; 3 | 4 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 5 | { 6 | internal class CommandSplatRefactorSettings : RefactorConfiguration 7 | { 8 | [Parameter(Position = 0)] 9 | [DefaultFromSetting("CommandSplatRefactor.VariableName", Default = "$null")] 10 | [ValidateNotNullOrEmpty] 11 | public string VariableName { get; set; } 12 | 13 | [Parameter] 14 | [DefaultFromSetting("CommandSplatRefactor.NoNewLineAfterHashtable", Default = "$false")] 15 | public SwitchParameter NoNewLineAfterHashtable { get; set; } 16 | 17 | [Parameter] 18 | [DefaultFromSetting("CommandSplatRefactor.AdditionalParameters", Default = "\'None\'")] 19 | public AdditionalParameterTypes AdditionalParameters { get; set; } 20 | 21 | [Parameter] 22 | [DefaultFromSetting("CommandSplatRefactor.ExcludeHints", Default = "$false")] 23 | public SwitchParameter ExcludeHints { get; set; } 24 | 25 | [Parameter] 26 | [DefaultFromSetting("CommandSplatRefactor.VariableCaseType", Default = "\'CamelCase\'")] 27 | public CaseType CaseType { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/StringEnclosureType.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 2 | { 3 | /// 4 | /// Represents the type of string expression to convert to. 5 | /// 6 | public enum StringEnclosureType 7 | { 8 | /// 9 | /// Indicates that the caller should prompt for an enclosure selection. 10 | /// 11 | Prompt, 12 | 13 | /// 14 | /// Indicates that the string should not be enclosed. 15 | /// 16 | BareWord, 17 | 18 | /// 19 | /// Indicates that the string should be enclosed in single quotes. 20 | /// 21 | Literal, 22 | 23 | /// 24 | /// Indicates that the string should be enclosed in double quotes. 25 | /// 26 | Expandable, 27 | 28 | /// 29 | /// Indicates that the string should be enclosed in a single quote here-string expression. 30 | /// 31 | LiteralHereString, 32 | 33 | /// 34 | /// Indicates that the string should be enclosed in a double quote here-string expression. 35 | /// 36 | ExpandableHereString, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Inference/ReflectionPropertyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | 4 | namespace EditorServicesCommandSuite.Inference 5 | { 6 | internal class ReflectionPropertyInfo : PSPropertyInfo 7 | { 8 | private readonly System.Reflection.PropertyInfo _property; 9 | 10 | internal ReflectionPropertyInfo(System.Reflection.PropertyInfo property) 11 | { 12 | _property = property; 13 | SetMemberName(_property.Name); 14 | } 15 | 16 | public override bool IsGettable => _property.GetGetMethod()?.IsPublic ?? false; 17 | 18 | public override bool IsSettable => _property.GetSetMethod()?.IsPublic ?? false; 19 | 20 | public override PSMemberTypes MemberType => PSMemberTypes.Property; 21 | 22 | public override string TypeNameOfValue => _property.PropertyType.FullName; 23 | 24 | public override object Value 25 | { 26 | get => throw new System.NotSupportedException(); 27 | set => throw new System.NotSupportedException(); 28 | } 29 | 30 | internal Type ReturnType => _property.PropertyType; 31 | 32 | public override PSMemberInfo Copy() 33 | { 34 | return new ReflectionPropertyInfo(_property); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/en-US/Import-CommandSuite.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Import-CommandSuite.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Import-CommandSuite 8 | 9 | ## SYNOPSIS 10 | 11 | Initialize the EditorServicesCommandSuite module. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Import-CommandSuite 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Import-CommandSuite function imports the EditorServicesCommandSuite module and initalizes internal processes like setting up event handlers. You can import the module directly without using this function, but it isn't supported and may cause unexpected behavior. This function can be invoked after the module is loaded in case of accidental or auto loading. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | Import-CommandSuite 29 | ``` 30 | 31 | Imports EditorServicesCommandSuite functions, editor commands, and event handlers. 32 | 33 | ## PARAMETERS 34 | 35 | ## INPUTS 36 | 37 | ### None 38 | 39 | This function does not accept input from the pipeline. 40 | 41 | ## OUTPUTS 42 | 43 | ### None 44 | 45 | This function does not output to the pipeline. 46 | 47 | ## NOTES 48 | 49 | ## RELATED LINKS 50 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/IRefactorInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using EditorServicesCommandSuite.Internal; 4 | 5 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 6 | { 7 | /// 8 | /// Provides context specific information for a refactor request. 9 | /// 10 | internal interface IRefactorInfo 11 | { 12 | /// 13 | /// Gets the name of the refactor provider. 14 | /// 15 | string Name { get; } 16 | 17 | /// 18 | /// Gets the description of the refactor option. 19 | /// 20 | string Description { get; } 21 | 22 | /// 23 | /// Gets the refactor provider. 24 | /// 25 | IDocumentRefactorProvider Provider { get; } 26 | 27 | /// 28 | /// Requests edits from the refactor provider based the 29 | /// context contained in the refactor information. 30 | /// 31 | /// 32 | /// A object representing the asynchronus operation. The Result property 33 | /// will contain the requested edits. 34 | /// 35 | Task> GetDocumentEdits(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/DiagnosticMarker.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | 3 | namespace EditorServicesCommandSuite.Internal 4 | { 5 | /// 6 | /// Represents a diagnostic marker created by an analyzer. 7 | /// 8 | internal class DiagnosticMarker 9 | { 10 | /// 11 | /// Gets or sets the script extent flagged by the marker. 12 | /// 13 | public IScriptExtent Extent { get; set; } 14 | 15 | /// 16 | /// Gets or sets the name of the rule violated. 17 | /// 18 | public string RuleName { get; set; } 19 | 20 | /// 21 | /// Gets or sets the ID of the rule violated. 22 | /// 23 | public string RuleSuppressionId { get; set; } 24 | 25 | /// 26 | /// Gets or sets the name of the script where this marker is present. 27 | /// 28 | public string ScriptName { get; set; } 29 | 30 | /// 31 | /// Gets or sets the path of the script where this marker is present. 32 | /// 33 | public string ScriptPath { get; set; } 34 | 35 | /// 36 | /// Gets or sets the severity of the violated rule. 37 | /// 38 | public string Severity { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | # The PowerShell Script Analyzer will generate a warning 2 | # diagnostic record for this file due to a bug - 3 | # https://github.com/PowerShell/PSScriptAnalyzer/issues/472 4 | @{ 5 | # Only diagnostic records of the specified severity will be generated. 6 | # Uncomment the following line if you only want Errors and Warnings but 7 | # not Information diagnostic records. 8 | 9 | # Severity = @('Error','Warning') 10 | 11 | # Analyze **only** the following rules. Use IncludeRules when you want 12 | # to invoke only a small subset of the defualt rules. 13 | 14 | # IncludeRules = @('PSAvoidDefaultValueSwitchParameter', 15 | # 'PSMissingModuleManifestField', 16 | # 'PSReservedCmdletChar', 17 | # 'PSReservedParams', 18 | # 'PSShouldProcess', 19 | # 'PSUseApprovedVerbs', 20 | # 'PSUseDeclaredVarsMoreThanAssigments') 21 | 22 | # Do not analyze the following rules. Use ExcludeRules when you have 23 | # commented out the IncludeRules settings above and want to include all 24 | # the default rules except for those you exclude below. 25 | # Note: if a rule is in both IncludeRules and ExcludeRules, the rule 26 | # will be excluded. 27 | 28 | # ExcludeRules = @('PSAvoidUsingWriteHost') 29 | ExcludeRules = 'PSUseShouldProcessForStateChangingFunctions' 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Patrick Meinecke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | IMPORTANT NOTICE: THE SOFTWARE ALSO CONTAINS THIRD PARTY AND OTHER 24 | PROPRIETARY SOFTWARE THAT ARE GOVERNED BY SEPARATE LICENSE TERMS. BY ACCEPTING 25 | THE LICENSE TERMS ABOVE, YOU ALSO ACCEPT THE LICENSE TERMS GOVERNING THE 26 | THIRD PARTY AND OTHER SOFTWARE, WHICH ARE SET FORTH IN ThirdPartyNotices.txt 27 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.Common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | ..\EditorServicesCommandSuite\EditorServicesCommandSuite.ruleset 6 | Debug;Release;Test 7 | true 8 | preview 9 | netstandard2.0;netcoreapp3.1 10 | 1.0.0.0 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | TEST 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/CodeActionIds.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration 2 | { 3 | internal static class CodeActionIds 4 | { 5 | public const string SurroundSelectedLines = "EC10001"; 6 | 7 | public const string AddModuleQualification = "EC10002"; 8 | 9 | public const string ChangeStringEnclosure = "EC10003"; 10 | 11 | public const string CommandSplat = "EC10004"; 12 | 13 | public const string CommandSplatAllParameters = "EC10004A"; 14 | 15 | public const string CommandSplatMandatoryParameters = "EC10004B"; 16 | 17 | public const string AlterTypeExpression = "EC10005A"; 18 | 19 | public const string AlterTypeExpressionFullyQualify = "EC10005B"; 20 | 21 | public const string ExpandMemberExpression = "EC10006"; 22 | 23 | public const string ExtractFunction = "EC10007"; 24 | 25 | public const string FormatMethodArguments = "EC10008"; 26 | 27 | public const string ImplementVirtualMethods = "EC10009A"; 28 | 29 | public const string ImplementVirtualMethodsAbstractOnly = "EC10009B"; 30 | 31 | public const string NameUnnamedBlock = "EC10010"; 32 | 33 | public const string RegisterCommandExport = "EC10011"; 34 | 35 | public const string SuppressDiagnostics = "EC10012"; 36 | 37 | public const string WrapSelection = "EC10013"; 38 | 39 | public const string ChangeNamedBlockKind = "EC10014"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/DocumentEdit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using EditorServicesCommandSuite.Utility; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// Represents an edit to a document. 9 | /// 10 | internal class DocumentEdit 11 | { 12 | private static int s_lastId = 1; 13 | 14 | /// 15 | /// Gets or sets the starting offset of the edit. 16 | /// 17 | public long StartOffset { get; set; } 18 | 19 | /// 20 | /// Gets or sets the ending offset of the edit. 21 | /// 22 | public long EndOffset { get; set; } 23 | 24 | /// 25 | /// Gets or sets the original text that the edit will be replacing. 26 | /// 27 | public string OriginalValue { get; set; } 28 | 29 | /// 30 | /// Gets or sets the new text that will be replacing the existing text. 31 | /// 32 | public string NewValue { get; set; } 33 | 34 | /// 35 | /// Gets or sets the name of the file the edit takes place in. 36 | /// 37 | public string FileName { get; set; } 38 | 39 | public Uri Uri => string.IsNullOrEmpty(FileName) 40 | ? null 41 | : new Uri(DocumentHelpers.GetPathAsClientPath(FileName)); 42 | 43 | internal int Id { get; } = s_lastId++; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Inference/ReflectionMethodInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Management.Automation; 4 | 5 | namespace EditorServicesCommandSuite.Inference 6 | { 7 | internal class ReflectionMethodInfo : PSMethodInfo 8 | { 9 | private readonly System.Reflection.MethodBase _method; 10 | 11 | internal ReflectionMethodInfo(System.Reflection.MethodBase method) 12 | { 13 | if (_method is System.Reflection.MethodInfo methodInfo) 14 | { 15 | ReturnType = methodInfo.ReturnType; 16 | } 17 | else 18 | { 19 | ReturnType = method.ReflectedType; 20 | } 21 | 22 | _method = method; 23 | SetMemberName(method.IsConstructor ? "new" : method.Name); 24 | } 25 | 26 | public override Collection OverloadDefinitions => throw new System.NotSupportedException(); 27 | 28 | public override PSMemberTypes MemberType => PSMemberTypes.Method; 29 | 30 | public override string TypeNameOfValue => ReturnType.FullName; 31 | 32 | internal Type ReturnType { get; } 33 | 34 | public override object Invoke(params object[] arguments) 35 | { 36 | throw new System.NotSupportedException(); 37 | } 38 | 39 | public override PSMemberInfo Copy() 40 | { 41 | return new ReflectionMethodInfo(_method); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/AssignmentVariableTargetVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management.Automation.Language; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal sealed class AssignmentVariableTargetVisitor : AstVisitor 7 | { 8 | private readonly List _variables = new List(); 9 | 10 | private AssignmentVariableTargetVisitor() 11 | { 12 | } 13 | 14 | public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst) 15 | { 16 | _variables.Add(variableExpressionAst); 17 | return AstVisitAction.Continue; 18 | } 19 | 20 | public override AstVisitAction VisitIndexExpression(IndexExpressionAst indexExpressionAst) => 21 | AstVisitAction.SkipChildren; 22 | 23 | public override AstVisitAction VisitMemberExpression(MemberExpressionAst memberExpressionAst) => 24 | AstVisitAction.SkipChildren; 25 | 26 | public override AstVisitAction VisitInvokeMemberExpression(InvokeMemberExpressionAst methodCallAst) => 27 | AstVisitAction.SkipChildren; 28 | 29 | internal static IList GetTargets(AssignmentStatementAst assignment) 30 | { 31 | var visitor = new AssignmentVariableTargetVisitor(); 32 | assignment.Left.Visit(visitor); 33 | return visitor._variables; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ExpressionSurroundType.cs: -------------------------------------------------------------------------------- 1 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 2 | { 3 | /// 4 | /// Represents the type of expression to wrap the selection in. 5 | /// 6 | public enum ExpressionSurroundType 7 | { 8 | /// 9 | /// Indicates that the caller should prompt for a surround type. 10 | /// 11 | Prompt, 12 | 13 | /// 14 | /// Indicates that the selection should be wrapped in an if statement. 15 | /// 16 | IfStatement, 17 | 18 | /// 19 | /// Indicates that the selection should be wrapped in an while statement. 20 | /// 21 | WhileStatement, 22 | 23 | /// 24 | /// Indicates that the selection should be wrapped in an foreach statement. 25 | /// 26 | ForeachStatement, 27 | 28 | /// 29 | /// Indicates that the selection should be wrapped in parenthesis. 30 | /// 31 | ParenExpression, 32 | 33 | /// 34 | /// Indicates that the selection should be wrapped in an array initalizer. (i.e. "@()") 35 | /// 36 | ArrayInitializer, 37 | 38 | /// 39 | /// Indicates that the selection should be wrapped in a dollar paren expression. (i.e. "$()") 40 | /// 41 | DollarParenExpression, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/en-US/Add-ModuleQualification.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/Add-ModuleQualification.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Add-ModuleQualification 8 | 9 | ## SYNOPSIS 10 | 11 | Add a commands module name to it's invocation expression. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Add-ModuleQualification [[-Ast] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Add-ModuleQualification function retrieves the module a command belongs to and prepends the module name to the expression. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | # Place your cursor within this command and invoke the Add-ModuleQualification command. 29 | Get-Command 30 | 31 | # It becomes: 32 | Microsoft.PowerShell.Core\Get-Command 33 | ``` 34 | 35 | Adds module qualification to a command expression. 36 | 37 | ## PARAMETERS 38 | 39 | ### -Ast 40 | 41 | Specifies the CommandAst or AST within the CommandAst to add module qualification. 42 | 43 | ```yaml 44 | Type: Ast 45 | Parameter Sets: (All) 46 | Aliases: 47 | 48 | Required: False 49 | Position: 1 50 | Default value: None 51 | Accept pipeline input: False 52 | Accept wildcard characters: False 53 | ``` 54 | 55 | ## INPUTS 56 | 57 | ### None 58 | 59 | This function does not accept input from the pipeline. 60 | 61 | ## OUTPUTS 62 | 63 | ### None 64 | 65 | This function does not output to the pipeline. 66 | 67 | ## NOTES 68 | 69 | ## RELATED LINKS 70 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.Where.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EditorServicesCommandSuite.Utility; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal partial class TokenFinder 7 | { 8 | public TokenFinder Where(TokenFilter filter) 9 | { 10 | return Where(filter, () => Error.TokenNotFound()); 11 | } 12 | 13 | public TokenFinder Where(TokenFilter filter, TState state) 14 | { 15 | return Where(filter, state, state => Error.TokenNotFound(state)); 16 | } 17 | 18 | private TokenFinder Where(TokenFilter filter, Func exceptionFactory) 19 | { 20 | if (_searchFailed) return this; 21 | if (TryFind(filter, out TokenNode node)) 22 | { 23 | _node = node; 24 | return this; 25 | } 26 | 27 | _searchFailed = true; 28 | _reason = exceptionFactory(); 29 | return this; 30 | } 31 | 32 | private TokenFinder Where( 33 | TokenFilter filter, 34 | TState state, 35 | Func exceptionFactory) 36 | { 37 | if (_searchFailed) return this; 38 | if (TryFind(filter, state, out TokenNode node)) 39 | { 40 | _node = node; 41 | return this; 42 | } 43 | 44 | _searchFailed = true; 45 | _reason = exceptionFactory(state); 46 | return this; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/ScriptBasedRefactorProviderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// Represents a function based refactor provider. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | public abstract class ScriptBasedRefactorProviderAttribute : Attribute 13 | { 14 | internal ScriptBasedRefactorProviderAttribute() 15 | { 16 | } 17 | 18 | /// 19 | /// Gets or sets the name of the refactor provider. 20 | /// 21 | [EditorBrowsable(EditorBrowsableState.Never)] 22 | public virtual string Name { get; set; } 23 | 24 | /// 25 | /// Gets or sets the description of the refactor provider. 26 | /// 27 | [EditorBrowsable(EditorBrowsableState.Never)] 28 | public virtual string Description { get; set; } 29 | 30 | /// 31 | /// Gets or sets the name of the variable that contains resource strings. 32 | /// 33 | [EditorBrowsable(EditorBrowsableState.Never)] 34 | public virtual string ResourceVariable { get; set; } 35 | 36 | /// 37 | /// Gets or sets the prefix of the resource name. 38 | /// 39 | [EditorBrowsable(EditorBrowsableState.Never)] 40 | public virtual string ResourcePrefix { get; set; } 41 | 42 | internal abstract RefactorKind Kind { get; } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/Menus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | 6 | namespace EditorServicesCommandSuite.PSReadLine 7 | { 8 | internal static class Menus 9 | { 10 | private static int s_alternateBufferCount; 11 | 12 | internal static bool IsInAlternateBuffer 13 | { 14 | get 15 | { 16 | return Interlocked.CompareExchange(ref s_alternateBufferCount, 0, 0) > 0; 17 | } 18 | } 19 | 20 | internal static InputPromptMenu InputPrompt(string caption, string message) 21 | { 22 | return new InputPromptMenu(caption, message); 23 | } 24 | 25 | internal static SelectItemMenu ItemSelect( 26 | string caption, 27 | string message, 28 | IEnumerable items) 29 | { 30 | return new SelectItemMenu( 31 | caption, 32 | message, 33 | items.ToArray()); 34 | } 35 | 36 | internal static IDisposable NewAlternateBuffer() 37 | { 38 | Interlocked.Increment(ref s_alternateBufferCount); 39 | return new MenuHandle(); 40 | } 41 | 42 | private class MenuHandle : IDisposable 43 | { 44 | internal MenuHandle() 45 | { 46 | Console.Write(Ansi.EnterAlternateBuffer); 47 | } 48 | 49 | public void Dispose() 50 | { 51 | Console.Write(Ansi.ExitAlternateBuffer); 52 | Interlocked.Decrement(ref s_alternateBufferCount); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Reflection/ReflectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Management.Automation.Language; 5 | using System.Reflection; 6 | 7 | namespace EditorServicesCommandSuite.Reflection 8 | { 9 | internal static class ReflectionExtensions 10 | { 11 | public static MemberDescription ToMemberDescription(this MemberInfo member) 12 | { 13 | return new ReflectedMemberDescription(member); 14 | } 15 | 16 | public static MemberDescription ToMemberDescription(this MemberAst member) 17 | { 18 | return new AstMemberDescription(member); 19 | } 20 | 21 | public static IEnumerable ToMemberDescriptions(this IEnumerable source) 22 | { 23 | return source.Select(member => member.ToMemberDescription()); 24 | } 25 | 26 | public static IEnumerable ToMemberDescriptions(this IEnumerable source) 27 | { 28 | return source.Select(member => member.ToMemberDescription()); 29 | } 30 | 31 | public static TAttribute GetAttribute(this MemberInfo member, bool inherit) 32 | where TAttribute : Attribute 33 | { 34 | return member.GetCustomAttributes( 35 | typeof(TAttribute), 36 | inherit) 37 | .OfType() 38 | .FirstOrDefault(); 39 | } 40 | 41 | public static bool IsDefined(this MemberInfo member, bool inherit) 42 | where TAttribute : Attribute 43 | { 44 | return member.IsDefined( 45 | typeof(TAttribute), 46 | inherit); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorProviderInfo.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Diagnostics; 3 | using System.Management.Automation; 4 | 5 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 6 | { 7 | /// 8 | /// Provides externally consumable information regarding available refactor providers. 9 | /// 10 | public sealed class RefactorProviderInfo 11 | { 12 | private readonly IDocumentRefactorProvider _provider; 13 | 14 | internal RefactorProviderInfo(IDocumentRefactorProvider provider, FunctionInfo command) 15 | { 16 | Debug.Assert(provider != null, nameof(provider)); 17 | Debug.Assert(provider != null, nameof(command)); 18 | _provider = provider; 19 | Command = command; 20 | } 21 | 22 | /// 23 | /// Gets the ID of the refactor option. 24 | /// 25 | public string Id => Command.Name; 26 | 27 | /// 28 | /// Gets the display name of the refactor option. 29 | /// 30 | public string DisplayName => _provider.Name ?? string.Empty; 31 | 32 | /// 33 | /// Gets the description of the refactor option. 34 | /// 35 | public string Description => _provider.Description ?? string.Empty; 36 | 37 | /// 38 | /// Gets the type of language elements that the refactor can target. 39 | /// 40 | public RefactorKind Targets => default; 41 | 42 | /// 43 | /// Gets the command that invokes this refactor option. 44 | /// 45 | [Hidden, EditorBrowsable(EditorBrowsableState.Never)] 46 | public FunctionInfo Command { get; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/ConcurrentCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace EditorServicesCommandSuite 7 | { 8 | internal sealed class ConcurrentCollection 9 | { 10 | private readonly SemaphoreSlim _handle = new SemaphoreSlim(1, 1); 11 | 12 | private readonly List _list; 13 | 14 | public ConcurrentCollection() => _list = new List(); 15 | 16 | public ConcurrentCollection(int capacity) => _list = new List(capacity); 17 | 18 | public async Task AddAsync(T item, CancellationToken cancellationToken = default) 19 | { 20 | await _handle.WaitAsync(cancellationToken).ConfigureAwait(false); 21 | try 22 | { 23 | _list.Add(item); 24 | } 25 | finally 26 | { 27 | _handle.Release(); 28 | } 29 | } 30 | 31 | public async Task> ToImmutableArray(CancellationToken cancellationToken = default) 32 | { 33 | await _handle.WaitAsync(cancellationToken).ConfigureAwait(false); 34 | try 35 | { 36 | return _list.ToImmutableArray(); 37 | } 38 | finally 39 | { 40 | _handle.Release(); 41 | } 42 | } 43 | 44 | public async Task ToArray(CancellationToken cancellationToken = default) 45 | { 46 | await _handle.WaitAsync(cancellationToken).ConfigureAwait(false); 47 | try 48 | { 49 | return _list.ToArray(); 50 | } 51 | finally 52 | { 53 | _handle.Release(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /docs/en-US/Expand-TypeImplementation.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Expand-TypeImplementation.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Expand-TypeImplementation 8 | 9 | ## SYNOPSIS 10 | 11 | Expand the closest type expression into a implementation using PowerShell classes. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Expand-TypeImplementation [[-Type] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Expand-TypeImplementation function generates code to implement a class. You can specify a type to implement, or place your cursor close to a type expression and invoke this as an editor command. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | $type = [System.Management.Automation.IArgumentCompleter] 29 | Expand-TypeImplementation -Type $type 30 | 31 | # Adds the following code to the current file. 32 | 33 | class NewIEqualityComparer : System.Collections.IEqualityComparer { 34 | [bool] Equals ([Object] $x, [Object] $y) { 35 | throw [NotImplementedException]::new() 36 | } 37 | 38 | [int] GetHashCode ([Object] $obj) { 39 | throw [NotImplementedException]::new() 40 | } 41 | } 42 | ``` 43 | 44 | ## PARAMETERS 45 | 46 | ### -Type 47 | 48 | Specifies the type to implement. 49 | 50 | ```yaml 51 | Type: Type[] 52 | Parameter Sets: (All) 53 | Aliases: 54 | 55 | Required: False 56 | Position: 1 57 | Default value: None 58 | Accept pipeline input: True (ByPropertyName, ByValue) 59 | Accept wildcard characters: False 60 | ``` 61 | 62 | ## INPUTS 63 | 64 | ### Type 65 | 66 | You can pass types to implement to this function. 67 | 68 | ## OUTPUTS 69 | 70 | ### None 71 | 72 | ## NOTES 73 | 74 | ## RELATED LINKS 75 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using EditorServicesCommandSuite.Internal; 5 | using EditorServicesCommandSuite.Utility; 6 | 7 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 8 | { 9 | internal class RefactorService 10 | { 11 | private readonly Dictionary _providers = 12 | new Dictionary(); 13 | 14 | internal void RegisterProvider(IDocumentRefactorProvider provider) 15 | { 16 | Validate.IsNotNull(nameof(provider), provider); 17 | _providers.Add(provider.Id, provider); 18 | } 19 | 20 | internal void UnregisterProvider(IDocumentRefactorProvider provider) 21 | { 22 | Validate.IsNotNull(nameof(provider), provider); 23 | _providers.Remove(provider.Name); 24 | } 25 | 26 | internal async Task GetCodeActionsAsync(DocumentContextBase context) 27 | { 28 | foreach (IDocumentRefactorProvider provider in _providers.Values) 29 | { 30 | await provider.ComputeCodeActions(context).ConfigureAwait(false); 31 | } 32 | 33 | return await context.FinalizeCodeActions().ConfigureAwait(false); 34 | } 35 | 36 | internal IDocumentRefactorProvider GetProvider(string typeName) 37 | { 38 | if (_providers.TryGetValue(typeName, out IDocumentRefactorProvider result)) 39 | { 40 | return result; 41 | } 42 | 43 | return null; 44 | } 45 | 46 | internal IDocumentRefactorProvider[] GetProviders() 47 | { 48 | return _providers.Values.ToArray(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/ContextService.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using System.Management.Automation.Language; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using EditorServicesCommandSuite.Internal; 6 | using EditorServicesCommandSuite.Utility; 7 | using Microsoft.PowerShell; 8 | 9 | namespace EditorServicesCommandSuite.PSReadLine 10 | { 11 | internal class ContextService : DocumentContextProvider 12 | { 13 | internal override string Workspace => string.Empty; 14 | 15 | internal override Task GetDocumentContextAsync( 16 | PSCmdlet cmdlet, 17 | CancellationToken cancellationToken, 18 | ThreadController threadController) 19 | { 20 | PSConsoleReadLine.GetSelectionState( 21 | out int selectionStart, 22 | out int selectionLength); 23 | 24 | PSConsoleReadLine.GetBufferState( 25 | out Ast ast, 26 | out Token[] tokens, 27 | out _, 28 | out int cursor); 29 | 30 | if (selectionLength == -1) 31 | { 32 | // Most of the selection based refactors make more sense in a command line context 33 | // if they operate on the entire input if there's no selection. 34 | selectionStart = 0; 35 | selectionLength = ast.Extent.Text.Length; 36 | } 37 | 38 | return Task.FromResult( 39 | (DocumentContextBase)GetContextBuilder(ast, tokens) 40 | .AddCursorPosition(cursor) 41 | .AddCancellationToken(cancellationToken) 42 | .AddSelectionRange(selectionStart, selectionStart + selectionLength) 43 | .AddCmdlet(cmdlet) 44 | .AddThreadController(threadController)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/en-US/New-ESCSSettingsFile.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/New-ESCSSettingsFile.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # New-ESCSSettingsFile 8 | 9 | ## SYNOPSIS 10 | 11 | Create a new settings file for the current workspace. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | New-ESCSSettingsFile [[-Path] ] [-Force] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The New-ESCSSettingsFile function creates a settings file in the current workspace. This file contains settings used by this module for determining where to find specific files. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | New-ESCSSettingsFile 29 | ``` 30 | 31 | Creates the file ESCSSettings.psd1 in the base of the current workspace with default values. 32 | 33 | ## PARAMETERS 34 | 35 | ### -Path 36 | 37 | Specifies the path to save the settings file to. If this parameter is not specified a settings file will be created in the base of the current workspace. 38 | 39 | ```yaml 40 | Type: String 41 | Parameter Sets: (All) 42 | Aliases: 43 | 44 | Required: False 45 | Position: 1 46 | Default value: $psEditor.Workspace.Path 47 | Accept pipeline input: False 48 | Accept wildcard characters: False 49 | ``` 50 | 51 | ### -Force 52 | 53 | If specified indicates that an existing settings file should be overridden without prompting. 54 | 55 | ```yaml 56 | Type: SwitchParameter 57 | Parameter Sets: (All) 58 | Aliases: 59 | 60 | Required: False 61 | Position: Named 62 | Default value: False 63 | Accept pipeline input: False 64 | Accept wildcard characters: False 65 | ``` 66 | 67 | ## INPUTS 68 | 69 | ### None 70 | 71 | This function does not accept value from the pipeline. 72 | 73 | ## OUTPUTS 74 | 75 | ### None 76 | 77 | This function does not output to the pipeline. 78 | 79 | ## NOTES 80 | 81 | ## RELATED LINKS 82 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/NoCommandSuiteInstanceException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Runtime.Serialization; 4 | 5 | namespace EditorServicesCommandSuite.Internal 6 | { 7 | /// 8 | /// The exception thrown when an attempt is made to interact with CommandSuite before it has 9 | /// been initialized. 10 | /// 11 | public class NoCommandSuiteInstanceException : Exception, IContainsErrorRecord 12 | { 13 | private ErrorRecord _error; 14 | 15 | internal NoCommandSuiteInstanceException() 16 | : base(RefactorStrings.NoCommandSuiteInstance) 17 | { 18 | } 19 | 20 | internal NoCommandSuiteInstanceException(string message) 21 | : base(message) 22 | { 23 | } 24 | 25 | internal NoCommandSuiteInstanceException(string message, Exception innerException) 26 | : base(message, innerException) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// The serialization information. 34 | /// The streaming context. 35 | protected NoCommandSuiteInstanceException(SerializationInfo info, StreamingContext context) 36 | : base(info, context) 37 | { 38 | } 39 | 40 | /// 41 | /// Gets the generated for the exception. 42 | /// 43 | public ErrorRecord ErrorRecord 44 | { 45 | get 46 | { 47 | return _error ?? (_error = new ErrorRecord( 48 | this, 49 | nameof(RefactorStrings.NoCommandSuiteInstance), 50 | ErrorCategory.ObjectNotFound, 51 | null)); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/INavigationSupportsOpenDocument.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace EditorServicesCommandSuite.Internal 5 | { 6 | /// 7 | /// Provides the ability to open documents in the host editor. 8 | /// 9 | internal interface INavigationSupportsOpenDocument 10 | { 11 | /// 12 | /// Opens a document in the host editor. 13 | /// 14 | /// The path of the document to open. 15 | void OpenDocument(string path); 16 | 17 | /// 18 | /// Opens a document in the host editor. 19 | /// 20 | /// The path of the document to open. 21 | /// 22 | /// The cancellation token that will be checked prior to completing the returned task. 23 | /// 24 | void OpenDocument(string path, CancellationToken cancellationToken); 25 | 26 | /// 27 | /// Opens a document in the host editor. 28 | /// 29 | /// The path of the document to open. 30 | /// 31 | /// A object representing the asynchronus operation. 32 | /// 33 | Task OpenDocumentAsync(string path); 34 | 35 | /// 36 | /// Opens a document in the host editor. 37 | /// 38 | /// The path of the document to open. 39 | /// 40 | /// The cancellation token that will be checked prior to completing the returned task. 41 | /// 42 | /// 43 | /// A object representing the asynchronus operation. 44 | /// 45 | Task OpenDocumentAsync(string path, CancellationToken cancellationToken); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/en-US/ConvertTo-SplatExpression.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/ConvertTo-SplatExpression.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # ConvertTo-SplatExpression 8 | 9 | ## SYNOPSIS 10 | 11 | Convert a command expression to use splatting. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | ConvertTo-SplatExpression [[-Ast] ] [[-VariableName] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The ConvertTo-SplatExpression function transforms a CommandAst to use a splat expression instead 22 | of inline parameters. 23 | 24 | ## EXAMPLES 25 | 26 | ### -------------------------- EXAMPLE 1 -------------------------- 27 | 28 | ```powershell 29 | # Place your cursor inside this command and run this function: 30 | Get-ChildItem .\Path -Force -File -Filter *.txt -Exclude *$myExclude* -Recurse 31 | 32 | # It becomes: 33 | $getChildItemSplat = @{ 34 | File = $true 35 | Filter = '*.txt' 36 | Exclude = "*$myExclude*" 37 | Force = $true 38 | Recurse = $true 39 | } 40 | Get-ChildItem @getChildItemSplat .\Path 41 | ``` 42 | 43 | Uses this function as an editor command to expand a long command into a splat expression. 44 | 45 | ## PARAMETERS 46 | 47 | ### -Ast 48 | 49 | Specifies an Ast that is, or is within the CommandAst to be converted. 50 | 51 | ```yaml 52 | Type: Ast 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: False 57 | Position: 1 58 | Default value: (Find-Ast -AtCursor) 59 | Accept pipeline input: False 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -VariableName 64 | 65 | Specifies the Varabile name to use for the splat hashtable variable. 66 | 67 | ```yaml 68 | Type: String 69 | Parameter Sets: (All) 70 | Aliases: 71 | 72 | Required: False 73 | Position: 2 74 | Default value: none 75 | Accept pipeline input: False 76 | Accept wildcard characters: False 77 | ``` 78 | 79 | ## INPUTS 80 | 81 | ## OUTPUTS 82 | 83 | ## NOTES 84 | 85 | ## RELATED LINKS 86 | -------------------------------------------------------------------------------- /docs/en-US/Expand-Expression.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Expand-Expression.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Expand-Expression 8 | 9 | ## SYNOPSIS 10 | 11 | Replaces an extent with the return value of it's text as an expression. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Expand-Expression [[-InputObject] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Expand-Expression function replaces text at a specified range with it's output in PowerShell. As an editor command it will expand output of selected text. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | $psEditor.GetEditorContext().SelectedRange | ConvertTo-ScriptExtent | Expand-Expression 29 | ``` 30 | 31 | Invokes the currently selected text and replaces it with it's output. 32 | This is also the default. 33 | 34 | ## PARAMETERS 35 | 36 | ### -InputObject 37 | 38 | Specifies the extent to invoke. 39 | 40 | ```yaml 41 | Type: IScriptExtent[] 42 | Parameter Sets: (All) 43 | Aliases: Extent 44 | 45 | Required: False 46 | Position: 1 47 | Default value: ($psEditor.GetEditorContext().SelectedRange | ConvertTo-ScriptExtent) 48 | Accept pipeline input: True (ByPropertyName, ByValue) 49 | Accept wildcard characters: False 50 | ``` 51 | 52 | ### CommonParameters 53 | 54 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 55 | 56 | ## INPUTS 57 | 58 | ### System.Management.Automation.Language.IScriptExtent 59 | 60 | You can pass extents to invoke from the pipeline. 61 | 62 | ## OUTPUTS 63 | 64 | ### None 65 | 66 | ## NOTES 67 | 68 | ## RELATED LINKS 69 | 70 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/ContextService.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Management.Automation; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | using EditorServicesCommandSuite.Internal; 7 | using EditorServicesCommandSuite.Utility; 8 | using Microsoft.PowerShell.EditorServices.Extensions.Services; 9 | 10 | namespace EditorServicesCommandSuite.EditorServices 11 | { 12 | internal class ContextService : DocumentContextProvider 13 | { 14 | private readonly IWorkspaceService _workspace; 15 | 16 | private readonly IEditorContextService _context; 17 | 18 | internal ContextService(IWorkspaceService workspace, IEditorContextService context) 19 | { 20 | _workspace = workspace; 21 | _context = context; 22 | } 23 | 24 | internal override string Workspace => _workspace.WorkspacePath; 25 | 26 | internal override async Task GetDocumentContextAsync( 27 | PSCmdlet cmdlet, 28 | CancellationToken cancellationToken, 29 | ThreadController threadController) 30 | { 31 | var context = await _context.GetCurrentLspFileContextAsync().ConfigureAwait(false); 32 | var scriptFile = _workspace.GetFile(context.Uri); 33 | return GetContextBuilder(scriptFile.Ast, scriptFile.Tokens.ToArray()) 34 | .AddCursorPosition( 35 | (int)context.CursorPosition.Line + 1, 36 | (int)context.CursorPosition.Character + 1) 37 | .AddSelectionRange( 38 | (int)context.SelectionRange.Start.Line + 1, 39 | (int)context.SelectionRange.Start.Character, 40 | (int)context.SelectionRange.End.Line + 1, 41 | (int)context.SelectionRange.End.Character) 42 | .AddCancellationToken(cancellationToken) 43 | .AddCmdlet(cmdlet) 44 | .AddThreadController(threadController); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Utility/DocumentHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Web; 6 | using EditorServicesCommandSuite.Internal; 7 | 8 | namespace EditorServicesCommandSuite.Utility 9 | { 10 | internal static class DocumentHelpers 11 | { 12 | private const string FileUriPrefix = "file:///"; 13 | 14 | internal static string GetPathAsClientPath(Uri uri) => uri?.ToString() ?? string.Empty; 15 | 16 | internal static string GetPathAsClientPath(string path) 17 | { 18 | if (path.StartsWith("untitled:", StringComparison.Ordinal)) 19 | { 20 | return path; 21 | } 22 | 23 | Debug.Assert( 24 | !string.IsNullOrWhiteSpace(path), 25 | "Caller should verify path is valid"); 26 | 27 | if (path.StartsWith(FileUriPrefix, StringComparison.Ordinal)) 28 | { 29 | return path; 30 | } 31 | 32 | if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 33 | { 34 | return new Uri(path).AbsoluteUri; 35 | } 36 | 37 | // VSCode file URIs on Windows need the drive letter lowercase, and the colon 38 | // URI encoded. System.Uri won't do that, so we manually create the URI. 39 | var newUri = new StringBuilder(HttpUtility.UrlPathEncode(path)); 40 | int colonIndex = path.IndexOf(Symbols.Colon); 41 | for (var i = colonIndex - 1; i >= 0; i--) 42 | { 43 | newUri.Remove(i, 1); 44 | newUri.Insert(i, char.ToLowerInvariant(path[i])); 45 | } 46 | 47 | return newUri 48 | .Remove(colonIndex, 1) 49 | .Insert(colonIndex, "%3A") 50 | .Replace(Symbols.Backslash, Symbols.ForwardSlash) 51 | .Insert(0, FileUriPrefix) 52 | .ToString(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | This project uses third-party libraries or other resources that may 2 | be distributed under different licenses. 3 | 4 | In the event that we accidentally failed to list a required notice, 5 | please bring it to our attention through any of the ways detailed here : 6 | 7 | seeminglyscience@gmail.com 8 | 9 | The attached notices are provided for information only. 10 | 11 | 1.) License Notice for PowerShell 12 | 13 | https://github.com/PowerShell/PowerShell/blob/master/LICENSE.txt 14 | 15 | PowerShell 6.0 16 | 17 | Copyright (c) Microsoft Corporation. All rights reserved. 18 | 19 | All rights reserved. 20 | 21 | MIT License 22 | 23 | Permission is hereby granted, free of charge, to any person obtaining a copy 24 | of this software and associated documentation files (the ""Software""), to deal 25 | in the Software without restriction, including without limitation the rights 26 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 27 | copies of the Software, and to permit persons to whom the Software is 28 | furnished to do so, subject to the following conditions: 29 | 30 | The above copyright notice and this permission notice shall be included in all 31 | copies or substantial portions of the Software. 32 | 33 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | SOFTWARE. 40 | 41 | IMPORTANT NOTICE: THE SOFTWARE ALSO CONTAINS THIRD PARTY AND OTHER 42 | PROPRIETARY SOFTWARE THAT ARE GOVERNED BY SEPARATE LICENSE TERMS. BY ACCEPTING 43 | THE LICENSE TERMS ABOVE, YOU ALSO ACCEPT THE LICENSE TERMS GOVERNING THE 44 | THIRD PARTY AND OTHER SOFTWARE, WHICH ARE SET FORTH IN ThirdPartyNotices.txt 45 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/IDocumentRefactorProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Threading.Tasks; 3 | 4 | using EditorServicesCommandSuite.Internal; 5 | 6 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 7 | { 8 | /// 9 | /// Provides a specific refactor option. 10 | /// 11 | internal interface IDocumentRefactorProvider 12 | { 13 | /// 14 | /// Gets the unique identifer. 15 | /// 16 | string Id { get; } 17 | 18 | /// 19 | /// Gets the name. 20 | /// 21 | string Name { get; } 22 | 23 | /// 24 | /// Gets the description of the refactor option. 25 | /// 26 | string Description { get; } 27 | 28 | /// 29 | /// Gets the code actions that this refactor provider supports. 30 | /// 31 | ImmutableArray SupportedActions { get; } 32 | 33 | /// 34 | /// Determines which code actions (if any) are applicable to the 35 | /// current context and registers them against . 36 | /// 37 | /// The context for the code action request. 38 | /// 39 | /// A task that represents the asynchronous code action request. 40 | /// 41 | Task ComputeCodeActions(DocumentContextBase context); 42 | 43 | /// 44 | /// Processes code actions in the context of an invoked command. This method 45 | /// should implement any logic specific to the cmdletized version of this 46 | /// refactor provider. 47 | /// 48 | /// The context for the command invocation. 49 | /// 50 | /// A task that represents the asynchronous invocation. 51 | /// 52 | Task Invoke(DocumentContextBase context); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Utility/Validate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace EditorServicesCommandSuite.Utility 5 | { 6 | internal static class Validate 7 | { 8 | public static void Is(string parameterName, T valueToCheck, Func predicate, string messageToThrow) 9 | { 10 | if (predicate(valueToCheck)) 11 | { 12 | return; 13 | } 14 | 15 | throw new ArgumentException(messageToThrow, parameterName); 16 | } 17 | 18 | public static void IsNotNull(string parameterName, object valueToCheck) 19 | { 20 | if (valueToCheck != null) 21 | { 22 | return; 23 | } 24 | 25 | throw new ArgumentNullException(parameterName); 26 | } 27 | 28 | public static void IsNotNullOrEmpty(string parameterName, string valueToCheck) 29 | { 30 | if (!string.IsNullOrEmpty(valueToCheck)) 31 | { 32 | return; 33 | } 34 | 35 | throw new ArgumentNullException(parameterName); 36 | } 37 | 38 | public static void IsNotNullOrWhiteSpace(string parameterName, string valueToCheck) 39 | { 40 | if (!string.IsNullOrWhiteSpace(valueToCheck)) 41 | { 42 | return; 43 | } 44 | 45 | throw new ArgumentNullException(parameterName); 46 | } 47 | 48 | public static void IsWithinRange( 49 | string parameterName, 50 | int valueToCheck, 51 | int minimum, 52 | int maximum) 53 | { 54 | if (!(valueToCheck < minimum || valueToCheck > maximum)) 55 | { 56 | return; 57 | } 58 | 59 | throw new ArgumentException( 60 | string.Format( 61 | CultureInfo.CurrentCulture, 62 | "Value is not between {0} and {1}.", 63 | minimum, 64 | maximum), 65 | parameterName); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Reflection/AstMemberDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Reflection; 5 | using EditorServicesCommandSuite.Language; 6 | 7 | namespace EditorServicesCommandSuite.Reflection 8 | { 9 | internal class AstMemberDescription : MemberDescription 10 | { 11 | internal AstMemberDescription(MemberAst member) 12 | : base() 13 | { 14 | Name = member.Name; 15 | if (member is FunctionMemberAst function) 16 | { 17 | var builder = ImmutableArray.CreateBuilder(function.Parameters.Count); 18 | foreach (ParameterAst parameter in function.Parameters) 19 | { 20 | builder.Add( 21 | new ParameterDescription( 22 | parameter.Name.VariablePath.UserPath, 23 | parameter.Attributes.GetOutputType())); 24 | } 25 | 26 | Parameters = builder.MoveToImmutable(); 27 | IsStatic = function.IsStatic; 28 | if (function.IsConstructor) 29 | { 30 | MemberType = MemberTypes.Constructor; 31 | ReturnType = new PSTypeName((TypeDefinitionAst)function.Parent); 32 | return; 33 | } 34 | 35 | MemberType = MemberTypes.Method; 36 | ReturnType = new PSTypeName(function.ReturnType.TypeName); 37 | return; 38 | } 39 | 40 | PropertyMemberAst property = (PropertyMemberAst)member; 41 | IsStatic = property.IsStatic; 42 | ReturnType = property.Attributes.GetOutputType(typeof(object)); 43 | } 44 | 45 | public override string Name { get; } 46 | 47 | public override MemberTypes MemberType { get; } 48 | 49 | public override ImmutableArray Parameters { get; } 50 | 51 | public override PSTypeName ReturnType { get; } 52 | 53 | public override bool IsStatic { get; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation.Language; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal readonly struct TokenCollection 7 | { 8 | private readonly ReadOnlyMemory _tokens; 9 | 10 | internal TokenCollection(ReadOnlyMemory tokens) 11 | { 12 | _tokens = tokens; 13 | } 14 | 15 | public bool IsEmpty => _tokens.IsEmpty; 16 | 17 | public int Length => _tokens.Length; 18 | 19 | public TokenNode First => IsEmpty ? default : TokenNode.Create(_tokens, index: 0); 20 | 21 | public TokenNode Last => IsEmpty ? default : TokenNode.Create(_tokens, index: _tokens.Length - 1); 22 | 23 | public TokenCollection Slice(int start) => new TokenCollection(_tokens.Slice(start)); 24 | 25 | public TokenCollection Slice(int start, int length) => new TokenCollection(_tokens.Slice(start, length)); 26 | 27 | public TokenCollection Slice(IScriptExtent extent) => Slice(new SimpleRange(extent)); 28 | 29 | public TokenCollection Slice(SimpleRange range) 30 | { 31 | ReadOnlySpan span = _tokens.Span; 32 | int index = 0; 33 | int? length = null; 34 | bool foundStart = false; 35 | for (int i = 0; i < span.Length; i++) 36 | { 37 | Token current = span[i]; 38 | if (!foundStart) 39 | { 40 | if (range.Start > current.Extent.StartOffset) 41 | { 42 | continue; 43 | } 44 | 45 | index = i; 46 | foundStart = true; 47 | } 48 | 49 | if (range.End < current.Extent.EndOffset) 50 | { 51 | length = i - index - 1; 52 | break; 53 | } 54 | } 55 | 56 | if (!foundStart) 57 | { 58 | return default; 59 | } 60 | 61 | return length == null ? Slice(index) : Slice(index, length.Value); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/en-US/Set-RuleSuppression.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Set-RuleSuppression.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Set-RuleSuppression 8 | 9 | ## SYNOPSIS 10 | 11 | Adds a SuppressMessage attribute to suppress a rule violation. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Set-RuleSuppression [[-Ast] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Set-RuleSupression function generates a SuppressMessage attribute and inserts it into a script file. The PSScriptAnalyzer rule will be determined automatically, as well as the best place to insert the Attribute. 22 | 23 | As an editor command it will attempt to suppress the Ast closest to the current cursor position. 24 | 25 | ## EXAMPLES 26 | 27 | ### -------------------------- EXAMPLE 1 -------------------------- 28 | 29 | ```powershell 30 | Set-RuleSuppression 31 | ``` 32 | 33 | Adds a SuppressMessage attribute to suppress a rule violation. 34 | 35 | ### -------------------------- EXAMPLE 2 -------------------------- 36 | 37 | ```powershell 38 | $propBlock = Find-Ast { $_.CommandElements -and $_.GetCommandName() -eq 'Properties' } 39 | $propBlock | Find-Ast { $_.VariablePath } | Set-RuleSuppression 40 | ``` 41 | 42 | Finds all variable expressions in a psake Properties block and creates a rule suppression for 43 | any that have a violation. 44 | 45 | ## PARAMETERS 46 | 47 | ### -Ast 48 | 49 | Specifies the Ast with a rule violation to suppress. 50 | 51 | ```yaml 52 | Type: Ast[] 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: False 57 | Position: 1 58 | Default value: (Find-Ast -AtCursor) 59 | Accept pipeline input: True (ByValue) 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ## INPUTS 64 | 65 | ### System.Management.Automation.Language.Ast 66 | 67 | You can pass Asts with violations to this function. 68 | 69 | ## OUTPUTS 70 | 71 | ### None 72 | 73 | ## NOTES 74 | 75 | This function does not use existing syntax markers from PowerShell Editor Services, and instead runs the Invoke-ScriptAnalyzer cmdlet on demand. This may create duplicate suppression attributes. 76 | 77 | ## RELATED LINKS 78 | 79 | -------------------------------------------------------------------------------- /test/EditorServicesCommandSuite.Tests/DropNamespaceTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using EditorServicesCommandSuite.CodeGeneration; 5 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 6 | using Xunit; 7 | 8 | namespace EditorServicesCommandSuite.Tests 9 | { 10 | public class DropNamespaceTests 11 | { 12 | private readonly MockedRefactorService _refactorService = new MockedRefactorService(new DropNamespaceRefactor()); 13 | 14 | [Fact] 15 | public async void CanDropNamespace() 16 | { 17 | using var mock = await _refactorService.CreateContextAsync("[System.IO.Path]"); 18 | var codeActions = await mock.GetCodeActionsAsync(); 19 | 20 | Assert.NotEmpty(codeActions); 21 | Assert.Single(codeActions); 22 | Assert.Equal(CodeActionIds.AlterTypeExpression, codeActions[0].Id); 23 | 24 | await codeActions[0].ComputeChanges(mock.Context); 25 | WorkspaceChange[] changes = await mock.Context.FinalizeWorkspaceChanges(); 26 | Assert.Equal( 27 | TestBuilder.Create() 28 | .Lines("using namespace System.IO") 29 | .Lines() 30 | .Texts("[Path]"), 31 | mock.ProcessChanges(changes)); 32 | } 33 | 34 | [Fact] 35 | public async void CanResolveType() 36 | { 37 | using var mock = await _refactorService.CreateContextAsync("[Path]", requiresRunspace: true); 38 | var codeActions = await mock.GetCodeActionsAsync(); 39 | var resolveAction = Array.Find( 40 | codeActions, 41 | action => action.Id == CodeActionIds.AlterTypeExpression); 42 | 43 | Assert.NotNull(resolveAction); 44 | 45 | await resolveAction?.ComputeChanges(mock.Context); 46 | WorkspaceChange[] changes = await mock.Context.FinalizeWorkspaceChanges(); 47 | Assert.Equal( 48 | TestBuilder.Create() 49 | .Lines("using namespace System.IO") 50 | .Lines() 51 | .Texts("[Path]"), 52 | mock.ProcessChanges(changes)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Internal/IRefactorAnalysisContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using EditorServicesCommandSuite.Utility; 5 | 6 | namespace EditorServicesCommandSuite.Internal 7 | { 8 | /// 9 | /// Provides the ability to retrieve diagnostic markers for the current document. 10 | /// 11 | internal interface IRefactorAnalysisContext 12 | { 13 | /// 14 | /// Gets diagnostic markers for a specific document. 15 | /// 16 | /// The path of the document. 17 | /// 18 | /// The controller for the PowerShell pipeline thread. 19 | /// 20 | /// 21 | /// The cancellation token that will be checked prior to completing the returned task. 22 | /// 23 | /// 24 | /// A object representing the asynchronus operation. The Result property 25 | /// will contain active diagnostic markers. 26 | /// 27 | Task> GetDiagnosticsFromPathAsync( 28 | string path, 29 | ThreadController pipelineThread, 30 | CancellationToken cancellationToken); 31 | 32 | /// 33 | /// Gets diagnostic markers for the contents of an untitled document. 34 | /// 35 | /// The text of the document to analyze. 36 | /// 37 | /// The controller for the PowerShell pipeline thread. 38 | /// 39 | /// 40 | /// The cancellation token that will be checked prior to completing the returned task. 41 | /// 42 | /// 43 | /// A object representing the asynchronus operation. The Result property 44 | /// will contain active diagnostic markers. 45 | /// 46 | Task> GetDiagnosticsFromContentsAsync( 47 | string contents, 48 | ThreadController pipelineThread, 49 | CancellationToken cancellationToken); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs/en-US/ConvertTo-LocalizationString.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/ConvertTo-LocalizationString.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # ConvertTo-LocalizationString 8 | 9 | ## SYNOPSIS 10 | 11 | Move a string expression to a localization resource file. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | ConvertTo-LocalizationString [[-Ast] ] [[-Name] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The ConvertTo-LocalizationString function will take the closest string expression and replace it with a variable that references a localization resource file. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | # Place your cursor inside the string and invoke this editor command: 29 | Write-Verbose ('Writing to file at path "{0}".' -f $Path) 30 | 31 | # It prompts you for a string name and becomes: 32 | Write-Verbose ($Strings.YourStringName -f $Path) 33 | 34 | # And adds this to your localization file: 35 | YourStringName=Writing to file at path "{0}". 36 | ``` 37 | 38 | Uses this function as an editor command to replace a string expression with a reference to a localization file. 39 | 40 | ## PARAMETERS 41 | 42 | ### -Ast 43 | 44 | Specifies the string expression to convert. 45 | 46 | ```yaml 47 | Type: Ast 48 | Parameter Sets: (All) 49 | Aliases: 50 | 51 | Required: False 52 | Position: 1 53 | Default value: (Find-Ast -AtCursor) 54 | Accept pipeline input: False 55 | Accept wildcard characters: False 56 | ``` 57 | 58 | ### -Name 59 | 60 | Specifies the name to give the string in the localization file. 61 | 62 | ```yaml 63 | Type: String 64 | Parameter Sets: (All) 65 | Aliases: 66 | 67 | Required: False 68 | Position: 2 69 | Default value: None 70 | Accept pipeline input: False 71 | Accept wildcard characters: False 72 | ``` 73 | 74 | ## INPUTS 75 | 76 | ### None 77 | 78 | This function does not accept input from the pipeline 79 | 80 | ## OUTPUTS 81 | 82 | ### None 83 | 84 | This function does not output to the pipeline. 85 | 86 | ## NOTES 87 | 88 | Current limitations: 89 | 90 | - Only supports localization files that use ConvertFrom-StringData and a here-string 91 | - Only supports using a single localization file 92 | 93 | ## RELATED LINKS 94 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/Internal/NullLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace EditorServicesCommandSuite.EditorServices.Internal 5 | { 6 | /// 7 | /// Provides a no-op implementation. 8 | /// 9 | public class NullLogger : ILogger 10 | { 11 | /// 12 | /// A no-op implemention of . 13 | /// 14 | /// The parameter is not used. 15 | /// The parameter is not used. 16 | /// A no-op implementation of . 17 | public IDisposable BeginScope(TState state) 18 | { 19 | return NullDisposable.Value; 20 | } 21 | 22 | /// 23 | /// A no-op implemention of . 24 | /// 25 | /// The parameter is not used. 26 | /// false. 27 | public bool IsEnabled(LogLevel logLevel) 28 | { 29 | return false; 30 | } 31 | 32 | /// 33 | /// A no-op implemention of . 34 | /// 35 | /// The parameter is not used. 36 | /// The parameter is not used. 37 | /// The parameter is not used. 38 | /// The parameter is not used. 39 | /// The parameter is not used. 40 | /// The parameter is not used. 41 | public void Log( 42 | LogLevel logLevel, 43 | EventId eventId, 44 | TState state, 45 | Exception exception, 46 | Func formatter) 47 | { 48 | } 49 | 50 | private class NullDisposable : IDisposable 51 | { 52 | public static readonly NullDisposable Value = new NullDisposable(); 53 | 54 | private NullDisposable() 55 | { 56 | } 57 | 58 | public void Dispose() 59 | { 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /docs/en-US/ConvertTo-MarkdownHelp.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/ConvertTo-MarkdownHelp.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # ConvertTo-MarkdownHelp 8 | 9 | ## SYNOPSIS 10 | 11 | Convert the current function from comment based help to markdown. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | ConvertTo-MarkdownHelp [[-Ast] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The ConvertTo-MarkdownHelp function will replace existing CBH (comment based help) with markdown generated by the PlatyPS module. The CBH will be replaced with a EXTERNALHELP comment, and the new markdown file will be opened in the editor. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | ConvertTo-MarkdownHelp 29 | ``` 30 | 31 | Converts the closest CBH to markdown. 32 | 33 | ### -------------------------- EXAMPLE 2 -------------------------- 34 | 35 | ```powershell 36 | $ast = Find-Ast { $_.GetType().Name -eq 'FunctionDefinitionAst' -and $_.Name -eq 'Invoke-MyCommand' } 37 | ``` 38 | 39 | ConvertTo-MarkdownHelp -Ast $ast 40 | 41 | Converts any CBH in the function "Invoke-MyCommand" to markdown. 42 | 43 | ## PARAMETERS 44 | 45 | ### -Ast 46 | 47 | Specifies the FunctionDefinitionAst containing the CBH to be replaced. The default value is the closest AST to the current cursor location. 48 | 49 | ```yaml 50 | Type: FunctionDefinitionAst 51 | Parameter Sets: (All) 52 | Aliases: 53 | 54 | Required: False 55 | Position: 1 56 | Default value: (Find-Ast -AtCursor | Find-Ast -Ancestor -First { $_ -is [FunctionDefinitionAst] }) 57 | Accept pipeline input: False 58 | Accept wildcard characters: False 59 | ``` 60 | 61 | ## INPUTS 62 | 63 | ### None 64 | 65 | This function does not accept input from the pipeline. 66 | 67 | ## OUTPUTS 68 | 69 | ### None 70 | 71 | This function does not output to the pipeline. 72 | 73 | ## NOTES 74 | 75 | Markdown generated from PlatyPS is altered in the following ways to conform to linting rules: 76 | 77 | - An extra new line is added between headers and content 78 | 79 | - Code blocks are marked as PowerShell code 80 | 81 | - Trailing spaces after blank aliases fields are removed 82 | 83 | - Examples with the header generated as ### Example are replaced with hyphen syntax. 84 | 85 | ## RELATED LINKS 86 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/SimpleRange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation.Language; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal readonly struct SimpleRange : IEquatable 7 | { 8 | public readonly int Start; 9 | 10 | public readonly int End; 11 | 12 | public SimpleRange(int start, int end) 13 | { 14 | Start = start; 15 | End = end; 16 | } 17 | 18 | public SimpleRange(IScriptExtent extent) 19 | { 20 | if (extent == null) 21 | { 22 | Start = 0; 23 | End = 0; 24 | return; 25 | } 26 | 27 | Start = extent.StartOffset; 28 | End = extent.EndOffset; 29 | } 30 | 31 | public static implicit operator SimpleRange(Ast ast) 32 | => new SimpleRange(ast.Extent); 33 | 34 | public static implicit operator SimpleRange(Token token) 35 | => new SimpleRange(token.Extent); 36 | 37 | public static implicit operator SimpleRange(TokenNode node) 38 | => new SimpleRange(node.Value?.Extent); 39 | 40 | public static implicit operator SimpleRange((int start, int end) source) 41 | => new SimpleRange(source.start, source.end); 42 | 43 | public static bool operator ==(SimpleRange left, SimpleRange right) => left.Equals(right); 44 | 45 | public static bool operator !=(SimpleRange left, SimpleRange right) => !left.Equals(right); 46 | 47 | public bool Equals(SimpleRange other) => Start == other.Start && End == other.End; 48 | 49 | public override bool Equals(object obj) => obj is SimpleRange other && Equals(other); 50 | 51 | public override int GetHashCode() 52 | { 53 | int hash = 17; 54 | hash = (hash * 31) + Start.GetHashCode(); 55 | hash = (hash * 31) + End.GetHashCode(); 56 | return hash; 57 | } 58 | 59 | public void Deconstruct(out int startOffset, out int endOffset) 60 | { 61 | startOffset = Start; 62 | endOffset = End; 63 | } 64 | 65 | public override string ToString() => string.Format( 66 | System.Globalization.CultureInfo.CurrentCulture, 67 | LanguageStrings.SimpleRangeFormat, 68 | Start, 69 | End); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /docs/en-US/Add-PinvokeMethod.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: EditorServicesCommandSuite-help.xml 3 | online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Add-PinvokeMethod.md 4 | schema: 2.0.0 5 | --- 6 | 7 | # Add-PinvokeMethod 8 | 9 | ## SYNOPSIS 10 | 11 | Find and insert a PInvoke function signature into the current file. 12 | 13 | ## SYNTAX 14 | 15 | ```powershell 16 | Add-PinvokeMethod [[-Function] ] [[-Module] ] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | 21 | The Add-PinvokeMethod function searches pinvoke.net for the requested function name and provides a list of matches to select from. Once selected, this function will get the signature and create a expression that uses the Add-Type cmdlet to create a type with the PInvoke method. 22 | 23 | ## EXAMPLES 24 | 25 | ### -------------------------- EXAMPLE 1 -------------------------- 26 | 27 | ```powershell 28 | Add-PinvokeMethod -Function SetConsoleTitle -Module Kernel32 29 | 30 | # Inserts the following into the file currently open in the editor. 31 | 32 | # Source: http://pinvoke.net/jump.aspx/kernel32.setconsoletitle 33 | Add-Type -Namespace PinvokeMethods -Name Kernel -MemberDefinition ' 34 | [DllImport("kernel32.dll")] 35 | public static extern bool SetConsoleTitle(string lpConsoleTitle);' 36 | ``` 37 | 38 | Adds code to use the SetConsoleTitle function from the kernel32 DLL. 39 | 40 | ## PARAMETERS 41 | 42 | ### -Function 43 | 44 | Specifies the function name to search for. If omitted, a prompt will be displayed within the editor. 45 | 46 | ```yaml 47 | Type: String 48 | Parameter Sets: (All) 49 | Aliases: 50 | 51 | Required: False 52 | Position: 1 53 | Default value: None 54 | Accept pipeline input: False 55 | Accept wildcard characters: False 56 | ``` 57 | 58 | ### -Module 59 | 60 | Specifies the module or dll the function resides in. If omitted, and multiple matching functions exist, a choice prompt will be displayed within the editor. 61 | 62 | ```yaml 63 | Type: String 64 | Parameter Sets: (All) 65 | Aliases: 66 | 67 | Required: False 68 | Position: 2 69 | Default value: None 70 | Accept pipeline input: False 71 | Accept wildcard characters: False 72 | ``` 73 | 74 | ## INPUTS 75 | 76 | ### None 77 | 78 | This function does not accept input from the pipeline. 79 | 80 | ## OUTPUTS 81 | 82 | ### None 83 | 84 | This function does not output to the pipeline. 85 | 86 | ## NOTES 87 | 88 | ## RELATED LINKS 89 | 90 | [pinvoke.net](http://pinvoke.net/) 91 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/NameUnnamedBlockRefactor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Immutable; 3 | using System.Management.Automation; 4 | using System.Management.Automation.Language; 5 | using System.Threading.Tasks; 6 | using EditorServicesCommandSuite.Internal; 7 | using EditorServicesCommandSuite.Language; 8 | 9 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 10 | { 11 | [Refactor(VerbsData.ConvertFrom, "UnnamedBlock")] 12 | internal class NameUnnamedBlockRefactor : RefactorProvider 13 | { 14 | public override string Name { get; } = NameUnnamedBlockStrings.ProviderDisplayName; 15 | 16 | public override string Description { get; } = NameUnnamedBlockStrings.ProviderDisplayDescription; 17 | 18 | public override ImmutableArray SupportedActions { get; } = ImmutableArray.Create( 19 | CodeAction.Inactive(CodeActionIds.NameUnnamedBlock, "Wrap in end { }", rank: -50)); 20 | 21 | public override async Task ComputeCodeActions(DocumentContextBase context) 22 | { 23 | if (!context.Ast.TryFindParent(maxDepth: int.MaxValue, out NamedBlockAst namedBlock)) 24 | { 25 | return; 26 | } 27 | 28 | if (!namedBlock.Unnamed) 29 | { 30 | return; 31 | } 32 | 33 | await context.RegisterCodeActionAsync( 34 | SupportedActions[0].With(ComputeUnnamedBlockNaming, namedBlock)) 35 | .ConfigureAwait(false); 36 | } 37 | 38 | internal static async Task ComputeUnnamedBlockNaming( 39 | DocumentContextBase context, 40 | NamedBlockAst namedBlock) 41 | { 42 | IScriptExtent allStatements = namedBlock.Statements.JoinExtents(); 43 | IScriptExtent fullLineStatements = PositionUtilities.GetFullLines(allStatements); 44 | var writer = new PowerShellScriptWriter(namedBlock); 45 | writer.StartWriting(allStatements); 46 | writer.Write(Symbols.End); 47 | writer.Write(Symbols.Space); 48 | writer.OpenScriptBlock(); 49 | writer.WriteIndentNormalizedLines(fullLineStatements.Text); 50 | writer.CloseScriptBlock(); 51 | writer.FinishWriting(); 52 | await context.RegisterWorkspaceChangeAsync(writer.CreateWorkspaceChange(context)).ConfigureAwait(false); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/EditorServicesCommandSuite.ruleset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/TokenFinder.ClosestTo.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Management.Automation.Language; 3 | 4 | namespace EditorServicesCommandSuite.Language 5 | { 6 | internal partial class TokenFinder 7 | { 8 | private static readonly TokenFilter ClosestToFilter 9 | = (in TokenNode node, State state) => 10 | { 11 | IScriptExtent extent = node.Value.Extent; 12 | if (extent.ContainsOffset(state.Position.Offset)) 13 | { 14 | state.LastNode = node; 15 | return true; 16 | } 17 | 18 | if (extent.EndOffset < state.Position.Offset) 19 | { 20 | state.LastNode = node; 21 | } 22 | 23 | return false; 24 | }; 25 | 26 | public TokenFinder ClosestTo(IScriptPosition position) 27 | => ClosestTo(new SimplePosition(position)); 28 | 29 | public TokenFinder ClosestTo(SimplePosition position) 30 | { 31 | if (_searchFailed) return this; 32 | void Stopper(in TokenNode node, ref bool stopSearch) 33 | { 34 | if (stopSearch) return; 35 | stopSearch = node.Value.Extent.EndOffset >= position.Offset; 36 | } 37 | 38 | _stopper += Stopper; 39 | try 40 | { 41 | var state = new State(position); 42 | Where(ClosestToFilter, state, _ => null); 43 | _searchFailed = false; 44 | _reason = null; 45 | _node = state.LastNode; 46 | Debug.Assert(!state.LastNode.IsDefault, "ClosestTo should never return default."); 47 | return this; 48 | } 49 | finally 50 | { 51 | _stopper -= Stopper; 52 | } 53 | } 54 | 55 | public TokenFinder ClosestToStartOf(IScriptExtent extent) => ClosestTo(extent.StartOffset); 56 | 57 | public TokenFinder ClosestToStartOf(SimpleRange range) => ClosestTo(range.Start); 58 | 59 | public TokenFinder ClosestToEndOf(IScriptExtent extent) => ClosestTo(extent.EndOffset); 60 | 61 | public TokenFinder ClosestToEndOf(SimpleRange range) => ClosestTo(range.End); 62 | 63 | private sealed class State 64 | { 65 | public readonly SimplePosition Position; 66 | 67 | public TokenNode LastNode; 68 | 69 | public State(SimplePosition position) => Position = position; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "PowerShell Launch Current File", 6 | "type": "PowerShell", 7 | "request": "launch", 8 | "script": "${file}", 9 | "cwd": "${file}" 10 | }, 11 | { 12 | "name": ".NET FullCLR Attach", 13 | "type": "clr", 14 | "request": "attach", 15 | "processId": "${command:pickProcess}", 16 | "justMyCode": true, 17 | }, 18 | { 19 | "name": ".NET FullCLR Launch (console)", 20 | "type": "clr", 21 | "request": "launch", 22 | "preLaunchTask": "Build", 23 | "cwd": "${workspaceFolder}", 24 | "stopAtEntry": false, 25 | "justMyCode": true, 26 | "console": "externalTerminal", 27 | "program": "powershell.exe", 28 | "args": [ 29 | "-ExecutionPolicy", 30 | "Bypass", 31 | "-NoExit", 32 | "-Command", 33 | ". \"${workspaceRoot}/debugHarness.ps1\"", 34 | ], 35 | }, 36 | { 37 | "name": ".NET CoreCLR Attach", 38 | "type": "coreclr", 39 | "request": "attach", 40 | "processId": "${command:pickProcess}", 41 | "justMyCode": true, 42 | }, 43 | { 44 | "name": ".NET CoreCLR Launch (console)", 45 | "type": "coreclr", 46 | "request": "launch", 47 | "preLaunchTask": "Build", 48 | "cwd": "${workspaceFolder}", 49 | "stopAtEntry": false, 50 | "justMyCode": true, 51 | "console": "externalTerminal", 52 | "linux": { 53 | "program": "/usr/bin/pwsh", 54 | "args": [ 55 | "-NoExit", 56 | "-Command", 57 | ". \"${workspaceRoot}/debugHarness.ps1\"", 58 | ], 59 | }, 60 | "osx": { 61 | "program": "/usr/local/bin/pwsh", 62 | "args": [ 63 | "-NoExit", 64 | "-Command", 65 | ". \"${workspaceRoot}/debugHarness.ps1\"", 66 | ], 67 | }, 68 | "program": "pwsh.exe", 69 | "args": [ 70 | "-ExecutionPolicy", 71 | "Bypass", 72 | "-NoExit", 73 | "-Command", 74 | ". \"${workspaceRoot}/debugHarness.ps1\"", 75 | ], 76 | }, 77 | ], 78 | } 79 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ChangeNamedBlockKindRefactor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using System.Management.Automation.Language; 3 | using System.Threading.Tasks; 4 | 5 | using EditorServicesCommandSuite.Internal; 6 | 7 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 8 | { 9 | internal class ChangeNamedBlockKindRefactor : RefactorProvider 10 | { 11 | public override ImmutableArray SupportedActions { get; } 12 | = ImmutableArray.Create( 13 | CodeAction.Inactive( 14 | CodeActionIds.ChangeNamedBlockKind, 15 | "Change block kind to '{0}'")); 16 | 17 | public override async Task ComputeCodeActions(DocumentContextBase context) 18 | { 19 | if (context.Token.Kind == TokenKind.Begin) 20 | { 21 | await context.RegisterCodeActionAsync(CreateCodeAction(TokenKind.Process)).ConfigureAwait(false); 22 | await context.RegisterCodeActionAsync(CreateCodeAction(TokenKind.End)).ConfigureAwait(false); 23 | return; 24 | } 25 | 26 | if (context.Token.Kind == TokenKind.Process) 27 | { 28 | await context.RegisterCodeActionAsync(CreateCodeAction(TokenKind.Begin)).ConfigureAwait(false); 29 | await context.RegisterCodeActionAsync(CreateCodeAction(TokenKind.End)).ConfigureAwait(false); 30 | return; 31 | } 32 | 33 | if (context.Token.Kind == TokenKind.End) 34 | { 35 | await context.RegisterCodeActionAsync(CreateCodeAction(TokenKind.Begin)).ConfigureAwait(false); 36 | await context.RegisterCodeActionAsync(CreateCodeAction(TokenKind.Process)).ConfigureAwait(false); 37 | return; 38 | } 39 | } 40 | 41 | private static Task ChangeBlockKindAsync(DocumentContextBase context, TokenKind kind) 42 | { 43 | var writer = new PowerShellScriptWriter(context); 44 | writer.StartWriting(context.Token); 45 | writer.Write(kind); 46 | return writer.RegisterWorkspaceChangeAsync(context); 47 | } 48 | 49 | private CodeAction CreateCodeAction(TokenKind kind) 50 | { 51 | CodeAction source = SupportedActions[0]; 52 | return source.With( 53 | (context) => ChangeBlockKindAsync(context, kind), 54 | title: string.Format( 55 | System.Globalization.CultureInfo.CurrentCulture, 56 | source.Title, 57 | TokenTraits.Text(kind))); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/PSReadLineNavigationService.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using EditorServicesCommandSuite.Internal; 5 | using Microsoft.PowerShell; 6 | 7 | namespace EditorServicesCommandSuite.PSReadLine 8 | { 9 | internal class PSReadLineNavigationService : NavigationService 10 | { 11 | public override void SetCursorPosition(int line, int column, CancellationToken cancellationToken) 12 | { 13 | SetCursorPositionImpl( 14 | GetOffsetFromLineAndColumn(line, column)); 15 | } 16 | 17 | public override Task SetCursorPositionAsync(int line, int column, CancellationToken cancellationToken) 18 | { 19 | SetCursorPositionImpl( 20 | GetOffsetFromLineAndColumn(line, column)); 21 | 22 | return Task.CompletedTask; 23 | } 24 | 25 | public override void SetSelection( 26 | int startLine, 27 | int startColumn, 28 | int endLine, 29 | int endColumn, 30 | CancellationToken cancellationToken) 31 | { 32 | SetCursorPosition(startLine, startColumn, cancellationToken); 33 | } 34 | 35 | public override Task SetSelectionAsync( 36 | int startLine, 37 | int startColumn, 38 | int endLine, 39 | int endColumn, 40 | CancellationToken cancellationToken) 41 | { 42 | SetCursorPosition(startLine, startColumn, cancellationToken); 43 | return Task.CompletedTask; 44 | } 45 | 46 | private static void SetCursorPositionImpl(int offset) 47 | { 48 | PSConsoleReadLine.SetCursorPosition(offset); 49 | } 50 | 51 | private static int GetOffsetFromLineAndColumn(int line, int column) 52 | { 53 | PSConsoleReadLine.GetBufferState(out string input, out _); 54 | var currentLine = 1; 55 | var currentColumn = 1; 56 | return input.TakeWhile( 57 | c => 58 | { 59 | if (currentLine < line) 60 | { 61 | if (c == '\n') 62 | { 63 | currentLine++; 64 | } 65 | 66 | return true; 67 | } 68 | 69 | if (currentColumn < column) 70 | { 71 | currentColumn++; 72 | return true; 73 | } 74 | 75 | return false; 76 | }) 77 | .Count(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/ILanguageServerServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.PowerShell.EditorServices.Extensions.Services; 3 | using OmniSharp.Extensions.LanguageServer.Protocol; 4 | using OmniSharp.Extensions.LanguageServer.Protocol.Models; 5 | 6 | namespace EditorServicesCommandSuite.EditorServices 7 | { 8 | internal static class ILanguageServerServiceExtensions 9 | { 10 | public static void Show( 11 | this ILanguageServerService mediator, 12 | ShowMessageParams @params) 13 | { 14 | ShowMessage(mediator, @params); 15 | } 16 | 17 | public static void Show(this ILanguageServerService mediator, string message) 18 | { 19 | ShowMessage( 20 | mediator, 21 | new ShowMessageParams() 22 | { 23 | Type = MessageType.Log, 24 | Message = message, 25 | }); 26 | } 27 | 28 | public static void ShowError(this ILanguageServerService mediator, string message) 29 | { 30 | ShowMessage( 31 | mediator, 32 | new ShowMessageParams() 33 | { 34 | Type = MessageType.Error, 35 | Message = message, 36 | }); 37 | } 38 | 39 | public static void ShowInfo(this ILanguageServerService mediator, string message) 40 | { 41 | ShowMessage( 42 | mediator, 43 | new ShowMessageParams() 44 | { 45 | Type = MessageType.Info, 46 | Message = message, 47 | }); 48 | } 49 | 50 | public static void ShowMessage(this ILanguageServerService mediator, ShowMessageParams @params) 51 | { 52 | mediator.SendNotification( 53 | WindowNames.ShowMessage, 54 | @params); 55 | } 56 | 57 | public static void ShowWarning(this ILanguageServerService mediator, string message) 58 | { 59 | ShowMessage( 60 | mediator, 61 | new ShowMessageParams() 62 | { 63 | Type = MessageType.Warning, 64 | Message = message, 65 | }); 66 | } 67 | 68 | public static Task ApplyEdit( 69 | this ILanguageServerService mediator, 70 | ApplyWorkspaceEditParams @params) 71 | { 72 | return mediator.SendRequestAsync( 73 | WorkspaceNames.ApplyEdit, 74 | @params); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ConfiguredDocumentContext.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using System.Management.Automation.Language; 3 | using System.Threading; 4 | using EditorServicesCommandSuite.Internal; 5 | using EditorServicesCommandSuite.Language; 6 | using EditorServicesCommandSuite.Utility; 7 | 8 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 9 | { 10 | internal class ConfiguredDocumentContext 11 | : DocumentContextBase 12 | where TConfiguration : class 13 | { 14 | internal ConfiguredDocumentContext( 15 | TConfiguration configuration, 16 | DocumentContextBase contextToCopy) 17 | : this( 18 | configuration, 19 | contextToCopy.RootAst, 20 | contextToCopy.Ast, 21 | contextToCopy.Token, 22 | contextToCopy.SelectionExtent, 23 | contextToCopy._psCmdlet, 24 | contextToCopy.CancellationToken, 25 | contextToCopy.PipelineThread) 26 | { 27 | } 28 | 29 | internal ConfiguredDocumentContext( 30 | TConfiguration configuration, 31 | DocumentContextBase contextToCopy, 32 | CancellationToken cancellationToken) 33 | : this( 34 | configuration, 35 | contextToCopy.RootAst, 36 | contextToCopy.Ast, 37 | contextToCopy.Token, 38 | contextToCopy.SelectionExtent, 39 | contextToCopy._psCmdlet, 40 | cancellationToken, 41 | contextToCopy.PipelineThread) 42 | { 43 | } 44 | 45 | internal ConfiguredDocumentContext( 46 | TConfiguration configuration, 47 | ScriptBlockAst rootAst, 48 | Ast currentAst, 49 | TokenNode currentToken, 50 | IScriptExtent selectionExtent, 51 | PSCmdlet cmdlet, 52 | CancellationToken cancellationToken, 53 | ThreadController threadController) 54 | : base( 55 | rootAst, 56 | currentAst, 57 | currentToken, 58 | selectionExtent, 59 | cmdlet, 60 | cancellationToken, 61 | threadController) 62 | { 63 | Configuration = configuration; 64 | } 65 | 66 | internal TConfiguration Configuration { get; } 67 | 68 | internal override TConfiguration1 GetConfiguration() 69 | { 70 | if (Configuration is TConfiguration1 targetConfiguration) 71 | { 72 | return targetConfiguration; 73 | } 74 | 75 | return new TConfiguration1(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/DocumentEditWriterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace EditorServicesCommandSuite.CodeGeneration 5 | { 6 | internal static class DocumentEditWriterExtensions 7 | { 8 | internal static void WriteEachWithSeparator( 9 | this DocumentEditWriter editWriter, 10 | IList source, 11 | Action writer, 12 | char separator) 13 | { 14 | editWriter.WriteEachWithSeparator( 15 | source, 16 | writer, 17 | () => editWriter.Write(separator)); 18 | } 19 | 20 | internal static void WriteEachWithSeparator( 21 | this DocumentEditWriter editWriter, 22 | IList source, 23 | Action writer, 24 | char[] separator) 25 | { 26 | editWriter.WriteEachWithSeparator( 27 | source, 28 | writer, 29 | () => editWriter.Write(separator)); 30 | } 31 | 32 | internal static void WriteEachWithSeparator( 33 | this DocumentEditWriter editWriter, 34 | IList source, 35 | Action writer, 36 | string separator) 37 | { 38 | editWriter.WriteEachWithSeparator( 39 | source, 40 | writer, 41 | () => editWriter.Write(separator)); 42 | } 43 | 44 | internal static void WriteEachWithSeparator( 45 | this DocumentEditWriter editWriter, 46 | IEnumerable source, 47 | Action writer, 48 | char separator) 49 | { 50 | editWriter.WriteEachWithSeparator( 51 | source, 52 | writer, 53 | () => editWriter.Write(separator)); 54 | } 55 | 56 | internal static void WriteEachWithSeparator( 57 | this DocumentEditWriter editWriter, 58 | IEnumerable source, 59 | Action writer, 60 | char[] separator) 61 | { 62 | editWriter.WriteEachWithSeparator( 63 | source, 64 | writer, 65 | () => editWriter.Write(separator)); 66 | } 67 | 68 | internal static void WriteEachWithSeparator( 69 | this DocumentEditWriter editWriter, 70 | IEnumerable source, 71 | Action writer, 72 | string separator) 73 | { 74 | editWriter.WriteEachWithSeparator( 75 | source, 76 | writer, 77 | () => editWriter.Write(separator)); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/IEditorScriptFileExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.PowerShell.EditorServices.Extensions.Services; 3 | 4 | namespace EditorServicesCommandSuite.EditorServices 5 | { 6 | internal static class IEditorScriptFileExtensions 7 | { 8 | public static LspPosition GetPositionAtOffset(this IEditorScriptFile scriptFile, int offset) 9 | => (LspPosition)GetRangeBetweenOffsets(scriptFile, offset, offset).Start; 10 | 11 | public static LspRange GetRangeBetweenOffsets( 12 | this IEditorScriptFile scriptFile, 13 | int startOffset, 14 | int endOffset) 15 | { 16 | bool foundStart = false; 17 | int currentOffset = 0; 18 | int searchedOffset = startOffset; 19 | 20 | var startPosition = new LspPosition(0, 0); 21 | LspPosition endPosition = startPosition; 22 | 23 | int line = 0; 24 | int totalLineCount = scriptFile.Lines.Count; 25 | while (line < totalLineCount) 26 | { 27 | if (searchedOffset <= currentOffset + scriptFile.Lines[line].Length) 28 | { 29 | int column = searchedOffset - currentOffset; 30 | 31 | // Have we already found the start position? 32 | if (foundStart) 33 | { 34 | // Assign the end position and end the search 35 | endPosition = new LspPosition(line + 1, column + 1); 36 | break; 37 | } 38 | else 39 | { 40 | startPosition = new LspPosition(line + 1, column + 1); 41 | 42 | // Do we only need to find the start position? 43 | if (startOffset == endOffset) 44 | { 45 | endPosition = startPosition; 46 | break; 47 | } 48 | else 49 | { 50 | // Since the end offset can be on the same line, 51 | // skip the line increment and continue searching 52 | // for the end position 53 | foundStart = true; 54 | searchedOffset = endOffset; 55 | continue; 56 | } 57 | } 58 | } 59 | 60 | // Increase the current offset and include newline length 61 | currentOffset += scriptFile.Lines[line].Length + Environment.NewLine.Length; 62 | line++; 63 | } 64 | 65 | return new LspRange(startPosition, endPosition); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Reflection/ReflectedMemberDescription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using System.Management.Automation; 4 | using System.Reflection; 5 | 6 | namespace EditorServicesCommandSuite.Reflection 7 | { 8 | internal class ReflectedMemberDescription : MemberDescription 9 | { 10 | private readonly MemberInfo _member; 11 | 12 | internal ReflectedMemberDescription(MemberInfo member) 13 | : base() 14 | { 15 | _member = member ?? throw new ArgumentNullException(nameof(member)); 16 | if (member is MethodBase methodBase) 17 | { 18 | ParameterInfo[] parameters = methodBase.GetParameters(); 19 | var builder = ImmutableArray.CreateBuilder(parameters.Length); 20 | foreach (ParameterInfo parameter in parameters) 21 | { 22 | builder.Add( 23 | new ParameterDescription( 24 | parameter.Name, 25 | new PSTypeName(parameter.ParameterType))); 26 | } 27 | 28 | Parameters = builder.MoveToImmutable(); 29 | 30 | if (member is MethodInfo method) 31 | { 32 | ReturnType = new PSTypeName(method.ReturnType); 33 | IsStatic = method.IsStatic; 34 | return; 35 | } 36 | 37 | if (member is ConstructorInfo constructor) 38 | { 39 | ReturnType = new PSTypeName(constructor.ReflectedType); 40 | return; 41 | } 42 | 43 | return; 44 | } 45 | 46 | Parameters = ImmutableArray.Empty; 47 | if (member is PropertyInfo property) 48 | { 49 | ReturnType = new PSTypeName(property.PropertyType); 50 | IsStatic = property.GetMethod.IsStatic; 51 | return; 52 | } 53 | 54 | if (member is FieldInfo field) 55 | { 56 | ReturnType = new PSTypeName(field.FieldType); 57 | IsStatic = field.IsStatic; 58 | return; 59 | } 60 | 61 | if (member is EventInfo eventInfo) 62 | { 63 | IsStatic = eventInfo.AddMethod.IsStatic; 64 | } 65 | 66 | ReturnType = new PSTypeName(typeof(void)); 67 | } 68 | 69 | public override string Name => _member.Name; 70 | 71 | public override MemberTypes MemberType => _member.MemberType; 72 | 73 | public override ImmutableArray Parameters { get; } 74 | 75 | public override PSTypeName ReturnType { get; } 76 | 77 | public override bool IsStatic { get; } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/EditorServicesCommandSuite.Tests/NameUnnamedBlockTests.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation.Language; 2 | using System.Threading.Tasks; 3 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 4 | using EditorServicesCommandSuite.Language; 5 | using Xunit; 6 | 7 | namespace EditorServicesCommandSuite.Tests 8 | { 9 | public class NameUnnamedBlockTests 10 | { 11 | private readonly MockedRefactorService _refactorService = new MockedRefactorService(new NameUnnamedBlockRefactor()); 12 | 13 | [Fact] 14 | public async void NamesUnnamedBlock() 15 | { 16 | Assert.Equal( 17 | TestBuilder.Create() 18 | .Lines("end {") 19 | .Lines(" Get-ChildItem") 20 | .Lines(" Get-Acl") 21 | .Texts("}"), 22 | await GetRefactoredTextAsync( 23 | TestBuilder.Create() 24 | .Lines("Get-ChildItem") 25 | .Texts("Get-Ac{0}l", hasCursor: true))); 26 | } 27 | 28 | [Fact(Skip = "Need to fix test")] 29 | public async void DoesNotBreakFunctionSyntax() 30 | { 31 | Assert.Equal( 32 | TestBuilder.Create() 33 | .Lines("function Test {") 34 | .Lines(" end {") 35 | .Lines(" Get-ChildItem") 36 | .Lines(" Get-Acl") 37 | .Lines(" }") 38 | .Texts("}"), 39 | await GetRefactoredTextAsync( 40 | TestBuilder.Create() 41 | .Lines("function Test {") 42 | .Lines(" Get-ChildItem") 43 | .Lines(" Get-Ac{0}l", hasCursor: true) 44 | .Texts("}"))); 45 | } 46 | 47 | [Fact] 48 | public async void RetainsIndent() 49 | { 50 | Assert.Equal( 51 | TestBuilder.Create() 52 | .Lines(" end {") 53 | .Lines(" Get-ChildItem") 54 | .Lines(" Get-Acl") 55 | .Texts(" }"), 56 | await GetRefactoredTextAsync( 57 | TestBuilder.Create() 58 | .Lines(" Get-ChildItem") 59 | .Texts(" Get-Acl"))); 60 | } 61 | 62 | private async Task GetRefactoredTextAsync(string testString) 63 | { 64 | return await _refactorService.GetRefactoredString( 65 | testString, 66 | context => NameUnnamedBlockRefactor.ComputeUnnamedBlockNaming( 67 | context, 68 | context.Ast.FindParent(maxDepth: int.MaxValue))) 69 | .ConfigureAwait(false); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/RefactorProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Threading.Tasks; 5 | using EditorServicesCommandSuite.Internal; 6 | 7 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 8 | { 9 | internal abstract class RefactorProvider : IDocumentRefactorProvider 10 | { 11 | protected static readonly ImmutableArray EmptyCodeActions = ImmutableArray.Empty; 12 | 13 | public virtual string Id => GetType().Name; 14 | 15 | public virtual string Name => GetType().Name; 16 | 17 | public virtual string Description => string.Empty; 18 | 19 | public abstract ImmutableArray SupportedActions { get; } 20 | 21 | public abstract Task ComputeCodeActions(DocumentContextBase context); 22 | 23 | public virtual async Task Invoke(DocumentContextBase context) 24 | { 25 | await ComputeCodeActions(context).ConfigureAwait(false); 26 | CodeAction[] codeActions = await context.FinalizeCodeActions().ConfigureAwait(false); 27 | if (codeActions == null || codeActions.Length == 0) 28 | { 29 | return; 30 | } 31 | 32 | CodeAction selectedAction = null; 33 | if (codeActions.Length == 1) 34 | { 35 | selectedAction = codeActions[0]; 36 | } 37 | else 38 | { 39 | IRefactorUI ui = CommandSuite.Instance.UI; 40 | if (CommandSuite.Instance.UI == null) 41 | { 42 | throw new InvalidOperationException( 43 | "This action requires a supported UI to be registered."); 44 | } 45 | 46 | selectedAction = await CommandSuite.Instance.UI.ShowChoicePromptAsync( 47 | RefactorStrings.SelectRefactorCaption, 48 | RefactorStrings.SelectRefactorMessage, 49 | codeActions, 50 | item => item.Title) 51 | .ConfigureAwait(false); 52 | } 53 | 54 | if (selectedAction == null) 55 | { 56 | return; 57 | } 58 | 59 | await ProcessActionForInvoke(context, selectedAction).ConfigureAwait(false); 60 | } 61 | 62 | protected async Task ProcessActionForInvoke(DocumentContextBase context, CodeAction action) 63 | { 64 | await action.ComputeChanges(context).ConfigureAwait(false); 65 | WorkspaceChange[] changes = await context.FinalizeWorkspaceChanges().ConfigureAwait(false); 66 | await CommandSuite.Instance 67 | .ProcessWorkspaceChanges(changes, context.CancellationToken) 68 | .ConfigureAwait(false); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/NodeNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | 5 | namespace EditorServicesCommandSuite.Language 6 | { 7 | /// 8 | /// The exception that is thrown when an expected node (e.g. a 9 | /// or ) is not found. 10 | /// 11 | public class NodeNotFoundException : InvalidOperationException, IContainsErrorRecord 12 | { 13 | private readonly string _errorId; 14 | 15 | private readonly object _target; 16 | 17 | private ErrorRecord _errorRecord; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public NodeNotFoundException() 23 | { 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class 28 | /// with the specified error message. 29 | /// 30 | /// The message that describes the error. 31 | public NodeNotFoundException(string message) 32 | : base(message) 33 | { 34 | } 35 | 36 | /// 37 | /// Initializes a new instance of the class 38 | /// with a specified error message and a reference to the inner exception that 39 | /// is the cause of this exception. 40 | /// 41 | /// The message that describes the error. 42 | /// 43 | /// The exception that is the cause of the current exception. If the 44 | /// parameter is not a null reference, 45 | /// the current exception is raised in a catch block that handles the 46 | /// inner exception. 47 | /// 48 | public NodeNotFoundException(string message, Exception innerException) 49 | : base(message, innerException) 50 | { 51 | } 52 | 53 | internal NodeNotFoundException( 54 | string message, 55 | Exception innerException, 56 | string errorId, 57 | object target) 58 | : base(message, innerException) 59 | { 60 | _errorId = errorId; 61 | _target = target; 62 | } 63 | 64 | /// 65 | /// Gets the which provides additional information 66 | /// about the error. 67 | /// 68 | public ErrorRecord ErrorRecord => _errorRecord ??= new ErrorRecord( 69 | new ParentContainsErrorRecordException(Message), 70 | _errorId ?? "NodeNotFound", 71 | ErrorCategory.ObjectNotFound, 72 | _target); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${relativeFile}: the current opened file relative to workspaceRoot 5 | // ${fileBasename}: the current opened file's basename 6 | // ${fileDirname}: the current opened file's dirname 7 | // ${fileExtname}: the current opened file's extension 8 | // ${cwd}: the current working directory of the spawned process 9 | { 10 | // See https://go.microsoft.com/fwlink/?LinkId=733558 11 | // for the documentation about the tasks.json format 12 | "version": "2.0.0", 13 | "windows": { 14 | "options": { 15 | "shell": { 16 | "executable": "powershell.exe", 17 | "args": [ "-ExecutionPolicy Bypass", "-NoProfile", "-Command" ], 18 | }, 19 | }, 20 | }, 21 | "linux": { 22 | "options": { 23 | "shell": { 24 | "executable": "/usr/bin/pwsh", 25 | "args": [ "-NoProfile", "-Command" ], 26 | }, 27 | }, 28 | }, 29 | "osx": { 30 | "options": { 31 | "shell": { 32 | "executable": "/usr/local/bin/pwsh", 33 | "args": [ "-NoProfile", "-Command" ], 34 | }, 35 | }, 36 | }, 37 | "type": "shell", 38 | "tasks": [ 39 | { 40 | "label": "Clean", 41 | "command": "Invoke-Build", 42 | "args": [ "-Task", "Clean" ], 43 | }, 44 | { 45 | "label": "Build", 46 | "command": "Invoke-Build", 47 | "group": { 48 | "kind": "build", 49 | "isDefault": true, 50 | }, 51 | "problemMatcher": { 52 | "base": "$msCompile", 53 | "fileLocation": "absolute", 54 | }, 55 | }, 56 | { 57 | "label": "Test", 58 | "command": "Invoke-Build", 59 | "args": [ "-Task", "Test" ], 60 | "group": { 61 | "kind": "test", 62 | "isDefault": true, 63 | }, 64 | }, 65 | { 66 | "label": "Test With Coverage", 67 | "group": "test", 68 | "command": "Invoke-Build", 69 | "args": [ "-Task", "Test", "-GenerateCodeCoverage" ], 70 | }, 71 | { 72 | "label": "Build and Test", 73 | "group": "test", 74 | "command": "Invoke-Build", 75 | "args": [ "-Task", "Prerelease"], 76 | }, 77 | { 78 | "label": "Build and Test With Coverage", 79 | "group": "test", 80 | "command": "Invoke-Build", 81 | "args": [ "-Task", "Prerelease", "-GenerateCodeCoverage"], 82 | }, 83 | { 84 | "label": "Install", 85 | "command": "Invoke-Build", 86 | "args": [ "-Task", "Install", "-Configuration", "Release" ], 87 | } 88 | ] 89 | } 90 | 91 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Utility/ManifestInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.IO; 4 | using EditorServicesCommandSuite.Internal; 5 | 6 | namespace EditorServicesCommandSuite.Utility 7 | { 8 | internal sealed class ManifestInfo : PowerShellDataFile 9 | { 10 | private ManifestInfo(string resolvedPath) 11 | : base(resolvedPath) 12 | { 13 | } 14 | 15 | internal string ModuleName => Path.GetFileNameWithoutExtension(FilePath); 16 | 17 | internal string RootModule => GetField(nameof(RootModule)); 18 | 19 | internal Version ModuleVersion => GetField(nameof(ModuleVersion)); 20 | 21 | internal string Author => GetField(nameof(Author)); 22 | 23 | internal string CompanyName => GetField(nameof(CompanyName)); 24 | 25 | internal string Copyright => GetField(nameof(Copyright)); 26 | 27 | internal string Description => GetField(nameof(Description)); 28 | 29 | internal string PowerShellVersion => GetField(nameof(PowerShellVersion)); 30 | 31 | internal string DotNetFrameworkVersion => GetField(nameof(DotNetFrameworkVersion)); 32 | 33 | internal string CLRVersion => GetField(nameof(CLRVersion)); 34 | 35 | internal string ProcessorArchitecture => GetField(nameof(ProcessorArchitecture)); 36 | 37 | internal string RequiredModules => GetField(nameof(RequiredModules)); 38 | 39 | internal string[] FunctionsToExport => GetArrayField(nameof(FunctionsToExport)); 40 | 41 | internal string[] CmdletsToExport => GetArrayField(nameof(CmdletsToExport)); 42 | 43 | internal string[] VariablesToExport => GetArrayField(nameof(VariablesToExport)); 44 | 45 | internal string[] AliasesToExport => GetArrayField(nameof(AliasesToExport)); 46 | 47 | internal string[] FileList => GetArrayField(nameof(FileList)); 48 | 49 | internal System.Collections.Hashtable PrivateData => GetField(nameof(PrivateData)); 50 | 51 | internal static bool TryGetWorkspaceManifest(IRefactorWorkspace workspace, out ManifestInfo manifestInfo) 52 | { 53 | if (workspace == null) 54 | { 55 | manifestInfo = null; 56 | return false; 57 | } 58 | 59 | string sourcePath = Settings.SourceManifestPath; 60 | if (string.IsNullOrEmpty(sourcePath)) 61 | { 62 | manifestInfo = null; 63 | return false; 64 | } 65 | 66 | workspace.TryResolveRelativePath(sourcePath, out bool doesExist, out string manifestPath); 67 | if (!doesExist) 68 | { 69 | manifestInfo = null; 70 | return false; 71 | } 72 | 73 | manifestInfo = GetOrCreate( 74 | manifestPath, 75 | _ => new ManifestInfo(manifestPath)); 76 | 77 | return true; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Reflection/ReflectionCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Reflection; 4 | 5 | namespace EditorServicesCommandSuite.Reflection 6 | { 7 | internal static class ReflectionCache 8 | { 9 | internal static Type PositionHelper; 10 | 11 | internal static Type InternalScriptExtent; 12 | 13 | internal static ConstructorInfo InternalScriptExtent_Ctor; 14 | 15 | internal static PropertyInfo InternalScriptExtent_PositionHelper; 16 | 17 | internal static Type InternalScriptPosition; 18 | 19 | internal static MethodInfo InternalScriptPosition_CloneWithNewOffset; 20 | 21 | internal static PropertyInfo ExpandableStringExpressionAst_FormatExpression; 22 | 23 | internal static Type TypeAccelerators; 24 | 25 | internal static PropertyInfo TypeAccelerators_Get; 26 | 27 | private static readonly BindingFlags s_instance = 28 | BindingFlags.NonPublic | BindingFlags.Instance; 29 | 30 | static ReflectionCache() 31 | { 32 | Initialize(); 33 | } 34 | 35 | internal static void Initialize() 36 | { 37 | PositionHelper = 38 | typeof(PSObject) 39 | .Assembly 40 | .GetType("System.Management.Automation.Language.PositionHelper"); 41 | 42 | InternalScriptExtent = 43 | typeof(PSObject) 44 | .Assembly 45 | .GetType("System.Management.Automation.Language.InternalScriptExtent"); 46 | 47 | InternalScriptExtent_Ctor = 48 | InternalScriptExtent 49 | .GetConstructor( 50 | s_instance, 51 | null, 52 | new[] { PositionHelper, typeof(int), typeof(int) }, 53 | new[] { new ParameterModifier(3) }); 54 | 55 | InternalScriptExtent_PositionHelper = 56 | InternalScriptExtent.GetProperty("PositionHelper", s_instance); 57 | 58 | InternalScriptPosition = 59 | typeof(PSObject) 60 | .Assembly 61 | .GetType("System.Management.Automation.Language.InternalScriptPosition"); 62 | 63 | InternalScriptPosition_CloneWithNewOffset = 64 | InternalScriptPosition 65 | .GetMethod( 66 | "CloneWithNewOffset", 67 | s_instance, 68 | null, 69 | new[] { typeof(int) }, 70 | new[] { new ParameterModifier(1) }); 71 | 72 | ExpandableStringExpressionAst_FormatExpression = 73 | typeof(System.Management.Automation.Language.ExpandableStringExpressionAst) 74 | .GetProperty( 75 | "FormatExpression", 76 | s_instance); 77 | 78 | TypeAccelerators = typeof(PSObject) 79 | .Assembly 80 | .GetType("System.Management.Automation.TypeAccelerators"); 81 | 82 | TypeAccelerators_Get = TypeAccelerators?.GetProperty("Get"); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/EditorServicesNavigationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | using EditorServicesCommandSuite.Internal; 6 | using Microsoft.PowerShell.EditorServices.Extensions; 7 | using Microsoft.PowerShell.EditorServices.Extensions.Services; 8 | 9 | namespace EditorServicesCommandSuite.EditorServices 10 | { 11 | internal class EditorServicesNavigationService : NavigationService, INavigationSupportsOpenDocument 12 | { 13 | private readonly IEditorContextService _context; 14 | 15 | internal EditorServicesNavigationService(IEditorContextService context) 16 | { 17 | _context = context; 18 | } 19 | 20 | public void OpenDocument(string path) 21 | { 22 | OpenDocument(path, CancellationToken.None); 23 | } 24 | 25 | public void OpenDocument(string path, CancellationToken cancellationToken) 26 | { 27 | _context.OpenFileAsync(new Uri(path)) 28 | .GetAwaiter() 29 | .GetResult(); 30 | } 31 | 32 | public Task OpenDocumentAsync(string path) 33 | { 34 | return OpenDocumentAsync(path, CancellationToken.None); 35 | } 36 | 37 | public Task OpenDocumentAsync(string path, CancellationToken cancellationToken) 38 | { 39 | return _context.OpenFileAsync(new Uri(path)); 40 | } 41 | 42 | public override void SetCursorPosition(int line, int column, CancellationToken cancellationToken) 43 | { 44 | _context.SetSelectionAsync( 45 | new LspRange( 46 | new LspFilePosition(line - 1, column - 1), 47 | new LspFilePosition(line - 1, column - 1))) 48 | .GetAwaiter() 49 | .GetResult(); 50 | } 51 | 52 | public override async Task SetCursorPositionAsync(int line, int column, CancellationToken cancellationToken) 53 | { 54 | await _context.SetSelectionAsync( 55 | new LspRange( 56 | new LspFilePosition(line - 1, column - 1), 57 | new LspFilePosition(line - 1, column - 1))) 58 | .ConfigureAwait(false); 59 | } 60 | 61 | public override void SetSelection( 62 | int startLine, 63 | int startColumn, 64 | int endLine, 65 | int endColumn, 66 | CancellationToken cancellationToken) 67 | { 68 | _context.SetSelectionAsync( 69 | new LspRange( 70 | new LspFilePosition(startLine - 1, startColumn - 1), 71 | new LspFilePosition(endLine - 1, endColumn - 1))) 72 | .GetAwaiter() 73 | .GetResult(); 74 | } 75 | 76 | public override async Task SetSelectionAsync(int startLine, int startColumn, int endLine, int endColumn, CancellationToken cancellationToken) 77 | { 78 | await _context.SetSelectionAsync( 79 | new LspRange( 80 | new LspFilePosition(startLine - 1, startColumn - 1), 81 | new LspFilePosition(endLine - 1, endColumn - 1))) 82 | .ConfigureAwait(false); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.PSReadLine/Internal/CommandSuite.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using System.Management.Automation.Host; 3 | using EditorServicesCommandSuite.Internal; 4 | 5 | namespace EditorServicesCommandSuite.PSReadLine.Internal 6 | { 7 | /// 8 | /// Provides a central entry point for interacting with a PSReadLine based command 9 | /// suite session. 10 | /// 11 | public sealed class CommandSuite : EditorServicesCommandSuite.Internal.CommandSuite 12 | { 13 | private readonly PSReadLineNavigationService _navigation; 14 | 15 | private CommandSuite(EngineIntrinsics engine, PSHost host) 16 | : base(engine, host) 17 | { 18 | Documents = new DocumentService(); 19 | UI = new UIService(); 20 | DocumentContext = new ContextService(); 21 | Diagnostics = new NullDiagnosticService(); 22 | _navigation = new PSReadLineNavigationService(); 23 | } 24 | 25 | /// 26 | /// Gets the diagnostics provider. 27 | /// 28 | internal override IRefactorAnalysisContext Diagnostics { get; } 29 | 30 | /// 31 | /// Gets the processor for objects. 32 | /// 33 | internal override IDocumentEditProcessor Documents { get; } 34 | 35 | /// 36 | /// Gets the interface for interacting with the UI. 37 | /// 38 | internal override IRefactorUI UI { get; } 39 | 40 | /// 41 | /// Gets the interface for getting information about the users current 42 | /// state in an open document (e.g. cursor position, selection, etc). 43 | /// 44 | internal override DocumentContextProvider DocumentContext { get; } 45 | 46 | /// 47 | /// Gets the command suite instance for the process, or creates 48 | /// it if it does not exist yet. 49 | /// 50 | /// The PowerShell engine. 51 | /// The PowerShell host. 52 | /// 53 | /// The command suite instance for the process. 54 | /// 55 | public static EditorServicesCommandSuite.Internal.CommandSuite GetCommandSuite( 56 | EngineIntrinsics engine, 57 | PSHost host) 58 | { 59 | try 60 | { 61 | return Instance; 62 | } 63 | catch (NoCommandSuiteInstanceException) 64 | { 65 | // Exception is thrown when CommandSuite has not yet been created. 66 | } 67 | 68 | var commandSuite = new CommandSuite(engine, host); 69 | commandSuite.InitializeRefactorProviders(); 70 | return commandSuite; 71 | } 72 | 73 | /// 74 | /// Get the that will be used to create the internal 75 | /// navigation service. 76 | /// 77 | /// The . 78 | private protected override NavigationService GetNavigationServiceImpl() 79 | { 80 | return _navigation; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Language/UsingUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Management.Automation.Language; 5 | using System.Text; 6 | using EditorServicesCommandSuite.CodeGeneration.Refactors; 7 | using EditorServicesCommandSuite.Utility; 8 | 9 | namespace EditorServicesCommandSuite.Language 10 | { 11 | internal static class UsingUtilities 12 | { 13 | private static UsingStatementsConfiguration _config; 14 | 15 | private static UsingStatementsConfiguration Config 16 | { 17 | get 18 | { 19 | if (_config != null) 20 | { 21 | return _config; 22 | } 23 | 24 | _config = new UsingStatementsConfiguration(); 25 | Settings.Main.Changed += 26 | (_, __) => _config = new UsingStatementsConfiguration(); 27 | 28 | return _config; 29 | } 30 | } 31 | 32 | public static string GetUsingStatementString(IEnumerable usings) 33 | { 34 | var sb = new StringBuilder(); 35 | var first = true; 36 | foreach (var group in GetUsingGroups(usings)) 37 | { 38 | if (first) 39 | { 40 | first = false; 41 | } 42 | else 43 | { 44 | if (Config.SeparateGroupsWithNewLine) 45 | { 46 | sb.AppendLine(); 47 | } 48 | 49 | sb.AppendLine(); 50 | } 51 | 52 | var sortedGroup = 53 | Config.SystemNamespaceFirst 54 | ? group 55 | .OrderByDescending(u => u.Text.StartsWith("System")) 56 | .ThenBy(u => u.Text) 57 | : group.OrderBy(u => u.Text); 58 | 59 | sb.Append(string.Join(Settings.NewLine, sortedGroup)); 60 | } 61 | 62 | return sb.ToString(); 63 | } 64 | 65 | private static IEnumerable> GetUsingGroups(IEnumerable usings) 66 | { 67 | return usings 68 | .GroupBy(u => u.Kind) 69 | .OrderBy(ug => Array.IndexOf(Config.UsingKindOrder, ug.Key)); 70 | } 71 | 72 | private class UsingStatementsConfiguration : RefactorConfiguration 73 | { 74 | [DefaultFromSetting("UsingStatements.SeparateGroupsWithNewLine", Default = "$true")] 75 | public bool SeparateGroupsWithNewLine { get; set; } = true; 76 | 77 | [DefaultFromSetting("UsingStatements.SystemNamespaceFirst", Default = "$true")] 78 | public bool SystemNamespaceFirst { get; set; } = true; 79 | 80 | [DefaultFromSetting("UsingStatements.UsingKindOrder", Default = "'Assembly', 'Module', 'Namespace'")] 81 | public UsingStatementKind[] UsingKindOrder { get; set; } = new UsingStatementKind[] 82 | { 83 | UsingStatementKind.Assembly, 84 | UsingStatementKind.Module, 85 | UsingStatementKind.Namespace, 86 | }; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/ConfigureRefactorBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Runtime.CompilerServices; 8 | using EditorServicesCommandSuite.Utility; 9 | 10 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 11 | { 12 | internal sealed class ConfigureRefactorBinder : CallSiteBinder 13 | { 14 | private static readonly MethodInfo s_tryGetSetting = 15 | typeof(Settings).GetMethod( 16 | nameof(Settings.TryGetSetting), 17 | BindingFlags.NonPublic | BindingFlags.Static, 18 | null, 19 | new[] { typeof(string), typeof(Type), typeof(object).MakeByRefType() }, 20 | new[] { new ParameterModifier(3) }); 21 | 22 | private static readonly ConcurrentDictionary>> s_binderCache = 23 | new ConcurrentDictionary>>(); 24 | 25 | private readonly Type _configType; 26 | 27 | private ConfigureRefactorBinder(Type configType) 28 | { 29 | _configType = configType; 30 | } 31 | 32 | public override Expression Bind( 33 | object[] args, 34 | ReadOnlyCollection parameters, 35 | LabelTarget returnLabel) 36 | { 37 | ParameterExpression tempVar = Expression.Variable(typeof(object)); 38 | var expressions = new List(); 39 | foreach (PropertyInfo property in _configType.GetProperties()) 40 | { 41 | var fromSetting = property.GetCustomAttribute(inherit: true); 42 | if (fromSetting == null) 43 | { 44 | continue; 45 | } 46 | 47 | expressions.Add( 48 | Expression.IfThen( 49 | Expression.Call( 50 | s_tryGetSetting, 51 | Expression.Constant(fromSetting.Key, typeof(string)), 52 | Expression.Constant(property.PropertyType, typeof(Type)), 53 | tempVar), 54 | Expression.Assign( 55 | Expression.Property( 56 | Expression.Convert(parameters[0], _configType), 57 | property), 58 | Expression.Convert(tempVar, property.PropertyType)))); 59 | } 60 | 61 | if (expressions.Count == 0) 62 | { 63 | return Expression.Return(returnLabel); 64 | } 65 | 66 | expressions.Add(Expression.Return(returnLabel)); 67 | return Expression.Block(new[] { tempVar }, expressions); 68 | } 69 | 70 | internal static CallSite> Get(RefactorConfiguration configuration) 71 | { 72 | return s_binderCache.GetOrAdd( 73 | configuration.GetType(), 74 | type => CallSite>.Create(new ConfigureRefactorBinder(type))); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at seeminglyscience@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | 76 | -------------------------------------------------------------------------------- /tools/AssertPSRL.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateNotNull()] 4 | [string] $RequiredVersion = '2.0.0-beta1' 5 | ) 6 | begin { 7 | Add-Type -AssemblyName System.IO.Compression 8 | function SaveEntry { 9 | param( 10 | [System.IO.Compression.ZipArchiveEntry] $Entry, 11 | [string] $Destination 12 | ) 13 | end { 14 | if (-not (Test-Path $Destination -PathType Container)) { 15 | throw 'Destination path must be a directory' 16 | } 17 | 18 | $Destination = Join-Path (Resolve-Path $Destination) -ChildPath $Entry.Name 19 | $entryStream = $Entry.Open() 20 | try { 21 | $destinationStream = [System.IO.FileStream]::new( 22 | <# path: #> $Destination, 23 | <# mode: #> [System.IO.FileMode]::Create, 24 | <# access: #> [System.IO.FileAccess]::Write, 25 | <# share: #> [System.IO.FileShare]::ReadWrite) 26 | try { 27 | $entryStream.CopyTo($destinationStream) 28 | } finally { 29 | $destinationStream.Dispose() 30 | } 31 | } finally { 32 | $entryStream.Dispose() 33 | } 34 | } 35 | } 36 | } 37 | end { 38 | $psrlFolder = $PSCmdlet.GetUnresolvedProviderPathFromPSPath("$PSScriptRoot/../lib/PSReadLine") 39 | 40 | if (Test-Path $psrlFolder\Microsoft.PowerShell.PSReadLine2.dll) { 41 | return 42 | } 43 | 44 | if (-not (Test-Path $psrlFolder)) { 45 | $null = New-Item $psrlFolder -ItemType Directory -Force 46 | } 47 | 48 | $version = $RequiredVersion 49 | 50 | $oldSecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol 51 | try { 52 | [System.Net.ServicePointManager]::SecurityProtocol = 'Tls, Tls11, Tls12' 53 | $downloadUri = "https://github.com/lzybkr/PSReadLine/releases/download/v$version/PSReadLine.zip" 54 | Invoke-WebRequest -UseBasicParsing -Uri $downloadUri -OutFile $psrlFolder/PSReadLine.zip 55 | } finally { 56 | [System.Net.ServicePointManager]::SecurityProtocol = $oldSecurityProtocol 57 | } 58 | 59 | # Why do all this when Expand-Archive exists? Well, it doesn't always resolve for me for some 60 | # reason. Still gotta figure that out, but this also lets us pick and choose what we want from 61 | # the archive anyway. 62 | $fileStream = [System.IO.FileStream]::new( 63 | <# path: #> (Join-Path $psrlFolder -ChildPath 'PSReadLine.zip'), 64 | <# mode: #> [System.IO.FileMode]::Open, 65 | <# access: #> [System.IO.FileAccess]::Read, 66 | <# share: #> [System.IO.FileShare]::ReadWrite) 67 | 68 | try { 69 | $archiveStream = [System.IO.Compression.ZipArchive]::new( 70 | <# stream: #> $fileStream, 71 | <# mode: #> [System.IO.Compression.ZipArchiveMode]::Read) 72 | 73 | try { 74 | $null = New-Item $psrlFolder -ItemType Directory -Force -ErrorAction Ignore 75 | foreach($entry in $archiveStream.Entries) { 76 | if (0 -eq $entry.Length -or $entry.FullName -notmatch '\.dll$') { 77 | continue 78 | } 79 | 80 | SaveEntry -Entry $entry -Destination $psrlFolder 81 | } 82 | } finally { 83 | $archiveStream.Dispose() 84 | } 85 | } finally { 86 | $fileStream.Dispose() 87 | Remove-Item $psrlFolder\PSReadLine.zip -Force 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite.EditorServices/DiagnosticsService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Management.Automation; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using EditorServicesCommandSuite.Internal; 10 | using EditorServicesCommandSuite.Utility; 11 | 12 | namespace EditorServicesCommandSuite.EditorServices 13 | { 14 | internal class DiagnosticsService : IRefactorAnalysisContext 15 | { 16 | private static readonly DiagnosticMarker[] s_emptyMarkers = new DiagnosticMarker[0]; 17 | 18 | public async Task> GetDiagnosticsFromPathAsync( 19 | string path, 20 | ThreadController pipelineThread, 21 | CancellationToken cancellationToken) 22 | { 23 | if (string.IsNullOrWhiteSpace(path)) 24 | { 25 | return s_emptyMarkers; 26 | } 27 | 28 | if (!File.Exists(Path.GetFullPath(path))) 29 | { 30 | return s_emptyMarkers; 31 | } 32 | 33 | return await GetDiagnosticsImplAsync( 34 | "Path", 35 | path, 36 | pipelineThread, 37 | cancellationToken) 38 | .ConfigureAwait(false); 39 | } 40 | 41 | public async Task> GetDiagnosticsFromContentsAsync( 42 | string contents, 43 | ThreadController pipelineThread, 44 | CancellationToken cancellationToken) 45 | { 46 | if (string.IsNullOrWhiteSpace(contents)) 47 | { 48 | return s_emptyMarkers; 49 | } 50 | 51 | return await GetDiagnosticsImplAsync( 52 | "ScriptDefinition", 53 | contents, 54 | pipelineThread, 55 | cancellationToken) 56 | .ConfigureAwait(false); 57 | } 58 | 59 | private static DiagnosticMarker ToDiagnosticMarker(PSObject marker) 60 | { 61 | return (DiagnosticMarker)LanguagePrimitives.ConvertPSObjectToType( 62 | marker, 63 | typeof(DiagnosticMarker), 64 | recursion: true, 65 | CultureInfo.InvariantCulture, 66 | ignoreUnknownMembers: true); 67 | } 68 | 69 | private async Task> GetDiagnosticsImplAsync( 70 | string pssaParameterName, 71 | string value, 72 | ThreadController pipelineThread, 73 | CancellationToken cancellationToken) 74 | { 75 | return await pipelineThread.InvokeAsync( 76 | () => 77 | { 78 | using (var pwsh = PowerShell.Create(RunspaceMode.CurrentRunspace)) 79 | using (cancellationToken.Register(() => pwsh.BeginStop(null, null))) 80 | { 81 | return 82 | pwsh.AddCommand("PSScriptAnalyzer\\Invoke-ScriptAnalyzer") 83 | .AddParameter(pssaParameterName, value) 84 | .AddCommand("Microsoft.PowerShell.Utility\\Select-Object") 85 | .AddParameter("Property", "*") 86 | .Invoke() 87 | .Select(ToDiagnosticMarker); 88 | } 89 | }).ConfigureAwait(false); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Commands/CommandSuiteSettingCompleter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Management.Automation; 6 | using System.Management.Automation.Language; 7 | using EditorServicesCommandSuite.Utility; 8 | 9 | namespace EditorServicesCommandSuite.Commands 10 | { 11 | /// 12 | /// Provides argument completion for Command Suite setting names. 13 | /// 14 | public class CommandSuiteSettingCompleter : IArgumentCompleter 15 | { 16 | private static CommandSuiteSettingInfo[] s_settingCache; 17 | 18 | /// 19 | /// Called by PowerShell to complete arguments for CommandSuiteSetting cmdlets. 20 | /// 21 | /// The name of the command that needs argument completion. 22 | /// The name of the parameter that needs argument completion. 23 | /// The (possibly empty) word being completed. 24 | /// The command AST in case it is needed for completion. 25 | /// 26 | /// This parameter is similar to $PSBoundParameters, except that sometimes PowerShell 27 | /// cannot or will not attempt to evaluate an argument, in which case you may need to 28 | /// use commandAst. 29 | /// 30 | /// 31 | /// A collection of completion results, most with ResultType set to ParameterValue. 32 | /// 33 | public IEnumerable CompleteArgument( 34 | string commandName, 35 | string parameterName, 36 | string wordToComplete, 37 | CommandAst commandAst, 38 | IDictionary fakeBoundParameters) 39 | { 40 | var wildcardPattern = new WildcardPattern(wordToComplete + "*", WildcardOptions.IgnoreCase); 41 | Func filterDelegate = GetFilterDelegate(wildcardPattern, parameterName); 42 | 43 | return GetSettingsCache() 44 | .Where(filterDelegate) 45 | .Select(setting => 46 | new CompletionResult( 47 | GetCompletionValue(parameterName, setting), 48 | setting.FullName, 49 | CompletionResultType.ParameterValue, 50 | string.IsNullOrEmpty(setting.Description) ? setting.FullName : setting.Description)); 51 | } 52 | 53 | private static string GetCompletionValue(string parameterName, CommandSuiteSettingInfo setting) 54 | { 55 | if (parameterName.Equals("Name", System.StringComparison.OrdinalIgnoreCase)) 56 | { 57 | return setting.Name; 58 | } 59 | 60 | return setting.FullName; 61 | } 62 | 63 | private static CommandSuiteSettingInfo[] GetSettingsCache() 64 | { 65 | return s_settingCache ?? (s_settingCache = Settings.GetAllSettings().ToArray()); 66 | } 67 | 68 | private Func GetFilterDelegate( 69 | WildcardPattern pattern, 70 | string parameterName) 71 | { 72 | if (parameterName.Equals("FullName", StringComparison.OrdinalIgnoreCase)) 73 | { 74 | return new Func( 75 | setting => pattern.IsMatch(setting.FullName)); 76 | } 77 | 78 | return new Func( 79 | setting => pattern.IsMatch(setting.Name)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/CodeGeneration/Refactors/InternalNavigationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using EditorServicesCommandSuite.Internal; 5 | 6 | namespace EditorServicesCommandSuite.CodeGeneration.Refactors 7 | { 8 | internal class InternalNavigationService : NavigationService, INavigationSupportsOpenDocument 9 | { 10 | private NavigationService _external; 11 | 12 | private INavigationSupportsOpenDocument _documentOpener; 13 | 14 | internal InternalNavigationService(NavigationService externalService) 15 | { 16 | ExternalService = externalService; 17 | } 18 | 19 | internal NavigationService ExternalService 20 | { 21 | get 22 | { 23 | return _external; 24 | } 25 | 26 | set 27 | { 28 | _external = value; 29 | _documentOpener = value as INavigationSupportsOpenDocument; 30 | } 31 | } 32 | 33 | internal bool DoesSupportOpenDocument => _documentOpener != null; 34 | 35 | public void OpenDocument(string path) 36 | { 37 | if (DoesSupportOpenDocument) 38 | { 39 | _documentOpener.OpenDocument(path); 40 | return; 41 | } 42 | 43 | throw new NotSupportedException(); 44 | } 45 | 46 | public void OpenDocument(string path, CancellationToken cancellationToken) 47 | { 48 | if (DoesSupportOpenDocument) 49 | { 50 | _documentOpener.OpenDocument(path, cancellationToken); 51 | return; 52 | } 53 | 54 | throw new NotSupportedException(); 55 | } 56 | 57 | public Task OpenDocumentAsync(string path) 58 | { 59 | if (DoesSupportOpenDocument) 60 | { 61 | return _documentOpener.OpenDocumentAsync(path); 62 | } 63 | 64 | throw new NotSupportedException(); 65 | } 66 | 67 | public Task OpenDocumentAsync(string path, CancellationToken cancellationToken) 68 | { 69 | if (DoesSupportOpenDocument) 70 | { 71 | return _documentOpener.OpenDocumentAsync(path, cancellationToken); 72 | } 73 | 74 | throw new NotSupportedException(); 75 | } 76 | 77 | public override void SetCursorPosition(int line, int column, CancellationToken cancellationToken) 78 | { 79 | ExternalService.SetCursorPosition(line, column, cancellationToken); 80 | } 81 | 82 | public override Task SetCursorPositionAsync(int line, int column, CancellationToken cancellationToken) 83 | { 84 | return ExternalService.SetCursorPositionAsync(line, column, cancellationToken); 85 | } 86 | 87 | public override void SetSelection(int startLine, int startColumn, int endLine, int endColumn, CancellationToken cancellationToken) 88 | { 89 | ExternalService.SetSelection( 90 | startLine, 91 | startColumn, 92 | endLine, 93 | endColumn, 94 | cancellationToken); 95 | } 96 | 97 | public override Task SetSelectionAsync(int startLine, int startColumn, int endLine, int endColumn, CancellationToken cancellationToken) 98 | { 99 | return ExternalService.SetSelectionAsync( 100 | startLine, 101 | startColumn, 102 | endLine, 103 | endColumn, 104 | cancellationToken); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/EditorServicesCommandSuite/Utility/CommandSuiteSettingInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using System.Management.Automation; 5 | using EditorServicesCommandSuite.Internal; 6 | 7 | namespace EditorServicesCommandSuite.Utility 8 | { 9 | /// 10 | /// Represents an individual setting for the current Command Suite session. 11 | /// 12 | public class CommandSuiteSettingInfo 13 | { 14 | /// 15 | /// Contains the default value displayed by the formatter when a setting is not set. 16 | /// 17 | [Hidden, EditorBrowsable(EditorBrowsableState.Never)] 18 | public static readonly string DefaultValueDisplay = 19 | string.Concat( 20 | Symbols.LessThan, 21 | SettingsStrings.SettingInfoDefaultValueDisplay, 22 | Symbols.GreaterThan); 23 | 24 | /// 25 | /// Contains the default value displayed by the formatter when a setting does not belong 26 | /// to a group. 27 | /// 28 | [Hidden, EditorBrowsable(EditorBrowsableState.Never)] 29 | public static readonly string DefaultGroupName = SettingsStrings.SettingInfoNoGroupMessage; 30 | 31 | private readonly string _descriptionResourceName; 32 | 33 | private readonly Type _typeOfValue; 34 | 35 | internal CommandSuiteSettingInfo(string key, Type typeOfValue, string defaultAsExpression) 36 | { 37 | _typeOfValue = typeOfValue ?? typeof(object); 38 | FullName = key; 39 | NameParts = key.Split(Symbols.Dot); 40 | 41 | _descriptionResourceName = 42 | string.Join(Symbols.Underscore.ToString(), NameParts) + "Description"; 43 | 44 | DefaultAsExpression = string.IsNullOrWhiteSpace(defaultAsExpression) 45 | ? new string(Symbols.Null) 46 | : defaultAsExpression; 47 | 48 | if (NameParts.Length == 1) 49 | { 50 | Group = string.Empty; 51 | return; 52 | } 53 | 54 | Group = string.Join(Symbols.Dot.ToString(), NameParts.Take(NameParts.Length - 1)); 55 | } 56 | 57 | /// 58 | /// Gets the name of the setting. 59 | /// 60 | public string Name => NameParts.Last(); 61 | 62 | /// 63 | /// Gets the description of the setting. 64 | /// 65 | public string Description => 66 | SettingsStrings.ResourceManager.GetString( 67 | string.Join(Symbols.Underscore.ToString(), NameParts) + "Description"); 68 | 69 | /// 70 | /// Gets the full name including group. 71 | /// 72 | public string FullName { get; } 73 | 74 | /// 75 | /// Gets the group the setting belongs to. 76 | /// 77 | public string Group { get; } 78 | 79 | /// 80 | /// Gets or sets the current value of the setting. 81 | /// 82 | public object Value 83 | { 84 | get { return Settings.GetSetting(FullName, _typeOfValue); } 85 | set { Settings.SetSetting(FullName, value, _typeOfValue); } 86 | } 87 | 88 | /// 89 | /// Gets an array containing each part of the settings group name as 90 | /// well as the setting's name. 91 | /// 92 | internal string[] NameParts { get; } 93 | 94 | /// 95 | /// Gets the default value of the setting as it would be expressed in a 96 | /// PowerShell script. 97 | /// 98 | internal string DefaultAsExpression { get; } 99 | } 100 | } 101 | --------------------------------------------------------------------------------