Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs')
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs444
1 files changed, 444 insertions, 0 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs
new file mode 100644
index 0000000000..db4c99f3c8
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs
@@ -0,0 +1,444 @@
+//
+// CSharpParsedDocument.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using MonoDevelop.Ide.TypeSystem;
+using Microsoft.CodeAnalysis;
+using System.Collections.Generic;
+using System.Linq;
+using MonoDevelop.Ide.Editor;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CSharp.Parser
+{
+ class CSharpParsedDocument : ParsedDocument
+ {
+ internal SyntaxTree Unit {
+ get;
+ set;
+ }
+
+ public CSharpParsedDocument (string fileName) : base (fileName)
+ {
+ }
+
+
+ #region implemented abstract members of ParsedDocument
+
+
+ WeakReference<IReadOnlyList<Comment>> weakComments;
+
+ public override Task<IReadOnlyList<Comment>> GetCommentsAsync (CancellationToken cancellationToken = default(CancellationToken))
+ {
+ IReadOnlyList<Comment> result;
+ if (weakComments == null || !weakComments.TryGetTarget (out result)) {
+ var visitor = new CommentVisitor (cancellationToken);
+ if (Unit != null)
+ visitor.Visit (Unit.GetRoot (cancellationToken));
+ result = visitor.Comments;
+
+ var newRef = new WeakReference<IReadOnlyList<Comment>> (result);
+ var oldRef = weakComments;
+ while (Interlocked.CompareExchange (ref weakComments, newRef, oldRef) == oldRef) {
+ }
+ }
+ return Task.FromResult (result);
+ }
+
+
+ class CommentVisitor : CSharpSyntaxWalker
+ {
+ public readonly List<Comment> Comments = new List<Comment> ();
+
+ CancellationToken cancellationToken;
+
+ public CommentVisitor (CancellationToken cancellationToken) : base(SyntaxWalkerDepth.Trivia)
+ {
+ this.cancellationToken = cancellationToken;
+ }
+
+ static DocumentRegion GetRegion (SyntaxTrivia trivia)
+ {
+ var fullSpan = trivia.FullSpan;
+ var text = trivia.ToString ();
+ if (text.Length > 2) {
+ if (text [text.Length - 2] == '\r' && text [text.Length - 1] == '\n')
+ fullSpan = new Microsoft.CodeAnalysis.Text.TextSpan (fullSpan.Start, fullSpan.Length - 2);
+ else if (ICSharpCode.NRefactory6.NewLine.IsNewLine (text [text.Length - 1]))
+ fullSpan = new Microsoft.CodeAnalysis.Text.TextSpan (fullSpan.Start, fullSpan.Length - 1);
+ }
+ try {
+ var lineSpan = trivia.SyntaxTree.GetLineSpan (fullSpan);
+ return (DocumentRegion)lineSpan;
+ } catch (Exception) {
+ return DocumentRegion.Empty;
+ }
+ }
+
+ public override void VisitBlock (BlockSyntax node)
+ {
+ cancellationToken.ThrowIfCancellationRequested ();
+ base.VisitBlock (node);
+ }
+
+ bool StartsLine (SyntaxTrivia trivia)
+ {
+ var sourceText = trivia.SyntaxTree.GetText (cancellationToken);
+ Microsoft.CodeAnalysis.Text.TextLine textLine;
+ try {
+ textLine = sourceText.Lines.GetLineFromPosition (trivia.SpanStart);
+ } catch (ArgumentOutOfRangeException) {
+ return false;
+ }
+ //We need start of trivia.FullSpan and not trivia.SpanStart
+ //because in case of documentation /// <summary...
+ //trivia.SpanStart is space after /// and not 1st /
+ //so with trivia.FullSpan.Start we get index of 1st /
+ var startSpan = trivia.FullSpan.Start;
+ for (int i = textLine.Start; i < startSpan; i++) {
+ char ch = sourceText [i];
+ if (!char.IsWhiteSpace (ch))
+ return false;
+ }
+ return true;
+ }
+
+ static string CropStart (string text, string crop)
+ {
+ text = text.Trim ();
+ if (text.StartsWith (crop))
+ return text.Substring (crop.Length).TrimStart ();
+ return text;
+ }
+
+ public override void VisitTrivia (SyntaxTrivia trivia)
+ {
+ base.VisitTrivia (trivia);
+ switch (trivia.Kind ()) {
+ case SyntaxKind.MultiLineCommentTrivia:
+ case SyntaxKind.MultiLineDocumentationCommentTrivia:
+ {
+ var cmt = new Comment (CropStart (trivia.ToString (), "/*"));
+ cmt.CommentStartsLine = StartsLine(trivia);
+ cmt.CommentType = CommentType.Block;
+ cmt.OpenTag = "/*";
+ cmt.ClosingTag = "*/";
+ cmt.Region = GetRegion (trivia);
+ Comments.Add (cmt);
+ break;
+ }
+ case SyntaxKind.SingleLineCommentTrivia:
+ {
+ var cmt = new Comment (CropStart (trivia.ToString (), "//"));
+ cmt.CommentStartsLine = StartsLine(trivia);
+ cmt.CommentType = CommentType.SingleLine;
+ cmt.OpenTag = "//";
+ cmt.Region = GetRegion (trivia);
+ Comments.Add (cmt);
+ break;
+ }
+ case SyntaxKind.SingleLineDocumentationCommentTrivia:
+ {
+ var cmt = new Comment (CropStart (trivia.ToString (), "///"));
+ cmt.CommentStartsLine = StartsLine(trivia);
+ cmt.IsDocumentation = true;
+ cmt.CommentType = CommentType.Documentation;
+ cmt.OpenTag = "///";
+ cmt.ClosingTag = "*/";
+ cmt.Region = GetRegion (trivia);
+ Comments.Add (cmt);
+ break;
+ }
+
+ }
+
+ }
+ }
+
+ WeakReference<IReadOnlyList<Tag>> weakTags;
+
+ public override Task<IReadOnlyList<Tag>> GetTagCommentsAsync (CancellationToken cancellationToken = default(CancellationToken))
+ {
+ IReadOnlyList<Tag> result;
+ if (weakTags == null || !weakTags.TryGetTarget (out result)) {
+ var visitor = new SemanticTagVisitor (cancellationToken);
+ if (Unit != null) {
+ try {
+ visitor.Visit (Unit.GetRoot (cancellationToken));
+ } catch {
+ }
+ }
+ result = visitor.Tags;
+
+ var newRef = new WeakReference<IReadOnlyList<Tag>> (result);
+ var oldRef = weakTags;
+ while (Interlocked.CompareExchange (ref weakTags, newRef, oldRef) == oldRef) {
+ }
+ }
+ return Task.FromResult (result);
+ }
+
+ sealed class SemanticTagVisitor : CSharpSyntaxWalker
+ {
+ string[] tagComments;
+ public List<Tag> Tags = new List<Tag> ();
+ CancellationToken cancellationToken;
+
+ public SemanticTagVisitor () : base (SyntaxWalkerDepth.Trivia)
+ {
+ tagComments = MonoDevelop.Ide.Tasks.CommentTag.SpecialCommentTags.Select (t => t.Tag).ToArray ();
+
+ }
+
+ public SemanticTagVisitor (CancellationToken cancellationToken)
+ {
+ this.cancellationToken = cancellationToken;
+ }
+
+ public override void VisitBlock (BlockSyntax node)
+ {
+ cancellationToken.ThrowIfCancellationRequested ();
+ base.VisitBlock (node);
+ }
+
+ public override void VisitTrivia (SyntaxTrivia trivia)
+ {
+ cancellationToken.ThrowIfCancellationRequested ();
+ if (trivia.IsKind (SyntaxKind.SingleLineCommentTrivia) ||
+ trivia.IsKind (SyntaxKind.MultiLineCommentTrivia) ||
+ trivia.IsKind (SyntaxKind.SingleLineDocumentationCommentTrivia)) {
+ foreach (string tag in tagComments) {
+ var trimmedContent = trivia.ToString ().TrimStart ('/', ' ', '*');
+ if (!trimmedContent.StartsWith (tag))
+ continue;
+ var loc = trivia.GetLocation ().GetLineSpan ();
+ Tags.Add (new Tag (tag, trimmedContent, new DocumentRegion (loc.StartLinePosition, loc.EndLinePosition)));
+ break;
+ }
+ }
+ }
+
+ public override void VisitThrowStatement (Microsoft.CodeAnalysis.CSharp.Syntax.ThrowStatementSyntax node)
+ {
+ cancellationToken.ThrowIfCancellationRequested ();
+ base.VisitThrowStatement (node);
+ var createExpression = node.Expression as ObjectCreationExpressionSyntax;
+ if (createExpression == null)
+ return;
+ var st = createExpression.Type.ToString ();
+ if (st == "NotImplementedException" || st == "System.NotImplementedException") {
+ var loc = node.GetLocation ().GetLineSpan ();
+ if (createExpression.ArgumentList.Arguments.Count > 0) {
+ Tags.Add (new Tag ("High", GettextCatalog.GetString ("NotImplementedException({0}) thrown.", createExpression.ArgumentList.Arguments.First ().ToString ()), new DocumentRegion (loc.StartLinePosition, loc.EndLinePosition)));
+ } else {
+ Tags.Add (new Tag ("High", GettextCatalog.GetString ("NotImplementedException thrown."), new DocumentRegion (loc.StartLinePosition, loc.EndLinePosition)));
+ }
+ }
+ }
+ }
+
+ WeakReference<IReadOnlyList<FoldingRegion>> weakFoldings;
+
+ public override Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default(CancellationToken))
+ {
+ IReadOnlyList<FoldingRegion> result;
+ if (weakFoldings == null || !weakFoldings.TryGetTarget (out result)) {
+
+ result = GenerateFoldings (cancellationToken).ToList ();
+
+ var newRef = new WeakReference<IReadOnlyList<FoldingRegion>> (result);
+ var oldRef = weakFoldings;
+ while (Interlocked.CompareExchange (ref weakFoldings, newRef, oldRef) == oldRef) {
+ }
+ }
+
+ return Task.FromResult (result);
+ }
+
+ IEnumerable<FoldingRegion> GenerateFoldings (CancellationToken cancellationToken)
+ {
+ foreach (var fold in GetCommentsAsync().Result.ToFolds ())
+ yield return fold;
+
+ var visitor = new FoldingVisitor (cancellationToken);
+ if (Unit != null) {
+ try {
+ visitor.Visit (Unit.GetRoot (cancellationToken));
+ } catch (Exception) { }
+ }
+ foreach (var fold in visitor.Foldings)
+ yield return fold;
+ }
+
+ class FoldingVisitor : CSharpSyntaxWalker
+ {
+ public readonly List<FoldingRegion> Foldings = new List<FoldingRegion> ();
+ CancellationToken cancellationToken;
+
+ public FoldingVisitor (CancellationToken cancellationToken)
+ {
+ this.cancellationToken = cancellationToken;
+ }
+
+ void AddUsings (SyntaxNode parent)
+ {
+ SyntaxNode firstChild = null, lastChild = null;
+ foreach (var child in parent.ChildNodes ()) {
+ if (child is UsingDirectiveSyntax) {
+ if (firstChild == null) {
+ firstChild = child;
+ }
+ lastChild = child;
+ continue;
+ }
+ if (firstChild != null)
+ break;
+ }
+
+ if (firstChild != null && firstChild != lastChild) {
+ var first = firstChild.GetLocation ().GetLineSpan ();
+ var last = lastChild.GetLocation ().GetLineSpan ();
+
+ Foldings.Add (new FoldingRegion (new DocumentRegion (first.StartLinePosition, last.EndLinePosition), FoldType.Undefined));
+ }
+ }
+
+ public override void VisitCompilationUnit (Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax node)
+ {
+ AddUsings (node);
+ base.VisitCompilationUnit (node);
+ }
+
+ void AddFolding (SyntaxToken openBrace, SyntaxToken closeBrace)
+ {
+ openBrace = openBrace.GetPreviousToken (false, false, true, true);
+
+ try {
+ var first = openBrace.GetLocation ().GetLineSpan ();
+ var last = closeBrace.GetLocation ().GetLineSpan ();
+
+ if (first.EndLinePosition.Line != last.EndLinePosition.Line)
+ Foldings.Add (new FoldingRegion (new DocumentRegion (first.EndLinePosition, last.EndLinePosition), FoldType.Undefined));
+ } catch (ArgumentOutOfRangeException) {}
+ }
+
+ public override void VisitNamespaceDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax node)
+ {
+ AddUsings (node);
+ AddFolding (node.OpenBraceToken, node.CloseBraceToken);
+ base.VisitNamespaceDeclaration (node);
+ }
+
+ public override void VisitClassDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax node)
+ {
+ AddFolding (node.OpenBraceToken, node.CloseBraceToken);
+ base.VisitClassDeclaration (node);
+ }
+
+ public override void VisitStructDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.StructDeclarationSyntax node)
+ {
+ AddFolding (node.OpenBraceToken, node.CloseBraceToken);
+ base.VisitStructDeclaration (node);
+ }
+
+ public override void VisitInterfaceDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.InterfaceDeclarationSyntax node)
+ {
+ AddFolding (node.OpenBraceToken, node.CloseBraceToken);
+ base.VisitInterfaceDeclaration (node);
+ }
+
+ public override void VisitEnumDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.EnumDeclarationSyntax node)
+ {
+ AddFolding (node.OpenBraceToken, node.CloseBraceToken);
+ base.VisitEnumDeclaration (node);
+ }
+
+ public override void VisitBlock (Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax node)
+ {
+ cancellationToken.ThrowIfCancellationRequested ();
+ AddFolding (node.OpenBraceToken, node.CloseBraceToken);
+ base.VisitBlock (node);
+ }
+ }
+
+ static readonly IReadOnlyList<Error> emptyErrors = new Error[0];
+ WeakReference<IReadOnlyList<Error>> weakErrors;
+
+ public override Task<IReadOnlyList<Error>> GetErrorsAsync (CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var model = GetAst<SemanticModel> ();
+ if (model == null)
+ return Task.FromResult (emptyErrors);
+
+ IReadOnlyList<Error> result;
+ if (weakErrors == null || !weakErrors.TryGetTarget (out result)) {
+ try {
+ result = model
+ .GetDiagnostics (null, cancellationToken)
+ .Where (diag => diag.Severity == DiagnosticSeverity.Error || diag.Severity == DiagnosticSeverity.Warning)
+ .Select ((Diagnostic diag) => new Error (GetErrorType (diag.Severity), diag.Id, diag.GetMessage (), GetRegion (diag)) { Tag = diag })
+ .ToList ();
+ var newRef = new WeakReference<IReadOnlyList<Error>> (result);
+ var oldRef = weakErrors;
+ while (Interlocked.CompareExchange (ref weakErrors, newRef, oldRef) == oldRef) {
+ }
+ } catch (OperationCanceledException) {
+ return Task.FromResult (emptyErrors);
+ } catch (Exception e) {
+ LoggingService.LogError ("Error while getting diagnostics.", e);
+ return Task.FromResult (emptyErrors);
+ }
+ }
+ return Task.FromResult (result);
+ }
+
+ static DocumentRegion GetRegion (Diagnostic diagnostic)
+ {
+ try {
+ var lineSpan = diagnostic.Location.GetLineSpan ();
+ return new DocumentRegion (lineSpan.StartLinePosition, lineSpan.EndLinePosition);
+ } catch (Exception) {
+ return DocumentRegion.Empty;
+ }
+ }
+
+ static ErrorType GetErrorType (DiagnosticSeverity severity)
+ {
+ switch (severity) {
+ case DiagnosticSeverity.Error:
+ return ErrorType.Error;
+ case DiagnosticSeverity.Warning:
+ return ErrorType.Warning;
+ }
+ return ErrorType.Unknown;
+ }
+
+ #endregion
+ }
+} \ No newline at end of file