Skip to content

Commit

Permalink
Merge pull request #17824 from CyrusNajmabadi/unifyCode
Browse files Browse the repository at this point in the history
Unify code between C# and VB.
  • Loading branch information
CyrusNajmabadi authored Mar 14, 2017
2 parents e42c79e + 4136f02 commit 2058387
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 302 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,7 @@ private async Task<IEnumerable<CodeActionOperation>> GetGenerateInNewFileOperati
{
var syntaxFacts = _document.Document.GetLanguageService<ISyntaxFactsService>();
var fileBanner = syntaxFacts.GetFileBanner(_document.Root);

if (fileBanner.Any(syntaxFacts.IsRegularComment))
{
newRoot = newRoot.WithPrependedLeadingTrivia(fileBanner);
}
newRoot = newRoot.WithPrependedLeadingTrivia(fileBanner);
}

return await CreateAddDocumentAndUpdateUsingsOrImportsOperationsAsync(
Expand Down
164 changes: 13 additions & 151 deletions src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,50 +186,6 @@ public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsi
}
}

// Matches the following:
//
// (whitespace* newline)+
private static readonly Matcher<SyntaxTrivia> s_oneOrMoreBlankLines;

// Matches the following:
//
// (whitespace* (single-comment|multi-comment) whitespace* newline)+ OneOrMoreBlankLines
private static readonly Matcher<SyntaxTrivia> s_bannerMatcher;

// Used to match the following:
//
// <start-of-file> (whitespace* (single-comment|multi-comment) whitespace* newline)+ blankLine*
private static readonly Matcher<SyntaxTrivia> s_fileBannerMatcher;

static SyntaxNodeExtensions()
{
var whitespace = Matcher.Repeat(Match(SyntaxKind.WhitespaceTrivia, "\\b"));
var endOfLine = Match(SyntaxKind.EndOfLineTrivia, "\\n");
var singleBlankLine = Matcher.Sequence(whitespace, endOfLine);

var shebangComment = Match(SyntaxKind.ShebangDirectiveTrivia, "#!");
var singleLineComment = Match(SyntaxKind.SingleLineCommentTrivia, "//");
var multiLineComment = Match(SyntaxKind.MultiLineCommentTrivia, "/**/");
var anyCommentMatcher = Matcher.Choice(shebangComment, singleLineComment, multiLineComment);

var commentLine = Matcher.Sequence(whitespace, anyCommentMatcher, whitespace, endOfLine);

s_oneOrMoreBlankLines = Matcher.OneOrMore(singleBlankLine);
s_bannerMatcher =
Matcher.Sequence(
Matcher.OneOrMore(commentLine),
s_oneOrMoreBlankLines);
s_fileBannerMatcher =
Matcher.Sequence(
Matcher.OneOrMore(commentLine),
Matcher.Repeat(singleBlankLine));
}

private static Matcher<SyntaxTrivia> Match(SyntaxKind kind, string description)
{
return Matcher.Single<SyntaxTrivia>(t => t.Kind() == kind, description);
}

public static IEnumerable<SyntaxTrivia> GetAllPrecedingTriviaToPreviousToken(
this SyntaxNode node, SourceText sourceText = null,
bool includePreviousTokenTrailingTriviaOnlyIfOnSameLine = false)
Expand Down Expand Up @@ -559,120 +515,26 @@ public static IList<IList<TSyntaxNode>> SplitNodesOnPreprocessorBoundaries<TSynt
return result;
}

public static IEnumerable<SyntaxTrivia> GetLeadingBlankLines<TSyntaxNode>(
this TSyntaxNode node)
where TSyntaxNode : SyntaxNode
{
node.GetNodeWithoutLeadingBlankLines(out var blankLines);
return blankLines;
}

public static TSyntaxNode GetNodeWithoutLeadingBlankLines<TSyntaxNode>(
this TSyntaxNode node)
where TSyntaxNode : SyntaxNode
{
return node.GetNodeWithoutLeadingBlankLines(out var blankLines);
}

public static TSyntaxNode GetNodeWithoutLeadingBlankLines<TSyntaxNode>(
this TSyntaxNode node, out IEnumerable<SyntaxTrivia> strippedTrivia)
where TSyntaxNode : SyntaxNode
{
var leadingTriviaToKeep = new List<SyntaxTrivia>(node.GetLeadingTrivia());

var index = 0;
s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index);

