protected override void CollectBlockSpans( TypeDeclarationSyntax typeDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( typeDeclaration, ref spans, optionProvider ); if ( !typeDeclaration.OpenBraceToken.IsMissing && !typeDeclaration.CloseBraceToken.IsMissing ) { var lastToken = typeDeclaration.TypeParameterList == null ? typeDeclaration.Identifier : typeDeclaration.TypeParameterList.GetLastToken(includeZeroWidth: true); SyntaxNodeOrToken current = typeDeclaration; var nextSibling = current.GetNextSibling(); // Check IsNode to compress blank lines after this node if it is the last child of the parent. // // Collapse to Definitions doesn't collapse type nodes, but a Toggle All Outlining would collapse groups // of types to the compressed form of not showing blank lines. All kinds of types are grouped together // in Metadata as Source. var compressEmptyLines = optionProvider.IsMetadataAsSource && (!nextSibling.IsNode || nextSibling.AsNode() is BaseTypeDeclarationSyntax); spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( typeDeclaration, lastToken, compressEmptyLines: compressEmptyLines, autoCollapse: false, type: BlockTypes.Type, isCollapsible: true ) ); } // add any leading comments before the end of the type block if (!typeDeclaration.CloseBraceToken.IsMissing) { var leadingTrivia = typeDeclaration.CloseBraceToken.LeadingTrivia; CSharpStructureHelpers.CollectCommentBlockSpans(leadingTrivia, ref spans); } }
protected override void CollectBlockSpans( SwitchStatementSyntax node, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { spans.Add(new BlockSpan( isCollapsible: true, textSpan: TextSpan.FromBounds((node.CloseParenToken != default) ? node.CloseParenToken.Span.End : node.Expression.Span.End, node.CloseBraceToken.Span.End), hintSpan: node.Span, type: BlockTypes.Conditional)); }
protected override void CollectBlockSpans( InitializerExpressionSyntax node, ArrayBuilder <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { if (node.Parent is InitializerExpressionSyntax) { // We have something like: // // new Dictionary<int, string> // { // ... // { // ... // }, // ... // } // // In this case, we want to collapse the "{ ... }," (including the comma). var nextToken = node.CloseBraceToken.GetNextToken(); var end = nextToken.Kind() == SyntaxKind.CommaToken ? nextToken.Span.End : node.Span.End; spans.Add(new BlockSpan( isCollapsible: true, textSpan: TextSpan.FromBounds(node.SpanStart, end), hintSpan: TextSpan.FromBounds(node.SpanStart, end), type: BlockTypes.Expression)); } else { // Parent is something like: // // new Dictionary<int, string> { // ... // } // // The collapsed textspan should be from the > to the } // // However, the hint span should be the entire object creation. var previousToken = node.OpenBraceToken.GetPreviousToken(); spans.Add(new BlockSpan( isCollapsible: true, textSpan: TextSpan.FromBounds(previousToken.Span.End, node.Span.End), hintSpan: node.Parent.Span, type: BlockTypes.Expression)); } }
protected override void CollectBlockSpans( EventFieldDeclarationSyntax eventFieldDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( eventFieldDeclaration, ref spans, optionProvider ); }
protected override void CollectBlockSpans( SyntaxToken previousToken, ArrowExpressionClauseSyntax node, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { spans.Add(new BlockSpan( isCollapsible: true, textSpan: TextSpan.FromBounds(previousToken.Span.End, node.Parent.Span.End), hintSpan: node.Parent.Span, type: BlockTypes.Nonstructural, autoCollapse: !node.IsParentKind(SyntaxKind.LocalFunctionStatement))); }
protected override void CollectBlockSpans( IndexerDeclarationSyntax indexerDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( indexerDeclaration, ref spans, optionProvider ); // fault tolerance if ( indexerDeclaration.AccessorList == null || indexerDeclaration.AccessorList.IsMissing || indexerDeclaration.AccessorList.OpenBraceToken.IsMissing || indexerDeclaration.AccessorList.CloseBraceToken.IsMissing ) { return; } SyntaxNodeOrToken current = indexerDeclaration; var nextSibling = current.GetNextSibling(); // Check IsNode to compress blank lines after this node if it is the last child of the parent. // // Indexers are grouped together with properties in Metadata as Source. var compressEmptyLines = optionProvider.IsMetadataAsSource && ( !nextSibling.IsNode || nextSibling.IsKind(SyntaxKind.IndexerDeclaration) || nextSibling.IsKind(SyntaxKind.PropertyDeclaration) ); spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( indexerDeclaration, indexerDeclaration.ParameterList.GetLastToken(includeZeroWidth: true), compressEmptyLines: compressEmptyLines, autoCollapse: true, type: BlockTypes.Member, isCollapsible: true ) ); }
internal sealed override async Task<ImmutableArray<BlockSpan>> GetBlockSpansWorkerAsync(Document document, int position) { var root = await document.GetSyntaxRootAsync(); var trivia = root.FindTrivia(position, findInsideTrivia: true); var outliner = CreateProvider(); using var actualRegions = TemporaryArray<BlockSpan>.Empty; var optionProvider = new BlockStructureOptionProvider( document.Project.Solution.Options, isMetadataAsSource: document.Project.Solution.Workspace.Kind == CodeAnalysis.WorkspaceKind.MetadataAsSource); outliner.CollectBlockSpans(trivia, ref actualRegions.AsRef(), optionProvider, CancellationToken.None); // TODO: Determine why we get null outlining spans. return actualRegions.ToImmutableAndClear(); }
internal sealed override async Task <ImmutableArray <BlockSpan> > GetBlockSpansWorkerAsync( Document document, int position ) { var root = await document.GetSyntaxRootAsync(CancellationToken.None); var token = root.FindToken(position, findInsideTrivia: true); var node = token.Parent.FirstAncestorOrSelf <TSyntaxNode>(); Assert.NotNull(node); // We prefer ancestor nodes if the position is on the edge of the located node's span. while (node.Parent is TSyntaxNode) { if ( (position == node.SpanStart && position == node.Parent.SpanStart) || (position == node.Span.End && position == node.Parent.Span.End) ) { node = (TSyntaxNode)node.Parent; } else { break; } } var outliner = CreateProvider(); using var actualRegions = TemporaryArray <BlockSpan> .Empty; var optionProvider = new BlockStructureOptionProvider( document.Project.Solution.Options, isMetadataAsSource: document.Project.Solution.Workspace.Kind == CodeAnalysis.WorkspaceKind.MetadataAsSource ); outliner.CollectBlockSpans( node, ref actualRegions.AsRef(), optionProvider, CancellationToken.None ); // TODO: Determine why we get null outlining spans. return(actualRegions.ToImmutableAndClear()); }
protected override void CollectBlockSpans( ConversionOperatorDeclarationSyntax operatorDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( operatorDeclaration, ref spans, optionProvider ); // fault tolerance if ( operatorDeclaration.Body == null || operatorDeclaration.Body.OpenBraceToken.IsMissing || operatorDeclaration.Body.CloseBraceToken.IsMissing ) { return; } SyntaxNodeOrToken current = operatorDeclaration; var nextSibling = current.GetNextSibling(); // Check IsNode to compress blank lines after this node if it is the last child of the parent. // // Whitespace between conversion operators is collapsed in Metadata as Source. var compressEmptyLines = optionProvider.IsMetadataAsSource && ( !nextSibling.IsNode || nextSibling.IsKind(SyntaxKind.ConversionOperatorDeclaration) ); spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( operatorDeclaration, operatorDeclaration.ParameterList.GetLastToken(includeZeroWidth: true), compressEmptyLines: compressEmptyLines, autoCollapse: true, type: BlockTypes.Member, isCollapsible: true ) ); }
protected override void CollectBlockSpans( EnumDeclarationSyntax enumDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( enumDeclaration, ref spans, optionProvider ); if ( !enumDeclaration.OpenBraceToken.IsMissing && !enumDeclaration.CloseBraceToken.IsMissing ) { SyntaxNodeOrToken current = enumDeclaration; var nextSibling = current.GetNextSibling(); // Check IsNode to compress blank lines after this node if it is the last child of the parent. // // Whitespace between type declarations is collapsed in Metadata as Source. var compressEmptyLines = optionProvider.IsMetadataAsSource && (!nextSibling.IsNode || nextSibling.AsNode() is BaseTypeDeclarationSyntax); spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( enumDeclaration, enumDeclaration.Identifier, compressEmptyLines: compressEmptyLines, autoCollapse: false, type: BlockTypes.Member, isCollapsible: true ) ); } // add any leading comments before the end of the type block if (!enumDeclaration.CloseBraceToken.IsMissing) { var leadingTrivia = enumDeclaration.CloseBraceToken.LeadingTrivia; CSharpStructureHelpers.CollectCommentBlockSpans(leadingTrivia, ref spans); } }
protected override void CollectBlockSpans( LiteralExpressionSyntax node, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { if (node.IsKind(SyntaxKind.StringLiteralExpression) && !node.ContainsDiagnostics) { spans.Add(new BlockSpan( isCollapsible: true, textSpan: node.Span, hintSpan: node.Span, type: BlockTypes.Expression, autoCollapse: true, isDefaultCollapsed: false)); } }
protected override void CollectBlockSpans( AccessorDeclarationSyntax accessorDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( accessorDeclaration, ref spans, optionProvider ); // fault tolerance if ( accessorDeclaration.Body == null || accessorDeclaration.Body.OpenBraceToken.IsMissing || accessorDeclaration.Body.CloseBraceToken.IsMissing ) { return; } SyntaxNodeOrToken current = accessorDeclaration; var nextSibling = current.GetNextSibling(); // Check IsNode to compress blank lines after this node if it is the last child of the parent. // // All accessor kinds are grouped together in Metadata as Source. var compressEmptyLines = optionProvider.IsMetadataAsSource && (!nextSibling.IsNode || nextSibling.AsNode() is AccessorDeclarationSyntax); spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( accessorDeclaration, accessorDeclaration.Keyword, compressEmptyLines: compressEmptyLines, autoCollapse: true, type: BlockTypes.Member, isCollapsible: true ) ); }
protected override void CollectBlockSpans( SyntaxToken previousToken, NamespaceDeclarationSyntax namespaceDeclaration, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { // add leading comments CSharpStructureHelpers.CollectCommentBlockSpans(namespaceDeclaration, ref spans, optionProvider); if (!namespaceDeclaration.OpenBraceToken.IsMissing && !namespaceDeclaration.CloseBraceToken.IsMissing) { spans.AddIfNotNull(CSharpStructureHelpers.CreateBlockSpan( namespaceDeclaration, namespaceDeclaration.Name.GetLastToken(includeZeroWidth: true), compressEmptyLines: false, autoCollapse: false, type: BlockTypes.Namespace, isCollapsible: true)); } // extern aliases and usings are outlined in a single region var externsAndUsings = Enumerable.Union <SyntaxNode>(namespaceDeclaration.Externs, namespaceDeclaration.Usings) .OrderBy(node => node.SpanStart) .ToList(); // add any leading comments before the extern aliases and usings if (externsAndUsings.Count > 0) { CSharpStructureHelpers.CollectCommentBlockSpans(externsAndUsings.First(), ref spans, optionProvider); } spans.AddIfNotNull(CSharpStructureHelpers.CreateBlockSpan( externsAndUsings, compressEmptyLines: false, autoCollapse: true, type: BlockTypes.Imports, isCollapsible: true)); // finally, add any leading comments before the end of the namespace block if (!namespaceDeclaration.CloseBraceToken.IsMissing) { CSharpStructureHelpers.CollectCommentBlockSpans( namespaceDeclaration.CloseBraceToken.LeadingTrivia, ref spans); } }
protected override void CollectBlockSpans( CompilationUnitSyntax compilationUnit, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { CSharpStructureHelpers.CollectCommentBlockSpans( compilationUnit, ref spans, optionProvider ); // extern aliases and usings are outlined in a single region var externsAndUsings = new List <SyntaxNode>(); externsAndUsings.AddRange(compilationUnit.Externs); externsAndUsings.AddRange(compilationUnit.Usings); externsAndUsings.Sort((node1, node2) => node1.SpanStart.CompareTo(node2.SpanStart)); spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( externsAndUsings, compressEmptyLines: false, autoCollapse: true, type: BlockTypes.Imports, isCollapsible: true ) ); if ( compilationUnit.Usings.Count > 0 || compilationUnit.Externs.Count > 0 || compilationUnit.Members.Count > 0 || compilationUnit.AttributeLists.Count > 0 ) { CSharpStructureHelpers.CollectCommentBlockSpans( compilationUnit.EndOfFileToken.LeadingTrivia, ref spans ); } }
protected override void CollectBlockSpans( InterpolatedStringExpressionSyntax node, ArrayBuilder <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { if (node.StringStartToken.IsMissing || node.StringEndToken.IsMissing) { return; } spans.Add(new BlockSpan( isCollapsible: true, textSpan: node.Span, hintSpan: node.Span, type: BlockTypes.Expression, autoCollapse: true, isDefaultCollapsed: false)); }
protected override void CollectBlockSpans( AnonymousMethodExpressionSyntax anonymousMethod, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { // fault tolerance if ( anonymousMethod.Block.IsMissing || anonymousMethod.Block.OpenBraceToken.IsMissing || anonymousMethod.Block.CloseBraceToken.IsMissing ) { return; } var lastToken = CSharpStructureHelpers.GetLastInlineMethodBlockToken(anonymousMethod); if (lastToken.Kind() == SyntaxKind.None) { return; } var startToken = anonymousMethod.ParameterList != null ? anonymousMethod.ParameterList.GetLastToken(includeZeroWidth : true) : anonymousMethod.DelegateKeyword; spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( anonymousMethod, startToken, lastToken, compressEmptyLines: false, autoCollapse: false, type: BlockTypes.Expression, isCollapsible: true ) ); }
protected override void CollectBlockSpans( SimpleLambdaExpressionSyntax lambdaExpression, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { // fault tolerance if (lambdaExpression.Body.IsMissing) { return; } if ( !(lambdaExpression.Body is BlockSyntax lambdaBlock) || lambdaBlock.OpenBraceToken.IsMissing || lambdaBlock.CloseBraceToken.IsMissing ) { return; } var lastToken = CSharpStructureHelpers.GetLastInlineMethodBlockToken(lambdaExpression); if (lastToken.Kind() == SyntaxKind.None) { return; } spans.AddIfNotNull( CSharpStructureHelpers.CreateBlockSpan( lambdaExpression, lambdaExpression.ArrowToken, lastToken, compressEmptyLines: false, autoCollapse: false, type: BlockTypes.Expression, isCollapsible: true ) ); }
protected override void CollectBlockSpans( RegionDirectiveTriviaSyntax regionDirective, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { var match = regionDirective.GetMatchingDirective(cancellationToken); if (match != null) { // Always auto-collapse regions for Metadata As Source. These generated files only have one region at // the top of the file, which has content like the following: // // #region Assembly System.Runtime, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a // // C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Runtime.dll // #endregion // // For other files, auto-collapse regions based on the user option. var autoCollapse = optionProvider.IsMetadataAsSource || optionProvider.GetOption( BlockStructureOptions.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp ); spans.Add( new BlockSpan( isCollapsible: true, textSpan: TextSpan.FromBounds(regionDirective.SpanStart, match.Span.End), type: BlockTypes.PreprocessorRegion, bannerText: GetBannerText(regionDirective), autoCollapse: autoCollapse, isDefaultCollapsed: !optionProvider.IsMetadataAsSource ) ); } }
protected override void CollectBlockSpans( DestructorDeclarationSyntax destructorDeclaration, ArrayBuilder <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { CSharpStructureHelpers.CollectCommentBlockSpans(destructorDeclaration, spans, optionProvider); // fault tolerance if (destructorDeclaration.Body == null || destructorDeclaration.Body.OpenBraceToken.IsMissing || destructorDeclaration.Body.CloseBraceToken.IsMissing) { return; } spans.AddIfNotNull(CSharpStructureHelpers.CreateBlockSpan( destructorDeclaration, destructorDeclaration.ParameterList.GetLastToken(includeZeroWidth: true), compressEmptyLines: false, autoCollapse: true, type: BlockTypes.Member, isCollapsible: true)); }
protected override void CollectBlockSpans( DocumentationCommentTriviaSyntax documentationComment, ArrayBuilder <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken) { var startPos = documentationComment.FullSpan.Start; // The trailing newline is included in XmlDocCommentSyntax, so we need to strip it. var endPos = documentationComment.SpanStart + documentationComment.ToString().TrimEnd().Length; var span = TextSpan.FromBounds(startPos, endPos); var bannerLength = optionProvider.GetOption(BlockStructureOptions.MaximumBannerLength, LanguageNames.CSharp); var bannerText = CSharpSyntaxFacts.Instance.GetBannerText( documentationComment, bannerLength, cancellationToken); spans.Add(new BlockSpan( isCollapsible: true, textSpan: span, type: BlockTypes.Comment, bannerText: bannerText, autoCollapse: true)); }
protected override void CollectBlockSpans( BlockSyntax node, ref TemporaryArray <BlockSpan> spans, BlockStructureOptionProvider optionProvider, CancellationToken cancellationToken ) { var parentKind = node.Parent.Kind(); // For most types of statements, just consider the block 'attached' to the // parent node. That means we'll show the parent node header when doing // things like hovering over the indent guide. // // This also works nicely as the close brace for these constructs will always // align with the start of these statements. if (IsNonBlockStatement(node.Parent) || parentKind == SyntaxKind.ElseClause) { var type = GetType(node.Parent); if (type != null) { spans.Add( new BlockSpan( isCollapsible: true, textSpan: GetTextSpan(node), hintSpan: GetHintSpan(node), type: type ) ); } } // Nested blocks aren't attached to anything. Just collapse them as is. // Switch sections are also special. Say you have the following: // // case 0: // { // // } // // We don't want to consider the block parented by the case, because // that would cause us to draw the following: // // case 0: // | { // | // | } // // Which would obviously be wonky. So in this case, we just use the // spanof the block alone, without consideration for the case clause. if (parentKind == SyntaxKind.Block || parentKind == SyntaxKind.SwitchSection) { var type = GetType(node.Parent); spans.Add( new BlockSpan( isCollapsible: true, textSpan: node.Span, hintSpan: node.Span, type: type ) ); } }