Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ static ExperimentalFeature()
new ExperimentalFeature(
name: "PSCommandNotFoundSuggestion",
description: "Recommend potential commands based on fuzzy search on a CommandNotFoundException"),
new ExperimentalFeature(
name: "PSImplicitLineContinuanceForNamedParameters",
description: "Allow commands to span multiple lines automatically when subsequent lines start with named parameters"),
};
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);

Expand Down
56 changes: 50 additions & 6 deletions src/System.Management.Automation/engine/parser/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation.Runspaces;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -5690,7 +5689,7 @@ private PipelineBaseAst PipelineRule()
// G expression assignment-operator statement
// G
// G pipeline-tail:
// G new-lines:opt '|' new-lines:opt command pipeline-tail:opt
// G new-line:opt '|' new-lines:opt command pipeline-tail:opt

var pipelineElements = new List<CommandBaseAst>();
IScriptExtent startExtent = null;
Expand Down Expand Up @@ -5726,6 +5725,8 @@ private PipelineBaseAst PipelineRule()
SetTokenizerMode(oldTokenizerMode);
}

bool foundImplicitPipeContinuance = false;

if (expr != null)
{
if (pipelineElements.Count > 0)
Expand Down Expand Up @@ -5783,7 +5784,7 @@ private PipelineBaseAst PipelineRule()
}
else
{
commandAst = (CommandAst)CommandRule(forDynamicKeyword: false);
commandAst = (CommandAst)CommandRule(forDynamicKeyword: false, out foundImplicitPipeContinuance);
}

if (commandAst != null)
Expand Down Expand Up @@ -5811,7 +5812,9 @@ private PipelineBaseAst PipelineRule()

// Skip newlines before pipe tokens to support (pipe)line continuance when pipe
// tokens start the next line of script
if (nextToken.Kind == TokenKind.NewLine && _tokenizer.IsPipeContinuance(nextToken.Extent))
if (nextToken.Kind == TokenKind.NewLine &&
(foundImplicitPipeContinuance ||
_tokenizer.CheckImplicitContinuance(nextToken.Extent, ImplicitContinuance.Pipeline) == ImplicitContinuance.Pipeline))
{
SkipNewlines();
nextToken = PeekToken();
Expand Down Expand Up @@ -6165,6 +6168,12 @@ private ExpressionAst GetCommandArgument(CommandArgumentContext context, Token t
}

internal Ast CommandRule(bool forDynamicKeyword)
{
bool foundImplicitPipeContinuance;
return CommandRule(forDynamicKeyword, out foundImplicitPipeContinuance);
}

internal Ast CommandRule(bool forDynamicKeyword, out bool foundImplicitPipeContinuance)
{
// G command:
// G command-name command-elements:opt
Expand All @@ -6185,12 +6194,15 @@ internal Ast CommandRule(bool forDynamicKeyword)
// G command-element
// G command-elements command-element
// G command-element:
// G command-parameter
// G new-line:opt command-parameter
// G new-line:opt splatted-variable
// G command-argument
// G redirection
// G command-argument:
// G command-name-expr

foundImplicitPipeContinuance = false;

Token firstToken;
bool dotSource, ampersand;
bool sawDashDash = false;
Expand Down Expand Up @@ -6230,8 +6242,35 @@ internal Ast CommandRule(bool forDynamicKeyword)
}

bool scanning = true;
bool checkForImplicitContinuance = true;
while (scanning)
{
if (ExperimentalFeature.IsEnabled("PSImplicitLineContinuanceForNamedParameters"))
{
// Newlines before named parameters and splatted collections are skipped
// (implicit line continuance)
if (token.Kind == TokenKind.NewLine && checkForImplicitContinuance)
{
switch (_tokenizer.CheckImplicitContinuance(token.Extent, ImplicitContinuance.All))
{
case ImplicitContinuance.VerbatimArgument:
checkForImplicitContinuance = false;
goto case ImplicitContinuance.NamedParameter;

case ImplicitContinuance.NamedParameter:
case ImplicitContinuance.SplattedCollection:
UngetToken(token);
SkipNewlines();
token = NextToken();
break;

case ImplicitContinuance.Pipeline:
foundImplicitPipeContinuance = true;
break;
}
}
}

switch (token.Kind)
{
case TokenKind.Pipe:
Expand Down Expand Up @@ -6337,7 +6376,12 @@ internal Ast CommandRule(bool forDynamicKeyword)
Diagnostics.Assert(elements.Count >= 1, "We should at least have the command name: inlinescript");
endExtent = elements.Last().Extent;

if (!scanning) { continue; }
// If the inline script failed to parse, scanning is set to false, allowing us to use
// continue to break out of the loop statement.
if (!scanning)
{
continue;
}
}
else
{
Expand Down
Loading