strippedTrivia = new List<SyntaxTrivia>(leadingTriviaToKeep.Take(index));

return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index));
}

public static IEnumerable<SyntaxTrivia> GetLeadingBannerAndPreprocessorDirectives<TSyntaxNode>(
this TSyntaxNode node)
where TSyntaxNode : SyntaxNode
{
node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(out var leadingTrivia);
return leadingTrivia;
}

public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives<TSyntaxNode>(
this TSyntaxNode node)
where TSyntaxNode : SyntaxNode
{
return node.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(out var strippedTrivia);
}

public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives<TSyntaxNode>(
this TSyntaxNode node, out IEnumerable<SyntaxTrivia> strippedTrivia)
where TSyntaxNode : SyntaxNode
{
var leadingTrivia = node.GetLeadingTrivia();

// Rules for stripping trivia:
// 1) If there is a pp directive, then it (and all preceding trivia) *must* be stripped.
// This rule supersedes all other rules.
// 2) If there is a doc comment, it cannot be stripped. Even if there is a doc comment,
// followed by 5 new lines, then the doc comment still must stay with the node. This
// rule does *not* supersede rule 1.
// 3) Single line comments in a group (i.e. with no blank lines between them) belong to
// the node *iff* there is no blank line between it and the following trivia.

List<SyntaxTrivia> leadingTriviaToStrip, leadingTriviaToKeep;

int ppIndex = -1;
for (int i = leadingTrivia.Count - 1; i >= 0; i--)
{
if (SyntaxFacts.IsPreprocessorDirective(leadingTrivia[i].Kind()))
{
ppIndex = i;
break;
}
}

if (ppIndex != -1)
{
// We have a pp directive. it (and all previous trivia) must be stripped.
leadingTriviaToStrip = new List<SyntaxTrivia>(leadingTrivia.Take(ppIndex + 1));
leadingTriviaToKeep = new List<SyntaxTrivia>(leadingTrivia.Skip(ppIndex + 1));
}
else
{
leadingTriviaToKeep = new List<SyntaxTrivia>(leadingTrivia);
leadingTriviaToStrip = new List<SyntaxTrivia>();
}
public static ImmutableArray<SyntaxTrivia> GetLeadingBlankLines<TSyntaxNode>(this TSyntaxNode node) where TSyntaxNode : SyntaxNode
=> CSharpSyntaxFactsService.Instance.GetLeadingBlankLines(node);

// Now, consume as many banners as we can. s_fileBannerMatcher will only be matched at
// the start of the file.
var index = 0;
while (
s_oneOrMoreBlankLines.TryMatch(leadingTriviaToKeep, ref index) ||
s_bannerMatcher.TryMatch(leadingTriviaToKeep, ref index) ||
(node.FullSpan.Start == 0 && s_fileBannerMatcher.TryMatch(leadingTriviaToKeep, ref index)))
{
}
public static TSyntaxNode GetNodeWithoutLeadingBlankLines<TSyntaxNode>(this TSyntaxNode node) where TSyntaxNode : SyntaxNode
=> CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBlankLines(node);

leadingTriviaToStrip.AddRange(leadingTriviaToKeep.Take(index));
public static TSyntaxNode GetNodeWithoutLeadingBlankLines<TSyntaxNode>(this TSyntaxNode node, out ImmutableArray<SyntaxTrivia> strippedTrivia) where TSyntaxNode : SyntaxNode
=> CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBlankLines(node, out strippedTrivia);

strippedTrivia = leadingTriviaToStrip;
return node.WithLeadingTrivia(leadingTriviaToKeep.Skip(index));
}

public static ImmutableArray<SyntaxTrivia> GetFileBanner(this SyntaxNode root)
{
Debug.Assert(root.FullSpan.Start == 0);
public static ImmutableArray<SyntaxTrivia> GetLeadingBannerAndPreprocessorDirectives<TSyntaxNode>(this TSyntaxNode node) where TSyntaxNode : SyntaxNode
=> CSharpSyntaxFactsService.Instance.GetLeadingBannerAndPreprocessorDirectives(node);

var leadingTrivia = root.GetLeadingTrivia();
var index = 0;
s_fileBannerMatcher.TryMatch(leadingTrivia.ToList(), ref index);
public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives<TSyntaxNode>(this TSyntaxNode node) where TSyntaxNode : SyntaxNode
=> CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node);

