From 53b8b3a7f565fb94a7cb983ffdf832d8168a42aa Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 16 May 2019 22:46:55 -0300 Subject: [PATCH 1/9] initial commit --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 105 +++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 1-Draft/RFCNNNN-Implicit-Line-Continuance.md diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md new file mode 100644 index 00000000..d5bdef41 --- /dev/null +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -0,0 +1,105 @@ +--- +RFC: RFCnnnn +Author: Kirk Munro +Status: Draft +SupercededBy: N/A +Version: 1.0 +Area: Parser/Tokenizer +Comments Due: June 16, 2019 +Plan to implement: Yes +--- + +# Support implicit line continuance when using named parameters or splatting in commands + +Nobody likes having to use the backtick to wrap commands in PowerShell, yet many users still use it to get the style they prefer in their scripts. + +For example, PowerShell has long supported explicit line continuance when you end a pipelined command line with a pipe symbol, like this: + +```PowerShell +Get-Process -Id $PID | + Stop-Process +``` + +Even though that is available, when you have a pipeline with many stages (commands) in it, the style is not as desirable as it could be if you could place the pipeline on the beginning of the line instead, like this: + +```PowerShell +Get-Process -Id $PID + | Stop-Process +``` + +Historically that syntax would not be possible unless you ended each line before the last line in the pipeline with a backtick. That shouldn't be necessary though, and PowerShell should be smart enough to recognize implicit line continuance when pipelines are placed at the beginning of the line. This was discussed in detail in [Issue #3020](https://github.com/PowerShell/PowerShell/issues/3020), and [PR #8938](https://github.com/PowerShell/PowerShell/pull/8938) that was recently merged added this support to PowerShell for version 7. + +While that is helpful, there is another potential improvement where PowerShell could support implicit line continuance: when using named parameters or splatting in commands, and that's what this RFC is about. + +For example, consider this example of a New-ADUser command invocation: + +```PowerShell +New-ADUser -Name 'Jack Robinson' -GivenName 'Jack' -Surname 'Robinson' -SamAccountName 'J.Robinson' -UserPrincipalName 'J.Robinson@enterprise.com' -Path 'OU=Users,DC=enterprise,DC=com' -AccountPassword (Read-Host -AsSecureString 'Input Password') -Enabled $true +``` + +By itself it's not too much to handle, but in a script commands with many parameters like this can be difficult to manage it in a script. To wrap this command across multiple lines, users can either use backticks, or they can use splatting. The former is a nuisance which should really only be used in situations when PowerShell cannot implicitly intuit how lines are wrapped. The latter is helpful, but users lose the benefits of tab completion and Intellisense for parameters when they use splatting. As a workaround, they can work out the parameters they want to use for the command first, and then convert it into a splatted command, but that's generally onerous. Even though Visual Studio Code has an extension that makes splatting easier, as can be seen [here](https://sqldbawithabeard.com/2018/03/11/easily-splatting-powershell-with-vs-code/), once you've converted to splatting you still lose Intellisense for future updates unless you work from the command first and then add to your splatted collection, and that's just in Visual Studio Code. Other editors may or may not support that functionality, and users working in a standalone terminal won't have that available to them either. + +Instead, why not allow users to do this by supporting implicit line continuance when using named parameters: + +```PowerShell +New-ADUser + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -Path 'OU=Users,DC=enterprise,DC=com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + -Enabled $true +``` + +Further, if they have some parameters they want to splat in (because splatting is not just about shortening lines -- it's very useful for users to apply common parameters/values to multiple commands), let them do this as well: + +```PowerShell +$commonParams = @{ + Path = 'OU=Users,DC=enterprise,DC=com' + Enabled = $true +} +New-ADUser + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + @commonParams +``` + +## Motivation + + As a script/module author, + I can wrap long commands across multiple lines at any named parameter or splatted collection, + so that my scripts are easier to maintain while I still get the benefits of Intellisense and tab completion. + +## Specification + +Note: This RFC is already implemented and submitted as [PR #9614](https://github.com/PowerShell/PowerShell/pull/9614). + +Since this RFC includes some breaking changes (see below), it will be initially implemented with the `PSImplicitLineContinuanceForNamedParameters` experimental feature flag. If the PowerShell Team and the community agree that the risk of this breaking change is low enough, and the workaround is sufficient for those low frequency use cases where it does become an issue, I would much prefer not using an experimental feature flag at all. + +For the implementation, this RFC builds on the work done in [PR #8938](https://github.com/PowerShell/PowerShell/pull/8938), evolving the logic used in the tokenizer that supports implicit line continuance for lines starting with a pipe such that it can be used to check for implicit line continuance for named parameters and splatted collections as well. To keep performance optimal, the parser logic that identifies command parameters where the implicit line continuance checks will be invoked will recognize if a pipe is found on a subsequent line as well, to ensure the lookahead logic is only invoked once per line in commands/pipelines. + +## Alternate Proposals and Considerations + +While implicit line continuance for splatted collections is not a breaking change (PowerShell syntax does not support `@variableName` at the start of a command), implicit line continuance for named parameters is a breaking change, because PowerShell syntax currently supports commands that start with dash. + +For example, consider this script: + +```PowerShell +function -dash { + 'run' +} + +-dash +``` + +In practice, I believe this to be a very low risk breaking change, because it is unlikely that (m)any users have defined functions or aliases that start with a dash, and in researching commands on Windows and Linux I was not able to find any commands with names that start with dash. However, in the event that someone is using such a command, they can still invoke it even if this breaking change is applied, by using the call operator as follows: + +```PowerShell +& -dash +``` From b1880c943d25bb6a969b495561eb4d40dfc1486f Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 17 May 2019 13:10:09 -0300 Subject: [PATCH 2/9] added new breaking change findings --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 49 ++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index d5bdef41..4ada4cf3 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -86,7 +86,7 @@ For the implementation, this RFC builds on the work done in [PR #8938](https://g ## Alternate Proposals and Considerations -While implicit line continuance for splatted collections is not a breaking change (PowerShell syntax does not support `@variableName` at the start of a command), implicit line continuance for named parameters is a breaking change, because PowerShell syntax currently supports commands that start with dash. +While implicit line continuance for splatted collections is not a breaking change (PowerShell syntax does not support `@variableName` at the start of a command), implicit line continuance for named parameters is a breaking change in some scenarios, because PowerShell syntax currently supports commands that start with dash, and PowerShell supports unary operators whose name starts with a single dash. For example, consider this script: @@ -95,11 +95,54 @@ function -dash { 'run' } +Get-Process -Id $PID -dash ``` -In practice, I believe this to be a very low risk breaking change, because it is unlikely that (m)any users have defined functions or aliases that start with a dash, and in researching commands on Windows and Linux I was not able to find any commands with names that start with dash. However, in the event that someone is using such a command, they can still invoke it even if this breaking change is applied, by using the call operator as follows: +In PowerShell 6.x and earlier, two commands will be executed: `Get-Process`, and `-dash`. With this PR in place and the experimental feature enabled, only one command would be executed: `Get-Process`. The reason is that the parser would identify `-dash` as being intended as a parameter on the command on the previous line. That's how the parser needs to work to make this implicit line continuance functional. + +Similarly, consider this script: ```PowerShell -& -dash +Get-Process -Id $PID +-split 'a b c d' ``` + +Similar to the previous example, in PowerShell 6.x and earlier, one command and one statement will be executed: `Get-Process`, and the unary `-split` statement. With this PR in place and the experimental feature enabled, only one command would be executed: `Get-Process`, because again, the parser would identify `-split` as being intended as a parameter on the command on the previous line. The same applies to the `-join` unary operator as well. It does not apply to the `--` prefix arithmetic operator because the parser knows parameter names cannot start with a dash. + +To fix this, users can do one of the following: + +1. For either example, insert a blank line between `Get-Process` and the next command that starts with dash, as shown here: + + ```PowerShell + function -dash { + 'run' + } + + Get-Process -Id $PID + + -dash # Runs the -dash command because implicit line continuance stops looking when it encounters a blank line + + Get-Process -Id $PID + + -split 'a b c d' # splits the string 'a b c d' into an array with four items + ``` + +1. In the example with a command that starts with a dash, invoke the command using the call operator, as shown here: + + ```PowerShell + function -dash { + 'run' + } + + Get-Process -Id $PID + & -dash # Runs the `-dash` command because implicit line continuance sees the call operator and recognizes that the line does not continue further + ``` + + Unfortunately this workaround does not work for the -split or -join unary operators, because you cannot invoke statement that starts with a unary operator with the call operator (the call operator is only for invoking commands). + +In practice, I believe that there are not many commands out there that start with a dash. While researching this, I couldn't find a single command on Windows or Linux that starts with a dash, so that's not a very risky scenario. However, since the `-split` and `-join` unary operators exist in PowerShell, there is a good likelihood that those may be used on a line following a command, and that is where this breaking change would have the most potential impact. While I don't feel that risk is enough to reject this proposal entirely, because it adds significant value to script authors, it is worth considering what the best approach would be. + +Special casing the named unary operators could work (among the thousands of commands on my system, none of them have split or join parameters, but some may exist somewhere), but that would mean future unary operators be special cased as well, so that approach isn't desirable because it risks future breaking changes. + +If the risk is deemed to be too high because of the risk with named unary operators, at a bare minimum I feel this feature offers enough significant value to the community that it should not be rejected, but instead offered as an optional feature so that users wanting the benefit can opt-into the functionality, and mark it as enabled for their scripts/modules. I also suspect the majority of scripters would want it turned on and would then simply write their scripts accordingly. It's really too bad though that unary operators with string names use the same single first character as parameter names because they get in the way here. In hindsight, named operators should probably have been prefixed with something other than a single dash to differentiate them from named parameters (something to consider if PowerShell ever comes out with a version with significant breaking changes plus conversion scripts). From 95051f0d3370de444bff434f42610d5be0b2e21f Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 17 May 2019 22:36:56 -0300 Subject: [PATCH 3/9] add alternatives and stop-parsing sigil support --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 39 +++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index 4ada4cf3..5549bafd 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -37,7 +37,7 @@ For example, consider this example of a New-ADUser command invocation: New-ADUser -Name 'Jack Robinson' -GivenName 'Jack' -Surname 'Robinson' -SamAccountName 'J.Robinson' -UserPrincipalName 'J.Robinson@enterprise.com' -Path 'OU=Users,DC=enterprise,DC=com' -AccountPassword (Read-Host -AsSecureString 'Input Password') -Enabled $true ``` -By itself it's not too much to handle, but in a script commands with many parameters like this can be difficult to manage it in a script. To wrap this command across multiple lines, users can either use backticks, or they can use splatting. The former is a nuisance which should really only be used in situations when PowerShell cannot implicitly intuit how lines are wrapped. The latter is helpful, but users lose the benefits of tab completion and Intellisense for parameters when they use splatting. As a workaround, they can work out the parameters they want to use for the command first, and then convert it into a splatted command, but that's generally onerous. Even though Visual Studio Code has an extension that makes splatting easier, as can be seen [here](https://sqldbawithabeard.com/2018/03/11/easily-splatting-powershell-with-vs-code/), once you've converted to splatting you still lose Intellisense for future updates unless you work from the command first and then add to your splatted collection, and that's just in Visual Studio Code. Other editors may or may not support that functionality, and users working in a standalone terminal won't have that available to them either. +By itself it's not too much to handle, but in a script commands with many parameters like this can be difficult to manage. To wrap this command across multiple lines, users can either use backticks, or they can use splatting. The former is a nuisance which should really only be used in situations when PowerShell cannot implicitly intuit how lines are wrapped. The latter is helpful, but users lose the benefits of tab completion and Intellisense for parameters when they use splatting. As a workaround, they can work out the parameters they want to use for the command first, and then convert it into a splatted command, but that's generally onerous. Even though Visual Studio Code has an extension that makes splatting easier, as can be seen [here](https://sqldbawithabeard.com/2018/03/11/easily-splatting-powershell-with-vs-code/), once you've converted to splatting you still lose Intellisense for future updates unless you work from the command first and then add to your splatted collection, and that's just in Visual Studio Code. Other editors may or may not support that functionality, and users working in a standalone terminal won't have that available to them either. Instead, why not allow users to do this by supporting implicit line continuance when using named parameters: @@ -70,6 +70,15 @@ New-ADUser @commonParams ``` +And lastly, if users have a bunch of arguments they want to pass after the stop parsing sigil (`--%`), let them do this also (thanks @Jaykul for the suggestion): + +```PowerShell +&"./plink.exe" + --% $Hostname -l $Username -pw $Password $Command +``` + +For the last one, `--%` stops the parsing of that command, so no further implicit line continuance may be used because that would require the parser which the scripter opted out of using for that command. + ## Motivation As a script/module author, @@ -146,3 +155,31 @@ In practice, I believe that there are not many commands out there that start wit Special casing the named unary operators could work (among the thousands of commands on my system, none of them have split or join parameters, but some may exist somewhere), but that would mean future unary operators be special cased as well, so that approach isn't desirable because it risks future breaking changes. If the risk is deemed to be too high because of the risk with named unary operators, at a bare minimum I feel this feature offers enough significant value to the community that it should not be rejected, but instead offered as an optional feature so that users wanting the benefit can opt-into the functionality, and mark it as enabled for their scripts/modules. I also suspect the majority of scripters would want it turned on and would then simply write their scripts accordingly. It's really too bad though that unary operators with string names use the same single first character as parameter names because they get in the way here. In hindsight, named operators should probably have been prefixed with something other than a single dash to differentiate them from named parameters (something to consider if PowerShell ever comes out with a version with significant breaking changes plus conversion scripts). + +### Some Non-Breaking Alternatives + +1. Require users to opt-in on any command where they want this wrapping by ending the first line (and only the first line) with a sigil. + + This suggestion was proposed by @jaykul. We could add a token at the end of a command and from that point on, treat subsequent lines as part of the same command if they begin with named parameters, splatted collections, or the stop-parsing sigil (`--%`). For example, this could be a multi-line command that follows these rules: + + ```PowerShell + New-ADUser @ + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -Path 'OU=Users,DC=enterprise,DC=com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + -Enabled $true + ``` + + Pros: The @ symbol is familiar since it is used for splatting. + Cons: Users lose the benefit of not having to use any characters for line continuance. + +1. Add support for optional features to PowerShell, and make this feature optional, rather than experimental. + + The experimental feature is designed to be a temporary holding place until features are fully developed. There are advantages to having optional features as well, which users can opt into if they want the benefit. Ideally such functionality would have support for enabling optional features in both scripts and modules, so that the features could be used by script/module authors without impacting the experience of individual consumers of those scripts/modules. This alone (support for optional features) warrants another RFC, but assuming it is there and assuming such features can be turned on for scripts (via `#requires`?) and/or modules (via the manifest), scripters who want this capability could have it automatically turned on for new scripts/modules without risk to existing scripts/modules because they wouldn't have that feature enabled, thus preventing a breaking change. Anyone who wants this for existing scripts/modules could manually enable it and take on the responsibility of making their code work appropriately. + + Pros: Allows the feature to be used without any breaking changes, and without extra continuance characters. + Cons: Requires optional feature work in PowerShell (an RFC that I'd be happy to write). From ed40b38d9a47014e4b8aa88fe53807241265aff6 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 17 May 2019 22:41:02 -0300 Subject: [PATCH 4/9] incorporated some additional feedback --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index 5549bafd..cde6a184 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -174,7 +174,7 @@ If the risk is deemed to be too high because of the risk with named unary operat -Enabled $true ``` - Pros: The @ symbol is familiar since it is used for splatting. + Pros: The @ symbol is familiar since it is used for splatting. Also, PSReadline could recognize when you're opting in to use this at the command prompt, only running the command if you hit enter twice (although this wouldn't be used nearly as often at the prompt -- it's really for scripts). Cons: Users lose the benefit of not having to use any characters for line continuance. 1. Add support for optional features to PowerShell, and make this feature optional, rather than experimental. From aef259ba2d3149e55e88ffb6cb052b1b099be48c Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 20 May 2019 11:39:48 -0300 Subject: [PATCH 5/9] added missing copyright notice --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index cde6a184..6d14df0b 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -177,9 +177,49 @@ If the risk is deemed to be too high because of the risk with named unary operat Pros: The @ symbol is familiar since it is used for splatting. Also, PSReadline could recognize when you're opting in to use this at the command prompt, only running the command if you hit enter twice (although this wouldn't be used nearly as often at the prompt -- it's really for scripts). Cons: Users lose the benefit of not having to use any characters for line continuance. + Here's another alternative sigil to do the same thing that is a bit more literal: + + ```PowerShell + New-ADUser ... + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -Path 'OU=Users,DC=enterprise,DC=com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + -Enabled $true + ``` + + Pros: ... is literal and familiar. The same PSReadline benefits listed for the previous example apply. + Cons: The same cons listed in the previous example apply. + 1. Add support for optional features to PowerShell, and make this feature optional, rather than experimental. The experimental feature is designed to be a temporary holding place until features are fully developed. There are advantages to having optional features as well, which users can opt into if they want the benefit. Ideally such functionality would have support for enabling optional features in both scripts and modules, so that the features could be used by script/module authors without impacting the experience of individual consumers of those scripts/modules. This alone (support for optional features) warrants another RFC, but assuming it is there and assuming such features can be turned on for scripts (via `#requires`?) and/or modules (via the manifest), scripters who want this capability could have it automatically turned on for new scripts/modules without risk to existing scripts/modules because they wouldn't have that feature enabled, thus preventing a breaking change. Anyone who wants this for existing scripts/modules could manually enable it and take on the responsibility of making their code work appropriately. Pros: Allows the feature to be used without any breaking changes, and without extra continuance characters. Cons: Requires optional feature work in PowerShell (an RFC that I'd be happy to write). + +1. Add argument enclosure support to PowerShell. + + One reader of this RFC who expressed their dislike for this functionality was pointing out that they don't like the fact that there is no terminator to the wrapped command parameters, so PowerShell needs to determine when the command ends based on lookahead. If preferable, new syntax could be added to allow for argument enclosures so that there is a defined terminator. For example, consider this syntax: + + ```PowerShell + New-ADUser -{ + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -Path 'OU=Users,DC=enterprise,DC=com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + -Enabled $true + } + ``` + + What's interesting about this syntax is that these don't need to be just (named) parameter enclosures. They can be defined as argument enclosures, allowing scripters to put whatever they want on whichever lines they want, wrapping where it makes sense for them to do so, while using named parameters, positional parameters, or arguments (for external commands that don't have parameters). + + Pros: Allows the feature to be used without any breaking changes (script blocks do not support negate or subtraction operators, so this new enclosure could be added without risk). Allows the scripter to wrap how they see fit, while still getting Intellisense and tab completion. Parameters and arguments are entered the same way they would be if they were all placed on one line (e.g. unlike splatting, parameter names are entered with dashes, no equals signs are required, etc.). + Cons: Requires a little extra syntax to make it work properly. + From d8c5ea0689565cf9775e079603ee48ff46ce9725 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 20 May 2019 11:39:48 -0300 Subject: [PATCH 6/9] added more alternatives --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index cde6a184..6d14df0b 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -177,9 +177,49 @@ If the risk is deemed to be too high because of the risk with named unary operat Pros: The @ symbol is familiar since it is used for splatting. Also, PSReadline could recognize when you're opting in to use this at the command prompt, only running the command if you hit enter twice (although this wouldn't be used nearly as often at the prompt -- it's really for scripts). Cons: Users lose the benefit of not having to use any characters for line continuance. + Here's another alternative sigil to do the same thing that is a bit more literal: + + ```PowerShell + New-ADUser ... + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -Path 'OU=Users,DC=enterprise,DC=com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + -Enabled $true + ``` + + Pros: ... is literal and familiar. The same PSReadline benefits listed for the previous example apply. + Cons: The same cons listed in the previous example apply. + 1. Add support for optional features to PowerShell, and make this feature optional, rather than experimental. The experimental feature is designed to be a temporary holding place until features are fully developed. There are advantages to having optional features as well, which users can opt into if they want the benefit. Ideally such functionality would have support for enabling optional features in both scripts and modules, so that the features could be used by script/module authors without impacting the experience of individual consumers of those scripts/modules. This alone (support for optional features) warrants another RFC, but assuming it is there and assuming such features can be turned on for scripts (via `#requires`?) and/or modules (via the manifest), scripters who want this capability could have it automatically turned on for new scripts/modules without risk to existing scripts/modules because they wouldn't have that feature enabled, thus preventing a breaking change. Anyone who wants this for existing scripts/modules could manually enable it and take on the responsibility of making their code work appropriately. Pros: Allows the feature to be used without any breaking changes, and without extra continuance characters. Cons: Requires optional feature work in PowerShell (an RFC that I'd be happy to write). + +1. Add argument enclosure support to PowerShell. + + One reader of this RFC who expressed their dislike for this functionality was pointing out that they don't like the fact that there is no terminator to the wrapped command parameters, so PowerShell needs to determine when the command ends based on lookahead. If preferable, new syntax could be added to allow for argument enclosures so that there is a defined terminator. For example, consider this syntax: + + ```PowerShell + New-ADUser -{ + -Name 'Jack Robinson' + -GivenName 'Jack' + -Surname 'Robinson' + -SamAccountName 'J.Robinson' + -UserPrincipalName 'J.Robinson@enterprise.com' + -Path 'OU=Users,DC=enterprise,DC=com' + -AccountPassword (Read-Host -AsSecureString 'Input Password') + -Enabled $true + } + ``` + + What's interesting about this syntax is that these don't need to be just (named) parameter enclosures. They can be defined as argument enclosures, allowing scripters to put whatever they want on whichever lines they want, wrapping where it makes sense for them to do so, while using named parameters, positional parameters, or arguments (for external commands that don't have parameters). + + Pros: Allows the feature to be used without any breaking changes (script blocks do not support negate or subtraction operators, so this new enclosure could be added without risk). Allows the scripter to wrap how they see fit, while still getting Intellisense and tab completion. Parameters and arguments are entered the same way they would be if they were all placed on one line (e.g. unlike splatting, parameter names are entered with dashes, no equals signs are required, etc.). + Cons: Requires a little extra syntax to make it work properly. + From f84afbb2a8d68651db57c2c272ee221e4095932b Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 20 May 2019 12:17:30 -0300 Subject: [PATCH 7/9] formatting changes --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 40 ++++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index 6d14df0b..0b316882 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -174,8 +174,14 @@ If the risk is deemed to be too high because of the risk with named unary operat -Enabled $true ``` - Pros: The @ symbol is familiar since it is used for splatting. Also, PSReadline could recognize when you're opting in to use this at the command prompt, only running the command if you hit enter twice (although this wouldn't be used nearly as often at the prompt -- it's really for scripts). - Cons: Users lose the benefit of not having to use any characters for line continuance. + **Pros:** + + * the @ symbol is familiar since it is used for splatting + * PSReadline could recognize when you're opting in to use this at the command prompt, only running the command if you hit enter twice (although this wouldn't be used nearly as often at the prompt -- it's really for scripts). + + **Cons:** + + * Users lose the benefit of not having to use any characters for line continuance. Here's another alternative sigil to do the same thing that is a bit more literal: @@ -191,15 +197,26 @@ If the risk is deemed to be too high because of the risk with named unary operat -Enabled $true ``` - Pros: ... is literal and familiar. The same PSReadline benefits listed for the previous example apply. - Cons: The same cons listed in the previous example apply. + **Pros:** + + * ... is literal and familiar + * the same PSReadline benefits listed for the previous example apply. + + **Cons:** + + * the same cons listed in the previous example apply. 1. Add support for optional features to PowerShell, and make this feature optional, rather than experimental. The experimental feature is designed to be a temporary holding place until features are fully developed. There are advantages to having optional features as well, which users can opt into if they want the benefit. Ideally such functionality would have support for enabling optional features in both scripts and modules, so that the features could be used by script/module authors without impacting the experience of individual consumers of those scripts/modules. This alone (support for optional features) warrants another RFC, but assuming it is there and assuming such features can be turned on for scripts (via `#requires`?) and/or modules (via the manifest), scripters who want this capability could have it automatically turned on for new scripts/modules without risk to existing scripts/modules because they wouldn't have that feature enabled, thus preventing a breaking change. Anyone who wants this for existing scripts/modules could manually enable it and take on the responsibility of making their code work appropriately. - Pros: Allows the feature to be used without any breaking changes, and without extra continuance characters. - Cons: Requires optional feature work in PowerShell (an RFC that I'd be happy to write). + **Pros:** + + * allows the feature to be used without any breaking changes, and without extra continuance characters. + + **Cons:** + + * requires optional feature work in PowerShell (an RFC that I'd be happy to write). 1. Add argument enclosure support to PowerShell. @@ -220,6 +237,13 @@ If the risk is deemed to be too high because of the risk with named unary operat What's interesting about this syntax is that these don't need to be just (named) parameter enclosures. They can be defined as argument enclosures, allowing scripters to put whatever they want on whichever lines they want, wrapping where it makes sense for them to do so, while using named parameters, positional parameters, or arguments (for external commands that don't have parameters). - Pros: Allows the feature to be used without any breaking changes (script blocks do not support negate or subtraction operators, so this new enclosure could be added without risk). Allows the scripter to wrap how they see fit, while still getting Intellisense and tab completion. Parameters and arguments are entered the same way they would be if they were all placed on one line (e.g. unlike splatting, parameter names are entered with dashes, no equals signs are required, etc.). - Cons: Requires a little extra syntax to make it work properly. + **Pros:** + + * allows the feature to be used without any breaking changes (script blocks do not support negate or subtraction operators, so this new enclosure could be added without risk) + * allows the scripter to wrap how they see fit, while still getting Intellisense and tab completion. P + * parameters and arguments are entered the same way they would be if they were all placed on one line (e.g. unlike splatting, parameter names are entered with dashes, no equals signs are required, etc.). + + **Cons:** + + * requires a little extra syntax to make it work properly. From 7211f19de56ed1209ab14aa3bbb528169edd1e0d Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 20 May 2019 12:28:27 -0300 Subject: [PATCH 8/9] cleaned up wording to keep focus on the intent of the RFC --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 34 ++++---------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index 0b316882..bab6765d 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -9,35 +9,15 @@ Comments Due: June 16, 2019 Plan to implement: Yes --- -# Support implicit line continuance when using named parameters or splatting in commands +# Support implicit line continuance when using named parameters, splatting, or the stop parsing sigil in commands -Nobody likes having to use the backtick to wrap commands in PowerShell, yet many users still use it to get the style they prefer in their scripts. - -For example, PowerShell has long supported explicit line continuance when you end a pipelined command line with a pipe symbol, like this: - -```PowerShell -Get-Process -Id $PID | - Stop-Process -``` - -Even though that is available, when you have a pipeline with many stages (commands) in it, the style is not as desirable as it could be if you could place the pipeline on the beginning of the line instead, like this: - -```PowerShell -Get-Process -Id $PID - | Stop-Process -``` - -Historically that syntax would not be possible unless you ended each line before the last line in the pipeline with a backtick. That shouldn't be necessary though, and PowerShell should be smart enough to recognize implicit line continuance when pipelines are placed at the beginning of the line. This was discussed in detail in [Issue #3020](https://github.com/PowerShell/PowerShell/issues/3020), and [PR #8938](https://github.com/PowerShell/PowerShell/pull/8938) that was recently merged added this support to PowerShell for version 7. - -While that is helpful, there is another potential improvement where PowerShell could support implicit line continuance: when using named parameters or splatting in commands, and that's what this RFC is about. - -For example, consider this example of a New-ADUser command invocation: +Consider this example of a New-ADUser command invocation: ```PowerShell New-ADUser -Name 'Jack Robinson' -GivenName 'Jack' -Surname 'Robinson' -SamAccountName 'J.Robinson' -UserPrincipalName 'J.Robinson@enterprise.com' -Path 'OU=Users,DC=enterprise,DC=com' -AccountPassword (Read-Host -AsSecureString 'Input Password') -Enabled $true ``` -By itself it's not too much to handle, but in a script commands with many parameters like this can be difficult to manage. To wrap this command across multiple lines, users can either use backticks, or they can use splatting. The former is a nuisance which should really only be used in situations when PowerShell cannot implicitly intuit how lines are wrapped. The latter is helpful, but users lose the benefits of tab completion and Intellisense for parameters when they use splatting. As a workaround, they can work out the parameters they want to use for the command first, and then convert it into a splatted command, but that's generally onerous. Even though Visual Studio Code has an extension that makes splatting easier, as can be seen [here](https://sqldbawithabeard.com/2018/03/11/easily-splatting-powershell-with-vs-code/), once you've converted to splatting you still lose Intellisense for future updates unless you work from the command first and then add to your splatted collection, and that's just in Visual Studio Code. Other editors may or may not support that functionality, and users working in a standalone terminal won't have that available to them either. +By itself it's not too much to handle, but in a script commands with many parameters like this can be difficult to manage. To wrap this command across multiple lines, users can either use backticks, or they can use splatting. The former is a syntactical nuisance which should really only be used in situations when PowerShell cannot implicitly intuit how lines are wrapped. The latter is helpful, but users lose the benefits of tab completion and Intellisense for parameters when they use splatting. As a workaround, they can work out the parameters they want to use for the command first, and then convert it into a splatted command, but that's onerous. Even though Visual Studio Code has an extension that makes splatting easier, as can be seen [here](https://sqldbawithabeard.com/2018/03/11/easily-splatting-powershell-with-vs-code/), once you've converted to splatting you still lose Intellisense for future updates unless you work from the command first and then add to your splatted collection, and that's just in Visual Studio Code. Other editors may or may not support that functionality, and users working in a standalone terminal won't have that available to them either. Instead, why not allow users to do this by supporting implicit line continuance when using named parameters: @@ -82,12 +62,12 @@ For the last one, `--%` stops the parsing of that command, so no further implici ## Motivation As a script/module author, - I can wrap long commands across multiple lines at any named parameter or splatted collection, - so that my scripts are easier to maintain while I still get the benefits of Intellisense and tab completion. + I can wrap long commands across multiple lines at any named parameter, splatted collection, or the stop-parsing sigil + so that my scripts are easier to write and maintain while I still get the benefits of Intellisense and tab completion. ## Specification -Note: This RFC is already implemented and submitted as [PR #9614](https://github.com/PowerShell/PowerShell/pull/9614). +Note: This RFC is already implemented and submitted as [PR #9614](https://github.com/PowerShell/PowerShell/pull/9614) in case you want to try it out early and see what it's like. Since this RFC includes some breaking changes (see below), it will be initially implemented with the `PSImplicitLineContinuanceForNamedParameters` experimental feature flag. If the PowerShell Team and the community agree that the risk of this breaking change is low enough, and the workaround is sufficient for those low frequency use cases where it does become an issue, I would much prefer not using an experimental feature flag at all. @@ -137,7 +117,7 @@ To fix this, users can do one of the following: -split 'a b c d' # splits the string 'a b c d' into an array with four items ``` -1. In the example with a command that starts with a dash, invoke the command using the call operator, as shown here: +2. In the example with a command that starts with a dash, invoke the command using the call operator, as shown here: ```PowerShell function -dash { From 65e4ba3388582139020af0211416e85b21cea90f Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 22 May 2019 09:26:15 -0300 Subject: [PATCH 9/9] correction of misleading wording --- 1-Draft/RFCNNNN-Implicit-Line-Continuance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md index bab6765d..c962bab0 100644 --- a/1-Draft/RFCNNNN-Implicit-Line-Continuance.md +++ b/1-Draft/RFCNNNN-Implicit-Line-Continuance.md @@ -57,7 +57,7 @@ And lastly, if users have a bunch of arguments they want to pass after the stop --% $Hostname -l $Username -pw $Password $Command ``` -For the last one, `--%` stops the parsing of that command, so no further implicit line continuance may be used because that would require the parser which the scripter opted out of using for that command. +For the last one, `--%` stops the parsing of that command until a newline or a pipe is encountered, so no further parameter line continuance could be used because that would require the parser which the scripter opted out of using for that command. ## Motivation