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
    }
}