return ImmutableArray.CreateRange(leadingTrivia.Take(index));
}
public static TSyntaxNode GetNodeWithoutLeadingBannerAndPreprocessorDirectives<TSyntaxNode>(this TSyntaxNode node, out ImmutableArray<SyntaxTrivia> strippedTrivia) where TSyntaxNode : SyntaxNode
=> CSharpSyntaxFactsService.Instance.GetNodeWithoutLeadingBannerAndPreprocessorDirectives(node, out strippedTrivia);

public static bool IsAnyAssignExpression(this SyntaxNode node)
{
return SyntaxFacts.IsAssignmentExpression(node.Kind());
}
=> SyntaxFacts.IsAssignmentExpression(node.Kind());

public static bool IsCompoundAssignExpression(this SyntaxNode node)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,13 @@ public static bool IsRegularOrDocComment(this SyntaxTrivia trivia)
}

public static bool IsSingleLineComment(this SyntaxTrivia trivia)
{
return trivia.Kind() == SyntaxKind.SingleLineCommentTrivia;
}
=> trivia.Kind() == SyntaxKind.SingleLineCommentTrivia;

public static bool IsMultiLineComment(this SyntaxTrivia trivia)
{
return trivia.Kind() == SyntaxKind.MultiLineCommentTrivia;
}
=> trivia.Kind() == SyntaxKind.MultiLineCommentTrivia;

public static bool IsShebangDirective(this SyntaxTrivia trivia)
{
return trivia.Kind() == SyntaxKind.ShebangDirectiveTrivia;
}
=> trivia.Kind() == SyntaxKind.ShebangDirectiveTrivia;

public static bool IsCompleteMultiLineComment(this SyntaxTrivia trivia)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1029,9 +1029,7 @@ private string GetTypeName(TypeSyntax type)
}

private static string GetSimpleTypeName(SimpleNameSyntax name)
{
return name.Identifier.ValueText;
}
=> name.Identifier.ValueText;

private static string ExpandExplicitInterfaceName(string identifier, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier)
{
Expand Down Expand Up @@ -1832,12 +1830,24 @@ public SyntaxNode GetOperandOfPrefixUnaryExpression(SyntaxNode node)
public SyntaxNode GetNextExecutableStatement(SyntaxNode statement)
=> ((StatementSyntax)statement).GetNextStatement();

public bool IsWhitespaceTrivia(SyntaxTrivia trivia)
public override bool IsWhitespaceTrivia(SyntaxTrivia trivia)
=> trivia.IsWhitespace();

public bool IsEndOfLineTrivia(SyntaxTrivia trivia)
public override bool IsEndOfLineTrivia(SyntaxTrivia trivia)
=> trivia.IsEndOfLine();

public override bool IsSingleLineCommentTrivia(SyntaxTrivia trivia)
=> trivia.IsSingleLineComment();

public override bool IsMultiLineCommentTrivia(SyntaxTrivia trivia)
=> trivia.IsMultiLineComment();

public override bool IsShebangDirectiveTrivia(SyntaxTrivia trivia)
=> trivia.IsShebangDirective();

public override bool IsPreprocessorDirective(SyntaxTrivia trivia)
=> SyntaxFacts.IsPreprocessorDirective(trivia.Kind());

private class AddFirstMissingCloseBaceRewriter: CSharpSyntaxRewriter
{
private readonly SyntaxNode _contextNode;
Expand Down Expand Up @@ -1979,8 +1989,5 @@ public bool IsBetweenTypeMembers(SourceText sourceText, SyntaxNode root, int pos

public ImmutableArray<SyntaxNode> GetSelectedMembers(SyntaxNode root, TextSpan textSpan)
=> ImmutableArray<SyntaxNode>.CastUp(root.GetMembersInSpan(textSpan));

public ImmutableArray<SyntaxTrivia> GetFileBanner(SyntaxNode root)
=> root.GetFileBanner();
}
}
Loading

0 comments on commit 2058387

Please sign in to comment.