Skip to content

Feature Request: Enforcement of non-newline command terminator (e.g. semicolon) within scripts, via opt-in directive #12803

@jimbobmcgee

Description

@jimbobmcgee

Summary of the new feature/enhancement

Is there any mileage or desire for an option that enforces the use of semicolon (and only semicolon) as the command terminator for a given script?

I regularly find myself at odds between the "natural" line-continuation points within the PowerShell language, and where I want to place a newline for the long-term readability of my scripts. As such, I find myself resorting to escaping newlines with the backtick, which I am repeatedly led to believe is a bad thing™.

I completely understand why the natural points are what they are, but most of the reasons seem to boil down to the expectation that a newline can terminate the command. If the script parser could be informed that semicolon was the only command terminator in a given script, it could bypass having to make continuation checks at each newline and make whitespace mostly-insignificant. This would allow for better alignment of pipelines and command parameters, without having to resort to backtick and without the language seeming to have opinions as to where line continuation should happen.

Proposed technical implementation details (optional)

I see this as an extension to #Requires because of its existing script scope, however, the notion of requiring command termination is not semantically ideal. As such, this could reasonably be via any other script-wide directive. It could be a dedicated Set-CommandTermination command, or maybe an additional parameter to Set-StrictMode (although there may be lexical scope expectations around such).

It would certainly need to be an opt-in state, so as not to affect existing scripts.

It might be desirable to later extend this to allow for alternate command terminators—such as DoubleNewline—for those who find semicolons repulsive.

It is intended to facilitate such script formatting as the following (which is appreciably my personal preference), without requiring further complex line-continuation checks:

Instead of...

Import-Module DummyExample
Invoke-DummyPipelineProducer -SomeArgument -OtherArgument |
  Invoke-DummyLongParameterList -SomeSwitch -OtherSwitch -SomeParameter 12345 -Expression (
    Invoke-DummyMethod | Select-Object -First 1) |
  Where-Object { $_.SomeProperty -contains (
    Invoke-DummyFilterList -Type A | Select-Object -First 1) } |
  Select-Object -ExpandProperty Foo |
  ForEach-Object {
    if (-not (Test-Dummy $_)) { Invoke-DummyAction $_ }
    else { Write-Verbose "$_ already done" }
  }

...or the backtick-laden...

Import-Module DummyExample
Invoke-DummyPipelineProducer -SomeArgument -OtherArgument `
  | Invoke-DummyLongParameterList `
      -SomeSwitch `
      -OtherSwitch `
      -SomeParameter 12345 `
      -Expression (Invoke-DummyMethod | Select-Object -First 1) `
  | Where-Object { $_.SomeProperty -contains (
      Invoke-DummyFilterList -Type A | Select-Object -First 1 `
    ) } `
  | Select-Object -ExpandProperty Foo `
  | ForEach-Object {
      if (-not (Test-Dummy $_)) { Invoke-DummyAction $_ }
      else { Write-Verbose "$_ already done" }
    }

...we could write...

#Requires -CommandTermination Semicolon;
Import-Module DummyExample;
Invoke-DummyPipelineProducer -SomeArgument -OtherArgument
  | Invoke-DummyLongParameterList
      -SomeSwitch
      -OtherSwitch
      -SomeParameter 12345
      -Expression (Invoke-DummyMethod | Select-Object -First 1)
  | Where-Object { 
      $_.SomeProperty -contains (Invoke-DummyFilterList -Type A
                                   | Select-Object -First 1) 
    }
  | Select-Object -ExpandProperty Foo
  | ForEach-Object {
      if (-not (Test-Dummy $_)) { Invoke-DummyAction $_ }
      else { Write-Verbose "$_ already done" }
    };

Unfortunately, I can't comment as to how much effort this would add to the script parser.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-No ActivityIssue has had no activity for 6 months or more

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions