diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 796db935dcd17c72849d5b963dce2be71cfe670d..f8d42ccc324a11a8c0299aec103087e89a32a5dc 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -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.VisitLoadDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax node) -> TResult \ No newline at end of file +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLoadDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LoadDirectiveTriviaSyntax node) -> TResult diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/Syntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/Syntax.cs index 1a6fa81f0016627b55ec1e2f4a045c380cb292fb..d942a8d44db5cc7b94d47b56d088ea486293626a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/Syntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/Syntax.cs @@ -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 { diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxNode.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxNode.cs index d50b2bb0a17c1644d6752d4397c5fe07c8e86b7d..ccb0db572a859043b9f1f30019ac18aa3cb49ce3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxNode.cs +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/SyntaxNode.cs @@ -1,10 +1,8 @@ // 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(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 diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index acc214ac2e0fead7417aafdb714abb5ba36c0cdc..0b87e7886ba21ae6415cbfaabf80fa4113750b3a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -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) } /// - /// 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. /// /// - /// A representing the specific kind of SyntaxTrivia. One of - /// WhitespaceTrivia, EndOfLineTrivia, CommentTrivia, - /// DocumentationCommentExteriorTrivia, DisabledTextTrivia. + /// A representing the specific kind of . One of + /// , , + /// , , + /// , , + /// /// /// /// 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); diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index a957075282dce1bc198074b337e18a965079fff0..3d7446ab45ffd32087353b7d0cd67d6d2a86b75b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -530,7 +530,7 @@ public enum SyntaxKind : ushort InterpolationAlignmentClause = 8920, InterpolationFormatClause = 8921, - ShebangCommentTrivia = 8922, + ShebangTrivia = 8922, LoadDirectiveTrivia = 8923, } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 0bdc3e4618c5ca4cb9752bd440ccfcadcae56247..7734f336d0c8edeb12fc7186351f94631fea2bf6 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -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: diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs index 058d8b7a01b67ef5ce07d484e71c32f1bc10ed46..75975fc7f199ac9fbff163e0d5610ee0dc9255cd 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs @@ -110,10 +110,16 @@ private void AddEndOfLine() } } + /// + /// Returns whether the specified token is also the end of the line. This will + /// be true for , , + /// , and all preprocessor directives. + /// private static bool IsEndOfLine(SyntaxTrivia trivia) { return trivia.Kind() == SyntaxKind.EndOfLineTrivia || trivia.Kind() == SyntaxKind.SingleLineCommentTrivia + || trivia.Kind() == SyntaxKind.ShebangTrivia || trivia.IsDirective; } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs index 2aea441c422fe561c1079b53bc5b7b8c9c48447d..642b58ba0f27bd41cb3b90109c341e5548461668 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs @@ -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; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/InteractiveParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/InteractiveParsingTests.cs index c42192d9892fc0a4176580921d1e55ac84b6f282..2d161453094f8c81010bc04ee52a1ed07d94aa3b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/InteractiveParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/InteractiveParsingTests.cs @@ -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()); } diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index 8d5218f4f3ba711239152fe4dd1877051c7cb90c..79d820e911637aee9553da8333cb31ab9e9dd4a9 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -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() { diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 95cb6981b1c7eee694c69148232f81fdf6126454..93087aad62122989ac1dafd525ea89d5b65479e0 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -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); + } } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index 8599812121977adeb37b236fabe391ae196b9cd4..a6b533d455212a566c2c803da6f2bb2894893574 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -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) diff --git a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs index de02b5c68a741318fb4f77dc34c5d5a1799e98b5..7497cfce96d190cfe94a5986fa50d5b366658c18 100644 --- a/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs +++ b/src/VisualStudio/CSharp/Impl/CodeModel/CSharpCodeModelService.cs @@ -1180,8 +1180,7 @@ private IList CollectComments(IList 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()); } diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index d1014e3ccaf46d4c2a5b80cdfac836023286838c..bb9d436cf8263083f693bca948f71a2f56130ec6 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -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 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); diff --git a/src/Workspaces/CSharp/Portable/Classification/Worker.cs b/src/Workspaces/CSharp/Portable/Classification/Worker.cs index 8963d9608182f138948c11e7acee48d0187428e9..73d7145dbfadb030b273c2be76a2cdabab65029a 100644 --- a/src/Workspaces/CSharp/Portable/Classification/Worker.cs +++ b/src/Workspaces/CSharp/Portable/Classification/Worker.cs @@ -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); } diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs index 7b162f4ae91a303e093b85d748bbbf3aa730bed0..141e6755d338f31b13c4e2e8f0228028d7ef14d8 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs @@ -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); diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs index 77a797163a55328a0976f204a4d4d79111a96574..cc853db79a2de0e2d17e3ef155d3fcdeff39825f 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs @@ -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; diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs index 651ef1bbb26de5c4b9c9278440f83bb727da73e7..4e8df34e8f6b5fa1f3d3921ddc1bd9a7e1536b4f 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaExtensions.cs @@ -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) diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaListExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaListExtensions.cs index 58da170d4b3a8aab749ac356da729c05d7539307..ceed34646e09842ae8c63b3fce30e36f72a573d3 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaListExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTriviaListExtensions.cs @@ -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(); } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.ChoiceMatcher.cs b/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.ChoiceMatcher.cs index 6acdfa69bb87278e03f063ddb9d81a4eee60bdb2..e4a4ca1d4162c5ee388884689f30cbf1180eafa6 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.ChoiceMatcher.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.ChoiceMatcher.cs @@ -1,7 +1,6 @@ // 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 { private class ChoiceMatcher : Matcher { - private readonly Matcher _matcher1; - private readonly Matcher _matcher2; + private readonly IEnumerable> _matchers; - public ChoiceMatcher(Matcher matcher1, Matcher matcher2) + public ChoiceMatcher(params Matcher[] matchers) { - _matcher1 = matcher1; - _matcher2 = matcher2; + _matchers = matchers; } public override bool TryMatch(IList 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)})"; } } } diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.cs b/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.cs index 97c357c9dee87d7bf8dcb1f5f1dd59caadbf620e..8e7797a0029740fe0892340b1afe83907e382e62 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/Matcher.cs @@ -1,7 +1,6 @@ // 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 OneOrMore(Matcher matcher) } /// - /// Matcher equivalent to (m_1|m_2) + /// Matcher equivalent to (m_1|m_2|...|m_n) /// - public static Matcher Choice(Matcher matcher1, Matcher matcher2) + public static Matcher Choice(params Matcher[] matchers) { - return Matcher.Choice(matcher1, matcher2); + return Matcher.Choice(matchers); } /// diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/Matcher`1.cs b/src/Workspaces/Core/Portable/Shared/Utilities/Matcher`1.cs index 40fb99fb3bd7aaf8c76b91f31e96eaaedceeb17b..ce7662bd94fadcf0e7da4c3d408b1edfa21fa714 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/Matcher`1.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/Matcher`1.cs @@ -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 OneOrMore(Matcher matcher) return Sequence(matcher, Repeat(matcher)); } - internal static Matcher Choice(Matcher matcher1, Matcher matcher2) + internal static Matcher Choice(params Matcher[] matchers) { - return new ChoiceMatcher(matcher1, matcher2); + return new ChoiceMatcher(matchers); } internal static Matcher Sequence(params Matcher[] matchers)