Ejemplo n.º 1
0
        internal static async Task <SyntaxNode> GetTransformedSyntaxRootAsync(
            ISyntaxFacts syntaxFacts,
            AbstractFileHeaderHelper fileHeaderHelper,
            SyntaxTrivia newLineTrivia,
            Document document,
            CancellationToken cancellationToken
            )
        {
            var tree = await document
                       .GetRequiredSyntaxTreeAsync(cancellationToken)
                       .ConfigureAwait(false);

            var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            if (
                !document.Project.AnalyzerOptions.TryGetEditorConfigOption <string>(
                    CodeStyleOptions2.FileHeaderTemplate,
                    tree,
                    out var fileHeaderTemplate
                    ) || string.IsNullOrEmpty(fileHeaderTemplate)
                )
            {
                // This exception would show up as a gold bar, but as indicated we do not believe this is reachable.
                throw ExceptionUtilities.Unreachable;
            }

            var expectedFileHeader = fileHeaderTemplate.Replace(
                "{fileName}",
                Path.GetFileName(document.FilePath)
                );

            var        fileHeader = fileHeaderHelper.ParseFileHeader(root);
            SyntaxNode newSyntaxRoot;

            if (fileHeader.IsMissing)
            {
                newSyntaxRoot = AddHeader(
                    syntaxFacts,
                    fileHeaderHelper,
                    newLineTrivia,
                    root,
                    expectedFileHeader
                    );
            }
            else
            {
                newSyntaxRoot = ReplaceHeader(
                    syntaxFacts,
                    fileHeaderHelper,
                    newLineTrivia,
                    root,
                    expectedFileHeader
                    );
            }

            return(newSyntaxRoot);
        }
Ejemplo n.º 2
0
        private static SyntaxNode AddHeader(
            ISyntaxFacts syntaxFacts,
            AbstractFileHeaderHelper fileHeaderHelper,
            SyntaxTrivia newLineTrivia,
            SyntaxNode root,
            string expectedFileHeader
            )
        {
            var newTrivia = CreateNewHeader(
                syntaxFacts,
                fileHeaderHelper.CommentPrefix,
                expectedFileHeader,
                newLineTrivia.ToFullString()
                )
                            .Add(newLineTrivia)
                            .Add(newLineTrivia);

            // Skip blank lines already at the beginning of the document, since we add our own
            var leadingTrivia = root.GetLeadingTrivia();
            var skipCount     = 0;

            for (var i = 0; i < leadingTrivia.Count; i++)
            {
                if (leadingTrivia[i].RawKind == syntaxFacts.SyntaxKinds.EndOfLineTrivia)
                {
                    skipCount = i + 1;
                }
                else if (leadingTrivia[i].RawKind != syntaxFacts.SyntaxKinds.WhitespaceTrivia)
                {
                    break;
                }
            }

            newTrivia = newTrivia.AddRange(leadingTrivia.Skip(skipCount));

            return(root.WithLeadingTrivia(newTrivia));
        }
        internal static async Task <SyntaxNode> GetTransformedSyntaxRootAsync(ISyntaxFacts syntaxFacts, AbstractFileHeaderHelper fileHeaderHelper, SyntaxTrivia newLineTrivia, Document document, string?fileHeaderTemplate, CancellationToken cancellationToken)
        {
            var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);

            var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);

            // If we weren't given a header lets get the one from editorconfig
            if (fileHeaderTemplate is null &&
                !document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(tree).TryGetEditorConfigOption(CodeStyleOptions2.FileHeaderTemplate, out fileHeaderTemplate))
            {
                // No header supplied, no editorconfig setting, nothing to do
                return(root);
            }

            if (RoslynString.IsNullOrEmpty(fileHeaderTemplate))
            {
                // Header template is empty, nothing to do. This shouldn't be possible if this method is called in
                // reaction to a diagnostic, but this method is also used when creating new documents so lets be defensive.
                return(root);
            }

            var expectedFileHeader = fileHeaderTemplate.Replace("{fileName}", Path.GetFileName(document.FilePath));

            var        fileHeader = fileHeaderHelper.ParseFileHeader(root);
            SyntaxNode newSyntaxRoot;

            if (fileHeader.IsMissing)
            {
                newSyntaxRoot = AddHeader(syntaxFacts, fileHeaderHelper, newLineTrivia, root, expectedFileHeader);
            }
            else
            {
                newSyntaxRoot = ReplaceHeader(syntaxFacts, fileHeaderHelper, newLineTrivia, root, expectedFileHeader);
            }

            return(newSyntaxRoot);
        }
        private static SyntaxNode ReplaceHeader(ISyntaxFacts syntaxFacts, AbstractFileHeaderHelper fileHeaderHelper, SyntaxTrivia newLineTrivia, SyntaxNode root, string expectedFileHeader)
        {
            // Skip single line comments, whitespace, and end of line trivia until a blank line is encountered.
            var triviaList = root.GetLeadingTrivia();

            // True if the current line is blank so far (empty or whitespace); otherwise, false. The first line is
            // assumed to not be blank, which allows the analysis to detect a file header which follows a blank line at
            // the top of the file.
            var onBlankLine = false;

            // The set of indexes to remove from 'triviaList'. After removing these indexes, the remaining trivia (if
            // any) will be preserved in the document along with the replacement header.
            var removalList = new List <int>();

            // The number of spaces to indent the new header. This is expected to match the indentation of the header
            // which is being replaced.
            var leadingSpaces = string.Empty;

            // The number of spaces found so far on the current line. This will become 'leadingSpaces' if the spaces are
            // followed by a comment which is considered a header comment.
            var possibleLeadingSpaces = string.Empty;

            // Need to do this with index so we get the line endings correct.
            for (var i = 0; i < triviaList.Count; i++)
            {
                var triviaLine = triviaList[i];
                if (triviaLine.RawKind == syntaxFacts.SyntaxKinds.SingleLineCommentTrivia)
                {
                    if (possibleLeadingSpaces != string.Empty)
                    {
                        // One or more spaces precedes the comment. Keep track of these spaces so we can indent the new
                        // header by the same amount.
                        leadingSpaces = possibleLeadingSpaces;
                    }

                    removalList.Add(i);
                    onBlankLine = false;
                }
                else if (triviaLine.RawKind == syntaxFacts.SyntaxKinds.WhitespaceTrivia)
                {
                    if (leadingSpaces == string.Empty)
                    {
                        possibleLeadingSpaces = triviaLine.ToFullString();
                    }

                    removalList.Add(i);
                }
                else if (triviaLine.RawKind == syntaxFacts.SyntaxKinds.EndOfLineTrivia)
                {
                    possibleLeadingSpaces = string.Empty;
                    removalList.Add(i);

                    if (onBlankLine)
                    {
                        break;
                    }
                    else
                    {
                        onBlankLine = true;
                    }
                }
                else
                {
                    break;
                }
            }

            // Remove copyright lines in reverse order.
            for (var i = removalList.Count - 1; i >= 0; i--)
            {
                triviaList = triviaList.RemoveAt(removalList[i]);
            }

            var newHeaderTrivia = CreateNewHeader(syntaxFacts, leadingSpaces + fileHeaderHelper.CommentPrefix, expectedFileHeader, newLineTrivia.ToFullString());

            // Add a blank line and any remaining preserved trivia after the header.
            newHeaderTrivia = newHeaderTrivia.Add(newLineTrivia).Add(newLineTrivia).AddRange(triviaList);

            // Insert header at top of the file.
            return(root.WithLeadingTrivia(newHeaderTrivia));
        }