提交 414d2efe 编写于 作者: B Brett Forsgren

Merge pull request #5019 from brettfo/classify-shebang

properly classify shebang comments in the IDE
......@@ -13,7 +13,7 @@ Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax.WithIsActive(bool
Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax.WithLoadKeyword(Microsoft.CodeAnalysis.SyntaxToken loadKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax
Microsoft.CodeAnalysis.CSharp.SyntaxKind.LoadDirectiveTrivia = 8923 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.LoadKeyword = 8485 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ShebangCommentTrivia = 8922 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ShebangTrivia = 8922 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
override Microsoft.CodeAnalysis.CSharp.CSharpParseOptions.CommonWithKind(Microsoft.CodeAnalysis.SourceCodeKind kind) -> Microsoft.CodeAnalysis.ParseOptions
override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitLoadDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode
override Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void
......@@ -30,4 +30,4 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxExtensions.NormalizeWhitespace(this M
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LoadDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken file, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LoadDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken loadKeyword, Microsoft.CodeAnalysis.SyntaxToken file, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLoadDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>.VisitLoadDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax node) -> TResult
\ No newline at end of file
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>.VisitLoadDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax node) -> TResult
......@@ -94,7 +94,7 @@ internal static SyntaxTrivia Comment(string text)
}
else if (text.StartsWith("#", StringComparison.Ordinal))
{
return SyntaxTrivia.Create(SyntaxKind.ShebangCommentTrivia, text);
return SyntaxTrivia.Create(SyntaxKind.ShebangTrivia, text);
}
else
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Collections;
using Roslyn.Utilities;
using System.Diagnostics;
......@@ -347,7 +345,8 @@ public override Microsoft.CodeAnalysis.SyntaxToken CreateSeparator<TNode>(Syntax
public override bool IsTriviaWithEndOfLine()
{
return this.Kind == SyntaxKind.EndOfLineTrivia
|| this.Kind == SyntaxKind.SingleLineCommentTrivia;
|| this.Kind == SyntaxKind.SingleLineCommentTrivia
|| this.Kind == SyntaxKind.ShebangTrivia;
}
// Use conditional weak table so we always return same identity for structured trivia
......
......@@ -8,7 +8,6 @@
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
namespace Microsoft.CodeAnalysis.CSharp
......@@ -168,14 +167,16 @@ public static SyntaxTrivia PreprocessingMessage(string text)
}
/// <summary>
/// Trivia nodes represents parts of the program text that are not parts of the
/// syntactic grammar, such as spaces, newlines, comments, preprocessors
/// Trivia nodes represent parts of the program text that are not parts of the
/// syntactic grammar, such as spaces, newlines, comments, preprocessor
/// directives, and disabled code.
/// </summary>
/// <param name="kind">
/// A <cref c="SyntaxKind"/> representing the specific kind of SyntaxTrivia. One of
/// WhitespaceTrivia, EndOfLineTrivia, CommentTrivia,
/// DocumentationCommentExteriorTrivia, DisabledTextTrivia.
/// A <see cref="SyntaxKind"/> representing the specific kind of <see cref="SyntaxTrivia"/>. One of
/// <see cref="SyntaxKind.WhitespaceTrivia"/>, <see cref="SyntaxKind.EndOfLineTrivia"/>,
/// <see cref="SyntaxKind.SingleLineCommentTrivia"/>, <see cref="SyntaxKind.MultiLineCommentTrivia"/>,
/// <see cref="SyntaxKind.DocumentationCommentExteriorTrivia"/>, <see cref="SyntaxKind.DisabledTextTrivia"/>,
/// <see cref="SyntaxKind.ShebangTrivia"/>
/// </param>
/// <param name="text">
/// The actual text of this token.
......@@ -194,6 +195,7 @@ public static SyntaxTrivia SyntaxTrivia(SyntaxKind kind, string text)
case SyntaxKind.EndOfLineTrivia:
case SyntaxKind.MultiLineCommentTrivia:
case SyntaxKind.SingleLineCommentTrivia:
case SyntaxKind.ShebangTrivia:
case SyntaxKind.WhitespaceTrivia:
return new SyntaxTrivia(default(SyntaxToken), new Syntax.InternalSyntax.SyntaxTrivia(kind, text, null, null), 0, 0);
......
......@@ -530,7 +530,7 @@ public enum SyntaxKind : ushort
InterpolationAlignmentClause = 8920,
InterpolationFormatClause = 8921,
ShebangCommentTrivia = 8922,
ShebangTrivia = 8922,
LoadDirectiveTrivia = 8923,
}
}
......@@ -209,6 +209,7 @@ public static bool IsTrivia(SyntaxKind kind)
case SyntaxKind.WhitespaceTrivia:
case SyntaxKind.SingleLineCommentTrivia:
case SyntaxKind.MultiLineCommentTrivia:
case SyntaxKind.ShebangTrivia:
case SyntaxKind.SingleLineDocumentationCommentTrivia:
case SyntaxKind.MultiLineDocumentationCommentTrivia:
case SyntaxKind.DisabledTextTrivia:
......
......@@ -110,10 +110,16 @@ private void AddEndOfLine()
}
}
/// <summary>
/// Returns whether the specified <see cref="SyntaxTrivia"/> token is also the end of the line. This will
/// be true for <see cref="SyntaxKind.EndOfLineTrivia"/>, <see cref="SyntaxKind.SingleLineCommentTrivia"/>,
/// <see cref="SyntaxKind.ShebangTrivia"/>, and all preprocessor directives.
/// </summary>
private static bool IsEndOfLine(SyntaxTrivia trivia)
{
return trivia.Kind() == SyntaxKind.EndOfLineTrivia
|| trivia.Kind() == SyntaxKind.SingleLineCommentTrivia
|| trivia.Kind() == SyntaxKind.ShebangTrivia
|| trivia.IsDirective;
}
......
......@@ -666,6 +666,7 @@ private static bool NeedsLineBreakAfter(SyntaxTrivia trivia, bool isTrailingTriv
switch (kind)
{
case SyntaxKind.SingleLineCommentTrivia:
case SyntaxKind.ShebangTrivia:
return true;
case SyntaxKind.MultiLineCommentTrivia:
return !isTrailingTrivia;
......
......@@ -8446,7 +8446,7 @@ public void Shebang()
Assert.Empty(root.ChildNodes());
var eof = root.EndOfFileToken;
Assert.Equal(SyntaxKind.EndOfFileToken, eof.Kind());
Assert.Equal(SyntaxKind.ShebangCommentTrivia, eof.GetLeadingTrivia().Single().Kind());
Assert.Equal(SyntaxKind.ShebangTrivia, eof.GetLeadingTrivia().Single().Kind());
tree = ParseAndValidate("#! /usr/bin/env scriptcs\r\n ", TestOptions.Script);
root = tree.GetCompilationUnitRoot();
......@@ -8456,7 +8456,7 @@ public void Shebang()
Assert.Equal(SyntaxKind.EndOfFileToken, eof.Kind());
var leading = eof.GetLeadingTrivia().ToArray();
Assert.Equal(3, leading.Length);
Assert.Equal(SyntaxKind.ShebangCommentTrivia, leading[0].Kind());
Assert.Equal(SyntaxKind.ShebangTrivia, leading[0].Kind());
Assert.Equal(SyntaxKind.EndOfLineTrivia, leading[1].Kind());
Assert.Equal(SyntaxKind.WhitespaceTrivia, leading[2].Kind());
......@@ -8469,7 +8469,7 @@ public void Shebang()
Assert.Equal(SyntaxKind.GlobalStatement, statement.Kind());
leading = statement.GetLeadingTrivia().ToArray();
Assert.Equal(2, leading.Length);
Assert.Equal(SyntaxKind.ShebangCommentTrivia, leading[0].Kind());
Assert.Equal(SyntaxKind.ShebangTrivia, leading[0].Kind());
Assert.Equal(SyntaxKind.EndOfLineTrivia, leading[1].Kind());
}
......
......@@ -999,6 +999,68 @@ class Bar {
Punctuation.CloseCurly);
}
[Fact, Trait(Traits.Feature, Traits.Features.Classification)]
public void ShebangAsFirstCommentInScript()
{
var code = @"#!/usr/bin/env scriptcs
System.Console.WriteLine();";
var expected = new[]
{
Comment("#!/usr/bin/env scriptcs"),
Identifier("System"),
Operators.Dot,
Identifier("Console"),
Operators.Dot,
Identifier("WriteLine"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Punctuation.Semicolon
};
Test(code, code, expected, Options.Script);
}
[Fact, Trait(Traits.Feature, Traits.Features.Classification)]
public void ShebangAsFirstCommentInNonScript()
{
var code = @"#!/usr/bin/env scriptcs
System.Console.WriteLine();";
var expected = new[]
{
PPKeyword("#"),
PPText("!/usr/bin/env scriptcs"),
Identifier("System"),
Operators.Dot,
Identifier("Console"),
Operators.Dot,
Identifier("WriteLine"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Punctuation.Semicolon
};
Test(code, code, expected, Options.Regular);
}
[Fact, Trait(Traits.Feature, Traits.Features.Classification)]
public void ShebangNotAsFirstCommentInScript()
{
var code = @" #!/usr/bin/env scriptcs
System.Console.WriteLine();";
var expected = new[]
{
PPKeyword("#"),
PPText("!/usr/bin/env scriptcs"),
Identifier("System"),
Operators.Dot,
Identifier("Console"),
Operators.Dot,
Identifier("WriteLine"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Punctuation.Semicolon
};
Test(code, code, expected, Options.Script);
}
[Fact, Trait(Traits.Feature, Traits.Features.Classification)]
public void CommentAsMethodBodyContent()
{
......
......@@ -8367,5 +8367,12 @@ class Class2
}";
VerifyItemExists(markup, "Property1");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public void NoCompletionInShebangComments()
{
VerifyNoItemsExist("#!$$", sourceCodeKind: SourceCodeKind.Script);
VerifyNoItemsExist("#! S$$", sourceCodeKind: SourceCodeKind.Script, usePreviousCharAsTrigger: true);
}
}
}
......@@ -318,10 +318,7 @@ private SyntaxTriviaList GetTriviaToPreserve(SyntaxTriviaList syntaxTriviaList)
private static bool ShouldPreserve(SyntaxTriviaList trivia)
{
return trivia.Any(
t => t.Kind() == SyntaxKind.SingleLineCommentTrivia ||
t.Kind() == SyntaxKind.MultiLineCommentTrivia ||
t.IsDirective);
return trivia.Any(t => t.IsRegularComment() || t.IsDirective);
}
private SyntaxNode RemoveDeclaratorFromVariableList(VariableDeclaratorSyntax variableDeclarator, VariableDeclarationSyntax variableDeclaration)
......
......@@ -1180,8 +1180,7 @@ private IList<SyntaxTrivia> CollectComments(IList<SyntaxTrivia> triviaList)
for (int i = triviaList.Count - 1; i >= 0; i--)
{
var trivia = triviaList[i];
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia ||
trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
if (trivia.IsRegularComment())
{
commentList.Add(trivia);
}
......@@ -1210,8 +1209,7 @@ public override string GetComment(SyntaxNode node)
var textBuilder = new StringBuilder();
foreach (var trivia in commentList)
{
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia ||
trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
if (trivia.IsRegularComment())
{
textBuilder.AppendLine(trivia.GetCommentText());
}
......
......@@ -8,11 +8,9 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.Implementation.F1Help;
using Roslyn.Utilities;
......@@ -77,7 +75,7 @@ public override async Task<string> GetHelpTermAsync(Document document, TextSpan
return "#region";
}
if (trivia.MatchesKind(SyntaxKind.MultiLineDocumentationCommentTrivia, SyntaxKind.SingleLineDocumentationCommentTrivia, SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia))
if (trivia.IsRegularOrDocComment())
{
// just find the first "word" that intersects with our position
var text = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false);
......
......@@ -121,7 +121,7 @@ private void ClassifyToken(SyntaxToken token)
private void ClassifyTrivia(SyntaxTrivia trivia)
{
if (trivia.Kind() == SyntaxKind.SingleLineCommentTrivia || trivia.Kind() == SyntaxKind.MultiLineCommentTrivia)
if (trivia.IsRegularComment())
{
AddClassification(trivia, ClassificationTypeNames.Comment);
}
......
......@@ -175,9 +175,10 @@ static SyntaxNodeExtensions()
var endOfLine = Match(SyntaxKind.EndOfLineTrivia, "\\n");
var singleBlankLine = Matcher.Sequence(whitespace, endOfLine);
var shebangComment = Match(SyntaxKind.ShebangTrivia, "#!");
var singleLineComment = Match(SyntaxKind.SingleLineCommentTrivia, "//");
var multiLineComment = Match(SyntaxKind.MultiLineCommentTrivia, "/**/");
var anyCommentMatcher = Matcher.Choice(singleLineComment, multiLineComment);
var anyCommentMatcher = Matcher.Choice(shebangComment, singleLineComment, multiLineComment);
var commentLine = Matcher.Sequence(whitespace, anyCommentMatcher, whitespace, endOfLine);
......
......@@ -348,7 +348,7 @@ public static bool IsEntirelyWithinCrefSyntax(this SyntaxTree syntaxTree, int po
trivia = trivia.GetPreviousTrivia(syntaxTree, cancellationToken);
}
if (trivia.IsSingleLineComment())
if (trivia.IsSingleLineComment() || trivia.IsShebang())
{
var span = trivia.FullSpan;
......
......@@ -32,12 +32,12 @@ public static bool MatchesKind(this SyntaxTrivia trivia, params SyntaxKind[] kin
public static bool IsRegularComment(this SyntaxTrivia trivia)
{
return trivia.IsSingleLineComment() || trivia.IsMultiLineComment();
return trivia.IsSingleLineComment() || trivia.IsMultiLineComment() || trivia.IsShebang();
}
public static bool IsRegularOrDocComment(this SyntaxTrivia trivia)
{
return trivia.IsSingleLineComment() || trivia.IsMultiLineComment() || trivia.IsDocComment();
return trivia.IsRegularComment() || trivia.IsDocComment();
}
public static bool IsSingleLineComment(this SyntaxTrivia trivia)
......@@ -50,6 +50,11 @@ public static bool IsMultiLineComment(this SyntaxTrivia trivia)
return trivia.Kind() == SyntaxKind.MultiLineCommentTrivia;
}
public static bool IsShebang(this SyntaxTrivia trivia)
{
return trivia.Kind() == SyntaxKind.ShebangTrivia;
}
public static bool IsCompleteMultiLineComment(this SyntaxTrivia trivia)
{
if (trivia.Kind() != SyntaxKind.MultiLineCommentTrivia)
......
......@@ -2,10 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Extensions
......@@ -35,7 +31,7 @@ public static bool Any(this SyntaxTriviaList triviaList, params SyntaxKind[] kin
public static SyntaxTrivia? GetLastComment(this SyntaxTriviaList triviaList)
{
return triviaList
.Where(t => t.MatchesKind(SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia))
.Where(t => t.IsRegularComment())
.LastOrNullable();
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Shared.Utilities
{
......@@ -9,25 +8,30 @@ internal partial class Matcher<T>
{
private class ChoiceMatcher : Matcher<T>
{
private readonly Matcher<T> _matcher1;
private readonly Matcher<T> _matcher2;
private readonly IEnumerable<Matcher<T>> _matchers;
public ChoiceMatcher(Matcher<T> matcher1, Matcher<T> matcher2)
public ChoiceMatcher(params Matcher<T>[] matchers)
{
_matcher1 = matcher1;
_matcher2 = matcher2;
_matchers = matchers;
}
public override bool TryMatch(IList<T> sequence, ref int index)
{
return
_matcher1.TryMatch(sequence, ref index) ||
_matcher2.TryMatch(sequence, ref index);
// we can't use .Any() here because ref parameters can't be used in lambdas
foreach (var matcher in _matchers)
{
if (matcher.TryMatch(sequence, ref index))
{
return true;
}
}
return false;
}
public override string ToString()
{
return string.Format("({0}|{1})", _matcher1, _matcher2);
return $"({string.Join("|", _matchers)})";
}
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Shared.Utilities
{
......@@ -24,11 +23,11 @@ public static Matcher<T> OneOrMore<T>(Matcher<T> matcher)
}
/// <summary>
/// Matcher equivalent to (m_1|m_2)
/// Matcher equivalent to (m_1|m_2|...|m_n)
/// </summary>
public static Matcher<T> Choice<T>(Matcher<T> matcher1, Matcher<T> matcher2)
public static Matcher<T> Choice<T>(params Matcher<T>[] matchers)
{
return Matcher<T>.Choice(matcher1, matcher2);
return Matcher<T>.Choice(matchers);
}
/// <summary>
......
......@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Shared.Utilities
{
......@@ -29,9 +28,9 @@ internal static Matcher<T> OneOrMore(Matcher<T> matcher)
return Sequence(matcher, Repeat(matcher));
}
internal static Matcher<T> Choice(Matcher<T> matcher1, Matcher<T> matcher2)
internal static Matcher<T> Choice(params Matcher<T>[] matchers)
{
return new ChoiceMatcher(matcher1, matcher2);
return new ChoiceMatcher(matchers);
}
internal static Matcher<T> Sequence(params Matcher<T>[] matchers)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册