diff options
author | Mike Krüger <mikkrg@microsoft.com> | 2018-04-17 16:00:07 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-17 16:00:07 +0300 |
commit | 345cfd1a81eb0bb1536e5b67e1bbade2fd2f78ef (patch) | |
tree | bbf1544cee46f964c1deddb386a4c1db2ac31c1c /main | |
parent | 680ba9d12caf2d4bfe0d331ba80f4333124016ed (diff) | |
parent | 5730bffe5c619077afbec5cb245591b9d8fe891d (diff) |
Merge pull request #4586 from mono/master-roslynfolding
[Ide] Switched to roslyn folding infrastructure.
Diffstat (limited to 'main')
4 files changed, 144 insertions, 174 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs index faff647bc7..4787f4e22d 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpParsedDocument.cs @@ -306,181 +306,9 @@ namespace MonoDevelop.CSharp.Parser } } - IReadOnlyList<FoldingRegion> foldings; - SemaphoreSlim foldingsSemaphore = new SemaphoreSlim (1, 1); + static readonly Task<IReadOnlyList<FoldingRegion>> foldings = Task.FromResult((IReadOnlyList<FoldingRegion>)new FoldingRegion[0]); - public override Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default(CancellationToken)) - { - if (foldings == null) { - return Task.Run (async delegate { - bool locked = false; - try { - locked = await foldingsSemaphore.WaitAsync (Timeout.Infinite, cancellationToken); - if (foldings == null) - foldings = (await GenerateFoldings (cancellationToken)).ToList (); - } catch (OperationCanceledException) { - return new List<FoldingRegion> (); - } finally { - if (locked) - foldingsSemaphore.Release (); - } - return foldings; - }); - } - - return Task.FromResult (foldings); - } - - async Task<IEnumerable<FoldingRegion>> GenerateFoldings (CancellationToken cancellationToken) - { - return GenerateFoldingsInternal (await GetCommentsAsync (cancellationToken), cancellationToken); - } - - IEnumerable<FoldingRegion> GenerateFoldingsInternal (IReadOnlyList<Comment> comments, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - yield break; - - foreach (var fold in comments.ToFolds ()) - yield return fold;
-
- if (cancellationToken.IsCancellationRequested) - yield break; - - var visitor = new FoldingVisitor (cancellationToken); - if (Unit != null) { - try { - visitor.Visit (Unit.GetRoot (cancellationToken)); - } catch (Exception) { } - } - - if (cancellationToken.IsCancellationRequested) - yield break; - 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) : base(SyntaxWalkerDepth.Trivia) - { - this.cancellationToken = cancellationToken; - } - - void AddUsings (SyntaxNode parent) - { - SyntaxNode firstChild = null, lastChild = null; - foreach (var child in parent.ChildNodes ()) {
- cancellationToken.ThrowIfCancellationRequested (); - 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) - { - cancellationToken.ThrowIfCancellationRequested (); - AddUsings (node); - base.VisitCompilationUnit (node); - } - - void AddFolding (SyntaxToken openBrace, SyntaxToken closeBrace, FoldType type) - { - cancellationToken.ThrowIfCancellationRequested (); - 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), type)); - } catch (ArgumentOutOfRangeException) {} - } - - Stack<SyntaxTrivia> regionStack = new Stack<SyntaxTrivia> (); - public override void VisitTrivia (SyntaxTrivia trivia) - { - cancellationToken.ThrowIfCancellationRequested (); - base.VisitTrivia (trivia); - if (trivia.IsKind (SyntaxKind.RegionDirectiveTrivia)) { - regionStack.Push (trivia); - } else if (trivia.IsKind (SyntaxKind.EndRegionDirectiveTrivia)) { - if (regionStack.Count == 0) - return; - var regionStart = regionStack.Pop (); - try { - var first = regionStart.GetLocation ().GetLineSpan (); - var last = trivia.GetLocation ().GetLineSpan (); - var v = regionStart.ToString (); - v = v.Substring ("#region".Length).Trim (); - if (v.Length == 0) - v = "..."; - Foldings.Add (new FoldingRegion(v, new DocumentRegion(first.StartLinePosition, last.EndLinePosition), FoldType.UserRegion, true)); - } catch (ArgumentOutOfRangeException) { } - } - } - - public override void VisitNamespaceDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax node) - { - cancellationToken.ThrowIfCancellationRequested (); - AddUsings (node); - AddFolding (node.OpenBraceToken, node.CloseBraceToken, FoldType.Undefined); - base.VisitNamespaceDeclaration (node); - } - - public override void VisitClassDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax node) - { - cancellationToken.ThrowIfCancellationRequested (); - AddFolding (node.OpenBraceToken, node.CloseBraceToken, FoldType.Type); - base.VisitClassDeclaration (node); - } - - public override void VisitStructDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.StructDeclarationSyntax node) - { - cancellationToken.ThrowIfCancellationRequested (); - AddFolding (node.OpenBraceToken, node.CloseBraceToken, FoldType.Type); - base.VisitStructDeclaration (node); - } - - public override void VisitInterfaceDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.InterfaceDeclarationSyntax node) - { - cancellationToken.ThrowIfCancellationRequested (); - AddFolding (node.OpenBraceToken, node.CloseBraceToken, FoldType.Type); - base.VisitInterfaceDeclaration (node); - } - - public override void VisitEnumDeclaration (Microsoft.CodeAnalysis.CSharp.Syntax.EnumDeclarationSyntax node) - { - cancellationToken.ThrowIfCancellationRequested (); - AddFolding (node.OpenBraceToken, node.CloseBraceToken, FoldType.Type); - base.VisitEnumDeclaration (node); - } - - public override void VisitBlock (Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax node) - { - cancellationToken.ThrowIfCancellationRequested (); - AddFolding (node.OpenBraceToken, node.CloseBraceToken, node.Parent is MemberDeclarationSyntax ? FoldType.Member : FoldType.Undefined); - base.VisitBlock (node); - } - } + public override Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default (CancellationToken)) => foldings; SemaphoreSlim errorLock = new SemaphoreSlim (1, 1); diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml index e881821723..3424347b8d 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml @@ -318,6 +318,7 @@ <Class class = "MonoDevelop.Ide.Editor.Extension.BraceMatcherTextEditorExtension" /> <Class class = "MonoDevelop.Ide.Editor.Extension.DefaultCommandTextEditorExtension" /> <Class class = "MonoDevelop.Ide.Editor.Extension.FoldingTextEditorExtension" /> + <Class class = "MonoDevelop.Ide.Editor.Extension.BlockStructureFoldingTextEditorExtension" /> <Class class = "MonoDevelop.Ide.Editor.Extension.ErrorHandlerTextEditorExtension" /> <Class class = "MonoDevelop.Ide.Editor.Extension.AutoInsertBracketTextEditorExtension" /> <Class class = "MonoDevelop.Ide.Editor.Extension.HighlightUrlExtension" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/BlockStructureFoldingTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/BlockStructureFoldingTextEditorExtension.cs new file mode 100644 index 0000000000..6727ea5c80 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/BlockStructureFoldingTextEditorExtension.cs @@ -0,0 +1,140 @@ +// +// BlockStructureFoldingTextEditorExtension.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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 System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Gtk; +using MonoDevelop.Core; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.Editor.Highlighting; +using MonoDevelop.Ide.TypeSystem; +using Microsoft.CodeAnalysis.Structure; +using System.Collections.Immutable; + +namespace MonoDevelop.Ide.Editor.Extension +{ + class BlockStructureFoldingTextEditorExtension : TextEditorExtension + { + CancellationTokenSource src = new CancellationTokenSource (); + bool isDisposed; + + protected override void Initialize () + { + DocumentContext.DocumentParsed += DocumentContext_DocumentParsed; + } + + public override void Dispose () + { + if (isDisposed) + return; + isDisposed = true; + CancelDocumentParsedUpdate (); + DocumentContext.DocumentParsed -= DocumentContext_DocumentParsed; + base.Dispose (); + } + + void CancelDocumentParsedUpdate () + { + src.Cancel (); + src = new CancellationTokenSource (); + } + + async void DocumentContext_DocumentParsed (object sender, EventArgs e) + { + CancelDocumentParsedUpdate (); + var analysisDocument = DocumentContext.AnalysisDocument; + if (analysisDocument == null || !Editor.Options.ShowFoldMargin) + return; + var caretLocation = Editor.CaretOffset; + + var outliningService = BlockStructureService.GetService (analysisDocument); + if (outliningService == null) + return; + var token = src.Token; + var blockStructure = await outliningService.GetBlockStructureAsync (analysisDocument, token).ConfigureAwait (false); + UpdateFoldings (Editor, blockStructure.Spans, caretLocation, false, token); + } + + static void UpdateFoldings (TextEditor Editor, ImmutableArray<BlockSpan> spans, int caretOffset, bool firstTime = false, CancellationToken token = default (CancellationToken)) + { + try { + var foldSegments = new List<IFoldSegment> (); + + foreach (var blockSpan in spans) { + if (token.IsCancellationRequested) + return; + if (!blockSpan.IsCollapsible) + continue; + var type = FoldingType.Unknown; + switch (blockSpan.Type) { + case BlockTypes.Member: + type = FoldingType.TypeMember; + break; + case BlockTypes.Type: + type = FoldingType.TypeDefinition; + break; + case BlockTypes.Comment: + type = FoldingType.Comment; + break; + default: + type = FoldingType.Unknown; + break; + } + var start = blockSpan.TextSpan.Start; + var end = blockSpan.TextSpan.End; + var marker = Editor.CreateFoldSegment (start, end - start); + if (marker == null) + continue; + foldSegments.Add (marker); + marker.CollapsedText = blockSpan.BannerText; + marker.FoldingType = type; + //and, if necessary, set its fold state + if (firstTime) { + // only fold on document open, later added folds are NOT folded by default. + marker.IsCollapsed = blockSpan.IsDefaultCollapsed; + continue; + } + if (blockSpan.TextSpan.Contains (caretOffset)) + marker.IsCollapsed = false; + } + if (firstTime) { + Editor.SetFoldings (foldSegments); + } else { + Application.Invoke ((o, args) => { + if (!token.IsCancellationRequested) + Editor.SetFoldings (foldSegments); + }); + } + } catch (OperationCanceledException) { + } catch (Exception ex) { + LoggingService.LogError ("Unhandled exception in ParseInformationUpdaterWorkerThread", ex); + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index b21db9d955..750687104d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -9646,6 +9646,7 @@ <Compile Include="Gui\MonoDevelop.Ide.Projects.SelectReferenceDialog.cs" /> <Compile Include="Gui\MonoDevelop.Ide.SelectEncodingsDialog.cs" /> <Compile Include="Gui\MonoDevelop.Ide.StandardHeader.StandardHeaderPolicyPanelWidget.cs" /> + <Compile Include="MonoDevelop.Ide.Editor.Extension\BlockStructureFoldingTextEditorExtension.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> |