Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert parens when hitting semicolon after 'new' #76152

Merged
merged 2 commits into from
Dec 2, 2024
Merged
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 @@ -92,42 +92,59 @@ public void ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler,
transaction?.Complete();
}

private SemicolonBehavior BeforeExecuteCommand(bool speculative, TypeCharCommandArgs args, CommandExecutionContext executionContext)
private SemicolonBehavior BeforeExecuteCommand(
bool speculative,
TypeCharCommandArgs args,
CommandExecutionContext executionContext)
{
if (args.TypedChar != ';' || !args.TextView.Selection.IsEmpty)
{
return SemicolonBehavior.None;
}

var caretOpt = args.TextView.GetCaretPoint(args.SubjectBuffer);
if (!caretOpt.HasValue)
{
if (args.TextView.GetCaretPoint(args.SubjectBuffer) is not { } caret)
return SemicolonBehavior.None;
}

if (!_globalOptions.GetOption(CompleteStatementOptionsStorage.AutomaticallyCompleteStatementOnSemicolon))
{
return SemicolonBehavior.None;
}

var caret = caretOpt.Value;
var document = caret.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document == null)
{
return SemicolonBehavior.None;
}

var cancellationToken = executionContext.OperationContext.UserCancellationToken;
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken);

if (!TryGetStartingNode(root, caret, out var currentNode, cancellationToken))
{
if (!TryGetStartingNode(root, caret, out var tokenOnLeft, out var currentNode, cancellationToken))
return SemicolonBehavior.None;

// If the user types `= new;` complete it out to `= new();`
if (tokenOnLeft.Kind() is SyntaxKind.NewKeyword &&
currentNode is BaseObjectCreationExpressionSyntax
{
Parent: EqualsValueClauseSyntax
{
Parent: VariableDeclaratorSyntax
{
Parent: VariableDeclarationSyntax
{
Parent: FieldDeclarationSyntax or LocalDeclarationStatementSyntax
}
}
}
})
{
if (!speculative)
{
var edit = args.SubjectBuffer.CreateEdit();
edit.Insert(caret, "();");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edit.Insert(caret, "();");

Is adding the class name for the new operator an option that can be specified?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatted offline, didn't consider that scenario important enough unless there is customer feedgack.

edit.Apply();
}

return SemicolonBehavior.Overtype;
}

return MoveCaretToSemicolonPosition(speculative, args, document, root, originalCaret: caret, caret, syntaxFacts, currentNode,
isInsideDelimiters: false, cancellationToken);
return MoveCaretToSemicolonPosition(
speculative, args, document, root, originalCaret: caret, caret, syntaxFacts, currentNode, isInsideDelimiters: false, cancellationToken);
}

/// <summary>
Expand All @@ -137,22 +154,23 @@ private SemicolonBehavior BeforeExecuteCommand(bool speculative, TypeCharCommand
private static bool TryGetStartingNode(
SyntaxNode root,
SnapshotPoint caret,
out SyntaxToken tokenOnLeft,
[NotNullWhen(true)] out SyntaxNode? startingNode,
CancellationToken cancellationToken)
{
// on the UI thread
startingNode = null;
var caretPosition = caret.Position;

var token = root.FindTokenOnLeftOfPosition(caretPosition);
tokenOnLeft = root.FindTokenOnLeftOfPosition(caretPosition);

if (token.SyntaxTree == null
|| token.SyntaxTree.IsInNonUserCode(caretPosition, cancellationToken))
if (tokenOnLeft.SyntaxTree == null ||
tokenOnLeft.SyntaxTree.IsInNonUserCode(caretPosition, cancellationToken))
{
return false;
}

startingNode = token.GetRequiredParent();
startingNode = tokenOnLeft.GetRequiredParent();

// If the caret is before an opening delimiter or after a closing delimeter,
// start analysis with node outside of delimiters.
Expand Down Expand Up @@ -224,7 +242,7 @@ SyntaxKind.CollectionExpression or
isInsideDelimiters = !HasDelimitersButCaretIsOutside(currentNode, caret.Position);

var newCaret = args.SubjectBuffer.CurrentSnapshot.GetPoint(newCaretPosition);
if (!TryGetStartingNode(root, newCaret, out currentNode, cancellationToken))
if (!TryGetStartingNode(root, newCaret, out _, out currentNode, cancellationToken))
return SemicolonBehavior.None;

return MoveCaretToSemicolonPosition(
Expand Down
Loading
Loading