Notices: This section not yet converted to new layout. Download stats are rolling back out.

EditorServicesCommandSuite (PowerShell Module)

0.4.0

This package skips automatic verification:

This module requires PS 5.1 - can it be exempted from testing.


This package was approved by moderator gep13 on 5/15/2018.

PlatyPS provides a way to:

  • Write PowerShell External Help in Markdown
  • Generate markdown help (example) for your existing modules
  • Keep markdown help up-to-date with your code

Markdown help docs can be generated from old external help files (also known as MAML-xml help), the command objects (reflection), or both.

PlatyPS can also generate cab files for Update-Help.

Why?

Traditionally PowerShell external help files have been authored by hand or using complex tool chains and rendered as MAML XML for use as console help. MAML is cumbersome to edit by hand, and common tools and editors don't support it for complex scenarios like they do with Markdown. PlatyPS is provided as a solution for allow documenting PowerShell help in any editor or tool that supports Markdown.

An additional challenge PlatyPS tackles, is to handle PowerShell documentation for complex scenarios (e.g. very large, closed source, and/or C#/binary modules) where it may be desirable to have documentation abstracted away from the codebase. PlatyPS does not need source access to generate documentation.

Markdown is designed to be human-readable, without rendering. This makes writing and editing easy and efficient. Many editors support it (Visual Studio Code, Sublime Text, etc), and many tools and collaboration platforms (GitHub, Visual Studio Online) render the Markdown nicely.

NOTE: This is an automatically updated package. If you find it is out of date by more than a week, please contact the maintainer(s) and let them know the package is no longer updating correctly.

To install EditorServicesCommandSuite (PowerShell Module), run the following command from the command line or from PowerShell:

C:\> choco install editor-services-command-suite

To upgrade EditorServicesCommandSuite (PowerShell Module), run the following command from the command line or from PowerShell:

C:\> choco upgrade editor-services-command-suite

Files

Hide
  • tools\.skipAutoUninstaller
  • tools\chocolateyBeforeModify.ps1 Show
    $ErrorActionPreference = 'Stop'
    
    $moduleName = 'EditorServicesCommandSuite'      # this could be different from package name
    Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
  • tools\chocolateyInstall.ps1 Show
    $ErrorActionPreference = 'Stop'
    
    $toolsDir   = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
    $moduleName = 'EditorServicesCommandSuite'  # this may be different from the package name and different case
    
    if ($PSVersionTable.PSVersion -lt [Version]"5.1") {
        throw "$moduleName module requires a minimum of PowerShell v5.1."
    }
    
    # module may already be installed outside of Chocolatey
    Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
    
    $sourcePath = Join-Path -Path $toolsDir -ChildPath "$modulename\*"
    $destPath   = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$moduleName"
    
    $manifestFile = Join-Path -Path $toolsDir -ChildPath "$moduleName\$moduleName.psd1"
    Write-Verbose "Searching manifest file '$manifestFile' for module version."
    $verFound = Get-Content -Path $manifestFile -Raw | ForEach-Object { $_ -match '\s*ModuleVersion\s*=\s*[''|""]{1}(?<version>[\d|\.]+)[''|""]{1}' }
    if (-not $verFound) {
        throw "Cannot find 'ModuleVersion' in manifest file '$manifestFile'."
    }
    
    Write-Verbose "Module version '$($matches.version)' found."
    $destPath = Join-Path -Path $destPath -ChildPath $matches.version
    
    Write-Verbose "Creating destination directory '$destPath' for module."
    New-Item -Path $destPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
    
    Write-Verbose "Moving '$moduleName' files from '$sourcePath' to '$destPath'."
    Move-Item -Path $sourcePath -Destination $destPath -Force
  • tools\chocolateyUninstall.ps1 Show
    $ErrorActionPreference = 'Stop'
    
    $moduleName = 'EditorServicesCommandSuite'
    $sourcePath = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$moduleName"
    
    Write-Verbose "Removing all version of '$moduleName' from '$sourcePath'."
    Remove-Item -Path $sourcePath -Recurse -Force -ErrorAction SilentlyContinue
  • tools\EditorServicesCommandSuite\Classes\Async.ps1 Show
    using namespace System.Collections.Generic
    using namespace System.Collections.ObjectModel
    using namespace System.Linq.Expressions
    using namespace System.Management.Automation
    using namespace System.Management.Automation.Runspaces
    using namespace System.Threading
    using namespace System.Threading.Tasks
    
    # Static class that facilitates the use of traditional .NET async techniques in PowerShell.
    class AsyncOps {
        static [PSTaskFactory] $Factory = [PSTaskFactory]::new();
    
        static [Task] ContinueWithCodeMethod([psobject] $instance, [scriptblock] $continuationAction) {
            $delegate = [AsyncOps]::CreateAsyncDelegate(
                $continuationAction,
                [Action[Task[Collection[psobject]]]])
    
            return [AsyncOps]::PrepareTask($instance.psadapted.ContinueWith($delegate))
        }
    
        # - Hides the result property on a Task object. This is done because the getter for Result waits
        #   for the task to finish, even if just output to the console.
        # - Adds ContinueWith code method that wraps scriptblocks with CreateAsyncDelegate
        static [Task] PrepareTask([Task] $target) {
            $propertyList    = $target.psobject.Properties.Name -notmatch 'Result' -as [string[]]
            $propertySet     = [PSPropertySet]::new('DefaultDisplayPropertySet', $propertyList) -as [PSMemberInfo[]]
            $standardMembers = [PSMemberSet]::new('PSStandardMembers', $propertySet)
    
            $target.psobject.Members.Add($standardMembers)
    
            $target.psobject.Methods.Add(
                [PSCodeMethod]::new(
                    'ContinueWith',
                    [AsyncOps].GetMethod('ContinueWithCodeMethod')))
    
            return $target
        }
    
        static [MulticastDelegate] CreateAsyncDelegate([scriptblock] $function, [type] $delegateType) {
            return [AsyncOps]::CreateAsyncDelegate($function, $delegateType, [AsyncOps]::Factory)
        }
    
        # Create a delegate from a scriptblock that can be used in threads without runspaces, like those
        # used in Tasks or AsyncCallbacks.
        static [MulticastDelegate] CreateAsyncDelegate([scriptblock] $function, [type] $delegateType, [PSTaskFactory] $factory) {
            $invokeMethod = $delegateType.GetMethod('Invoke')
            $returnType = $invokeMethod.ReturnType
            # Create a parameter expression for each parameter the delegate takes.
            $parameters = $invokeMethod.
                GetParameters().
                ForEach{ [Expression]::Parameter($PSItem.ParameterType, $PSItem.Name) }
    
            $scriptParameters = [string]::Empty
            if ($parameters) {
                $scriptParameters = '$' + ($invokeMethod.GetParameters().Name -join ', $')
            }
    
            # Allow access to parameters in the following ways:
            # - By the name given to them by the delegate's invoke method
            # - $args
            # - $PSItem/$_  (first parameter only)
            $preparedScript =
                'param({0}) process {{ return {{ {1} }}.InvokeReturnAsIs($PSBoundParameters.Values) }}' -f
                $scriptParameters,
                $function
    
            # Prepare variable and constant expressions.
            $scriptText = [Expression]::Constant($preparedScript, [string])
            $ps         = [Expression]::Variable([powershell], 'ps')
            $collectionResultType = [Collection[psobject]]
            if ($returnType -ne [void] -and $returnType -ne [Collection[psobject]]) {
                $collectionResultType = [Collection`1].MakeGenericType($returnType)
            }
    
            $result     = [Expression]::Variable($collectionResultType, 'result')
            $psInput    = [Expression]::Variable([Object[]], 'psInput')
            $guid       = [Expression]::Constant($factory.InstanceId, [guid])
            $pool       = [Expression]::Property(
                [Expression]::Property(
                    [Expression]::Property($null, [PSTaskFactory], 'Instances'),
                    'Item',
                    $guid),
                'RunspacePool')
    
            # Group the expressions for the body by creating them in a scriptblock.
            [Expression[]]$expressions = & {
                [Expression]::Assign($ps, [Expression]::Call([powershell], 'Create', @(), @()))
                [Expression]::Assign([Expression]::Property($ps, 'RunspacePool'), $pool)
                [Expression]::Call($ps, 'AddScript', @(), $scriptText)
    
                foreach ($parameter in $parameters) {
                    [Expression]::Call($ps, 'AddArgument', @(), $parameter)
                }
    
                $invokeArgs = @()
                if ($parameters) {
                    [Expression]::Assign(
                        $psInput,
                        [Expression]::NewArrayInit([object], $parameters[0] -as [Expression[]]))
    
                    $invokeArgs = @($psInput)
                }
    
                $invokeTypeArgs = @()
                if ($returnType -ne [void] -and $returnType -ne [Collection[psobject]]) {
                    $invokeTypeArgs = @($returnType)
                }
    
                [Expression]::Assign($result, [Expression]::Call($ps, 'Invoke', $invokeTypeArgs, $invokeArgs))
                [Expression]::Call($ps, 'Dispose', @(), @())
                if ($returnType -ne [void]) {
                    [Expression]::Call(
                        [LanguagePrimitives],
                        'ConvertTo',
                        @($returnType),
                        $result -as [Expression[]])
                } else {
                    $result
                }
            }
    
            $block  = [Expression]::Block([ParameterExpression[]]($ps, $result, $psInput), $expressions)
            $lambda = [Expression]::Lambda(
                $delegateType,
                $block,
                $parameters -as [ParameterExpression[]])
            return $lambda.Compile()
        }
    }
    
    # A TaskFactory implementation that creates tasks that run scriptblocks in a runspace pool.
    class PSTaskFactory : TaskFactory[Collection[psobject]] {
        hidden static [Dictionary[guid, PSTaskFactory]] $Instances = [Dictionary[guid, PSTaskFactory]]::new()
    
        hidden [RunspacePool] $RunspacePool;
        hidden [guid] $InstanceId;
        hidden [bool] $IsDisposed = $false;
    
        PSTaskFactory() : base() {
            $this.Initialize()
        }
    
        PSTaskFactory([CancellationToken] $cancellationToken) : base($cancellationToken) {
            $this.Initialize()
        }
    
        PSTaskFactory([TaskScheduler] $scheduler) : base($scheduler) {
            $this.Initialize()
        }
    
        PSTaskFactory(
            [TaskCreationOptions] $creationOptions,
            [TaskContinuationOptions] $continuationOptions)
            : base($creationOptions, $continuationOptions) {
            $this.Initialize()
        }
    
        PSTaskFactory(
            [CancellationToken] $cancellationToken,
            [TaskCreationOptions] $creationOptions,
            [TaskContinuationOptions] $continuationOptions,
            [TaskScheduler] $scheduler)
            : base($cancellationToken, $creationOptions, $continuationAction, $scheduler) {
            $this.Initialize()
        }
    
        hidden [void] Initialize() {
            $this.RunspacePool = [runspacefactory]::CreateRunspacePool(1, 4)
            $this.RunspacePool.Open()
            $this.InstanceId = [guid]::NewGuid()
            [PSTaskFactory]::Instances.Add($this.InstanceId, $this)
        }
    
        # Can't implement IDisposable while inheriting a generic class because of a parse error, need
        # to create an issue.
        [void] Dispose() {
            $this.AssertNotDisposed()
            [PSTaskFactory]::Instances.Remove($this.InstanceId)
            $this.RunspacePool.Dispose()
            $this.IsDisposed = $true
        }
    
        hidden [void] AssertNotDisposed() {
            if ($this.IsDisposed) {
                throw [InvalidOperationException]::new(
                    'Cannot perform operation because object "PSTaskFactory" has already been disposed.')
            }
        }
    
        # Shortcut to AsyncOps.CreateAsyncDelegate
        hidden [MulticastDelegate] Wrap([scriptblock] $function, [type] $delegateType) {
            $this.AssertNotDisposed()
            return [AsyncOps]::CreateAsyncDelegate($function, $delegateType, $this)
        }
    
        # The remaining functions implement methods from TaskFactory. All of these methods call the base
        # method after wrapping the scriptblock to create a delegate that will work in tasks.
        [Task[Collection[psobject]]] ContinueWhenAll([Task[]] $tasks, [scriptblock] $continuationAction) {
            $this.AssertNotDisposed()
            $delegateType = [Func`2].MakeGenericType([Task[]], [Collection[psobject]])
            return [AsyncOps]::PrepareTask(
                ([TaskFactory[Collection[psobject]]]$this).ContinueWhenAll(
                    $tasks,
                    $this.Wrap($continuationAction, $delegateType),
                    $this.CancellationToken,
                    $this.ContinuationOptions,
                    [TaskScheduler]::Current))
        }
    
        [Task[Collection[psobject]]] ContinueWhenAny([Task[]] $tasks, [scriptblock] $continuationAction) {
            $this.AssertNotDisposed()
            $delegateType = [Func`2].MakeGenericType([Task], [Collection[psobject]])
            return [AsyncOps]::PrepareTask(
                ([TaskFactory[Collection[psobject]]]$this).ContinueWhenAny(
                    $tasks,
                    $this.Wrap($continuationAction, $delegateType),
                    $this.CancellationToken,
                    $this.ContinuationOptions,
                    [TaskScheduler]::Current))
        }
    
        [Task[Collection[psobject]]] StartNew([scriptblock] $function) {
            $this.AssertNotDisposed()
            return [AsyncOps]::PrepareTask(
                ([TaskFactory[Collection[psobject]]]$this).StartNew(
                    $this.Wrap($function, [Func[Collection[psobject]]]),
                    $this.CancellationToken,
                    $this.CreationOptions,
                    [TaskScheduler]::Current))
        }
    
        [Task[Collection[psobject]]] StartNew([scriptblock] $function, [object] $state) {
            $this.AssertNotDisposed()
            return [AsyncOps]::PrepareTask(
                ([TaskFactory[Collection[psobject]]]$this).StartNew(
                    $this.Wrap($function, [Func[object, Collection[psobject]]]),
                    $state,
                    $this.CancellationToken,
                    $this.CreationOptions,
                    [TaskScheduler]::Current))
        }
    }
    
    function async {
        [CmdletBinding()]
        param(
            [scriptblock]
            $ScriptBlock,
    
            [Parameter(ValueFromPipeline)]
            [object]
            $ArgumentList
        )
        process {
            if (-not $scriptblock) { return }
            [AsyncOps]::Factory.StartNew($ScriptBlock, $ArgumentList)
        }
    }
    
    function await {
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline)]
            [Task]
            $Task
        )
        begin {
            $taskList = [List[Task]]::new()
        }
        process {
            if ($Task) { $taskList.Add($Task) }
        }
        end {
            if (-not $taskList.Count) { return }
    
            $finished = $false
            while (-not $finished) {
                $finished = $taskList.TrueForAll({
                    param([Task]$task)
    
                    $task.IsCompleted -or
                    $task.IsCanceled -or
                    $task.IsFaulted
                })
            }
            $taskList.ToArray().ForEach{
                if ($PSItem.IsFaulted -and $PSItem.Exception) {
                    if (-not ($exception = $PSItem.Exception.InnerException)) {
                        $exception = $PSItem.Exception
                    }
                    $PSCmdlet.WriteError(
                        [ErrorRecord]::new(
                            $exception,
                            $exception.GetType().Name,
                            [ErrorCategory]::InvalidResult,
                            $PSItem))
                }
                $PSItem.Result
            }
        }
    }
    
    function ContinueWith {
        [CmdletBinding()]
        param(
            [scriptblock]
            $ContinuationAction,
    
            [switch]
            $WhenAny,
    
            [Parameter(ValueFromPipeline)]
            [Task]
            $Task
        )
        begin {
            $taskList = [List[Task]]::new()
        }
        process {
            if ($Task) { $taskList.Add($Task) }
        }
        end {
            if (-not $taskList.Count) { return }
    
            if ($WhenAny.IsPresent) {
                return [AsyncOps]::Factory.ContinueWhenAny($taskList, $ContinuationAction)
            }
            return [AsyncOps]::Factory.ContinueWhenAll($taskList, $ContinuationAction)
        }
    }
    
  • tools\EditorServicesCommandSuite\Classes\Expressions.ps1 Show
    using namespace System.Reflection
    using namespace System.Collections.ObjectModel
    using namespace System.Management.Automation.Language
    
    class TypeExpressionHelper {
        [type] $Type;
    
        hidden [bool] $encloseWithBrackets;
        hidden [bool] $needsProxy;
    
        TypeExpressionHelper ([type] $type) {
            $this.Type = $Type
        }
        static [string] Create ([type] $type) {
            return [TypeExpressionHelper]::Create($type, $true)
        }
        static [string] Create ([type] $type, [bool] $encloseWithBrackets) {
            $helper = [TypeExpressionHelper]::new($type)
            $helper.encloseWithBrackets = $encloseWithBrackets
            return $helper.Create()
        }
       [string] Create () {
            # Non public types can't be retrieved with a type literal expression and need to be retrieved
            # from their assembly directly. The easiest way is to get a type from the same assembly and
            # get the assembly from that. The goal here is to build it as short as possible, hopefully
            # retaining some semblance of readability.
            if (-not $this.Type.IsPublic -or $this.Type.GenericTypeArguments.IsPublic -contains $false) {
                $this.needsProxy = $true
                return $this.CreateProxy()
            }
            else {
                return $this.CreateLiteral()
            }
        }
        hidden [string] CreateProxy () {
            $builder = [System.Text.StringBuilder]::new('[')
            $assembly = $this.Type.Assembly
    
            # First check if there are any type accelerators in the same assembly.
            $choices = $this.GetAccelerators().GetEnumerator().Where{ $PSItem.Value.Assembly -eq $assembly }.Key
    
            if (-not $choices) {
                # Then as a last resort pull every type from the assembly. This takes a extra second or
                # two the first time.
                $choices = $assembly.GetTypes().ToString
            }
    
            $builder.
                Append(($choices | Sort-Object Length)[0]).
                Append('].Assembly.GetType(''')
    
            if ($this.Type.GenericTypeArguments) {
                # Using the GetType method on the full name doesn't work for every type/combination, so
                # we use the MakeGenericType method.
                return $builder.AppendFormat('{0}.{1}'').MakeGenericType(', $this.Type.Namespace, $this.Type.Name).
                    Append($this.GetGenericArguments()).
                    Append(')').
                    ToString()
            }
            else {
                return $builder.
                    AppendFormat('{0}'')', $this.Type.ToString()).
                    ToString()
            }
        }
        hidden [string] CreateLiteral () {
            $builder = [System.Text.StringBuilder]::new()
            # If we are building the type name as a generic type argument in a type literal we don't want
            # to enclose it with brackets.
            if ($this.encloseWithBrackets) { $builder.Append('[') }
    
            if ($this.Type.GenericTypeArguments) {
                $builder.
                    AppendFormat('{0}.{1}', $this.Type.Namespace, $this.Type.Name).
                    Append('[').
                    Append($this.GetGenericArguments()).
                    Append(']')
            }
            else {
                $name = $this.GetAccelerators().
                    GetEnumerator().
                    Where{ $PSItem.Value -eq $this.Type }.
                    Key |
                    Sort-Object Length
    
                if (-not $name) { $name = ($this.Type.Name -as [type]).Name }
                if (-not $name) { $name = $this.Type.ToString() }
    
                if ($name.Count -gt 1) { $name = $name[0] }
    
                $builder.Append($name)
            }
    
            if ($this.encloseWithBrackets) { $builder.Append(']') }
    
            return $builder.ToString()
        }
        hidden [string] GetGenericArguments () {
            $typeArguments = $this.Type.GenericTypeArguments
    
            $enclose = $false
            if ($this.needsProxy) { $enclose = $true }
    
            return $typeArguments.ForEach{
                [TypeExpressionHelper]::Create($PSItem, $enclose)
            } -join ', '
        }
        hidden [System.Collections.Generic.Dictionary[string, type]] GetAccelerators () {
           return [ref].Assembly.GetType('System.Management.Automation.TypeAccelerators')::Get
        }
    }
    
    class ExtendedMemberExpressionAst : MemberExpressionAst {
        [type] $InferredType;
        [MemberInfo] $InferredMember;
        [BindingFlags] $BindingFlags;
        [ReadOnlyCollection[ExpressionAst]] $Arguments;
    
        ExtendedMemberExpressionAst ([IScriptExtent] $extent,
                                     [ExpressionAst] $expression,
                                     [CommandElementAst] $member,
                                     [bool] $static,
                                     [ReadOnlyCollection[ExpressionAst]] $arguments) :
                                     base($extent, $expression, $member, $static) {
    
            try {
                $this.Arguments      = $arguments
                $this.InferredMember = GetInferredMember -Ast $this
                $this.InferredType   = ($this.InferredMember.ReturnType,
                                        $this.InferredMember.PropertyType,
                                        $this.InferredMember.FieldType).
                                        Where({ $PSItem }, 'First')[0]
    
                $this.BindingFlags   = $this.InferredMember.GetType().
                    GetProperty('BindingFlags', [BindingFlags]'Instance, NonPublic').
                    GetValue($this.InferredMember)
            } catch {
                $this.InferredType = [object]
            }
        }
        static [ExtendedMemberExpressionAst] op_Implicit ([MemberExpressionAst] $ast) {
    
            $expression = $ast.Expression.Copy()
            if ($expression -is [MemberExpressionAst]) {
                $expression = [ExtendedMemberExpressionAst]$expression
            }
            $newAst = [ExtendedMemberExpressionAst]::new(
                $ast.Extent,
                $expression,
                $ast.Member.Copy(),
                $ast.Static,
                $ast.Arguments
            )
    
            if ($ast.Parent) {
                $ast.Parent.GetType().
                    GetMethod('SetParent', [BindingFlags]'Instance, NonPublic').
                    Invoke($ast.Parent, $newAst)
            }
    
            return $newAst
        }
    }
    
  • tools\EditorServicesCommandSuite\Classes\Renderers.ps1 Show
    # These classes are the renderers that provide custom format functions for use in StringTemplates.
    using namespace Antlr4.StringTemplate
    
    enum IndentKind {
        Space;
        Tab;
    }
    
    # Base class for custom format functions in StringTemplates.
    #
    # TODO: Add indentation frames similar to in CustomControlBuilder to avoid having to fix indentation
    #       post template invocation.
    class StringExpressionRenderer : StringRenderer {
        [IndentKind] $IndentKind = [IndentKind]::Space;
    
        [string] ToString([object] $o, [string] $formatString, [cultureinfo] $culture) {
            if ($formatString -and $this.psobject.Methods.Match($formatString)) {
                return $this.$formatString($o)
            } elseif ($formatString) {
                return ([StringRenderer]$this).ToString($o, $formatString, $culture)
            }
            return $o -as [string]
        }
    
        [string] ToCamelCase([string] $o) {
            if (-not $o) { return $o }
    
            if ($o.Length -gt 1) {
                return '{0}{1}' -f $o.Substring(0, 1).ToLower(), $o.SubString(1, $o.Length - 1)
            } else {
                return $o.ToLower()
            }
        }
    
        # Allows inserting multiple tabs with one template call.
        [string] Tab([string] $o) {
            return $this.GetIndent() * [int]$o
        }
    
        # Currently always returns spaces.
        # TODO: Rig this up as a setting, or preferably get it from PSES.
        hidden [string] GetIndent() {
            if ($this.IndentKind -eq [IndentKind]::Space) {
                return '    '
            } else {
                return "`t"
            }
        }
    }
    # Format functions specific to Expand-MemberExpression.
    class MemberExpressionRenderer : StringExpressionRenderer {
        # Transform member name for use as a variable name.
        [string] TransformMemberName([string] $o) {
            return $this.ToCamelCase(($o -replace '^\.ctor', 'new'))
        }
    }
    
    # Format function to allow using [TypeExpressionHelper] in StringTemplates.
    class TypeRenderer : StringRenderer {
        [string] ToString([object] $o, [string] $formatString, [cultureinfo] $culture) {
            if ($o -is [type]) {
                return [TypeExpressionHelper]::Create($o)
            }
            return ([StringRenderer]$this).ToString($o, $formatString, $culture)
        }
    }
    
  • tools\EditorServicesCommandSuite\Classes\Utility.ps1 Show
    using namespace System.Management.Automation
    using namespace System.Management.Automation.Language
    
    class SpecialVariables {
        static [System.Lazy[string[]]] $SpecialVariables = [Lazy[string[]]]::new(
            [Func[string[]]]{
                # Nothing public exists to get this unfortunately.
                return [ref].
                    Assembly.
                    GetType('System.Management.Automation.SpecialVariables').
                    DeclaredFields.
                    Where{ $PSItem.FieldType -eq [string] }.
                    ForEach{ $PSItem.GetValue($null) }
            });
    
        static [bool] IsSpecialVariable([VariableExpressionAst] $variable) {
            return [SpecialVariables]::IsSpecialVariable($variable.VariablePath)
        }
    
        static [bool] IsSpecialVariable([VariablePath] $variable) {
            return [SpecialVariables]::IsSpecialVariable($variable.UserPath)
        }
    
        static [bool] IsSpecialVariable([string] $variable) {
            if ([string]::IsNullOrEmpty($variable)) {
                return $false
            }
    
            return $variable -in [SpecialVariables]::SpecialVariables.Value -or $variable -eq 'psEditor'
        }
    }
    
  • tools\EditorServicesCommandSuite\EditorServicesCommandSuite.psd1 Show
    #
    # Module manifest for module 'EditorServicesCommandSuite'
    #
    # Generated by: Patrick Meinecke
    #
    # Generated on: 7/15/2017
    #
    
    @{
    
    # Script module or binary module file associated with this manifest.
    RootModule = 'EditorServicesCommandSuite.psm1'
    
    # Version number of this module.
    ModuleVersion = '0.4.0'
    
    # ID used to uniquely identify this module
    GUID = '97607afd-d9bd-4a2e-a9f9-70fe1a0a9e4c'
    
    # Author of this module
    Author = 'Patrick Meinecke'
    
    # Company or vendor of this module
    CompanyName = 'Community'
    
    # Copyright statement for this module
    Copyright = '(c) 2017 Patrick Meinecke. All rights reserved.'
    
    # Description of the functionality provided by this module
    Description = 'Collection of editor commands for use in PowerShell Editor Services.'
    
    # Minimum version of the Windows PowerShell engine required by this module
    PowerShellVersion = '5.1'
    
    # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
    DotNetFrameworkVersion = '4.0'
    
    # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
    CLRVersion = '4.0'
    
    # Processor architecture (None, X86, Amd64) required by this module
    ProcessorArchitecture = 'None'
    
    # Modules that must be imported into the global environment prior to importing this module
    RequiredModules = 'PSStringTemplate'
    
    # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
    FunctionsToExport = 'Add-CommandToManifest',
                        'Add-ModuleQualification',
                        'Add-PinvokeMethod',
                        'ConvertTo-FunctionDefinition',
                        'ConvertTo-LocalizationString',
                        'ConvertTo-MarkdownHelp',
                        'ConvertTo-SplatExpression',
                        'Expand-Expression',
                        'Expand-MemberExpression',
                        'Expand-TypeImplementation',
                        'New-ESCSSettingsFile',
                        'Remove-Semicolon',
                        'Set-HangingIndent',
                        'Set-RuleSuppression',
                        'Set-UsingStatementOrder'
    
    # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
    CmdletsToExport = @()
    
    # Variables to export from this module
    VariablesToExport = @()
    
    # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
    AliasesToExport = @()
    
    # List of all files packaged with this module
    FileList = 'EditorServicesCommandSuite.psd1',
               'EditorServicesCommandSuite.psm1',
               'Public\Add-CommandToManifest.ps1',
               'Public\Add-ModuleQualification.ps1',
               'Public\Add-PinvokeMethod.ps1',
               'Public\ConvertTo-FunctionDefinition.ps1',
               'Public\ConvertTo-LocalizationString.ps1',
               'Public\ConvertTo-MarkdownHelp.ps1',
               'Public\ConvertTo-SplatExpression.ps1',
               'Public\Expand-Expression.ps1',
               'Public\Expand-MemberExpression.ps1',
               'Public\Expand-TypeImplementation.ps1',
               'Public\New-ESCSSettingsFile.ps1',
               'Public\Remove-Semicolon.ps1',
               'Public\Set-HangingIndent.ps1',
               'Public\Set-RuleSuppression.ps1',
               'Public\Set-UsingStatementOrder.ps1'
    
    # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
    PrivateData = @{
    
        PSData = @{
    
            # Tags applied to this module. These help with module discovery in online galleries.
            Tags = @('Editor', 'EditorServices', 'VSCode')
    
            # A URL to the license for this module.
            LicenseUri = 'https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/LICENSE'
    
            # A URL to the main website for this project.
            ProjectUri = 'https://github.com/SeeminglyScience/EditorServicesCommandSuite'
    
            # A URL to an icon representing this module.
            # IconUri = ''
    
            # ReleaseNotes of this module
            ReleaseNotes = @'
    - New editor command ConvertTo-FunctionDefinition for generating functions from selected text.
    '@
    
        } # End of PSData hashtable
    
    } # End of PrivateData hashtable
    
    }
    
    
    
    
  • tools\EditorServicesCommandSuite\EditorServicesCommandSuite.psm1 Show
    Import-LocalizedData -BindingVariable Strings -FileName Strings -ErrorAction Ignore
    
    $script:DEFAULT_SETTINGS = @{
        MainModuleDirectory        = '.\module'
        SourceManifestPath         = '.\module\*.psd1'
        MarkdownDocsPath           = '.\docs'
        StringLocalizationManifest = '.\module\en-US\Strings.psd1'
    }
    
    # PSST doesn't load Antlr until first use, and we need them loaded
    # to create renderers.
    if (-not ('Antlr4.StringTemplate.StringRenderer' -as [type])) {
        if (-not ($psstPath = (Get-Module PSStringTemplate).ModuleBase)) {
            # platyPS doesn't seem to be following RequiredModules, this should only ever run
            # while running platyPS.  Need to look into this more.
            $psstPath = (Get-Module PSStringTemplate -ListAvailable).ModuleBase
        }
        Add-Type -Path $psstPath\Antlr3.Runtime.dll
        Add-Type -Path $psstPath\Antlr4.StringTemplate.dll
    }
    
    . $PSScriptRoot\Classes\Expressions.ps1
    . $PSScriptRoot\Classes\Renderers.ps1
    . $PSScriptRoot\Classes\Async.ps1
    . $PSScriptRoot\Classes\Utility.ps1
    
    Get-ChildItem $PSScriptRoot\Public, $PSScriptRoot\Private -Filter '*.ps1' | ForEach-Object {
        . $PSItem.FullName
    }
    
    # Export only the functions using PowerShell standard verb-noun naming.
    # Be sure to list each exported functions in the FunctionsToExport field of the module manifest file.
    # This improves performance of command discovery in PowerShell.
    Export-ModuleMember -Function *-*
    
  • tools\EditorServicesCommandSuite\en-US\EditorServicesCommandSuite-help.xml Show
    <?xml version="1.0" encoding="utf-8"?>
    <helpItems schema="maml" xmlns="http://msh">
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Add-CommandToManifest</command:name>
          <command:verb>Add</command:verb>
          <command:noun>CommandToManifest</command:noun>
          <maml:description>
            <maml:para>Add a function to the workspace module manifest.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The Add-CommandToManifest function finds the closest function definition in the current file and uses it to update manifest fields.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Add-CommandToManifest</maml:name>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters />
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept input from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not return objects to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>Add-CommandToManifest</dev:code>
            <dev:remarks>
              <maml:para>Adds the closest function to the workspace module manifest.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/Add-CommandToManifest.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Add-ModuleQualification</command:name>
          <command:verb>Add</command:verb>
          <command:noun>ModuleQualification</command:noun>
          <maml:description>
            <maml:para>Add a commands module name to it's invocation expression.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The Add-ModuleQualification function retrieves the module a command belongs to and prepends the module name to the expression.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Add-ModuleQualification</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
              <maml:name>Ast</maml:name>
              <maml:Description>
                <maml:para>Specifies the CommandAst or AST within the CommandAst to add module qualification.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
              <dev:type>
                <maml:name>Ast</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
            <maml:name>Ast</maml:name>
            <maml:Description>
              <maml:para>Specifies the CommandAst or AST within the CommandAst to add module qualification.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
            <dev:type>
              <maml:name>Ast</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept input from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not output to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code># Place your cursor within this command and invoke the Add-ModuleQualification command.
    Get-Command
    
    # It becomes:
    Microsoft.PowerShell.Core\Get-Command</dev:code>
            <dev:remarks>
              <maml:para>Adds module qualification to a command expression.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/Add-ModuleQualification.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Add-PinvokeMethod</command:name>
          <command:verb>Add</command:verb>
          <command:noun>PinvokeMethod</command:noun>
          <maml:description>
            <maml:para>Find and insert a PInvoke function signature into the current file.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Add-PinvokeMethod</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
              <maml:name>Function</maml:name>
              <maml:Description>
                <maml:para>Specifies the function name to search for. If omitted, a prompt will be displayed within the editor.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
              <maml:name>Module</maml:name>
              <maml:Description>
                <maml:para>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.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
            <maml:name>Function</maml:name>
            <maml:Description>
              <maml:para>Specifies the function name to search for. If omitted, a prompt will be displayed within the editor.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
            <maml:name>Module</maml:name>
            <maml:Description>
              <maml:para>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.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept input from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not output to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>Add-PinvokeMethod -Function SetConsoleTitle -Module Kernel32
    
    # Inserts the following into the file currently open in the editor.
    
    # Source: http://pinvoke.net/jump.aspx/kernel32.setconsoletitle
    Add-Type -Namespace PinvokeMethods -Name Kernel -MemberDefinition '
    [DllImport("kernel32.dll")]
    public static extern bool SetConsoleTitle(string lpConsoleTitle);'</dev:code>
            <dev:remarks>
              <maml:para>Adds code to use the SetConsoleTitle function from the kernel32 DLL.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Add-PinvokeMethod.md</maml:uri>
          </maml:navigationLink>
          <maml:navigationLink>
            <maml:linkText>pinvoke.net</maml:linkText>
            <maml:uri>http://pinvoke.net/</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>ConvertTo-FunctionDefinition</command:name>
          <command:verb>ConvertTo</command:verb>
          <command:noun>FunctionDefinition</command:noun>
          <maml:description>
            <maml:para>Create a new function from a selection or specified script extent object.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The ConvertTo-FunctionDefintion function takes a section of the current file and creates a function definition from it. The generated function includes a parameter block with parameters for variables that are not defined in the selection. In the place of the selected text will be the invocation of the generated command including parameters.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>ConvertTo-FunctionDefinition</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>Extent</maml:name>
              <maml:Description>
                <maml:para>The ScriptExtent to convert to a function. If not specified, the currently selected text in the editor will be used.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">IScriptExtent</command:parameterValue>
              <dev:type>
                <maml:name>IScriptExtent</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>FunctionName</maml:name>
              <maml:Description>
                <maml:para>Specifies the name to give the generated function. If not specified, a input prompt will be displayed in the editor.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>DestinationPath</maml:name>
              <maml:Description>
                <maml:para>Specifies a path relative to the file open in the editor to save the function to. You can specify an existing or new file. If the file extension is omitted, the path is assumed to be a directory and a file name is assumed to be the function name.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
          <command:syntaxItem>
            <maml:name>ConvertTo-FunctionDefinition</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>Extent</maml:name>
              <maml:Description>
                <maml:para>The ScriptExtent to convert to a function. If not specified, the currently selected text in the editor will be used.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">IScriptExtent</command:parameterValue>
              <dev:type>
                <maml:name>IScriptExtent</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>FunctionName</maml:name>
              <maml:Description>
                <maml:para>Specifies the name to give the generated function. If not specified, a input prompt will be displayed in the editor.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>BeginBlock</maml:name>
              <maml:Description>
                <maml:para>If specified, the function will be saved to the Begin block of either the closest parent function definition, or of the root script block if no function definitions exist.</maml:para>
                <maml:para>If there is no Begin block available, one will be created. If a begin block must be created and no named blocks exist yet, a separate End block will be created from the existing unnamed block.</maml:para>
              </maml:Description>
              <dev:type>
                <maml:name>SwitchParameter</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>False</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
          <command:syntaxItem>
            <maml:name>ConvertTo-FunctionDefinition</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>Extent</maml:name>
              <maml:Description>
                <maml:para>The ScriptExtent to convert to a function. If not specified, the currently selected text in the editor will be used.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">IScriptExtent</command:parameterValue>
              <dev:type>
                <maml:name>IScriptExtent</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>FunctionName</maml:name>
              <maml:Description>
                <maml:para>Specifies the name to give the generated function. If not specified, a input prompt will be displayed in the editor.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>Inline</maml:name>
              <maml:Description>
                <maml:para>If specified, the function will be saved directly above the selection.</maml:para>
              </maml:Description>
              <dev:type>
                <maml:name>SwitchParameter</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>False</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>Extent</maml:name>
            <maml:Description>
              <maml:para>The ScriptExtent to convert to a function. If not specified, the currently selected text in the editor will be used.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">IScriptExtent</command:parameterValue>
            <dev:type>
              <maml:name>IScriptExtent</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>FunctionName</maml:name>
            <maml:Description>
              <maml:para>Specifies the name to give the generated function. If not specified, a input prompt will be displayed in the editor.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>DestinationPath</maml:name>
            <maml:Description>
              <maml:para>Specifies a path relative to the file open in the editor to save the function to. You can specify an existing or new file. If the file extension is omitted, the path is assumed to be a directory and a file name is assumed to be the function name.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>BeginBlock</maml:name>
            <maml:Description>
              <maml:para>If specified, the function will be saved to the Begin block of either the closest parent function definition, or of the root script block if no function definitions exist.</maml:para>
              <maml:para>If there is no Begin block available, one will be created. If a begin block must be created and no named blocks exist yet, a separate End block will be created from the existing unnamed block.</maml:para>
            </maml:Description>
            <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
            <dev:type>
              <maml:name>SwitchParameter</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>False</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>Inline</maml:name>
            <maml:Description>
              <maml:para>If specified, the function will be saved directly above the selection.</maml:para>
            </maml:Description>
            <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
            <dev:type>
              <maml:name>SwitchParameter</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>False</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept input from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not return output to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code># Open a new untitled file
    $psEditor.Workspace.NewFile()
    
    # Insert some text into the file
    $psEditor.GetEditorContext().CurrentFile.InsertText('
    $myVar = "testing"
    Get-ChildItem $myVar
    ')
    
    # Select the Get-ChildItem line
    $psEditor.GetEditorContext().SetSelection(3, 1, 4, 1)
    
    # Convert it to a function
    ConvertTo-FunctionDefinition -FunctionName GetMyDirectory -Inline
    
    # Show the new contents of the file
    $psEditor.GetEditorContext.CurrentFile.GetText()
    
    # $myVar = "testing"
    # function GetMyDirectory {
    #     param([string] $MyVar)
    #     end {
    #         Get-ChildItem $MyVar
    #     }
    # }
    #
    # GetMyDirectory -MyVar $myVar</dev:code>
            <dev:remarks>
              <maml:para>Creates a new untitled file in the editor, inserts demo text, and then converts a line to a inline function.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/ConvertTo-FunctionDefinition.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>ConvertTo-LocalizationString</command:name>
          <command:verb>ConvertTo</command:verb>
          <command:noun>LocalizationString</command:noun>
          <maml:description>
            <maml:para>Move a string expression to a localization resource file.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The ConvertTo-LocalizationString function will take the closest string expression and replace it with a variable that references a localization resource file.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>ConvertTo-LocalizationString</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
              <maml:name>Ast</maml:name>
              <maml:Description>
                <maml:para>Specifies the string expression to convert.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
              <dev:type>
                <maml:name>Ast</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
              <maml:name>Name</maml:name>
              <maml:Description>
                <maml:para>Specifies the name to give the string in the localization file.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
            <maml:name>Ast</maml:name>
            <maml:Description>
              <maml:para>Specifies the string expression to convert.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
            <dev:type>
              <maml:name>Ast</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
            <maml:name>Name</maml:name>
            <maml:Description>
              <maml:para>Specifies the name to give the string in the localization file.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept input from the pipeline</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not output to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para>Current limitations:</maml:para>
            <maml:para>- Only supports localization files that use ConvertFrom-StringData and a here-string</maml:para>
            <maml:para>- Only supports using a single localization file</maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code># Place your cursor inside the string and invoke this editor command:
    Write-Verbose ('Writing to file at path "{0}".' -f $Path)
    
    # It prompts you for a string name and becomes:
    Write-Verbose ($Strings.YourStringName -f $Path)
    
    # And adds this to your localization file:
    YourStringName=Writing to file at path "{0}".</dev:code>
            <dev:remarks>
              <maml:para>Uses this function as an editor command to replace a string expression with a reference to a localization file.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/ConvertTo-LocalizationString.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>ConvertTo-MarkdownHelp</command:name>
          <command:verb>ConvertTo</command:verb>
          <command:noun>MarkdownHelp</command:noun>
          <maml:description>
            <maml:para>Convert the current function from comment based help to markdown.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>ConvertTo-MarkdownHelp</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
              <maml:name>Ast</maml:name>
              <maml:Description>
                <maml:para>Specifies the FunctionDefinitionAst containing the CBH to be replaced. The default value is the closest AST to the current cursor location.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">FunctionDefinitionAst</command:parameterValue>
              <dev:type>
                <maml:name>FunctionDefinitionAst</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>(Find-Ast -AtCursor | Find-Ast -Ancestor -First { $_ -is [FunctionDefinitionAst] })</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
            <maml:name>Ast</maml:name>
            <maml:Description>
              <maml:para>Specifies the FunctionDefinitionAst containing the CBH to be replaced. The default value is the closest AST to the current cursor location.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">FunctionDefinitionAst</command:parameterValue>
            <dev:type>
              <maml:name>FunctionDefinitionAst</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>(Find-Ast -AtCursor | Find-Ast -Ancestor -First { $_ -is [FunctionDefinitionAst] })</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept input from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not output to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para>Markdown generated from PlatyPS is altered in the following ways to conform to linting rules:</maml:para>
            <maml:para>- An extra new line is added between headers and content</maml:para>
            <maml:para>- Code blocks are marked as PowerShell code</maml:para>
            <maml:para>- Trailing spaces after blank aliases fields are removed</maml:para>
            <maml:para>- Examples with the header generated as ### Example are replaced with hyphen syntax.</maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>ConvertTo-MarkdownHelp</dev:code>
            <dev:remarks>
              <maml:para>Converts the closest CBH to markdown.</maml:para>
            </dev:remarks>
          </command:example>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
            <dev:code>$ast = Find-Ast { $_.GetType().Name -eq 'FunctionDefinitionAst' -and $_.Name -eq 'Invoke-MyCommand' }</dev:code>
            <dev:remarks>
              <maml:para>ConvertTo-MarkdownHelp -Ast $ast</maml:para>
              <maml:para>Converts any CBH in the function "Invoke-MyCommand" to markdown.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/ConvertTo-MarkdownHelp.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>ConvertTo-SplatExpression</command:name>
          <command:verb>ConvertTo</command:verb>
          <command:noun>SplatExpression</command:noun>
          <maml:description>
            <maml:para>Convert a command expression to use splatting.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The ConvertTo-SplatExpression function transforms a CommandAst to use a splat expression instead of inline parameters.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>ConvertTo-SplatExpression</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
              <maml:name>Ast</maml:name>
              <maml:Description>
                <maml:para>Specifies an Ast that is, or is within the CommandAst to be converted.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
              <dev:type>
                <maml:name>Ast</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
            <maml:name>Ast</maml:name>
            <maml:Description>
              <maml:para>Specifies an Ast that is, or is within the CommandAst to be converted.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
            <dev:type>
              <maml:name>Ast</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes />
        <command:returnValues />
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code># Place your cursor inside this command and run this function:
    Get-ChildItem .\Path -Force -File -Filter *.txt -Exclude *$myExclude* -Recurse
    
    # It becomes:
    $getChildItemSplat = @{
        File = $true
        Filter = '*.txt'
        Exclude = "*$myExclude*"
        Force = $true
        Recurse = $true
    }
    Get-ChildItem @getChildItemSplat .\Path</dev:code>
            <dev:remarks>
              <maml:para>Uses this function as an editor command to expand a long command into a splat expression.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/ConvertTo-SplatExpression.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Expand-Expression</command:name>
          <command:verb>Expand</command:verb>
          <command:noun>Expression</command:noun>
          <maml:description>
            <maml:para>Replaces an extent with the return value of it's text as an expression.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Expand-Expression</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="1" aliases="Extent">
              <maml:name>InputObject</maml:name>
              <maml:Description>
                <maml:para>Specifies the extent to invoke.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">IScriptExtent[]</command:parameterValue>
              <dev:type>
                <maml:name>IScriptExtent[]</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>($psEditor.GetEditorContext().SelectedRange | ConvertTo-ScriptExtent)</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="1" aliases="Extent">
            <maml:name>InputObject</maml:name>
            <maml:Description>
              <maml:para>Specifies the extent to invoke.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">IScriptExtent[]</command:parameterValue>
            <dev:type>
              <maml:name>IScriptExtent[]</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>($psEditor.GetEditorContext().SelectedRange | ConvertTo-ScriptExtent)</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>System.Management.Automation.Language.IScriptExtent</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>You can pass extents to invoke from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>$psEditor.GetEditorContext().SelectedRange | ConvertTo-ScriptExtent | Expand-Expression</dev:code>
            <dev:remarks>
              <maml:para>Invokes the currently selected text and replaces it with it's output. This is also the default.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Expand-Expression.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Expand-MemberExpression</command:name>
          <command:verb>Expand</command:verb>
          <command:noun>MemberExpression</command:noun>
          <maml:description>
            <maml:para>Builds an expression for accessing or invoking a member through reflection.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The Expand-MemberExpression function creates an expression for the closest MemberExpressionAst to the cursor in the current editor context. This is mainly to assist with creating expressions to access private members of .NET classes through reflection.</maml:para>
          <maml:para>The expression is created using string templates. There are templates for several ways of accessing members including InvokeMember, GetProperty/GetValue, and a more verbose GetMethod/Invoke. If using the GetMethod/Invoke template it will automatically build type expressions for the "types" argument including nonpublic and generic types. If a template is not specified, this function will attempt to determine the most fitting template. If you have issues invoking a method with the default, try the VerboseInvokeMethod template. This function currently works on member expressions attached to the following:</maml:para>
          <maml:para>1. Type literal expressions (including invalid expressions with non public types)</maml:para>
          <maml:para>2. Variable expressions where the variable exists within a currently existing scope.</maml:para>
          <maml:para>3. Any other scenario where standard completion works.</maml:para>
          <maml:para>4. Any number of nested member expressions where one of the above is true at some point in the chain.</maml:para>
          <maml:para></maml:para>
          <maml:para>Additionally chains may break if a member returns a type that is too generic like System.Object or a vague interface.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Expand-MemberExpression</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="2" aliases="none">
              <maml:name>Ast</maml:name>
              <maml:Description>
                <maml:para>Specifies the member expression ast (or child of) to expand.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
              <dev:type>
                <maml:name>Ast</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>TemplateName</maml:name>
              <maml:Description>
                <maml:para>A template is automatically chosen based on member type and visibility.  You can use this parameter to force the use of a specific template.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>NoParameterNameComments</maml:name>
              <maml:Description>
                <maml:para>By default expanded methods will have a comment with the parameter name on each line. (e.g. `&lt;# paramName: #&gt; $paramName,`) If you specify this parameter it will be omitted.</maml:para>
              </maml:Description>
              <dev:type>
                <maml:name>SwitchParameter</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>False</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="2" aliases="none">
            <maml:name>Ast</maml:name>
            <maml:Description>
              <maml:para>Specifies the member expression ast (or child of) to expand.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">Ast</command:parameterValue>
            <dev:type>
              <maml:name>Ast</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>TemplateName</maml:name>
            <maml:Description>
              <maml:para>A template is automatically chosen based on member type and visibility.  You can use this parameter to force the use of a specific template.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>NoParameterNameComments</maml:name>
            <maml:Description>
              <maml:para>By default expanded methods will have a comment with the parameter name on each line. (e.g. `&lt;# paramName: #&gt; $paramName,`) If you specify this parameter it will be omitted.</maml:para>
            </maml:Description>
            <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
            <dev:type>
              <maml:name>SwitchParameter</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>False</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>Expand-MemberExpression</dev:code>
            <dev:remarks>
              <maml:para>Expands the member expression closest to the cursor in the current editor context using an automatically determined template.</maml:para>
            </dev:remarks>
          </command:example>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
            <dev:code>Expand-MemberExpression -Template VerboseInvokeMethod</dev:code>
            <dev:remarks>
              <maml:para>Expands the member expression closest to the cursor in the current editor context using the VerboseInvokeMethod template.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Expand-MemberExpression.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Expand-TypeImplementation</command:name>
          <command:verb>Expand</command:verb>
          <command:noun>TypeImplementation</command:noun>
          <maml:description>
            <maml:para>Expand the closest type expression into a implementation using PowerShell classes.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Expand-TypeImplementation</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="1" aliases="none">
              <maml:name>Type</maml:name>
              <maml:Description>
                <maml:para>Specifies the type to implement.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">Type[]</command:parameterValue>
              <dev:type>
                <maml:name>Type[]</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>None</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="1" aliases="none">
            <maml:name>Type</maml:name>
            <maml:Description>
              <maml:para>Specifies the type to implement.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">Type[]</command:parameterValue>
            <dev:type>
              <maml:name>Type[]</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>None</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>Type</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>You can pass types to implement to this function.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>$type = [System.Management.Automation.IArgumentCompleter]
    Expand-TypeImplementation -Type $type
    
    # Adds the following code to the current file.
    
    class NewIEqualityComparer : System.Collections.IEqualityComparer {
        [bool] Equals ([Object] $x, [Object] $y) {
            throw [NotImplementedException]::new()
        }
    
        [int] GetHashCode ([Object] $obj) {
            throw [NotImplementedException]::new()
        }
    }</dev:code>
            <dev:remarks>
              <maml:para></maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Expand-TypeImplementation.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>New-ESCSSettingsFile</command:name>
          <command:verb>New</command:verb>
          <command:noun>ESCSSettingsFile</command:noun>
          <maml:description>
            <maml:para>Create a new settings file for the current workspace.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>New-ESCSSettingsFile</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
              <maml:name>Path</maml:name>
              <maml:Description>
                <maml:para>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.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
              <dev:type>
                <maml:name>String</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>$psEditor.Workspace.Path</dev:defaultValue>
            </command:parameter>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
              <maml:name>Force</maml:name>
              <maml:Description>
                <maml:para>If specified indicates that an existing settings file should be overridden without prompting.</maml:para>
              </maml:Description>
              <dev:type>
                <maml:name>SwitchParameter</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>False</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
            <maml:name>Path</maml:name>
            <maml:Description>
              <maml:para>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.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
            <dev:type>
              <maml:name>String</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>$psEditor.Workspace.Path</dev:defaultValue>
          </command:parameter>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
            <maml:name>Force</maml:name>
            <maml:Description>
              <maml:para>If specified indicates that an existing settings file should be overridden without prompting.</maml:para>
            </maml:Description>
            <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
            <dev:type>
              <maml:name>SwitchParameter</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>False</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not accept value from the pipeline.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>This function does not output to the pipeline.</maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>New-ESCSSettingsFile</dev:code>
            <dev:remarks>
              <maml:para>Creates the file ESCSSettings.psd1 in the base of the current workspace with default values.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/docs/en-US/New-ESCSSettingsFile.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Remove-Semicolon</command:name>
          <command:verb>Remove</command:verb>
          <command:noun>Semicolon</command:noun>
          <maml:description>
            <maml:para>Remove useless semicolons from the current file.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Remove-Semicolon</maml:name>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters />
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>Remove-Semicolon</dev:code>
            <dev:remarks>
              <maml:para>Removes all useless semicolons from the current file.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks />
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Set-RuleSuppression</command:name>
          <command:verb>Set</command:verb>
          <command:noun>RuleSuppression</command:noun>
          <maml:description>
            <maml:para>Adds a SuppressMessage attribute to suppress a rule violation.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>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.</maml:para>
          <maml:para>As an editor command it will attempt to suppress the Ast closest to the current cursor position.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Set-RuleSuppression</maml:name>
            <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="1" aliases="none">
              <maml:name>Ast</maml:name>
              <maml:Description>
                <maml:para>Specifies the Ast with a rule violation to suppress.</maml:para>
              </maml:Description>
              <command:parameterValue required="true" variableLength="false">Ast[]</command:parameterValue>
              <dev:type>
                <maml:name>Ast[]</maml:name>
                <maml:uri />
              </dev:type>
              <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
            </command:parameter>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters>
          <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByValue)" position="1" aliases="none">
            <maml:name>Ast</maml:name>
            <maml:Description>
              <maml:para>Specifies the Ast with a rule violation to suppress.</maml:para>
            </maml:Description>
            <command:parameterValue required="true" variableLength="false">Ast[]</command:parameterValue>
            <dev:type>
              <maml:name>Ast[]</maml:name>
              <maml:uri />
            </dev:type>
            <dev:defaultValue>(Find-Ast -AtCursor)</dev:defaultValue>
          </command:parameter>
        </command:parameters>
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>System.Management.Automation.Language.Ast</maml:name>
            </dev:type>
            <maml:description>
              <maml:para>You can pass Asts with violations to this function.</maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para>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.</maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>Set-RuleSuppression</dev:code>
            <dev:remarks>
              <maml:para>Adds a SuppressMessage attribute to suppress a rule violation.</maml:para>
            </dev:remarks>
          </command:example>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
            <dev:code>$propBlock = Find-Ast { $_.CommandElements -and $_.GetCommandName() -eq 'Properties' }
    $propBlock | Find-Ast { $_.VariablePath } | Set-RuleSuppression</dev:code>
            <dev:remarks>
              <maml:para>Finds all variable expressions in a psake Properties block and creates a rule suppression for any that have a violation.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Set-RuleSuppression.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
      <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
        <command:details>
          <command:name>Set-UsingStatementOrder</command:name>
          <command:verb>Set</command:verb>
          <command:noun>UsingStatementOrder</command:noun>
          <maml:description>
            <maml:para>Sort using statements in the current file.</maml:para>
          </maml:description>
        </command:details>
        <maml:description>
          <maml:para>The Set-UsingStatementOrder function will sort using statements by type (e.g. Assembly &gt; Module &gt; Namespace) and then alphabetically.</maml:para>
        </maml:description>
        <command:syntax>
          <command:syntaxItem>
            <maml:name>Set-UsingStatementOrder</maml:name>
          </command:syntaxItem>
        </command:syntax>
        <command:parameters />
        <command:inputTypes>
          <command:inputType>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:inputType>
        </command:inputTypes>
        <command:returnValues>
          <command:returnValue>
            <dev:type>
              <maml:name>None</maml:name>
            </dev:type>
            <maml:description>
              <maml:para></maml:para>
            </maml:description>
          </command:returnValue>
        </command:returnValues>
        <maml:alertSet>
          <maml:alert>
            <maml:para></maml:para>
          </maml:alert>
        </maml:alertSet>
        <command:examples>
          <command:example>
            <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
            <dev:code>Set-UsingStatementOrder</dev:code>
            <dev:remarks>
              <maml:para>Sort using statements in the current file.</maml:para>
            </dev:remarks>
          </command:example>
        </command:examples>
        <command:relatedLinks>
          <maml:navigationLink>
            <maml:linkText>Online Version:</maml:linkText>
            <maml:uri>https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Set-UsingStatementOrder.md</maml:uri>
          </maml:navigationLink>
        </command:relatedLinks>
      </command:command>
    </helpItems>
  • tools\EditorServicesCommandSuite\en-US\Strings.psd1 Show
    ConvertFrom-StringData @'
    SettingCommentMainModuleDirectory=The relative path from the current workspace to the root directory of the module.
    SettingCommentSourceManifestPath=The relative path from the current workspace to the main module manifest file.
    SettingCommentMarkdownDocsPath=The relative path from the current workspace to the directory where markdown files are stored.
    SettingCommentStringLocalizationManifest=The relative path from the current workspace to the string localization psd1 file.
    
    EditorCommandExists=Editor command '{0}' already exists, skipping.
    EnumeratingScopesForMember=Enumerating scopes to find a matching member.
    VariableFound=Found variable with type '{0}'.
    SkippingEditorContext=PowerShell Editor Services API not available, skipping.
    InferringFromCompletion=Checking for type using standard command completion.
    
    WhatIfSetExtent=Changing '{0}' to '{1}'
    ConfirmSetExtent=Continuing will change the the text of extent '{0}' to '{1}'. Are you sure you want to continue?
    ConfirmTitle=Confirm
    ShouldReplaceSettingsCaption=Replace existing settings?
    ShouldReplaceSettingsMessage=A settings file already exists in the specified folder and the "Force" switch parameter was not specified.  If you continue, existing settings will be overridden.  Do you want to continue?
    StringNamePrompt=String Name
    
    MissingAst=Unable to find an AST of type '{0}' at the specified location.
    MissingMemberExpressionAst=Unable to find a member expression ast near the current cursor location.
    MissingEditorContext=Unable to obtain editor context. Make sure PowerShell Editor Services is running and then try the command again.
    ExpandEmptyExtent=Cannot expand the extent with start offset '{0}' for file '{1}' because it is empty.
    CannotInferType=Unable to infer type for expression '{0}'.
    CannotFindModule=Unable to find the module '{0}' in the current session.
    TypeNotFound=Unable to find type [{0}].
    TemplateGroupCompileError=Internal module error: Unable to compile default template group. Please file an issue on GitHub.
    FailureGettingMarkdown=Unable to generate markdown content. Ensure you have the module PlatyPS installed and the comment based help is formatted correctly.
    SettingsFileExists=The settings file for workspace '{0}' already exists.
    InvalidSettingValue=The value of the setting '{0}' is invalid.  If you have not already created a settings file for this workspace, you can create one with the 'New-ESCSSettingsFile' function.
    VerboseInvalidManifest=Unable to retrieve module manifest for current workspace.
    CannotInferModule=Unable to infer module information for the selected command.
    CommandNotInModule=The selected command does not belong to a module.
    StringNamePromptFail=You must supply a string name for it to be added to the localization table.  Please try the command again.
    CannotFindPInvokeFunction=Unable to find a PInvoke function that starts with '{0}'
    PInvokeFunctionChoice=Multiple matches found, please select below
    PInvokeFunctionNamePrompt=PInvoke Function Name
    MissingPInvokeSignature=The function was found but pinvoke.net did not return signature information.
    NoExtentSelected=The parameter "Extent" was not specified and no text has been selected. Please select the text you would like to extract and run this command again.
    NoDestinationFile=You must specify a destination file.
    EnterDestinationFilePrompt=Enter the path to the destination file (relative to current file)
    ExportFunctionPrompt=Where would you like the new function?
    ExportFunctionBeginDescription=The begin block of the current function.
    ExportFunctionInlineDescription=Directly above the target extent.
    ExportFunctionExternalFileDescription=In an existing or new file.
    ExportFunctionNamePrompt=Name the new function
    MissingFunctionName=You must specify a function name.
    '@
    
  • tools\EditorServicesCommandSuite\Private\AddIndent.ps1 Show
    function AddIndent {
        [OutputType([string])]
        [CmdletBinding()]
        param(
            [Parameter(Mandatory, ValueFromPipeline)]
            [string[]] $Source,
            [string] $Indent = ' ',
            [int] $Amount = 4,
            [switch] $ExcludeFirstLine
        )
        begin {
            $stringList = [System.Collections.Generic.List[string]]::new()
        }
        process {
            if ($null -eq $Source) {
                return
            }
    
            $stringList.AddRange($Source)
        }
        end {
            $sourceText = $stringList -join [Environment]::NewLine
            if ($Amount -lt 1) {
                return $sourceText
            }
    
            $indentText = $Indent * $Amount
            # Preserve new line characters. Only works if not sent a stream.
            $newLine    = [regex]::Match($sourceText, '\r?\n').Value
            $asLines    = $sourceText -split '\r?\n'
            $first      = $true
            $indentedLines = foreach ($line in $asLines) {
                if ($first) {
                    $first = $false
                    if ($ExcludeFirstLine.IsPresent) {
                        $line
                        continue
                    }
                }
    
                # Don't indent blank lines or here-string ending tags
                $shouldNotIndent = [string]::IsNullOrWhiteSpace($line) -or
                                   $line.StartsWith("'@") -or
                                   $line.StartsWith('"@')
                if ($shouldNotIndent) {
                    $line
                    continue
                }
    
                $indentText + $line
            }
    
            return $indentedLines -join $newLine
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\GetAncestorOrThrow.ps1 Show
    using namespace System.Management.Automation.Language
    
    function GetAncestorOrThrow {
        [OutputType([System.Management.Automation.Language.Ast])]
        [CmdletBinding()]
        param(
            [System.Management.Automation.Language.Ast]
            $Ast,
    
            [string]
            $AstTypeName,
    
            [System.Management.Automation.PSCmdlet]
            $ErrorContext
        )
        end {
            $astType = $AstTypeName -as [type]
            if (-not $astType) {
                $astType = 'System.Management.Automation.Language.' + $AstTypeName -as [type]
            }
    
            if (-not $Ast) { $Ast = Find-Ast -AtCursor }
    
            if ($Ast -is $astType) { return $Ast }
            $Ast = Find-Ast -Ast $Ast -Ancestor -First { $PSItem -is $astType }
            if ($Ast) { return $Ast }
    
            $throwErrorSplat = @{
                Exception = ([ArgumentException]::new($Strings.MissingAst -f $astType.Name))
                Target    = $Ast
                Category  = 'InvalidArgument'
                Id        = 'MissingAst'
            }
            if ($ErrorContext) { $throwErrorSplat.ErrorContext = $ErrorContext }
            ThrowError @throwErrorSplat
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\GetInferredManifest.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    
    function GetInferredManifest {
        [CmdletBinding()]
        param()
        end {
            $manifestPath = ResolveRelativePath (GetSettings).SourceManifestPath
            if (-not $manifestPath -or -not (Test-Path $manifestPath)) {
                ThrowError -Exception ([IO.InvalidDataException]::new($Strings.InvalidManifestSetting)) `
                           -Id        InvalidManifestSetting `
                           -Category  InvalidDataException `
                           -Target    $manifestPath
            }
            $data = Import-LocalizedData -BaseDirectory (Split-Path $manifestPath) `
                                         -FileName      (Split-Path -Leaf $manifestPath)
            $null = $data.Add('Name', ((Split-Path $manifestPath -Leaf) -replace '.psd1$'))
            return $data
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\GetInferredMember.ps1 Show
    using namespace System.Reflection
    
    function GetInferredMember {
        <#
        .SYNOPSIS
            Get inferred member info from a MemberExpressionAst.
        .DESCRIPTION
            This function attempts to infer the class it belongs to with the function GetInferredType.
            Once the class is determined, it looks for members with the same name, preferring properties
            over fields if both are present.
    
            If a method or constructor is overloaded, the member with the lowest parameter count will be
            chosen by default. If the ast has arguments specified, the parameter count will be checked for
            a match as well. If the ast has one argument specified and it is of the type int, that will
            be checked as the parameter count. (e.g. [exception]::new(2) would return the overload with
            two parameters)
        .INPUTS
            System.Management.Automation.Language.MemberExpressionAst
    
            You can pass member expressions to this function.
        .OUTPUTS
            System.Reflection.MemberInfo
    
            The inferred member information will be returned if found.
        .EXAMPLE
            PS C:\> [Parser]::ParseInput('$host.Context').FindAll({$args[0].Member}, $true) | GetInferredMember
            Returns a System.Reflection.MemberInfo object for the context property.
        #>
        [CmdletBinding()]
        param(
            # Specifies the member expression ast to infer member info from.
            [Parameter(Position=0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
            [Alias('MemberExpressionAst', 'Expression')]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.Language.MemberExpressionAst]
            $Ast
        )
        process {
            # If the ast that was passed is our ExtendedMemberExpressionAst class then return the already
            # inferred member info.
            if ($Ast.InferredMember) { return $Ast.InferredMember }
    
            $type = GetInferredType -Ast $Ast.Expression
    
            # Predicate to use with FindMembers.
            $predicate = {
                param($Member, $Criteria)
    
                $nameFilter, $argCountFilter = $Criteria
    
                $Member.Name -eq $nameFilter -and
                (-not $argCountFilter -or $Member.GetParameters().Count -eq $argCountFilter)
            }
            # Check if it looks like the argument count was specified explicitly.
            $argumentCount     = $Ast.Arguments.Count
            if ($Ast.Arguments.Count -eq 1 -and $Ast.Arguments.StaticType -eq ([int])) {
                $argumentCount = $Ast.Arguments.Value
            }
            $member = $type.FindMembers(
                <# memberType:     #> 'All',
                <# bindingAttr:    #> [BindingFlags]'NonPublic, Public, Instance, Static, IgnoreCase',
                <# filter:         #> $predicate,
                <# filterCriteria: #> @(($Ast.Member.Value -replace '^new$', '.ctor'), $argumentCount)
    
                # Prioritize properties over fields and methods with smaller parameter counts.
            ) | Sort-Object -Property `
                @{Expression = { $PSItem.MemberType }; Ascending = $false },
                @{Expression = {
                    if ($PSItem -is [MethodBase]) { $PSItem.GetParameters().Count }
                    else { 0 }
                }}
    
            if ($member.Count -gt 1) { $member = $member[0] }
    
            if (-not $member) {
                ThrowError -Exception ([MissingMemberException]::new($Ast.Expression, $Ast.Member.Value)) `
                           -Id        MissingMember `
                           -Category  InvalidResult `
                           -Target    $Ast
            }
    
            $member
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\GetInferredType.ps1 Show
    using namespace System.Reflection
    
    function GetInferredType {
        <#
        .SYNOPSIS
            Attempts to determine the type of a variable within a script file.
        .DESCRIPTION
            This function first attempts to infer type from command completion context.  Failing that
            it will enumerate the scopes of any modules contained in the workspace as well as the global
            scope. If the variable is found in one of the scopes, the type of it's value will be returned.
    
            If the type cannot be inferred from command completion and the variable is defined in a function
            (or other child scope) this method will not work. The variable needs to be in a scope that
            exists at the time this function is ran. A workaround is to set a breakpoint right after the
            variable is defined.
        .INPUTS
            None
        .OUTPUTS
            System.Type
    
            Returns the inferred type if it was determined.  This function does not have output otherwise.
        .EXAMPLE
            PS C:\> GetInferredType -Ast $memberExpressionAst.Expression
            Determines the type of the variable used in a member expression.
        #>
        [CmdletBinding()]
        [OutputType([type])]
        param(
            # Specifies the current context of the editor.
            [Parameter(Position=0)]
            [ValidateNotNullOrEmpty()]
            [Microsoft.PowerShell.EditorServices.Extensions.EditorContext]
            $Context = $psEditor.GetEditorContext(),
    
            # Specifies the ast to analyze.
            [Parameter(Position=1, Mandatory)]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.Language.Ast]
            $Ast
        )
        begin {
            function GetInferredTypeImpl {
                # Return cached inferred type if it's our custom MemberExpressionAst
                if ($Ast.InferredType -and $Ast.InferredType -ne [object]) {
                    return $Ast.InferredType
                }
    
                if ($Ast -is [System.Management.Automation.Language.TypeExpressionAst]) {
                    return GetType -TypeName $Ast.TypeName
                }
    
                if ($Ast.StaticType -and $Ast.StaticType -ne [object]) {
                    return $Ast.StaticType
                }
    
                $PSCmdlet.WriteDebug("TYPEINF: Starting engine inference")
                try {
                    $flags = [BindingFlags]'Instance, NonPublic'
                    $mappedInput = [System.Management.Automation.CommandCompletion]::
                        MapStringInputToParsedInput(
                            $Ast.Extent.StartScriptPosition.GetFullScript(),
                            $Ast.Extent.EndOffset)
    
                    # If anyone knows a public way to go about getting the type inference from the engine
                    # give me a shout.
                    $analysis = [ref].
                        Assembly.
                        GetType('System.Management.Automation.CompletionAnalysis').
                        InvokeMember(
                            <# name:       #> $null,
                            <# invokeAttr: #> $flags -bor [BindingFlags]::CreateInstance,
                            <# binder:     #> $null,
                            <# target:     #> $null,
                            <# args: #> @(
                                <# ast:            #> $mappedInput.Item1,
                                <# tokens:         #> $mappedInput.Item2,
                                <# cursorPosition: #> $mappedInput.Item3,
                                <# options:        #> @{}))
    
                    $engineContext = $ExecutionContext.GetType().
                        GetField('_context', $flags).
                        GetValue($ExecutionContext)
    
                    $completionContext = $analysis.GetType().
                        GetMethod('CreateCompletionContext', $flags).
                        Invoke($analysis, @($engineContext))
    
                    $type = $Ast.GetType().
                        GetMethod('GetInferredType', $flags).
                        Invoke($Ast, @($completionContext)).
                        Where({ $null -ne $PSItem.Type -and $PSItem.Type -ne [object]}, 'First')[0].
                        Type
    
                    if ($type) {
                        return $type
                    }
    
                } catch {
                    $PSCmdlet.WriteDebug('TYPEINF: Engine failed with error ID "{0}"' -f $Error[0].FullyQualifiedErrorId)
                }
    
                if ($Ast -is [System.Management.Automation.Language.MemberExpressionAst]) {
                    $PSCmdlet.WriteDebug('TYPEINF: Starting member inference')
                    if ($member = GetInferredMember -Ast $Ast) {
                        return (
                            $member.ReturnType,
                            $member.PropertyType,
                            $member.FieldType
                        ).Where({ $PSItem -is [type] }, 'First')[0]
                    }
                }
    
                if ($Ast -is [System.Management.Automation.Language.VariableExpressionAst]) {
                    $PSCmdlet.WriteDebug('TYPEINF: Starting module state inference')
                    $inferredManifest = GetInferredManifest -ErrorAction Ignore
                    $moduleVariable = Get-Module |
                        Where-Object Guid -eq $inferredManifest.GUID |
                        ForEach-Object { $PSItem.SessionState.PSVariable.GetValue($Ast.VariablePath.UserPath) } |
                        Where-Object { $null -ne $PSItem }
    
                    if ($moduleVariable) {
                        return $moduleVariable.Where({ $null -ne $PSItem }, 'First')[0].GetType()
                    }
    
                    $PSCmdlet.WriteDebug('TYPEINF: Starting global state inference')
    
                    $foundInGlobal = $ExecutionContext.
                        SessionState.
                        Module.
                        GetVariableFromCallersModule(
                            $Ast.VariablePath.UserPath)
                    if ($foundInGlobal -and $null -ne $foundInGlobal.Value) {
                        return $foundInGlobal.Value.GetType()
                    }
                }
            }
        }
        end {
            $type = GetInferredTypeImpl
            if (-not $type) {
                ThrowError -Exception ([InvalidOperationException]::new($Strings.CannotInferType -f $Ast)) `
                           -Id        CannotInferType `
                           -Category  InvalidOperation `
                           -Target    $Ast
                return
            }
    
            $type
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\GetSettings.ps1 Show
    function GetSettings {
        [CmdletBinding()]
        param()
        end {
            function GetHashtable {
                if ($script:CSSettings) { return $script:CSSettings }
    
                $targetPath = Join-Path $psEditor.Workspace.Path -ChildPath 'ESCSSettings.psd1'
    
                if (Test-Path $targetPath) {
                    $script:CSSettings = Import-LocalizedData -BaseDirectory $psEditor.Workspace.Path `
                                                            -FileName 'ESCSSettings.psd1'
    
                    return $script:CSSettings
                }
    
                $script:CSSettings = $script:DEFAULT_SETTINGS
    
                return $script:CSSettings
            }
    
            $settings = GetHashtable
    
            # Ensure all settings have a default value even if not present in user supplied file.
            if ($settings.PreValidated) { return $settings }
    
            foreach ($setting in $script:DEFAULT_SETTINGS.GetEnumerator()) {
                if (-not ($settings.ContainsKey($setting.Key))) {
                    $settings.Add($setting.Key, $setting.Value)
                }
            }
            $settings.PreValidated = $true
            return $settings
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\GetType.ps1 Show
    using namespace System.Management.Automation
    
    function GetType {
        <#
        .SYNOPSIS
            Get a type info object for any nonpublic or public type.
        .DESCRIPTION
            Retrieve type info directly from the assembly if nonpublic or from implicitly casting if public.
        .INPUTS
            System.String
    
            You can pass type names to this function.
        .OUTPUTS
            System.Type
    
            Returns a Type object if a match is found.
        .EXAMPLE
            PS C:\> 'System.Management.Automation.SessionStateScope' | GetType
            Returns a Type object for SessionStateScope.
        #>
        [CmdletBinding()]
        param (
            # Specifies the type name to search for.
            [Parameter(Mandatory, ValueFromPipeline)]
            [ValidateNotNullOrEmpty()]
            [string]
            $TypeName
        )
        process {
            $type = $TypeName -as [type]
    
            if (-not $type) {
                $type = [AppDomain]::CurrentDomain.
                    GetAssemblies().
                    ForEach{ $PSItem.GetType($TypeName, $false, $true) }.
                    Where({ $PSItem }, 'First')[0]
            }
    
            if (-not $type) {
                $type = [AppDomain]::CurrentDomain.
                    GetAssemblies().
                    GetTypes().
                    Where({ $PSItem.ToString() -match "$TypeName$" }, 'First')[0]
            }
            # TODO: Pull using statements from the ast to catch some edge cases.
            if (-not $type) {
                ThrowError -Exception ([RuntimeException]::new($Strings.TypeNotFound -f $TypeName)) `
                           -Id        TypeNotFound `
                           -Category  InvalidOperation `
                           -Target    $TypeName
                return
            }
            $type
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\NormalizeIndent.ps1 Show
    function NormalizeIndent {
        [OutputType([string])]
        [CmdletBinding()]
        param(
            [Parameter(Mandatory, ValueFromPipeline)]
            [ValidateNotNullOrEmpty()]
            [string[]] $Source,
    
            [ValidateRange(0, [int]::MaxValue)]
            [int] $DecreaseIndentAmount
        )
        begin {
            $stringList = [System.Collections.Generic.List[string]]::new()
        }
        process {
            if ($null -eq $Source) {
                return
            }
    
            $stringList.AddRange($Source)
        }
        end {
            $sourceText = $stringList -join [Environment]::NewLine
            # Preserve new line characters. Only works if not sent a stream.
            $newLine    = [regex]::Match($sourceText, '\r?\n').Value
            $asLines    = $sourceText -split '\r?\n'
    
            if (-not $DecreaseIndentAmount) {
                # Get the smallest index of each lines first non-whitespace character. Ignore
                # here string ending tags and lines with only whitespace or nothing.
                $DecreaseIndentAmount = $asLines |
                    Select-String "^(?!'@)\s*(\S)" |
                    ForEach-Object { $PSItem.Matches[0].Groups[1].Index } |
                    Sort-Object |
                    Select-Object -First 1
            }
    
            $asLines -replace "^\s{0,$DecreaseIndentAmount}" -join $newLine
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\ReadChoicePrompt.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol
    using namespace Microsoft.PowerShell.EditorServices.Protocol.Messages
    using namespace Microsoft.PowerShell.EditorServices
    
    function ReadChoicePrompt {
        param([string]$Prompt, [System.Management.Automation.Host.ChoiceDescription[]]$Choices)
        end {
            $choiceIndex = 0
            $convertedChoices = $Choices.ForEach{
                $newLabel = '{0} - {1}' -f ($choiceIndex + 1), $PSItem.Label
                [ChoiceDetails]::new($newLabel, $PSItem.HelpMessage)
                $choiceIndex++
            } -as [ChoiceDetails[]]
    
            $result = $psEditor.
                Components.
                Get([IMessageSender]).SendRequest(
                    [ShowChoicePromptRequest]::Type,
                    [ShowChoicePromptRequest]@{
                        Caption        = $Prompt
                        Message        = $Prompt
                        Choices        = $convertedChoices
                        DefaultChoices = 0
                    },
                    $true).
                Result
    
            if (-not $result.PromptCanceled) {
                # yield
                $result.ResponseText |
                    Select-String '^(\d+) - ' |
                    ForEach-Object { $PSItem.Matches.Groups[1].Value - 1 }
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\ReadInputPrompt.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol
    using namespace Microsoft.PowerShell.EditorServices.Protocol.Messages
    
    function ReadInputPrompt {
        param([string]$Prompt)
        end {
            $result = $psEditor.
                Components.
                Get([IMessageSender]).SendRequest(
                    [ShowInputPromptRequest]::Type,
                    [ShowInputPromptRequest]@{
                        Name  = $Prompt
                        Label = $Prompt
                    },
                    $true).
                Result
    
            if (-not $result.PromptCanceled) {
                $result.ResponseText
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\ResolveRelativePath.ps1 Show
    function ResolveRelativePath {
        [OutputType([System.Management.Automation.PathInfo])]
        [CmdletBinding()]
        param([string]$Path)
        end {
            if ($resolved = (Resolve-Path (Join-Path $psEditor.Workspace.Path $Path) -ErrorAction Ignore)) {
                return $resolved
            }
            return Resolve-Path $Path
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\SetEditorLocation.ps1 Show
    function SetEditorLocation {
        [CmdletBinding()]
        param([string]$Path)
        end {
            $resolved = ResolveRelativePath $Path
            $psEditor.Workspace.OpenFile($resolved)
    
            WaitUntil { $psEditor.GetEditorContext().CurrentFile.Path -eq $resolved.Path }
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\ThrowError.ps1 Show
    using namespace System.Management.Automation
    
    function ThrowError {
        [CmdletBinding()]
        param(
            [Parameter(Position=0, Mandatory, ParameterSetName='New')]
            [ValidateNotNullOrEmpty()]
            [Exception]
            $Exception,
    
            [Parameter(Position=1, Mandatory, ParameterSetName='New')]
            [ValidateNotNullOrEmpty()]
            [string]
            $Id,
    
            [Parameter(Position=2, Mandatory, ParameterSetName='New')]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.ErrorCategory]
            $Category,
    
            [Parameter(Position=3, ParameterSetName='New')]
            [AllowNull()]
            [object]
            $Target,
    
            [Parameter(Position=0, Mandatory, ParameterSetName='Rethrow')]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.ErrorRecord]
            $ErrorRecord,
    
            [Alias('PSCmdlet')]
            [System.Management.Automation.PSCmdlet]
            $ErrorContext,
    
            [switch]
            $Show
        )
        end {
            # Need to manually check error action because of calling the error methods from a different
            # cmdlet context. Also reading/setting the error preference variable when the value is "Ignore"
            # throws, so we get it through variable intrinsics.
            $errorPreference = $ExecutionContext.SessionState.PSVariable.GetValue('ErrorActionPreference')
            if ($errorPreference -eq 'Ignore') { return }
    
            if (-not $ErrorContext) {
                foreach ($frame in (Get-PSCallStack)) {
                    if ($frame.Command -eq $MyInvocation.MyCommand.Name) { continue }
                    if ($ErrorContext = $frame.GetFrameVariables().PSCmdlet.Value) { break }
                }
                if (-not $ErrorContext) { $ErrorContext = $PSCmdlet }
            }
            if ($PSCmdlet.ParameterSetName -eq 'New') {
                $ErrorRecord = [ErrorRecord]::new($Exception, $Id, $Category, $TargetObject)
            }
    
            if ($errorPreference -eq 'SilentlyContinue') {
                $ErrorContext.WriteError($ErrorRecord)
                return
            }
    
            if ($psEditor -and $Show.IsPresent) {
                $psEditor.Window.ShowErrorMessage($ErrorRecord.Exception.Message)
            }
    
            $ErrorContext.ThrowTerminatingError($ErrorRecord)
        }
    }
    
  • tools\EditorServicesCommandSuite\Private\WaitUntil.ps1 Show
    function WaitUntil {
        param([scriptblock]$Predicate, [int]$Timeout = 300, [switch]$PassThru)
    
        $loop = 0
        while (-not $Predicate.Invoke()) {
            Start-Sleep -Milliseconds 50
            $loop += 50
            if ($loop -ge $Timeout) {
                if ($PassThru.IsPresent) {
                    return $false
                }
                break
            }
        }
        if ($PassThru.IsPresent) {
            return $true
        }
    }
    
  • tools\EditorServicesCommandSuite\PSGetModuleInfo.xml Show
    <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
      <Obj RefId="0">
        <TN RefId="0">
          <T>Microsoft.PowerShell.Commands.PSRepositoryItemInfo</T>
          <T>System.Management.Automation.PSCustomObject</T>
          <T>System.Object</T>
        </TN>
        <MS>
          <S N="Name">EditorServicesCommandSuite</S>
          <Version N="Version">0.4.0</Version>
          <S N="Type">Module</S>
          <S N="Description">Collection of editor commands for use in PowerShell Editor Services.</S>
          <S N="Author">Patrick Meinecke</S>
          <S N="CompanyName">SeeminglyScience</S>
          <S N="Copyright">(c) 2017 Patrick Meinecke. All rights reserved.</S>
          <DT N="PublishedDate">2017-09-26T02:07:30+01:00</DT>
          <Nil N="InstalledDate" />
          <Nil N="UpdatedDate" />
          <URI N="LicenseUri">https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/LICENSE</URI>
          <URI N="ProjectUri">https://github.com/SeeminglyScience/EditorServicesCommandSuite</URI>
          <Nil N="IconUri" />
          <Obj N="Tags" RefId="1">
            <TN RefId="1">
              <T>System.Object[]</T>
              <T>System.Array</T>
              <T>System.Object</T>
            </TN>
            <LST>
              <S>Editor</S>
              <S>EditorServices</S>
              <S>VSCode</S>
              <S>PSModule</S>
            </LST>
          </Obj>
          <Obj N="Includes" RefId="2">
            <TN RefId="2">
              <T>System.Collections.Hashtable</T>
              <T>System.Object</T>
            </TN>
            <DCT>
              <En>
                <S N="Key">Function</S>
                <Obj N="Value" RefId="3">
                  <TNRef RefId="1" />
                  <LST>
                    <S>Add-CommandToManifest</S>
                    <S>Add-ModuleQualification</S>
                    <S>Add-PinvokeMethod</S>
                    <S>ConvertTo-FunctionDefinition</S>
                    <S>ConvertTo-LocalizationString</S>
                    <S>ConvertTo-MarkdownHelp</S>
                    <S>ConvertTo-SplatExpression</S>
                    <S>Expand-Expression</S>
                    <S>Expand-MemberExpression</S>
                    <S>Expand-TypeImplementation</S>
                    <S>New-ESCSSettingsFile</S>
                    <S>Remove-Semicolon</S>
                    <S>Set-HangingIndent</S>
                    <S>Set-RuleSuppression</S>
                    <S>Set-UsingStatementOrder</S>
                  </LST>
                </Obj>
              </En>
              <En>
                <S N="Key">RoleCapability</S>
                <Obj N="Value" RefId="4">
                  <TNRef RefId="1" />
                  <LST />
                </Obj>
              </En>
              <En>
                <S N="Key">Command</S>
                <Obj N="Value" RefId="5">
                  <TNRef RefId="1" />
                  <LST>
                    <S>Add-CommandToManifest</S>
                    <S>Add-ModuleQualification</S>
                    <S>Add-PinvokeMethod</S>
                    <S>ConvertTo-FunctionDefinition</S>
                    <S>ConvertTo-LocalizationString</S>
                    <S>ConvertTo-MarkdownHelp</S>
                    <S>ConvertTo-SplatExpression</S>
                    <S>Expand-Expression</S>
                    <S>Expand-MemberExpression</S>
                    <S>Expand-TypeImplementation</S>
                    <S>New-ESCSSettingsFile</S>
                    <S>Remove-Semicolon</S>
                    <S>Set-HangingIndent</S>
                    <S>Set-RuleSuppression</S>
                    <S>Set-UsingStatementOrder</S>
                  </LST>
                </Obj>
              </En>
              <En>
                <S N="Key">DscResource</S>
                <Obj N="Value" RefId="6">
                  <TNRef RefId="1" />
                  <LST />
                </Obj>
              </En>
              <En>
                <S N="Key">Workflow</S>
                <Obj N="Value" RefId="7">
                  <TNRef RefId="1" />
                  <LST />
                </Obj>
              </En>
              <En>
                <S N="Key">Cmdlet</S>
                <Obj N="Value" RefId="8">
                  <TNRef RefId="1" />
                  <LST />
                </Obj>
              </En>
            </DCT>
          </Obj>
          <Nil N="PowerShellGetFormatVersion" />
          <S N="ReleaseNotes">- New editor command ConvertTo-FunctionDefinition for generating functions from selected text.</S>
          <Obj N="Dependencies" RefId="9">
            <TNRef RefId="1" />
            <LST>
              <Obj RefId="10">
                <TN RefId="3">
                  <T>System.Collections.Specialized.OrderedDictionary</T>
                  <T>System.Object</T>
                </TN>
                <DCT>
                  <En>
                    <S N="Key">Name</S>
                    <S N="Value">PSStringTemplate</S>
                  </En>
                  <En>
                    <S N="Key">CanonicalId</S>
                    <S N="Value">nuget:PSStringTemplate</S>
                  </En>
                </DCT>
              </Obj>
            </LST>
          </Obj>
          <S N="RepositorySourceLocation">https://www.powershellgallery.com/api/v2/</S>
          <S N="Repository">PSGallery</S>
          <S N="PackageManagementProvider">NuGet</S>
          <Obj N="AdditionalMetadata" RefId="11">
            <TN RefId="4">
              <T>System.Management.Automation.PSCustomObject</T>
              <T>System.Object</T>
            </TN>
            <MS>
              <S N="copyright">(c) 2017 Patrick Meinecke. All rights reserved.</S>
              <S N="description">Collection of editor commands for use in PowerShell Editor Services.</S>
              <S N="requireLicenseAcceptance">True</S>
              <S N="releaseNotes">- New editor command ConvertTo-FunctionDefinition for generating functions from selected text.</S>
              <S N="isLatestVersion">True</S>
              <S N="isAbsoluteLatestVersion">True</S>
              <S N="versionDownloadCount">329</S>
              <S N="downloadCount">410</S>
              <S N="packageSize">60156</S>
              <S N="published">26/09/2017 02:07:30 +01:00</S>
              <S N="created">26/09/2017 02:07:30 +01:00</S>
              <S N="tags">Editor EditorServices VSCode PSModule PSFunction_Add-CommandToManifest PSCommand_Add-CommandToManifest PSFunction_Add-ModuleQualification PSCommand_Add-ModuleQualification PSFunction_Add-PinvokeMethod PSCommand_Add-PinvokeMethod PSFunction_ConvertTo-FunctionDefinition PSCommand_ConvertTo-FunctionDefinition PSFunction_ConvertTo-LocalizationString PSCommand_ConvertTo-LocalizationString PSFunction_ConvertTo-MarkdownHelp PSCommand_ConvertTo-MarkdownHelp PSFunction_ConvertTo-SplatExpression PSCommand_ConvertTo-SplatExpression PSFunction_Expand-Expression PSCommand_Expand-Expression PSFunction_Expand-MemberExpression PSCommand_Expand-MemberExpression PSFunction_Expand-TypeImplementation PSCommand_Expand-TypeImplementation PSFunction_New-ESCSSettingsFile PSCommand_New-ESCSSettingsFile PSFunction_Remove-Semicolon PSCommand_Remove-Semicolon PSFunction_Set-HangingIndent PSCommand_Set-HangingIndent PSFunction_Set-RuleSuppression PSCommand_Set-RuleSuppression PSFunction_Set-UsingStatementOrder PSCommand_Set-UsingStatementOrder PSIncludes_Function</S>
              <S N="developmentDependency">False</S>
              <S N="updated">2018-05-12T16:42:02Z</S>
              <S N="NormalizedVersion">0.4.0</S>
              <S N="IsPrerelease">false</S>
              <S N="ItemType">Module</S>
              <S N="FileList">EditorServicesCommandSuite.nuspec|EditorServicesCommandSuite.psd1|EditorServicesCommandSuite.psm1|Classes\Async.ps1|Classes\Expressions.ps1|Classes\Renderers.ps1|Classes\Utility.ps1|en-US\EditorServicesCommandSuite-help.xml|en-US\Strings.psd1|Private\AddIndent.ps1|Private\GetAncestorOrThrow.ps1|Private\GetInferredManifest.ps1|Private\GetInferredMember.ps1|Private\GetInferredType.ps1|Private\GetSettings.ps1|Private\GetType.ps1|Private\NormalizeIndent.ps1|Private\ReadChoicePrompt.ps1|Private\ReadInputPrompt.ps1|Private\ResolveRelativePath.ps1|Private\SetEditorLocation.ps1|Private\ThrowError.ps1|Private\WaitUntil.ps1|Public\Add-CommandToManifest.ps1|Public\Add-ModuleQualification.ps1|Public\Add-PinvokeMethod.ps1|Public\ConvertTo-FunctionDefinition.ps1|Public\ConvertTo-LocalizationString.ps1|Public\ConvertTo-MarkdownHelp.ps1|Public\ConvertTo-SplatExpression.ps1|Public\Expand-Expression.ps1|Public\Expand-MemberExpression.ps1|Public\Expand-TypeImplementation.ps1|Public\New-ESCSSettingsFile.ps1|Public\Remove-Semicolon.ps1|Public\Set-HangingIndent.ps1|Public\Set-RuleSuppression.ps1|Public\Set-UsingStatementOrder.ps1|Templates\MemberExpression.stg|Templates\SettingsFile.stg</S>
              <S N="GUID">97607afd-d9bd-4a2e-a9f9-70fe1a0a9e4c</S>
              <S N="PowerShellVersion">5.1</S>
              <S N="DotNetFrameworkVersion">4.0</S>
              <S N="CLRVersion">4.0</S>
              <S N="ProcessorArchitecture">None</S>
              <S N="CompanyName">Community</S>
            </MS>
          </Obj>
          <S N="InstalledLocation">C:\Users\Paul\AppData\Local\Temp\5c31292c-d739-4ad3-98b4-5645381db12b\EditorServicesCommandSuite\0.4.0</S>
        </MS>
      </Obj>
    </Objs>
    
  • tools\EditorServicesCommandSuite\Public\Add-CommandToManifest.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Collections.Generic
    using namespace System.Linq
    
    function Add-CommandToManifest {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [CmdletBinding()]
        [EditorCommand(DisplayName='Add Closest Function To Manifest')]
        param()
    
        $commandAst = Find-Ast -AtCursor |
            Find-Ast -Ancestor -First -IncludeStartingAst { $PSItem.Name -and $PSItem.Name -match '\w+-\w+'}
    
        $settings     = GetSettings
        $functionName = $commandAst.Name
        $filePath     = $psEditor.GetEditorContext().CurrentFile.Path
    
        try {
            $fileListEntry = $PSCmdlet.SessionState.Path.NormalizeRelativePath(
                $filePath,
                (ResolveRelativePath $settings.MainModuleDirectory))
    
            $manifestFile = ResolveRelativePath $settings.SourceManifestPath
        } catch {
            ThrowError -Exception ([ArgumentException]::new($Strings.InvalidSettingValue -f 'SourceManifestPath')) `
                       -Id        InvalidSettingValue `
                       -Category  InvalidArgument `
                       -Target    $settings
        }
    
        SetEditorLocation $manifestFile
    
        function GetManifestField ([string]$Name) {
            $field = Find-Ast -First { $PSItem.Value -eq $Name } | Find-Ast -First
            # This transforms a literal string array expression into it's output without invoking.
            $valueString = $field.ToString() -replace '@\(\)' `
                                             -split   '[,\n\s]' `
                                             -replace '['',\s]' `
                                             -match   '.' `
                                             -as      [List[string]]
            # yield
            [PSCustomObject]@{
                Ast    = $field
                Extent = $field.Extent
                Value  = $valueString
            }
        }
    
        $functions = GetManifestField -Name FunctionsToExport
        $functions.Value.Add($functionName)
        $functions.Value.Sort({ $args[0].CompareTo($args[1]) })
        $functions.Extent | Set-ScriptExtent -Text ([Enumerable]::Distinct($functions.Value)) -AsArray
    
        $fileList = GetManifestField -Name FileList
    
        $fileList.Value.Add($fileListEntry)
        $fileList.Value.Sort({ $args[0].CompareTo($args[1]) })
        $fileList.Extent | Set-ScriptExtent -Text ([Enumerable]::Distinct($fileList.Value)) -AsArray
    
        #SetEditorLocation $filePath
    }
    
  • tools\EditorServicesCommandSuite\Public\Add-ModuleQualification.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation.Language
    
    function Add-ModuleQualification {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Add Module Name to Closest Command')]
        [CmdletBinding()]
        param(
            [System.Management.Automation.Language.Ast]
            $Ast
        )
        begin {
            function InferCommandInfo([string]$commandName) {
                # HACK: If someone knows a reliable way to perform command lookup outside the module,
                #       without reflection, please let me know or send a PR.
                $flags = [Reflection.BindingFlags]'Instance, NonPublic'
                $context = $ExecutionContext.
                    GetType().
                    GetField('_context', $flags).
                    GetValue($ExecutionContext)
                $globalState = $context.
                    GetType().
                    GetProperty('TopLevelSessionState', $flags).
                    GetValue($context)
    
                $getCommand = { $ExecutionContext.InvokeCommand.GetCommand($args[0], 'All') }
    
                $null = [scriptblock].
                    GetProperty('SessionStateInternal', $flags).
                    SetValue($getCommand, $globalState)
    
                $PSCmdlet.WriteVerbose($Strings.InferringFromSession)
    
                $command = $getCommand.InvokeReturnAsIs($commandName)
    
                if ($command) { return $command }
    
                $PSCmdlet.WriteVerbose($Strings.InferringFromWorkspace)
                try {
                    $manifest = GetInferredManifest
                    if (($moduleInfo = Get-Module $manifest.Name -ErrorAction Ignore)) {
                        # Retrieve command info from the first returned module incase multiple versions
                        # are loaded into the session.
                        return $moduleInfo[0].Invoke($getCommand, $commandName)
                    }
                    $isExport = $manifest.FunctionsToExport -contains $commandName -or
                                $manifest.CmdletsToExport   -contains $commandName
                    # If it's exported in the manifest but not loaded we can't actually get CommandInfo,
                    # but we can return the properties we expect anyway.
                    if ($isExport) {
                        return @{
                            ModuleName = $manifest.Name
                            Name       = $commandName
                        }
                    }
                } catch {
                    $PSCmdlet.WriteVerbose($Strings.VerboseInvalidManifest)
                }
                $PSCmdlet.WriteVerbose('Unable to find command "{0}".' -f $commandName)
            }
        }
        end {
            $Ast = GetAncestorOrThrow $Ast -AstType CommandAst
    
            $command = InferCommandInfo $Ast.GetCommandName()
            if (-not $command) {
                ThrowError -Exception ([ArgumentException]::new($Strings.CannotInferModule)) `
                           -Id        CannotInferModule `
                           -Category  InvalidArgument `
                           -Target    $Ast.GetCommandName() `
                           -Show
            }
    
            if (-not $command.ModuleName) {
                $PSCmdlet.WriteVerbose($Strings.CommandNotInModule)
                return
            }
    
            $newExpression = '{0}\{1}' -f $command.ModuleName, $command.Name
            $Ast.CommandElements[0] | Set-ScriptExtent -Text $newExpression
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Add-PinvokeMethod.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation.Host
    using namespace System.Management.Automation.Language
    
    function Add-PinvokeMethod {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Insert Pinvoke Method Definition')]
        [CmdletBinding()]
        param(
            [ValidateNotNullOrEmpty()]
            [string]
            $Function,
    
            [ValidateNotNullOrEmpty()]
            [string]
            $Module
        )
        begin {
            if (-not $script:PinvokeWebService) {
                # Get the web service async so there isn't a hang before prompting for function name.
                $script:PinvokeWebService = async {
                    $newWebServiceProxySplat = @{
                        Namespace = 'PinvokeWebService'
                        Class     = 'Main'
                        Uri       = 'http://pinvoke.net/pinvokeservice.asmx?wsdl'
                    }
                    New-WebServiceProxy @newWebServiceProxySplat
                }
            }
    
            # Return parameters if they exist, otherwise handle user input.
            function GetFunctionInfo([string] $functionName, [string] $moduleName) {
                if ($functionName -and $moduleName) {
                    return [PSCustomObject]@{
                        Function = $functionName
                        Module   = $moduleName
                    }
                }
                if (-not $functionName) {
                    $functionName = ReadInputPrompt $Strings.PInvokeFunctionNamePrompt
                    if (-not $functionName) { return }
                }
                $pinvoke = await $script:PinvokeWebService
                $searchResults = $pinvoke.SearchFunction($functionName, $null)
    
                if (-not $searchResults) {
                    ThrowError -Exception ([ArgumentException]::new($Strings.CannotFindPInvokeFunction -f $functionName)) `
                               -Id        CannotFindPInvokeFunction `
                               -Category  InvalidArgument `
                               -Target    $functionName `
                               -Show
                }
    
                $choice = $null
                if ($searchResults.Count -gt 1) {
                    $choices = $searchResults.ForEach{
                        [ChoiceDescription]::new(
                            $PSItem.Function,
                            ('Module: {0} Function: {1}' -f $PSItem.Module, $PSItem.Function))
                    }
                    $choice = ReadChoicePrompt $Strings.PInvokeFunctionChoice -Choices $choices
                    if ($null -eq $choice) { return }
                }
                $searchResults[[int]$choice]
            }
    
            # Some modules don't always return correctly, commonly structs.  This is a last ditch catch
            # all that parses the HTML content directly.
            # TODO: Replace calls to IE COM object with HtmlAgilityPack or similar.
            function GetUnsupportedSignature {
                $url = 'http://pinvoke.net/default.aspx/{0}/{1}.html' -f
                    $functionInfo.Module,
                    $functionInfo.Function
                try {
                    $request = Invoke-WebRequest $url
                } catch {
                    return
                }
    
                if ($request.Content -match 'The module <b>([^<]+)</b> does not exist') {
                    $PSCmdlet.WriteDebug('Module {0} not found.' -f $matches[1])
                    return
                }
    
                if ($request.Content -match 'You are about to create a new page called <b>([^<]+)</b>') {
                    $PSCmdlet.WriteDebug('Function {0} not found' -f $matches[1])
                    return
                }
    
                $nodes = $request.ParsedHtml.body.getElementsByClassName('TopicBody')[0].childNodes
                for ($i = 0; $i -lt $nodes.length; $i++) {
    
                    $node = $nodes[$i]
                    if ($node.tagName -ne 'H4') { continue }
                    if ($node.innerText -notmatch 'C# Definition') { continue }
    
                    $sig = $nodes[$i + 1]
                    if ($sig.tagName -ne 'P' -or $sig.className -ne 'pre') { continue }
                    return [PSCustomObject]@{
                        Signature = $sig.innerText -replace '\r?\n', '|'
                        Url = $url
                    }
                }
            }
    
            # Get template and insertion extent.  If cursor is in a Add-Type command AST that has a member
            # definiton parameter, it will insert the signature into the existing command.  Otherwise it
            # will create a new Add-Type command expression at the current cursor position.
            function GetTemplateInfo {
                $defaultAction = {
                    [PSCustomObject]@{
                        Template = "# Source: <SourceUri><\n>" +
                                   "Add-Type -Namespace <Namespace> -Name <Class> -MemberDefinition '<\n><Signature>'"
                        Position = [FullScriptExtent]::new(
                            $context.CurrentFile,
                            [BufferRange]::new(
                                $context.CursorPosition.Line,
                                $context.CursorPosition.Column,
                                $context.CursorPosition.Line,
                                $context.CursorPosition.Column))
                    }
                }
                $context = $psEditor.GetEditorContext()
                $commandAst = Find-Ast -AtCursor | Find-Ast -Ancestor -First { $PSItem -is [CommandAst] }
    
                if (-not $commandAst -or $commandAst.GetCommandName() -ne 'Add-Type') {
                    return & $defaultAction
                }
                $binding = [StaticParameterBinder]::BindCommand($commandAst, $true)
    
                $memberDefinition = $binding.BoundParameters.MemberDefinition
    
                if (-not $memberDefinition) { return & $defaultAction }
    
                $targetOffset = $memberDefinition.Value.Extent.EndOffset - 1
                return [PSCustomObject]@{
                    Template = '<\n><\n>// Source: <SourceUri><\n><Signature>'
                    Position = [FullScriptExtent]::new($context.CurrentFile, $targetOffset, $targetOffset)
                }
            }
    
            # Get first non-whitespace character location if the line has text, otherwise get the current
            # cursor column.
            function GetIndentLevel {
                try {
                    $context   = $psEditor.GetEditorContext()
                    $lineStart = $context.CursorPosition.GetLineStart()
                    $lineEnd   = $context.CursorPosition.GetLineEnd()
                    $lineText  = $context.CurrentFile.GetText(
                        [BufferRange]::new(
                            $lineStart.Line,
                            $lineStart.Column,
                            $lineEnd.Line,
                            $lineEnd.Column))
    
                    if ($lineText -match '\S') {
                        return $lineStart.Column - 1
                    }
                } catch {
                    $PSCmdlet.WriteDebug('Exception occurred while getting indent level')
                }
                return $context.CursorPosition.Column - 1
            }
        }
        end {
            $functionInfo = GetFunctionInfo $Function $Module
            if (-not $functionInfo) { return }
    
            $pinvoke = await $script:PinvokeWebService
    
            # Get signatures from pinvoke.net and filter by C#
            $signatureInfo = $null
            try {
                $signatureInfo = $pinvoke.
                    GetResultsForFunction(
                        $functionInfo.Function,
                        $functionInfo.Module).
                    Where{ $PSItem.Language -eq 'C#' }
            } catch [System.Web.Services.Protocols.SoapException] {
                if ($PSItem.Exception.Message -match 'but no signatures could be extracted') {
                    $signatureInfo = GetUnsupportedSignature
                }
            }
    
            if (-not $signatureInfo) {
                ThrowError -Exception ([InvalidOperationException]::new($Strings.MissingPInvokeSignature)) `
                           -Id        MissingPInvokeSignature `
                           -Category  InvalidOperation `
                           -Target    $functionInfo `
                           -Show
            }
    
            # - Replace pipes with new lines
            # - Add public modifier
            # - Trim white trailing whitespace
            # - Escape single quotes
            $signature = $signatureInfo.Signature `
                -split '\|' `
                -join [Environment]::NewLine `
                -replace '(?<!public )(?<!\[)(?:private |internal )?(static|struct)', 'public $1' `
                -replace '\s+$' `
                -replace "'", "''"
    
            # Strip module name of numbers and make PascalCase.
            $formattedModuleName = [regex]::Replace(
                ($functionInfo.Module -replace '\d'),
                '^\w',
                { $args[0].Value.ToUpper() })
    
            $templateInfo = GetTemplateInfo
            $expression = Invoke-StringTemplate -Definition $templateInfo.Template -Parameters @{
                Namespace = 'PinvokeMethods'
                Class     = $formattedModuleName
                Signature = $signature
                SourceUri = $signatureInfo.Url.Where({ $PSItem }, 'First')[0]
            }
    
            $indentLevel = GetIndentLevel
            $indent      = ' ' * ($indentLevel - 1)
            $expression  = $expression -split '\r?\n' -join ([Environment]::NewLine + $indent)
    
            Set-ScriptExtent -Extent $templateInfo.Position -Text $expression
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\ConvertTo-FunctionDefinition.ps1 Show
    using namespace System.Collections.Generic
    using namespace System.Management.Automation
    using namespace System.Management.Automation.Language
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    
    function ConvertTo-FunctionDefinition {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Create New Function From Selection')]
        [CmdletBinding(DefaultParameterSetName='__AllParameterSets')]
        param(
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.Language.IScriptExtent] $Extent,
    
            [ValidateNotNullOrEmpty()]
            [string] $FunctionName,
    
            [Parameter(ParameterSetName='ExternalFile')]
            [ValidateNotNull()]
            [string] $DestinationPath,
    
            [Parameter(ParameterSetName='BeginBlock')]
            [switch] $BeginBlock,
    
            [Parameter(ParameterSetName='Inline')]
            [switch] $Inline
        )
        begin {
            # Ensure a script extent includes the entire starting line including whitespace.
            function ExpandExtent {
                param(
                    [Parameter(ValueFromPipeline)]
                    [IScriptExtent] $ExtentToExpand
                )
                process {
                    if (-not $ExtentToExpand -or $ExtentToExpand.StartColumnNumber -eq 1) {
                        return $ExtentToExpand
                    }
    
                    return [Microsoft.PowerShell.EditorServices.FullScriptExtent]::new(
                        $psEditor.GetEditorContext().CurrentFile,
                        [Microsoft.PowerShell.EditorServices.BufferRange]::new(
                            $ExtentToExpand.StartLineNumber,
                            1,
                            $ExtentToExpand.EndLineNumber,
                            $ExtentToExpand.EndColumnNumber))
                }
            }
    
            # Create an named end block from the default unnamed end block.
            function CreateEndBlock {
                param([NamedBlockAst] $Ast)
                end {
                    $statements = $Ast.Statements | Join-ScriptExtent
                    $endBlockIndent = $statements.StartColumnNumber - 1
                    $statements = $statements | ExpandExtent
    
                    $endBlockText = 'end {',
                                    ($statements | NormalizeIndent | AddIndent -Amount 4),
                                    '}' |
                                    AddIndent -Amount $endBlockIndent
    
                    $statements | Set-ScriptExtent -Text $endBlockText
                }
            }
    
            # Get specified extent, selected text, or throw.
            function GetTargetExtent {
                if ($Extent) {
                    return $Extent | ExpandExtent
                }
    
                $selectedRange = $psEditor.GetEditorContext().SelectedRange
                if ($selectedRange.Start -ne $selectedRange.End) {
                    return $selectedRange | ConvertTo-ScriptExtent | ExpandExtent
                }
    
                ThrowError -Exception ([PSArgumentException]::new($Strings.NoExtentSelected)) `
                           -Id NoExtentSelected `
                           -Category InvalidArgument `
                           -Target $Extent `
                           -Show
            }
    
            # Prompt for destination if not specified, throw if no selection is made.
            function ValidateDestination {
                if ($PSCmdlet.ParameterSetName -in 'BeginBlock', 'Inline', 'ExternalFile') {
                    return $PSCmdlet.ParameterSetName
                }
    
                $choices = [Host.ChoiceDescription]::new('BeginBlock', $Strings.ExportFunctionBeginDescription),
                           [Host.ChoiceDescription]::new('Inline', $Strings.ExportFunctionInlineDescription),
                           [Host.ChoiceDescription]::new('ExternalFile', $Strings.ExportFunctionExternalFileDescription)
    
                $choice = ReadChoicePrompt -Prompt $Strings.ExportFunctionPrompt -Choices $choices
                return $choices[$choice].Label
            }
    
            # Prompt for file path if selected from the menu, throw if not specified.
            function ValidateDestinationFile {
                if (-not [string]::IsNullOrWhiteSpace($DestinationPath)) {
                    return $DestinationPath
                }
    
                $file = ReadInputPrompt -Prompt $Strings.EnterDestinationFilePrompt
                if (-not [string]::IsNullOrWhiteSpace($file)) {
                    return $file
                }
    
                ThrowError -Exception ([PSArgumentException]::new($Strings.NoDestinationFile)) `
                           -Id NoDestinationFile `
                           -Category InvalidArgument `
                           -Target $file `
                           -Show
            }
    
            # Prompt for function name if not specified in parameters. Throw if still null.
            function ValidateFunctionName {
                if (-not [string]::IsNullOrWhitespace($FunctionName)) {
                    return $FunctionName
                }
    
                $FunctionName = ReadInputPrompt -Prompt $Strings.ExportFunctionNamePrompt
    
                if (-not [string]::IsNullOrWhiteSpace($FunctionName)) {
                    return $FunctionName
                }
    
                ThrowError -Exception ([PSArgumentException]::new($Strings.MissingFunctionName)) `
                           -Id MissingFunctionName `
                           -Category InvalidArgument `
                           -Target $FunctionName `
                           -Show
            }
    
            # Safely captialize the first character if a string. If the string is two or less characters
            # then capitialize the whole string.
            function ToPascalCase {
                param([string] $String)
                end {
                    if ($String.Length -le 2) {
                        return $String.ToUpperInvariant()
                    }
    
                    return $String.Substring(0, 1).ToUpperInvariant() +
                           ($String[1..$String.Length] -join '')
                }
            }
    
            # Compile a dictionary of unique variables that should be parameters, along with their
            # inferred type if possible.
            function GetInferredParameters {
                param([VariableExpressionAst[]] $Variables)
                end {
                    $parameters = [Dictionary[string, Tuple[string, string, type, bool]]]::new(
                        [StringComparer]::InvariantCultureIgnoreCase)
    
                    if (-not $Variables.Count) {
                        return $parameters
                    }
    
                    foreach ($variable in $Variables) {
                        $asPascalCase = ToPascalCase $variable.VariablePath.UserPath
    
                        $existingParameter = $null
                        if ($parameters.TryGetValue($asPascalCase, [ref]$existingParameter)) {
                            if ($existingParameter.Item3 -ne [object]) {
                                continue
                            }
    
                            $inferredType = GetInferredType -Ast $variable -ErrorAction Ignore
                            if ($inferredType -ne [object]) {
                                $parameters[$asPascalCase] = [Tuple[string, string, type, bool]]::new(
                                    $asPascalCase,
                                    $variable.VariablePath.UserPath,
                                    $inferredType,
                                    $existingParameter.Item4)
                            }
    
                            continue
                        }
    
                        $inferredType = GetInferredType -Ast $variable -ErrorAction Ignore
                        if (-not $inferredType) {
                            $inferredType = [object]
                        }
    
                        $parseErrors = $null
                        $parsedVariableName = [Parser]::ParseInput(
                            '${0}' -f $variable.VariablePath.UserPath,
                            [ref]$null,
                            [ref]$parseErrors)
    
                        $shouldEscape = $parseErrors.Count -or
                            $parsedVariableName.EndBlock.Statements.PipelineElements.Count -gt 1
    
                        $parameters.Add(
                            $asPascalCase,
                            [Tuple[string, string, type, bool]]::new(
                                $asPascalCase,
                                $variable.VariablePath.UserPath,
                                $inferredType,
                                $shouldEscape))
                    }
    
                    return $parameters
                }
            }
    
            # Get variable names for the scope that are considered for our purposes as "locals".
            # Include variables that are:
            # 1 - Assigned within in the target AST
            # 2 - Assigned from language constructs like foreach statements
            # 3 - Special variables like $_/$ExecutionContext/etc
            # 4 - Have a scope in the user path (i.e $global:varName)
            function GetLocalVariables {
                param([Ast] $Ast)
                end {
                    $localVariables = [List[string]]::new()
                    $assignmentAsts = Find-Ast -Ast $targetAst -Family {
                        # Find variable assignments, exlude member/index expression assignments.
                        $PSItem -is [AssignmentStatementAst] -and (
                        $PSItem.Left -is [VariableExpressionAst] -or (
                        $PSItem.Left -is [ConvertExpressionAst] -and
                        $PSItem.Left.Child -is [VariableExpressionAst]))
                    }
    
                    if ($assignmentAsts.Count) {
                        $assignmentAsts.Left.ForEach{
                            if ($PSItem -is [VariableExpressionAst]) {
                                $localVariables.Add($PSItem.VariablePath.UserPath)
                                return
                            }
    
                            $localVariables.Add($PSItem.Child.VariablePath.UserPath)
                        }
                    }
    
                    $forEachStatements = Find-Ast -Ast $targetAst -Family { $PSItem -is [ForEachStatementAst] }
                    if ($forEachStatements.Count) {
                        $localVariables.AddRange(
                            $forEachStatements.Variable.VariablePath.UserPath -as [string[]])
                    }
    
                    return $localVariables
                }
            }
    
            # Create the function definition expression.
            function NewFunctionDefinition {
                end {
                    $function = [System.Text.StringBuilder]::new()
                    $null = & {
                        $indent = '    '
                        $function.
                            AppendFormat('function {0} {{', $FunctionName).
                            AppendLine().
                            Append($indent).
                            Append('param(')
    
                        $paramText = $parameters.Values.ForEach{
                            $parameterType = [Microsoft.PowerShell.ToStringCodeMethods]::Type($PSItem.Item3)
    
                            $variableName = $PSItem.Item1
                            if ($PSItem.Item4) {
                                $variableName = '{' +
                                    [CodeGeneration]::EscapeVariableName($PSItem.Item1) +
                                    '}'
                            }
    
                            # Ensure the parameter type is not too generic and is resolvable.
                            if ($parameterType -ne 'System.Object' -and $parameterType -as [type]) {
                                return '[{0}] ${1}' -f $parameterType, $variableName
                            }
    
                            return '${0}' -f $variableName
                        }
    
                        if ($paramText.Count) {
                            $shouldMultiline = $paramText.Count -gt 3
                            $delim = ', '
                            if ($shouldMultiline) {
                                $function.AppendLine().Append($indent + $indent)
                                $delim = ',', [Environment]::NewLine, $indent, $indent -join ''
                            }
    
                            $function.Append($paramText -join $delim)
    
                            if ($shouldMultiline) {
                                $function.AppendLine().Append($indent)
                            }
                        }
    
                        $function.
                            AppendLine(')').
                            Append($indent).
                            AppendLine('end {')
    
                        $targetWithCorrections = [System.Text.StringBuilder]::new($targetExtent.Text)
    
                        $targetStartOffset = $targetExtent.StartOffset
                        foreach ($expression in $variableExpressions) {
                            $variableName = $expression.VariablePath.UserPath
                            $asPascalCase = ToPascalCase $variableName
                            $variableOffset = $expression.Extent.StartOffset - $targetStartOffset
    
                            # Account for escaped variabled names (e.g ${my strange var name})
                            if ($expression.ToString().IndexOf('{') -ne -1) {
                                $variableOffset++
                            }
    
                            $targetWithCorrections.
                                Remove(
                                    $variableOffset,
                                    [CodeGeneration]::EscapeVariableName($variableName).Length).
                                Insert(
                                    $variableOffset,
                                    [CodeGeneration]::EscapeVariableName($asPascalCase))
                        }
    
                        $targetWithIndent = $targetWithCorrections |
                            NormalizeIndent |
                            AddIndent -Amount 8
    
                        $function.
                            AppendLine($targetWithIndent).
                            Append($indent).
                            AppendLine('}').
                            Append('}')
                    }
    
                    return $function.ToString()
                }
            }
    
            # Handle exporting the generated function to an external file.
            function ExportFunctionExternalFile {
                param()
                end {
                    $currentFolder = [System.IO.Path]::GetDirectoryName(
                        $psEditor.GetEditorContext().CurrentFile.Path)
    
                    # If the file is untitled, use the workspace path instead.
                    if ([string]::IsNullOrWhiteSpace($currentFolder)) {
                        $currentFolder = $psEditor.Workspace.Path
    
                        # If untitled workspace, use current provider path.
                        if ([string]::IsNullOrWhiteSpace($currentFolder)) {
                            $currentFolder = $PSCmdlet.CurrentProviderLocation('FileSystem').Path
                        }
                    }
    
                    $path = $PSCmdlet.SessionState.Path
                    $targetFile = $path.
                        GetUnresolvedProviderPathFromPSPath(
                            $path.Combine(
                                $currentFolder,
                                $targetFile))
    
                    if (-not [System.IO.Path]::GetExtension($targetFile)) {
                        $targetFile = Join-Path $targetFile "$FunctionName.ps1"
                    }
    
                    if (-not (Test-Path $targetFile)) {
                        $directory = Split-Path $targetFile
                        if (-not (Test-Path $directory)) {
                            $null = New-Item $directory -ItemType Directory -Force
                        }
    
                        $null = New-Item $targetFile -ItemType File
                    }
    
                    $targetFile = Resolve-Path $targetFile
    
                    $psEditor.Workspace.OpenFile($targetFile)
                    WaitUntil { $psEditor.GetEditorContext().CurrentFile.Path -eq $targetFile }
    
                    $psEditor.GetEditorContext().CurrentFile.InsertText($functionText)
                }
            }
    
            # Handle exporting the generated function to the line directly above the selection.
            function ExportFunctionInline {
                param()
                end {
                    $indentedFunction = $functionText | AddIndent -Amount ($targetExtentIndent - 1)
                    $psEditor.GetEditorContext().CurrentFile.InsertText(
                        ($indentedFunction + [Environment]::NewLine + [Environment]::NewLine),
                        $targetExtent.StartLineNumber,
                        1)
                }
            }
    
            # Handle exporting the generated function to the begin block of the closest ancestor function
            # definition.  If there is no ancestor function definition then export to the begin block of
            # the main script AST. This also handles creating a begin block if it doesn't exit, and creating
            # a named end block if there are no named blocks.
            function ExportFunctionBegin {
                param()
                end {
                    $findAstSplat = @{
                        Ast          = $targetAst
                        Ancestor     = $true
                        FilterScript = {
                            $PSItem -is [FunctionDefinitionAst] -and
                            $PSItem.Parent -isnot [FunctionMemberAst]
                        }
                    }
    
                    # Find the parent function definition from before we removed the target extent
                    $targetBlock = Find-Ast @findAstSplat | ForEach-Object Body
                    if (-not $targetBlock) {
                        $targetBlock = $psEditor.GetEditorContext().CurrentFile.Ast
                    }
    
                    if ($targetBegin = $targetBlock.BeginBlock) {
                        $entryLine         = $targetBegin.Extent.StartLineNumber
                        $beginIndent       = $targetBegin.Extent.StartColumnNumber + 3
                        $fullScriptAsLines = $fullScript -split '\r?\n'
                        $beginLineText     = $fullScriptAsLines[$entryLine - 1]
                        $braceOffset       = $beginLineText.IndexOf(
                            '{',
                            $beginLineText.IndexOf(
                                'begin',
                                [StringComparison]::InvariantCultureIgnoreCase))
    
                        # If we couldn't find the begin text and brace, they are probably on different lines
                        if ($braceOffset -eq -1) {
                            $entryLine++
                            $braceOffset = $fullScriptAsLines[$entryLine - 1].IndexOf('{')
                        }
    
                        $entryColumn = $braceOffset + 2
    
                        $indentedFunctionText = $functionText | AddIndent -Amount $beginIndent
                        $psEditor.GetEditorContext().CurrentFile.InsertText(
                            [Environment]::NewLine + $indentedFunctionText,
                            $entryLine,
                            $entryColumn)
                        return
                    }
    
                    if ($targetBlock.EndBlock.Unnamed) {
                        # We have to wrap the unnamed block, so we need to get the updated AST. If the block wasn't
                        # nested then we already have the new one.
                        if ($targetBlock.Parent -is [FunctionDefinitionAst]) {
                            $targetBlock = Find-Ast -First {
                                $PSItem -is [ScriptBlockAst] -and
                                $PSItem.Parent -is [FunctionDefinitionAst] -and
                                $PSItem.Extent.StartOffset -eq $targetBlock.Extent.StartOffset
                            }
                        }
    
                        CreateEndBlock -Ast $targetBlock.EndBlock
                    }
    
                    $beginText = 'begin {',
                                 (AddIndent -Source $FunctionText),
                                 '}' -join
                                 [Environment]::NewLine
    
                    $fullScriptAsLines = $fullScript -split '\r?\n'
    
                    [int] $parentBlockIndent = $fullScriptAsLines[$targetBlock.Extent.StartLine - 1] |
                        Select-String '^\s+' |
                        ForEach-Object { $PSItem.Matches[0].Length }
    
                    $entryLine = $targetBlock.Extent.StartLineNumber
                    $entryIsRoot = $true
                    if ($targetBlock.UsingStatements.Count) {
                        $entryLine = $targetBlock.UsingStatements[-1].Extent.EndLineNumber
                        $entryIsRoot = $false
                    }
    
                    if ($targetBlock.ParamBlock) {
                       $entryLine = $targetBlock.ParamBlock.Extent.EndLineNumber
                       $entryIsRoot = $false
                    }
    
                    $beginIndent = $parentBlockIndent + 4
                    $parentIsRoot = -not $targetBlock.Parent
                    if ($parentIsRoot) {
                        $beginIndent = 0
                    }
    
                    if ($parentIsRoot -and $entryIsRoot) {
                        $entryColumn = 1
                        $beginText = $beginText + [Environment]::NewLine
                    } else {
                        $entryColumn = $fullScriptAsLines[$entryLine - 1].Length + 1
                        $beginText = [Environment]::NewLine + $beginText
                    }
    
                    $beginText = AddIndent $beginText -Amount $beginIndent
    
                    $psEditor.GetEditorContext().CurrentFile.InsertText(
                        $beginText,
                        $entryLine,
                        $entryColumn)
                }
            }
        }
        end {
            $FunctionName = ValidateFunctionName
            $targetExtent = GetTargetExtent
    
            [int] $targetExtentIndent = [regex]::Match(($targetExtent.Text -replace '\r?\n'), '\S').Index
    
            $fullScript = $targetExtent.StartScriptPosition.GetFullScript()
    
            # Add braces to the selection so we can have a single AST to use for analysis.
            $alteredScript = $fullScript.
                Insert($targetExtent.EndOffset, '}').
                Insert($targetExtent.StartOffset, '{')
    
            $scriptAst = [Parser]::ParseInput(
                $alteredScript,
                $targetExtent.File,
                [ref]$null,
                [ref]$null)
    
            $targetAst = Find-Ast -Ast $scriptAst -First {
                $PSItem.Extent.StartOffset -eq $targetExtent.StartOffset
            }
    
            $localVariables = GetLocalVariables -Ast $targetAst
    
            $variableExpressions = Find-Ast -Ast $targetAst -Family {
                $PSItem -is [VariableExpressionAst] -and
                $PSItem.VariablePath.IsUnscopedVariable -and
                $PSItem.VariablePath.UserPath -notin $localVariables -and
                -not [SpecialVariables]::IsSpecialVariable($PSItem)
            }
    
            $parameters = GetInferredParameters $variableExpressions
            $destination = ValidateDestination
            if ($destination -eq 'ExternalFile') {
                [string] $targetFile = ValidateDestinationFile
            }
    
            $functionText = NewFunctionDefinition
    
            $invocation = [System.Text.StringBuilder]::new($FunctionName)
            foreach ($parameter in $parameters.Values) {
                $variableName = $parameter.Item2
                if ($parameter.Item4) {
                    $variableName = '{' + [CodeGeneration]::EscapeVariableName($parameter.Item2) + '}'
                }
    
                $null = $invocation.AppendFormat(' -{0} ${1}', $parameter.Item1, $variableName)
            }
    
            $invocation = $invocation | AddIndent -Amount $targetExtentIndent
    
            $targetExtent | Set-ScriptExtent -Text $invocation
    
            switch ($destination) {
                Inline       { ExportFunctionInline }
                ExternalFile { ExportFunctionExternalFile }
                default      { ExportFunctionBegin }
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\ConvertTo-LocalizationString.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation.Language
    
    function ConvertTo-LocalizationString {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [CmdletBinding()]
        [EditorCommand(DisplayName='Add Closest String to Localization File')]
        param(
            [System.Management.Automation.Language.Ast]
            $Ast = (Find-Ast -AtCursor),
    
            [string]
            $Name
        )
        end {
            $Ast = GetAncestorOrThrow $Ast -AstTypeName StringConstantExpressionAst -ErrorContext $PSCmdlet
    
            if (-not $Name) {
                if ($Host -is [System.Management.Automation.Host.IHostSupportsInteractiveSession]) {
                    $Name = ReadInputPrompt $Strings.StringNamePrompt
                } else {
                    $Name = (Split-Path $psEditor.GetEditorContext().CurrentFile.Path -Leaf) +
                             '-' +
                             [guid]::NewGuid().Guid
                }
            }
            if (-not $Name) {
                ThrowError -Exception ([ArgumentException]::new($Strings.StringNamePromptFail)) `
                           -Id        StringNamePromptFail `
                           -Category  InvalidArgument `
                           -Target    $Name
            }
    
            $originalContents = $Ast.Value
            $Ast | Set-ScriptExtent -Text ('$Strings.{0}' -f $Name)
    
            try {
                SetEditorLocation (ResolveRelativePath (GetSettings).StringLocalizationManifest)
            } catch {
                ThrowError -Exception ([ArgumentException]::new($Strings.InvalidSettingValue -f 'StringLocalizationManifest')) `
                           -Id         `
                           -Category   `
                           -Target     $null
            }
    
            $hereString = Find-Ast { 'SingleQuotedHereString' -eq $_.StringConstantType } -First
    
            $newHereString = $hereString.Extent.Text -replace
                "(\r?\n)'@",
                ('$1' + $Name + '=' + $originalContents + '$1''@')
    
            $hereString | Set-ScriptExtent -Text $newHereString
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\ConvertTo-MarkdownHelp.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation.Language
    
    function ConvertTo-MarkdownHelp {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Generate Markdown from Closest Function')]
        [CmdletBinding()]
        param(
            [System.Management.Automation.Language.Ast]
            $Ast
        )
        end {
            $Ast = GetAncestorOrThrow $Ast -AstTypeName FunctionDefinitionAst -ErrorContext $PSCmdlet
    
            $settings = GetSettings
            $manifest = GetInferredManifest
            $docsPath = Join-Path (ResolveRelativePath $settings.MarkdownDocsPath) $PSCulture
            # If project uri is defined in the manifest then take a guess at what the online uri
            # should be.
            if ($projectUri = $manifest.PrivateData.PSData.ProjectUri) {
                $normalizedDocs = $PSCmdlet.SessionState.Path.NormalizeRelativePath(
                    $docsPath,
                    $psEditor.Workspace.Path)
    
                $onlineUri = $projectUri,
                             'blob/master',
                             $normalizedDocs,
                             ($Ast.Name + '.md') -join '/' -replace '\\', '/'
            }
    
            # Wrap this whole thing in a try/finally so we can dispose of temp files and PowerShell
            # session in event of an error or CTRL + C
            try {
                $tempFolder = Join-Path $env:TEMP -ChildPath (New-Guid).Guid
                $null = New-Item $tempFolder -ItemType Directory
    
                # Load the the module and create markdown in a new runspace so we don't pollute the
                # current session.
                $ps = [powershell]::Create('NewRunspace')
                $null = $ps.AddScript('
                    param($manifestPath, $commandName, $onlineUrl, $tempFolder)
    
                    Import-Module $manifestPath
                    New-MarkdownHelp -Command $commandName `
                                     -OnlineVersionUrl $onlineUrl `
                                     -OutputFolder $tempFolder
                ').
                    AddArgument((ResolveRelativePath $settings.SourceManifestPath)).
                    AddArgument($Ast.Name).
                    AddArgument($onlineUri).
                    AddArgument($tempFolder).
                    Invoke()
    
                $markdownFile    = Get-ChildItem $tempFolder\*.md | Select-Object -First 1
                $markdownContent = Get-Content $markdownFile.FullName -Raw
    
            } finally {
                if ($ps) { $ps.Dispose() }
    
                if ($tempFolder -and (Test-Path $tempFolder) -and $tempFolder -match 'Temp\\^[A-z0-9-]+$') {
                    Remove-Item $tempFolder -Recurse -Force
                }
            }
    
            if ([string]::IsNullOrWhiteSpace($markdownContent)) {
                ThrowError -Exception ([InvalidOperationException]::new($Strings.FailureGettingMarkdown)) `
                           -Id        FailureGettingMarkdown `
                           -Category  InvalidOperation `
                           -Target    $markdownContent `
                           -Show
            }
    
            $helpToken = $ast | Get-Token |
                Where-Object Kind -EQ Comment |
                Where-Object Text -Match '\.EXTERNALHELP|\.SYNOPSIS'
    
            $helpIndentLevel = $helpToken.Extent.StartColumnNumber - 1
    
            $newHelpComment = '<#',
                              ('.EXTERNALHELP {0}-help.xml' -f $manifest.Name),
                              '#>' -join ([Environment]::NewLine + (' ' * $helpIndentLevel))
    
            if ($helpToken.Text -ne $newHelpComment) {
                $helpToken | Set-ScriptExtent -Text $newHelpComment
            }
    
            Start-Sleep -Milliseconds 50
    
            if (-not (Test-Path $docsPath)) {
                $null = New-Item $docsPath -ItemType Directory
            }
    
            $targetMarkdownPath = '{0}\{1}.md' -f $docsPath, $Ast.Name
            if (-not (Test-Path $targetMarkdownPath)) {
                $null = New-Item $targetMarkdownPath -ItemType File
            }
    
            SetEditorLocation $targetMarkdownPath
    
            # Shape markdown according to linting rules.
            $markdownContent = $markdownContent -replace
                # Add a new line after headers.
                '([#]+) ([ \w\(\)\-_]+)(\r?\n)(?=[\w{`])', '$1 $2$3$3' -replace
                # Add powershell to code start markers.
                '(?<=\r?\n\r?\n)```(?!powershell|yaml)', '```powershell' -replace
                # Remove the trailing spaces from blank Aliases.
                '(?<=Aliases:) (?!\w)' -replace
                # Replace inconsistent example titles.
                '### Example (\d+)', '### -------------------------- EXAMPLE $1 --------------------------'
    
    
            WaitUntil { $psEditor.GetEditorContext().CurrentFile.Path -eq $targetMarkdownPath }
    
            Find-Ast -IncludeStartingAst -First | Set-ScriptExtent -Text $markdownContent
        }
    }
    
    
  • tools\EditorServicesCommandSuite\Public\ConvertTo-SplatExpression.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Collections.Generic
    using namespace System.Management.Automation.Language
    
    function ConvertTo-SplatExpression {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [CmdletBinding()]
        [EditorCommand(DisplayName='Convert Command to Splat Expression')]
        param(
            [System.Management.Automation.Language.Ast]
            $Ast
        )
        begin {
            function ConvertFromExpressionAst($expression) {
                $isStringExpression = $expression -is [StringConstantExpressionAst] -or
                                      $expression -is [ExpandableStringExpressionAst]
    
                if ($isStringExpression) {
                    # If kind isn't BareWord then it's already enclosed in quotes.
                    if ('BareWord' -ne $expression.StringConstantType) {
                        return $expression.Extent.Text
                    }
                    $enclosure = "'"
                    if ($expression.NestedExpressions) {
                        $enclosure = '"'
                    }
    
                    return '{0}{1}{0}' -f $enclosure, $expression.Value
                }
                # When we handle switch parameters we don't create an AST.
                if ($pair.Value -isnot [Ast]) {
                    return $expression
                }
    
                return $expression.Extent.Text
            }
        }
        end {
            $Ast = GetAncestorOrThrow $Ast -AstTypeName CommandAst -ErrorContext $PSCmdlet
    
            $commandName, $elements = $Ast.CommandElements.Where({ $true }, 'Split', 1)
    
            $splat           = @{}
            $retainedArgs    = [List[Ast]]::new()
            $elementsExtent  = $elements.Extent | Join-ScriptExtent
            $boundParameters = [StaticParameterBinder]::BindCommand($Ast).BoundParameters
    
            # Start building the hash table of named parameters and values
            foreach ($parameter in $boundParameters.GetEnumerator()) {
                # If the command isn't loaded positional parameters come through as their numeric position.
                if ($parameter.Key -match '\d+' -and -not $parameter.Value.Parameter) {
                    $retainedArgs.Add($parameter.Value.Value)
                    continue
                }
                # The "Value" property for switches is the parameter AST (e.g. -Force) so we need to
                # manually build the expression.
                if ($parameter.Value.ConstantValue -is [bool]) {
                    $splat.($parameter.Key) = '${0}' -f $parameter.Value.ConstantValue.ToString().ToLower()
                    continue
                }
                $splat.($parameter.Key) = $parameter.Value.Value
            }
    
            # Remove the hypen, change to camelCase and add 'Splat'
            $variableName = [regex]::Replace(
                ($commandName.Extent.Text -replace '-'),
                '^[A-Z]',
                { $args[0].Value.ToLower() }) +
                'Splat'
    
            $sb = [System.Text.StringBuilder]::
                new('${0}' -f $variableName).
                AppendLine(' = @{')
    
            # All StringBuilder methods return itself so it can be chained.  We null the whole scriptblock
            # here so unchained method calls don't add to our output.
            $null = & {
                foreach($pair in $splat.GetEnumerator()) {
                    $sb.Append('    ').
                        Append($pair.Key).
                        Append(' = ')
                    if ($pair.Value -is [ArrayLiteralAst]) {
                        $sb.AppendLine($pair.Value.Elements.ForEach{
                            ConvertFromExpressionAst $PSItem
                        } -join ', ')
                    } else {
                        $sb.AppendLine((ConvertFromExpressionAst $pair.Value))
                    }
                }
                $sb.Append('}')
            }
            $splatText = $sb.ToString()
    
            # New CommandAst will be `Command @splatvar [PositionalArguments]`
            $newCommandParameters = '@' + $variableName
            if ($retainedArgs) {
                $newCommandParameters += ' ' + ($retainedArgs.Extent.Text -join ' ')
            }
    
            # Change the command expression first so we don't need to track it's position.
            $elementsExtent | Set-ScriptExtent -Text $newCommandParameters
    
            # Get the parent PipelineAst so we don't add the splat in the middle of a pipeline.
            $pipeline = $Ast | Find-Ast -Ancestor -First { $PSItem -is [PipelineAst] }
    
            # Prepend the existing indent.
            $lineText = ($psEditor.GetEditorContext().
                CurrentFile.
                Ast.
                Extent.
                Text -split '\r?\n')[$pipeline.Extent.StartLineNumber - 1]
    
            $lineIndent  = $lineText -match '^\s*' | ForEach-Object { $matches[0] }
            $splatText   = $lineIndent + (
                           $splatText -split '\r?\n' -join ([Environment]::NewLine + $lineIndent))
    
            # HACK: Temporary workaround until https://github.com/PowerShell/PowerShellEditorServices/pull/541
            #$splatTarget = ConvertTo-ScriptExtent -Line $pipeline.Extent.StartLineNumber
            $splatTarget = [Microsoft.PowerShell.EditorServices.FullScriptExtent]::new(
                $psEditor.GetEditorContext().CurrentFile,
                [Microsoft.PowerShell.EditorServices.BufferRange]::new(
                    $pipeline.Extent.StartLineNumber,
                    1,
                    $pipeline.Extent.StartLineNumber,
                    1))
    
            $splatTarget | Set-ScriptExtent -Text ($splatText + [Environment]::NewLine)
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Expand-Expression.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation
    using namespace System.Management.Automation.Language
    
    function Expand-Expression {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Expand Selection Text to Output')]
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
            [ValidateNotNullOrEmpty()]
            [Alias('Extent')]
            [System.Management.Automation.Language.IScriptExtent[]]
            $InputObject = ($psEditor.GetEditorContext().SelectedRange | ConvertTo-ScriptExtent)
        )
        process {
            foreach ($object in $InputObject) {
                if ([string]::IsNullOrWhiteSpace($object.Text)) {
                    $message = $Strings.ExpandEmptyExtent -f $object.StartOffset, $object.File
                    ThrowError -Exception ([InvalidOperationException]::new($message)) `
                               -Id        ExpandEmptyExtent `
                               -Category  InvalidOperation `
                               -Target    $object `
                               -Show
                }
                $parseErrors = $null
                $null = [Parser]::ParseInput(
                    <# input:  #> $object.Text,
                    <# tokens: #> [ref]$null,
                    <# errors: #> [ref]$parseErrors
                )
                if ($parseErrors) {
                    ThrowError -Exception ([ParseException]::new($parseErrors)) `
                               -Id        ExpandExpressionParseError `
                               -Category  InvalidArgument `
                               -Target    $object `
                               -Show
                }
                try {
                    $output = & ([scriptblock]::Create($object.Text)) | Out-String
                } catch {
                    ThrowError -ErrorRecord $PSItem -Show
                }
    
                Set-ScriptExtent -Extent $object -Text $output
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Expand-MemberExpression.ps1 Show
    using namespace Antlr4.StringTemplate.Compiler
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Collections.Generic
    using namespace System.Management.Automation.Language
    using namespace System.Reflection
    
    function Expand-MemberExpression {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Expand Member Expression')]
        [CmdletBinding()]
        param(
            [Parameter(Position=1, ValueFromPipeline, ValueFromPipelineByPropertyName)]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.Language.Ast]
            $Ast = (Find-Ast -AtCursor),
    
            [ValidateSet('GetMethod', 'InvokeMember', 'VerboseGetMethod', 'GetValue', 'SetValue')]
            [string]
            $TemplateName,
    
            [switch]
            $NoParameterNameComments
        )
        begin {
            try {
                $groupSource = Get-Content -Raw $PSScriptRoot\..\Templates\MemberExpression.stg
                $group = New-StringTemplateGroup -Definition $groupSource -ErrorAction Stop
    
                $instance = $group.GetType().
                    GetProperty('Instance', [BindingFlags]'Instance, NonPublic').
                    GetValue($group)
    
                $renderer = [MemberExpressionRenderer]::new()
                $instance.RegisterRenderer([string], $renderer)
                $instance.RegisterRenderer([type], [TypeRenderer]::new())
            } catch {
                ThrowError -Exception ([TemplateException]::new($Strings.TemplateGroupCompileError, $null)) `
                           -Id        TemplateGroupCompileError `
                           -Category  InvalidData `
                           -Target    $PSItem
            }
    }
        process {
            $context = $psEditor.GetEditorContext()
            $memberExpressionAst = $Ast
    
            if ($memberExpressionAst -isnot [MemberExpressionAst]) {
    
                $memberExpressionAst = $Ast | Find-Ast { $PSItem -is [MemberExpressionAst] } -Ancestor -First
    
                if ($memberExpressionAst -isnot [MemberExpressionAst]) {
                    ThrowError -Exception ([InvalidOperationException]::new($Strings.MissingMemberExpressionAst)) `
                               -Id        MissingMemberExpressionAst `
                               -Category  InvalidOperation `
                               -Target    $Ast `
                               -Show
                }
            }
            [Stack[ExtendedMemberExpressionAst]]$expressionAsts = $memberExpressionAst
            if ($memberExpressionAst.Expression -is [MemberExpressionAst]) {
                for ($nested = $memberExpressionAst.Expression; $nested; $nested = $nested.Expression) {
                    if ($nested -is [MemberExpressionAst]) {
                        $expressionAsts.Push($nested)
                    } else { break }
                }
            }
            [List[string]]$expressions = @()
            while ($expressionAsts.Count -and ($current = $expressionAsts.Pop())) {
    
                # Throw if we couldn't find member information at any point.
                if (-not ($current.InferredMember)) {
                    ThrowError -Exception ([MissingMemberException]::new($current.Expression, $current.Member.Value)) `
                               -Id        MissingMember `
                               -Category  InvalidResult `
                               -Target    $Ast `
                               -Show
                }
    
                switch ($current.Expression) {
                    { $PSItem -is [MemberExpressionAst] } {
                        $variable = $renderer.TransformMemberName($PSItem.InferredMember.Name)
                    }
                    { $PSItem -is [VariableExpressionAst] } {
                        $variable = $PSItem.VariablePath.UserPath
                    }
                    { $PSItem -is [TypeExpressionAst] } {
                        $source = $current.InferredMember.ReflectedType
                    }
                }
                if ($variable) {
                    $source = '${0}' -f $variable
    
                    # We don't want to build out reflection expressions for public members so we chain
                    # them together in one of the expressions.
                    while (($current.InferredMember.IsPublic            -or
                            $current.InferredMember.GetMethod.IsPublic) -and
                            $expressionAsts.Count) {
                        $source += '.{0}' -f $current.InferredMember.Name
    
                        if ($current.InferredMember.MemberType -eq 'Method') {
                            $source += '({0})' -f $current.Arguments.Extent.Text
                        }
                        $current = $expressionAsts.Pop()
                    }
                }
    
                if ($psEditor) {
                    $scriptFile     = $context.CurrentFile.GetType().
                                                           GetField('scriptFile', 60).
                                                           GetValue($context.CurrentFile)
    
                    $line           = $scriptFile.GetLine($memberExpressionAst.Extent.StartLineNumber)
                    $indentOffset   = [regex]::Match($line, '^\s*').Value
                }
    
                $templateParameters = @{
                    ast                  = $current
                    source               = $source
                    includeParamComments = -not $NoParameterNameComments
                }
                $member = $current.InferredMember
    
                # Automatically use the more explicit VerboseGetMethod template if building a reflection
                # statement for a method with multiple overloads with the same parameter count.
                $needsVerbose = $member -is [MethodInfo] -and -not
                                $member.IsPublic -and
                                $member.ReflectedType.GetMethods(60).Where{
                                    $PSItem.Name -eq $current.InferredMember.Name -and
                                    $PSItem.GetParameters().Count -eq $member.GetParameters().Count }.
                                    Count -gt 1
    
                if ($TemplateName -and -not $expressionAsts.Count) {
                    $templateParameters.template = $TemplateName
                } elseif ($needsVerbose) {
                    $templateParameters.template = 'VerboseGetMethod'
                }
                $expression = Invoke-StringTemplate -Group $group -Name Main -Parameters $templateParameters
                $expressions.Add($expression)
            }
    
            $result = $expressions -join (,[Environment]::NewLine * 2) `
                                   -split '\r?\n' `
                                   -join ([Environment]::NewLine + $indentOffset)
            if ($psEditor) {
                Set-ScriptExtent -Extent $memberExpressionAst.Extent `
                                 -Text   $result
            } else {
                $result
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Expand-TypeImplementation.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation.Language
    
    function Expand-TypeImplementation {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Expand Closest Type to Implementation')]
        [CmdletBinding()]
        param(
            [Parameter(Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
            [ValidateNotNullOrEmpty()]
            [type[]]
            $Type
        )
        begin {
            $renderer = [TypeRenderer]::new()
            $group = @'
    class(FullName, Name, DeclaredMethods) ::= <<
    class New<Name> : <FullName> {
        <DeclaredMethods:methods(); separator={<\n><\n>}>
    }
    
    >>
    methods(m) ::= <<
    <m.ReturnType> <if(m.IsStatic)>static <endif><m.Name> (<m.Parameters:params(); separator=", ">) {
        throw [NotImplementedException]::new()
    }
    >>
    params(p) ::= "<p.ParameterType> $<p.Name>"
    '@
            $group    = New-StringTemplateGroup -Definition $group
            $instance = $group.GetType().GetProperty('Instance', 60).GetValue($group)
    
            $instance.RegisterRenderer([type], $renderer)
        }
        process {
            $typeList
            if ($Type) {
                $cursorPosition = $psEditor.GetEditorContext().CursorPosition
                # HACK: Temporary workaround until https://github.com/PowerShell/PowerShellEditorServices/pull/541
                #$targetExtent = $psEditor.GetEditorContext().CursorPosition | ConvertTo-ScriptExtent
                $targetExtent = [Microsoft.PowerShell.EditorServices.FullScriptExtent]::new(
                    $psEditor.GetEditorContext().CurrentFile,
                    [Microsoft.PowerShell.EditorServices.BufferRange]::new(
                        $cursorPosition.Line,
                        $cursorPosition.Column,
                        $cursorPosition.Line,
                        $cursorPosition.Column))
            } else {
                $ast = Find-Ast -AtCursor |
                    Find-Ast -Family -First -IncludeStartingAst { $PSItem.TypeName }
    
                $targetExtent = $ast.Extent
    
                $resolvedType = $ast.TypeName.Name -as [type]
    
                if (-not $resolvedType) {
                    # Type resolution scope is this function, so we need to pull the namespaces from
                    # the current file and test against that.
                    $using = Find-Ast { $_ -is [UsingStatementAst] -and
                                        $_.UsingStatementKind -eq 'Namespace' }
    
                    foreach ($namespace in $using.Name) {
                        if ($resolvedType = ('{0}.{1}' -f $using.Name, $ast.TypeName.Name) -as [type]) {
                            break
                        }
                    }
                }
                $Type = $resolvedType
            }
            $result = foreach ($aType in $Type) {
                Invoke-StringTemplate -Group $group -Name class -Parameters ($aType)
            }
            Set-ScriptExtent -Extent $targetExtent -Text $result
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\New-ESCSSettingsFile.ps1 Show
    using namespace System.Diagnostics.CodeAnalysis
    
    function New-ESCSSettingsFile {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [CmdletBinding()]
        [SuppressMessage('PSAvoidShouldContinueWithoutForce', '',
                         Justification='ShouldContinue is called from a subroutine without CmdletBinding.')]
        param(
            [ValidateNotNullOrEmpty()]
            [string]
            $Path = $psEditor.Workspace.Path,
    
            [switch]
            $Force
        )
        begin {
            function HandleFileExists($filePath) {
                if (-not (Test-Path $filePath)) {
                    return
                }
                $shouldRemove = $Force.IsPresent -or
                                $PSCmdlet.ShouldContinue(
                                    $Strings.ShouldReplaceSettingsMessage,
                                    $Strings.ShouldReplaceSettingsCaption)
    
                if ($shouldRemove) {
                    Remove-Item $targetFilePath
                    return
                }
                $exception = [System.ArgumentException]::new(
                    $Strings.SettingsFileExists -f $psEditor.Workspace.Path)
                ThrowError -Exception $exception `
                           -Id        SettingsFileExists `
                           -Category  InvalidArgument `
                           -Target    $targetFilePath
            }
        }
        end {
            $targetFilePath = Join-Path $Path -ChildPath 'ESCSSettings.psd1'
            HandleFileExists $targetFilePath
    
            try {
                $groupDefinition = Get-Content $PSScriptRoot\..\Templates\SettingsFile.stg -Raw -ErrorAction Stop
    
                $templateSplat = @{
                    Group = (New-StringTemplateGroup -Definition $groupDefinition)
                    Name  = 'Base'
                    Parameters = @{
                        Settings = $script:DEFAULT_SETTINGS.GetEnumerator()
                        Strings  = [pscustomobject]$Strings
                    }
                }
    
                $content = Invoke-StringTemplate @templateSplat
            } catch {
                ThrowError -Exception ([InvalidOperationException]::new($Strings.TemplateGroupCompileError)) `
                           -Id        TemplateGroupCompileError `
                           -Category  InvalidOperation `
                           -Target    $groupDefinition
            }
    
            $null = New-Item $targetFilePath -Value $content
            if ($psEditor) {
                SetEditorLocation $targetFilePath
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Remove-Semicolon.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Collections.Generic
    using namespace System.Linq
    using namespace System.Management.Automation.Language
    
    function Remove-Semicolon {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [CmdletBinding()]
        [EditorCommand(DisplayName='Remove cosmetic semicolons')]
        param()
        end {
            $propertyDefinitions = Find-Ast { $PSItem -is [PropertyMemberAst] }
            $tokens = (Get-Token).Where{ $PSItem.Extent.StartOffset + 1 -notin $propertyDefinitions.Extent.EndOffset }
    
            $extentsToRemove = [List[IScriptExtent]]::new()
    
            for ($i = 0; $i -lt $tokens.Count; $i++) {
                if ($tokens[$i].Kind -ne [TokenKind]::Semi) { continue }
    
                if ($tokens[$i+1].Kind -eq [TokenKind]::NewLine) {
                    $extentsToRemove.Add($tokens[$i].Extent)
                }
            }
            [Enumerable]::Distinct($extentsToRemove) | Set-ScriptExtent -Text ''
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Set-HangingIndent.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Collections.Generic
    using namespace System.Management.Automation.Language
    
    function Set-HangingIndent {
        [EditorCommand(DisplayName='Set Selection Indent to Selection Start')]
        [CmdletBinding()]
        param()
        end {
            $context   = $psEditor.GetEditorContext()
            $selection = $context.SelectedRange | ConvertTo-ScriptExtent
    
            foreach ($token in ($selection | Get-Token)) {
                if ('NewLine', 'LineContinuation' -notcontains $token.Kind) {
                    continue
                }
                if (-not $foreach.MoveNext()) { break }
    
                $current = $foreach.Current
    
                $difference = $selection.StartColumnNumber - $current.Extent.StartColumnNumber
                if ($difference -gt 0) {
    
                    # HACK: Temporary workaround until https://github.com/PowerShell/PowerShellEditorServices/pull/541
                    #ConvertTo-ScriptExtent -Line $current.Extent.StartLineNumber |
                    $targetExtent = [Microsoft.PowerShell.EditorServices.FullScriptExtent]::new(
                        $psEditor.GetEditorContext().CurrentFile,
                        [Microsoft.PowerShell.EditorServices.BufferRange]::new(
                            $current.Extent.StartLineNumber,
                            1,
                            $current.Extent.StartLineNumber,
                            1))
    
                    $targetExtent | Set-ScriptExtent -Text (' ' * $difference)
                }
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Set-RuleSuppression.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Collections.Generic
    using namespace System.Management.Automation.Language
    
    function Set-RuleSuppression {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [EditorCommand(DisplayName='Suppress Closest Analyzer Rule Violation')]
        [CmdletBinding()]
        param(
            [Parameter(Position=0, ValueFromPipeline)]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.Language.Ast[]]
            $Ast = (Find-Ast -AtCursor)
        )
        begin {
            function GetAttributeTarget {
                param(
                    [System.Management.Automation.Language.Ast]
                    $SubjectAst
                )
                if (-not $Ast) { return }
                $splat = @{
                    Ast   = $SubjectAst
                    First = $true
                }
    
                # Attribute can go right on top of variable expressions.
                if ($SubjectAst.VariablePath -or (Find-Ast @splat -Ancestor { $_.VariablePath })) {
                    return $SubjectAst
                }
    
                $splat.FilterScript = { $PSItem.ParamBlock }
                # This isn't a variable expression so we need to find the closest param block.
                if ($scriptBlockAst = Find-Ast @splat -Ancestor) { return $scriptBlockAst.ParamBlock }
    
                # No param block anywhere in it's ancestry so try to find a physically close param block
                if ($scriptBlockAst = Find-Ast @splat -Before) { return $scriptBlockAst.ParamBlock }
    
                # Check if part of a method definition in a class.
                $splat.FilterScript = { $PSItem -is [FunctionMemberAst] }
                if ($methodAst = Find-Ast @splat -Ancestor) { return $methodAst }
    
                # Check if part of a class period.
                $splat.FilterScript = { $PSItem -is [TypeDefinitionAst] }
                if ($classAst = Find-Ast @splat -Ancestor) { return $classAst}
    
                # Give up and just create it above the original ast.
                return $SubjectAst
            }
            $astList = [List[Ast]]::new()
        }
        process {
            if ($Ast) {
                $astList.AddRange($Ast)
            }
        }
        end {
            $context    = $psEditor.GetEditorContext()
            $scriptFile = $context.CurrentFile.GetType().
                                               GetField('scriptFile', 60).
                                               GetValue($context.CurrentFile)
    
            $markers = Invoke-ScriptAnalyzer -Path $Context.CurrentFile.Path
    
            $extentsToSuppress = [List[psobject]]::new()
            foreach ($aAst in $astList) {
                # Get the closest valid ast that can be assigned an attribute.
                $target = GetAttributeTarget $aAst
    
                foreach ($marker in $markers) {
                    $isWithinMarker = $aAst.Extent.StartOffset -ge $marker.Extent.StartOffset -and
                                      $aAst.Extent.EndOffset   -le $marker.Extent.EndOffset
    
                    if (-not $isWithinMarker) { continue }
    
                    # FilePosition gives us some nice methods for indent aware navigation.
                    $position = [FilePosition]::new($scriptFile, $target.Extent.StartLineNumber, 1)
    
                    # GetLineStart puts us at the first non-whitespace character, which we use to get indent level.
                    $indentOffset = ' ' * ($position.GetLineStart().Column - 1)
    
                    $string = '{0}{1}[System.Diagnostics.CodeAnalysis.SuppressMessage(''{2}'', '''')]' -f
                        [Environment]::NewLine, $indentOffset, $marker.RuleName
    
                    # AddOffset is line/column based, and will throw if you try to move to a column where
                    # there is no text.
                    $newPosition = $position.AddOffset(-1, ($position.Column - 1) * -1).GetLineEnd()
                    # HACK: Temporary workaround until https://github.com/PowerShell/PowerShellEditorServices/pull/541
                    # $extent = $position.AddOffset(-1, ($position.Column - 1) * -1).GetLineEnd() |
                    #     ConvertTo-ScriptExtent
                    $extent = [Microsoft.PowerShell.EditorServices.FullScriptExtent]::new(
                        $psEditor.GetEditorContext().CurrentFile,
                        [Microsoft.PowerShell.EditorServices.BufferRange]::new(
                            $newPosition.Line,
                            $newPosition.Column,
                            $newPosition.Line,
                            $newPosition.Column))
    
                    $extentsToSuppress.Add([PSCustomObject]@{
                        Extent     = $extent
                        Expression = $string
                    })
                }
            }
            # Need to pass extents all at once to Set-ScriptExtent for position tracking.
            $extentsToSuppress | Group-Object -Property Expression | ForEach-Object {
                $PSItem.Group.Extent | Set-ScriptExtent -Text $PSItem.Name
            }
        }
    }
    
  • tools\EditorServicesCommandSuite\Public\Set-UsingStatementOrder.ps1 Show
    using namespace Microsoft.PowerShell.EditorServices.Extensions
    using namespace System.Management.Automation.Language
    
    function Set-UsingStatementOrder {
        <#
        .EXTERNALHELP EditorServicesCommandSuite-help.xml
        #>
        [CmdletBinding()]
        [EditorCommand(DisplayName='Sort Using Statements')]
        param()
        end {
            $statements = Find-Ast { $PSItem -is [UsingStatementAst] }
    
            $groups = $statements | Group-Object UsingStatementKind -AsHashTable -AsString
    
            $sorted = & {
                if ($groups.Assembly)  { $groups.Assembly  | Sort-Object Name }
                if ($groups.Module)    { $groups.Module    | Sort-Object Name }
                if ($groups.Namespace) { $groups.Namespace | Sort-Object Name }
            } | ForEach-Object -MemberName ToString
    
            $statements | Join-ScriptExtent | Set-ScriptExtent -Text ($sorted -join [Environment]::NewLine)
        }
    }
    
  • tools\EditorServicesCommandSuite\Templates\MemberExpression.stg
  • tools\EditorServicesCommandSuite\Templates\SettingsFile.stg
  • tools\LICENSE.txt Show
    From: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/LICENSE
    
    LICENSE
    
    MIT License
    
    Copyright (c) 2017 Patrick Meinecke
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
  • tools\VERIFICATION.txt Show
    VERIFICATION
    Verification is intended to assist the Chocolatey moderators and community in verifying that this package's contents are trustworthy.
    
    To verify the files using the project source:
    
    1. Please go to the project source location (https://github.com/SeeminglyScience/EditorServicesCommandSuite) and download the source files;
    2. Build the source to create the binary files to verify;
    3. Use Get-FileHash -Path <FILE TO VERIFY> to get the file hash value from both the built file (from step 1 above) and the file from the package and compare them;
    
    Alternatively you can download the module from the PowerShell Gallery ...
    
        Save-Module -Name EditorServicesCommandSuite -Path <PATH TO DOWNLOAD TO>
    
    ... and compare the files from the package against those in the installed module. Again use Get-FileHash -Path <FILE TO VERIFY> to retrieve those hash values.

Virus Scan Results

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Dependencies

Package Maintainer(s)

Software Author(s)

  • Patrick Meinecke

Copyright

2017 Patrick Meinecke

Tags

Release Notes

https://github.com/SeeminglyScience/EditorServicesCommandSuite/releases

Version History

Version Downloads Last updated Status

Discussion for the EditorServicesCommandSuite (PowerShell Module) Package

Ground rules:

  • This discussion is only about EditorServicesCommandSuite (PowerShell Module) and the EditorServicesCommandSuite (PowerShell Module) package. If you have feedback for Chocolatey, please contact the google group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or EditorServicesCommandSuite (PowerShell Module), or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.

comments powered by Disqus
Chocolatey.org uses cookies to enhance the user experience of the site.
Ok