Unpacking Software Livestream

Join our monthly Unpacking Software livestream to hear about the latest news, chat and opinion on packaging, software deployment and lifecycle management!

Learn More

Chocolatey Product Spotlight

Join the Chocolatey Team on our regular monthly stream where we put a spotlight on the most recent Chocolatey product releases. You'll have a chance to have your questions answered in a live Ask Me Anything format.

Learn More

Chocolatey Coding Livestream

Join us for the Chocolatey Coding Livestream, where members of our team dive into the heart of open source development by coding live on various Chocolatey projects. Tune in to witness real-time coding, ask questions, and gain insights into the world of package management. Don't miss this opportunity to engage with our team and contribute to the future of Chocolatey!

Learn More

Calling All Chocolatiers! Whipping Up Windows Automation with Chocolatey Central Management

Webinar from
Wednesday, 17 January 2024

We are delighted to announce the release of Chocolatey Central Management v0.12.0, featuring seamless Deployment Plan creation, time-saving duplications, insightful Group Details, an upgraded Dashboard, bug fixes, user interface polishing, and refined documentation. As an added bonus we'll have members of our Solutions Engineering team on-hand to dive into some interesting ways you can leverage the new features available!

Watch On-Demand
Chocolatey Community Coffee Break

Join the Chocolatey Team as we discuss all things Community, what we do, how you can get involved and answer your Chocolatey questions.

Watch The Replays
Chocolatey and Intune Overview

Webinar Replay from
Wednesday, 30 March 2022

At Chocolatey Software we strive for simple, and teaching others. Let us teach you just how simple it could be to keep your 3rd party applications updated across your devices, all with Intune!

Watch On-Demand
Chocolatey For Business. In Azure. In One Click.

Livestream from
Thursday, 9 June 2022

Join James and Josh to show you how you can get the Chocolatey For Business recommended infrastructure and workflow, created, in Azure, in around 20 minutes.

Watch On-Demand
The Future of Chocolatey CLI

Livestream from
Thursday, 04 August 2022

Join Paul and Gary to hear more about the plans for the Chocolatey CLI in the not so distant future. We'll talk about some cool new features, long term asks from Customers and Community and how you can get involved!

Watch On-Demand
Hacktoberfest Tuesdays 2022

Livestreams from
October 2022

For Hacktoberfest, Chocolatey ran a livestream every Tuesday! Re-watch Cory, James, Gary, and Rain as they share knowledge on how to contribute to open-source projects such as Chocolatey CLI.

Watch On-Demand

Downloads:

965

Downloads of v 0.4.0:

965

Last Update:

12 May 2018

Package Maintainer(s):

Software Author(s):

  • Patrick Meinecke

Tags:

admin powershell module visual studio code vsacode platyps markdown

EditorServicesCommandSuite (PowerShell Module)

  • 1
  • 2
  • 3

0.4.0 | Updated: 12 May 2018

Downloads:

965

Downloads of v 0.4.0:

965

Maintainer(s):

Software Author(s):

  • Patrick Meinecke

EditorServicesCommandSuite (PowerShell Module) 0.4.0

  • 1
  • 2
  • 3

This Package Contains an Exempted Check

Not All Tests Have Passed


Validation Testing Passed


Verification Testing Exemption:

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

Details

Scan Testing Successful:

No detections found in any package files

Details
Learn More

Deployment Method: Individual Install, Upgrade, & Uninstall

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

>

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

>

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

>

Deployment Method:

NOTE

This applies to both open source and commercial editions of Chocolatey.

1. Enter Your Internal Repository Url

(this should look similar to https://community.chocolatey.org/api/v2/)


2. Setup Your Environment

1. Ensure you are set for organizational deployment

Please see the organizational deployment guide

2. Get the package into your environment

  • Open Source or Commercial:
    • Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
    • You can also just download the package and push it to a repository Download

3. Copy Your Script

choco upgrade editor-services-command-suite -y --source="'INTERNAL REPO URL'" [other options]

See options you can pass to upgrade.

See best practices for scripting.

Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.

If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:


choco upgrade editor-services-command-suite -y --source="'INTERNAL REPO URL'" 
$exitCode = $LASTEXITCODE

Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
  Exit 0
}

Exit $exitCode

- name: Install editor-services-command-suite
  win_chocolatey:
    name: editor-services-command-suite
    version: '0.4.0'
    source: INTERNAL REPO URL
    state: present

See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.


chocolatey_package 'editor-services-command-suite' do
  action    :install
  source   'INTERNAL REPO URL'
  version  '0.4.0'
end

See docs at https://docs.chef.io/resource_chocolatey_package.html.


cChocoPackageInstaller editor-services-command-suite
{
    Name     = "editor-services-command-suite"
    Version  = "0.4.0"
    Source   = "INTERNAL REPO URL"
}

Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.


package { 'editor-services-command-suite':
  ensure   => '0.4.0',
  provider => 'chocolatey',
  source   => 'INTERNAL REPO URL',
}

Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.


4. If applicable - Chocolatey configuration/installation

See infrastructure management matrix for Chocolatey configuration elements and examples.

Package Approved

This package was approved by moderator gep13 on 15 May 2018.

Description

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.


tools\.skipAutoUninstaller
 
tools\chocolateyBeforeModify.ps1
$ErrorActionPreference = 'Stop'

$moduleName = 'EditorServicesCommandSuite'      # this could be different from package name
Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
tools\chocolateyInstall.ps1
$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
$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
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
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
# 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
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
#
# 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
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
<?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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
<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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.

Log in or click on link to see number of positives.

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.

Add to Builder 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