SeeminglyScience / EditorServicesCommandSuite

Collection of editor commands for use in PowerShell Editor Services.
Other
151 stars 13 forks source link

FeatureRequest #29

Closed Jawz84 closed 6 years ago

Jawz84 commented 6 years ago

I love your ConvertTo-SplatExpression command, and I have a suggestion about it. I have looked at your code for this command, and I need more time to fully understand how this works. But bear with me for the idea I have please:

For some scenarios it would be great to have the possibility to:

  1. Create a splat for all mandatory parameters of a given command
  2. Create a splat for all possible parameters of a given command
  3. Create a splat including currently filled in parameters, plus missing mandatory parameters
  4. Create a splat including currently filled in parameters, and all other possible parameters

Just to give you an idea of what I mean, here's an example for 2. I include this raw snippet here:

function ConvertTo-SplatExpressionFull {
    param($cmd)

    $help = Get-Help $cmd -Parameter *
    $longestParamName = $help.Name | ForEach-Object {$_.Length} | Sort-Object -Descending | Select-Object -First 1

    '$splat = @{'
    $help | ForEach-Object {
        "    {0,-$longestParamName} =   # {1,1} [{2}]"  -f  $_.name, $(if ($_.Required -eq 'true') {"*"}), $_.type.name
    }
    "}"
    "$cmd @splat"
}

For a given command Send-Mailmessage, this would result in:

$splat = @{
        Attachments                =   #   [String[]]
        Bcc                        =   #   [String[]]
        Body                       =   #   [String]
        BodyAsHtml                 =   #   [SwitchParameter]
        Cc                         =   #   [String[]]
        Credential                 =   #   [PSCredential]
        DeliveryNotificationOption =   #   [DeliveryNotificationOptions]
        Encoding                   =   #   [Encoding]
        From                       =   # * [String]
        Port                       =   #   [Int32]
        Priority                   =   #   [MailPriority]
        SmtpServer                 =   #   [String]
        Subject                    =   # * [String]
        To                         =   # * [String[]]
        UseSsl                     =   #   [SwitchParameter]
}
Send-Mailmessage @splat

I'm not sure how to implement this idea in your current code yet. Also, I read somewhere that you are currently in the process of rebuilding things in C#. I'm willing to contribute though, if you can help me find my way. Let me know what you think.

SeeminglyScience commented 6 years ago

Hmm, that's a really interesting idea. I can definitely see how that would be useful :)

There are a couple of things to consider that make this a little challenging (but definitely doable)

Parameter sets

Ideally we would try to identify the best parameter set based on already supplied parameters, but that would take considerable effort. Still, it may not be a bad idea to build that functionality into a shared utility class as it could be useful for other refactors. We could also just present the parameter sets as options in a prompt.

Getting the CommandInfo

So right now we use the StaticParameterBinder class to determine what parameters are specified. This handles resolving the command and determining the name of positional arguments. We can't get CommandInfo from that, so there would be a little bit of work duplication.

That's fine, but the question is how do we handle commands that are not imported. PowerShellEditorServices loads commands as you write them to get the syntax string, but I'm not paticularly fond of the behavior. So do we fail if it isn't loaded? At some point I'd like to add something that uses MetadataReader (or just AST analysis for PowerShell based commands) to build a stub command info object, that would be ideal for this.

Implementing in the 0.5.0 Overhaul

A large part of why I am rewriting this module is because every command is pretty rigid (as you've noticed). Once it's written, it's pretty hard to make a change. And even harder to make something optional.

The new system is a bit more generalized. It's not EditorServices specific (now also works in PSReadLine) and could potentially work without an active PowerShell runspace.

CommandSplatRefactor has the logic for ConvertTo-SplatExpression.

CommandSplatRefactorSettings holds the settings and/or parameters for the refactor. Assuming the proposed feature is not it's own refactor option, I would definitely like to see a parameter (with default from settings) that determines if it's a normal "splat existing params", "splat existing and mandatory", etc.

At build time a cdxml file (the file type used most commonly to define CIM modules) is created for each class decorated with the Refactor attribute. Every property within it's RefactorConfiguration that is decorated with the Parameter attribute is defined as a parameter. The DefaultFromSetting attribute declares that if the parameter is not specified, then the properties value should be retrieved from the settings file.

If you want to pick this up, feel free to reach out with questions any time. You can ask here or find me on the PowerShell slack in vscode-contributers/vscode/editors/direct message :)

Jawz84 commented 6 years ago

Closed by #30