// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp; using static CSharpSyntaxTokens; internal sealed partial class CSharpSyntaxFactsServiceFactory { private sealed class CSharpSyntaxFactsService : CSharpSyntaxFacts, ISyntaxFactsService { internal static new readonly CSharpSyntaxFactsService Instance = new(); public bool IsInNonUserCode(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { if (syntaxTree == null) { return false; } return syntaxTree.IsInNonUserCode(position, cancellationToken); } private static readonly SyntaxAnnotation s_annotation = new(); public void AddFirstMissingCloseBrace<TContextNode>( SyntaxNode root, TContextNode contextNode, out SyntaxNode newRoot, out TContextNode newContextNode) where TContextNode : SyntaxNode { newRoot = new AddFirstMissingCloseBraceRewriter(contextNode).Visit(root); newContextNode = (TContextNode)newRoot.GetAnnotatedNodes(s_annotation).Single(); } private sealed class AddFirstMissingCloseBraceRewriter : CSharpSyntaxRewriter { private readonly SyntaxNode _contextNode; private bool _seenContextNode = false; private bool _addedFirstCloseCurly = false; public AddFirstMissingCloseBraceRewriter(SyntaxNode contextNode) => _contextNode = contextNode; public override SyntaxNode Visit(SyntaxNode node) { if (node == _contextNode) { _seenContextNode = true; // Annotate the context node so we can find it again in the new tree // after we've added the close curly. return node.WithAdditionalAnnotations(s_annotation); } // rewrite this node normally. var rewritten = base.Visit(node); if (rewritten == node) { return rewritten; } // This node changed. That means that something underneath us got // rewritten. (i.e. we added the annotation to the context node). Debug.Assert(_seenContextNode); // Ok, we're past the context node now. See if this is a node with // curlies. If so, if it has a missing close curly then add in the // missing curly. Also, even if it doesn't have missing curlies, // then still ask to format its close curly to make sure all the // curlies up the stack are properly formatted. var braces = rewritten.GetBraces(); if (braces.openBrace.Kind() == SyntaxKind.None && braces.closeBrace.Kind() == SyntaxKind.None) { // Not an item with braces. Just pass it up. return rewritten; } // See if the close brace is missing. If it's the first missing one // we're seeing then definitely add it. if (braces.closeBrace.IsMissing) { if (!_addedFirstCloseCurly) { var closeBrace = CloseBraceToken .WithAdditionalAnnotations(Formatter.Annotation); rewritten = rewritten.ReplaceToken(braces.closeBrace, closeBrace); _addedFirstCloseCurly = true; } } else { // Ask for the close brace to be formatted so that all the braces // up the spine are in the right location. rewritten = rewritten.ReplaceToken(braces.closeBrace, braces.closeBrace.WithAdditionalAnnotations(Formatter.Annotation)); } return rewritten; } } public Task<ImmutableArray<SyntaxNode>> GetSelectedFieldsAndPropertiesAsync(SyntaxTree tree, TextSpan textSpan, bool allowPartialSelection, CancellationToken cancellationToken) => CSharpSelectedMembers.Instance.GetSelectedFieldsAndPropertiesAsync(tree, textSpan, allowPartialSelection, cancellationToken); } } |