diff options
author | Lluis Sanchez <llsan@microsoft.com> | 2019-06-07 17:24:45 +0300 |
---|---|---|
committer | Lluis Sanchez <llsan@microsoft.com> | 2019-06-07 17:24:45 +0300 |
commit | adc0c4e33b695c315488262c3c09112e87800111 (patch) | |
tree | e4c93ea740a9d3b691e14fd04878c98adc4c34de /main/src/addins | |
parent | 526ae087002e5e8aba0c5430fea9142942f85635 (diff) | |
parent | b19a7bf2d6a71057f0820de1c12b9a5faa7d9b53 (diff) |
Merge remote-tracking branch 'origin/master' into release-8.2monodevelop-8.2.0.707
Diffstat (limited to 'main/src/addins')
45 files changed, 1037 insertions, 463 deletions
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj index 2027c0db24..3766cbf2b2 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.csproj +++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj @@ -291,6 +291,7 @@ <Compile Include="MonoDevelop.CSharp.Refactoring\CommandArgsFactories.cs" /> <Compile Include="MonoDevelop.CSharp.Debugger\DebuggerCompletionController.cs" /> <Compile Include="MonoDevelop.CSharp.ClassOutline\CSharpOutlineTextEditorExtensionProvider.cs" /> + <Compile Include="MonoDevelop.CSharp.Debugger\CSharpBreakpointSpanResolver.cs" /> </ItemGroup> <ItemGroup> <None Include="MonoDevelop.CSharp.CodeRefactorings\ConvertToEnum\ConvertToEnumCodeRefactoringProvider.cs" /> diff --git a/main/src/addins/CSharpBinding/Gui/MonoDevelop.CSharp.Project.CompilerOptionsPanelWidget.cs b/main/src/addins/CSharpBinding/Gui/MonoDevelop.CSharp.Project.CompilerOptionsPanelWidget.cs index 232893c387..15aaf60a49 100644 --- a/main/src/addins/CSharpBinding/Gui/MonoDevelop.CSharp.Project.CompilerOptionsPanelWidget.cs +++ b/main/src/addins/CSharpBinding/Gui/MonoDevelop.CSharp.Project.CompilerOptionsPanelWidget.cs @@ -1,5 +1,7 @@ // This file has been generated by the GUI designer. Do not modify. +using MonoDevelop.Ide.Gui; + namespace MonoDevelop.CSharp.Project { partial class CompilerOptionsPanelWidget @@ -47,6 +49,8 @@ namespace MonoDevelop.CSharp.Project private global::Gtk.VBox vbox3; private global::Gtk.Table table2; + + private MonoDevelop.Components.ImageView langVersionWarningIcon; private global::Gtk.Label label2; @@ -254,14 +258,21 @@ namespace MonoDevelop.CSharp.Project this.table2.RowSpacing = ((uint)(6)); this.table2.ColumnSpacing = ((uint)(6)); // Container child table2.Gtk.Table+TableChild + this.langVersionWarningIcon = new MonoDevelop.Components.ImageView (Stock.Warning, Gtk.IconSize.Menu); + this.langVersionWarningIcon.Visible = false; + var langVersionHbox = new global::Gtk.HBox (false, 0); + langVersionHbox.Name = "langVersionHbox"; + langVersionHbox.PackStart (this.langVersionWarningIcon, false, false, 0); this.label2 = new global::Gtk.Label (); this.label2.Name = "label2"; this.label2.Xalign = 0F; this.label2.LabelProp = global::Mono.Unix.Catalog.GetString ("C# Language Version:"); - this.table2.Add (this.label2); - global::Gtk.Table.TableChild w19 = ((global::Gtk.Table.TableChild)(this.table2 [this.label2])); + langVersionHbox.PackStart (this.label2, false, false, 6); + this.table2.Add (langVersionHbox); + global::Gtk.Table.TableChild w19 = ((global::Gtk.Table.TableChild)(this.table2 [langVersionHbox])); w19.XOptions = ((global::Gtk.AttachOptions)(4)); w19.YOptions = ((global::Gtk.AttachOptions)(4)); + w19.XPadding = 6; // Container child table2.Gtk.Table+TableChild this.langVerCombo = global::Gtk.ComboBox.NewText (); this.langVerCombo.Name = "langVerCombo"; diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Debugger/CSharpBreakpointSpanResolver.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Debugger/CSharpBreakpointSpanResolver.cs new file mode 100644 index 0000000000..f21971dc9f --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Debugger/CSharpBreakpointSpanResolver.cs @@ -0,0 +1,58 @@ +// +// CSharpBreakpointSpanResolver.cs +// +// Author: +// Jeffrey Stedfast <jestedfa@microsoft.com> +// +// Copyright (c) 2019 Microsoft Corp. +// +// 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.Threading; +using System.Threading.Tasks; + +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using Microsoft.CodeAnalysis.CSharp.EditAndContinue; + +using MonoDevelop.Debugger; +using MonoDevelop.Ide.Gui.Documents; + +namespace MonoDevelop.CSharp.Debugger +{ + [ExportDocumentControllerExtension (MimeType = "text/x-csharp")] + class CSharpBreakpointSpanResolver : DocumentControllerExtension, IBreakpointSpanResolver + { + public override Task<bool> SupportsController (DocumentController controller) + { + return Task.FromResult (controller.GetContent<ITextBuffer> () != null); + } + + public async Task<Span> GetBreakpointSpanAsync (ITextBuffer buffer, int position, CancellationToken cancellationToken) + { + var document = buffer.AsTextContainer ().GetOpenDocumentInCurrentContext (); + var tree = await document.GetSyntaxTreeAsync (cancellationToken); + + BreakpointSpans.TryGetBreakpointSpan (tree, position, cancellationToken, out var span); + + return new Span (span.Start, span.Length); + } + } +} diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs index 9d30f7a21c..ef73cbd84e 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs @@ -227,9 +227,11 @@ namespace MonoDevelop.CSharp.Project return val; } set { - if (LangVersion == value) { - return; - } + try { + if (LangVersion == value) { + return; + } + } catch (Exception) { } langVersion = LanguageVersionToString (value); NotifyChange (); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs index 396dc158c6..99525f77db 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs @@ -29,6 +29,8 @@ using System; using System.Linq; +using System.Collections.Generic; +using System.Text.RegularExpressions; using Gtk; using MonoDevelop.Components; @@ -55,7 +57,7 @@ namespace MonoDevelop.CSharp.Project LanguageVersion.LatestMajor, LanguageVersion.Preview }; - + public CompilerOptionsPanelWidget (DotNetProject project) { this.Build(); @@ -98,26 +100,51 @@ namespace MonoDevelop.CSharp.Project iconEntry.DefaultPath = project.BaseDirectory; allowUnsafeCodeCheckButton.Active = compilerParameters.UnsafeCode; noStdLibCheckButton.Active = compilerParameters.NoStdLib; + langVersionWarningIcon.Visible = false; - var langVerStore = new ListStore (typeof (string), typeof(LanguageVersion)); - foreach (var (text, version) in CSharpLanguageVersionHelper.GetKnownLanguageVersions ()) { - if (unsupportedLanguageVersions.Contains (version) && compilerParameters.LangVersion != version) { - // Mono's MSBuild does not currently support C# 8. + var langVerStore = new ListStore (typeof (string), typeof(LanguageVersion), typeof (bool)); + var langVersions = CSharpLanguageVersionHelper.GetKnownLanguageVersions (); + string badVersion = null; + LanguageVersion? langVersion = null; + + try { + langVersion = compilerParameters.LangVersion; + } catch (Exception) { + badVersion = configuration.Properties.GetProperty ("LangVersion").Value; + } + + foreach (var (text, version) in langVersions) { + if (unsupportedLanguageVersions.Contains (version)) { + if (langVersion == version) { + if (badVersion == null) + badVersion = text; + } else { + // Otherwise if it's an unsupported language but it's not the current project's + // version then it must be an unsupported version of Mono. Let's not add that to + // the list store. + } } else { - langVerStore.AppendValues (text, version); + langVerStore.AppendValues (text, version, false); } } + langVerCombo.Model = langVerStore; - TreeIter iter; - if (langVerStore.GetIterFirst (out iter)) { - do { - var val = (LanguageVersion)(int)langVerStore.GetValue (iter, 1); - if (val == compilerParameters.LangVersion) { - langVerCombo.SetActiveIter (iter); - break; - } - } while (langVerStore.IterNext (ref iter)); + if (badVersion != null) { + var badIter = langVerStore.AppendValues (GettextCatalog.GetString ("{0} (Unknown Version)", badVersion), LanguageVersion.Default, true); + langVerCombo.SetActiveIter (badIter); + langVersionWarningIcon.Visible = true; + } else { + TreeIter iter; + if (langVerStore.GetIterFirst (out iter)) { + do { + var val = (LanguageVersion)(int)langVerStore.GetValue (iter, 1); + if (val == compilerParameters.LangVersion) { + langVerCombo.SetActiveIter (iter); + break; + } + } while (langVerStore.IterNext (ref iter)); + } } SetupAccessibility (); @@ -175,11 +202,13 @@ namespace MonoDevelop.CSharp.Project public void Store (ItemConfigurationCollection<ItemConfiguration> configs) { int codePage; + bool isBadVersion = false; var langVersion = LanguageVersion.Default; TreeIter iter; if (langVerCombo.GetActiveIter (out iter)) { langVersion = (LanguageVersion)langVerCombo.Model.GetValue (iter, 1); + isBadVersion = (bool)langVerCombo.Model.GetValue (iter, 2); } if (codepageEntry.Entry.Text.Length > 0) { @@ -218,7 +247,8 @@ namespace MonoDevelop.CSharp.Project CSharpCompilerParameters compilerParameters = (CSharpCompilerParameters) configuration.CompilationParameters; compilerParameters.UnsafeCode = allowUnsafeCodeCheckButton.Active; compilerParameters.NoStdLib = noStdLibCheckButton.Active; - compilerParameters.LangVersion = langVersion; + if (!isBadVersion) + compilerParameters.LangVersion = langVersion; } } diff --git a/main/src/addins/MacPlatform/MacPlatform.cs b/main/src/addins/MacPlatform/MacPlatform.cs index d60c4f79c8..90116f6d53 100644 --- a/main/src/addins/MacPlatform/MacPlatform.cs +++ b/main/src/addins/MacPlatform/MacPlatform.cs @@ -1281,7 +1281,7 @@ namespace MonoDevelop.MacIntegration y += GetTitleBarHeight (w); var dr = FromDesktopRect (new Gdk.Rectangle (x, y, width, height)); var r = w.FrameRectFor (dr); - w.SetFrame (r, true); + base.PlaceWindow (window, x, y, width, height); } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs index 15a38cfc9f..c4afa8c1b6 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserViewContent.cs @@ -40,6 +40,7 @@ using System.Collections.Immutable; using MonoDevelop.Ide; using Microsoft.CodeAnalysis.CodeRefactorings; using MonoDevelop.Ide.Gui.Documents; +using System.Threading; namespace MonoDevelop.AssemblyBrowser { @@ -47,7 +48,8 @@ namespace MonoDevelop.AssemblyBrowser { readonly static string[] defaultAssemblies = new string[] { "mscorlib", "System", "System.Core", "System.Xml" }; AssemblyBrowserWidget widget; - + CancellationTokenSource cts = new CancellationTokenSource (); + protected override Control OnGetViewControl (DocumentViewContent view) { widget.SetToolbar (view.GetToolbar ()); @@ -67,11 +69,13 @@ namespace MonoDevelop.AssemblyBrowser FillWidget (); } - protected override async Task OnInitialize (ModelDescriptor modelDescriptor, Properties status) + protected override Task OnInitialize (ModelDescriptor modelDescriptor, Properties status) { if (modelDescriptor is FileDescriptor fileDescriptor) { Load (fileDescriptor.FilePath); } + + return Task.CompletedTask; } protected override bool OnTryReuseDocument (ModelDescriptor modelDescriptor) @@ -92,9 +96,9 @@ namespace MonoDevelop.AssemblyBrowser { var loader = widget.AddReferenceByFileName (filePath); if (loader != null) { - loader.LoadingTask.ContinueWith (delegate { - widget.SelectAssembly (loader); - }); + loader.LoadingTask + .ContinueWith (t => widget.SelectAssembly (t.Result), Runtime.MainTaskScheduler) + .Ignore (); } } @@ -105,12 +109,14 @@ namespace MonoDevelop.AssemblyBrowser protected override void OnDispose () { - if (currentWs != null) - currentWs.WorkspaceLoaded -= Handle_WorkspaceLoaded; + if (cts != null) { + cts.Cancel (); + cts.Dispose (); + cts = null; + } widget = null; - if (Disposed != null) - Disposed (this, EventArgs.Empty); + Disposed?.Invoke (this, EventArgs.Empty); base.OnDispose (); } @@ -162,40 +168,29 @@ namespace MonoDevelop.AssemblyBrowser //FindDerivedClassesHandler.FindDerivedClasses (type); } - void Handle_WorkspaceLoaded (object sender, EventArgs e) - { - foreach (var project in Ide.IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { - var nav = Widget.TreeView.GetNodeAtObject (project); - if (nav != null) - Widget.TreeView.RefreshNode (nav); - } - } - - Ide.TypeSystem.MonoDevelopWorkspace currentWs; - public async void FillWidget () + public void FillWidget () { if (Ide.IdeApp.ProjectOperations.CurrentSelectedSolution == null) { foreach (var assembly in defaultAssemblies) { - Widget.AddReferenceByAssemblyName (assembly); + Widget.AddReferenceByAssemblyName (assembly); } } else { - var alreadyAdded = new HashSet<string> (); - currentWs = IdeApp.TypeSystemService.GetWorkspace (IdeApp.ProjectOperations.CurrentSelectedSolution); - if (currentWs != null) - currentWs.WorkspaceLoaded += Handle_WorkspaceLoaded; - var allTasks = new List<Task> (); - foreach (var project in Ide.IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { - try { - Widget.AddProject (project, false); - } catch (Exception e) { - LoggingService.LogError ("Error while adding project " + project.Name + " to the tree.", e); - } - } - await Task.WhenAll (allTasks).ContinueWith (delegate { - Runtime.RunInMainThread (delegate { + var token = cts.Token; + + var workspace = IdeApp.TypeSystemService.GetWorkspaceAsync (IdeApp.ProjectOperations.CurrentSelectedSolution) + .ContinueWith (t => { + if (token.IsCancellationRequested) + return; + + foreach (var project in IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { + try { + Widget.AddProject (project, false); + } catch (Exception e) { + LoggingService.LogError ("Error while adding project " + project.Name + " to the tree.", e); + } + } widget.StartSearch (); - }); - }); + }, token, TaskContinuationOptions.DenyChildAttach, Runtime.MainTaskScheduler); } } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs index fc47e392e0..681fdac935 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs @@ -880,19 +880,19 @@ namespace MonoDevelop.AssemblyBrowser inspectEditor.Options = assemblyBrowserEditorOptions; this.inspectEditor.MimeType = "text/x-csharp"; builder.DecompileAsync (inspectEditor, nav, new DecompileFlags { PublicOnly = PublicApiOnly, MethodBodies = false }) - .ContinueWith (l => SetReferencedSegments (l.Result), Runtime.MainTaskScheduler); + .ContinueWith (l => SetReferencedSegments (l.Result), Runtime.MainTaskScheduler).Ignore (); break; case 1: inspectEditor.Options = assemblyBrowserEditorOptions; this.inspectEditor.MimeType = "text/x-ilasm"; builder.DisassembleAsync (inspectEditor, nav) - .ContinueWith (l => SetReferencedSegments (l.Result), Runtime.MainTaskScheduler); + .ContinueWith (l => SetReferencedSegments (l.Result), Runtime.MainTaskScheduler).Ignore (); break; case 2: inspectEditor.Options = assemblyBrowserEditorOptions; this.inspectEditor.MimeType = "text/x-csharp"; builder.DecompileAsync (inspectEditor, nav, new DecompileFlags { PublicOnly = PublicApiOnly, MethodBodies = true }) - .ContinueWith (l => SetReferencedSegments (l.Result), Runtime.MainTaskScheduler); + .ContinueWith (l => SetReferencedSegments (l.Result), Runtime.MainTaskScheduler).Ignore (); break; default: inspectEditor.Options = assemblyBrowserEditorOptions; @@ -914,24 +914,30 @@ namespace MonoDevelop.AssemblyBrowser internal void Open (string url, AssemblyLoader currentAssembly = null, bool expandNode = true) { Task.WhenAll (this.definitions.Select (d => d.LoadingTask)).ContinueWith (d => { - Application.Invoke ((o, args) => { - suspendNavigation = false; - ITreeNavigator nav = SearchMember (url, expandNode); - if (definitions.Count == 0) // we've been disposed - return; - if (nav != null) - return; - try { - if (currentAssembly != null) { - OpenFromAssembly (url, currentAssembly); - } else { - OpenFromAssemblyNames (url); - } - } catch (Exception e) { - LoggingService.LogError ("Error while opening the assembly browser with id:" + url, e); + // At least one of them failed. + if (d.IsFaulted) { + LoggingService.LogError ("Failed to load assemblies", d.Exception); + + // It's possible the assembly in which the type we're looking for exists + // so try probing for it regardless. + } + + suspendNavigation = false; + ITreeNavigator nav = SearchMember (url, expandNode); + if (definitions.Count == 0) // we've been disposed + return; + if (nav != null) + return; + try { + if (currentAssembly != null) { + OpenFromAssembly (url, currentAssembly); + } else { + OpenFromAssemblyNames (url); } - }); - }); + } catch (Exception e) { + LoggingService.LogError ("Error while opening the assembly browser with id:" + url, e); + } + }, Runtime.MainTaskScheduler).Ignore (); } void OpenFromAssembly (string url, AssemblyLoader currentAssembly, bool expandNode = true) @@ -960,16 +966,14 @@ namespace MonoDevelop.AssemblyBrowser result.LoadingTask.ContinueWith (t2 => { if (definitions.Count == 0) // disposed return; - Application.Invoke ((o, args) => { - var nav = SearchMember (url, expandNode); - if (nav == null) { - if (++i == references.Length) - LoggingService.LogError ("Assembly browser: Can't find: " + url + "."); - else - loadNext (); - } - }); - }, TaskScheduler.Current); + var nav = SearchMember (url, expandNode); + if (nav == null) { + if (++i == references.Length) + LoggingService.LogError ("Assembly browser: Can't find: " + url + "."); + else + loadNext (); + } + }, Runtime.MainTaskScheduler).Ignore (); }; } @@ -997,51 +1001,44 @@ namespace MonoDevelop.AssemblyBrowser LoggingService.LogError ("Assembly browser: Can't find: " + url + "."); } return; - }; - Task.Factory.ContinueWhenAll (tasks.ToArray (), tarr => { - var exceptions = tarr.Where (t => t.IsFaulted).Select (t => t.Exception).ToArray (); - if (exceptions != null) { - var ex = new AggregateException (exceptions).Flatten (); - if (ex.InnerExceptions.Count > 0) { - foreach (var inner in ex.InnerExceptions) { - LoggingService.LogError ("Error while loading assembly in the browser.", inner); - } - throw ex; + } + + Task.WhenAll (tasks.ToArray ()) + .ContinueWith (t => { + if (t.IsFaulted) { + LoggingService.LogError ("Error while loading assemblies in the browser", t.Exception); + return; } - } - if (definitions.Count == 0) // disposed - return; - Application.Invoke ((o, args) => { + + if (definitions.Count == 0) // disposed + return; + var nav = SearchMember (url); if (nav == null) { LoggingService.LogError ("Assembly browser: Can't find: " + url + "."); } - }); - }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current); + }, Runtime.MainTaskScheduler).Ignore (); } - internal void SelectAssembly (AssemblyLoader loader) + internal void SelectAssembly (PEFile cu) { - PEFile cu = loader.Assembly; - Application.Invoke ((o, args) => { - ITreeNavigator nav = TreeView.GetRootNode (); - if (nav == null) - return; + ITreeNavigator nav = TreeView.GetRootNode (); + if (nav == null) + return; + + if (expandedMember) { + expandedMember = false; + return; + } - if (expandedMember) { - expandedMember = false; + do { + if (nav.DataItem == cu || (nav.DataItem as AssemblyLoader)?.Assembly == cu) { + nav.ExpandToNode (); + nav.Selected = true; + nav.ScrollToNode (); return; } - - do { - if (nav.DataItem == cu || (nav.DataItem as AssemblyLoader)?.Assembly == cu) { - nav.ExpandToNode (); - nav.Selected = true; - nav.ScrollToNode (); - return; - } - } while (nav.MoveNext ()); - }); + } while (nav.MoveNext ()); } void Dispose<T> (ITreeNavigator nav) where T:class, IDisposable @@ -1095,6 +1092,8 @@ namespace MonoDevelop.AssemblyBrowser def.Dispose (); definitions = definitions.Clear (); } + + projects.Clear (); ActiveMember = null; resultListStore = null; @@ -1124,7 +1123,7 @@ namespace MonoDevelop.AssemblyBrowser ImmutableList<AssemblyLoader> definitions = ImmutableList<AssemblyLoader>.Empty; - List<Project> projects = new List<Project> (); + HashSet<Project> projects = new HashSet<Project> (); internal AssemblyLoader AddReferenceByAssemblyName (PEFile reference, bool expand = false) { @@ -1158,38 +1157,36 @@ namespace MonoDevelop.AssemblyBrowser var result = new AssemblyLoader (this, fileName); definitions = definitions.Add (result); result.LoadingTask = result.LoadingTask.ContinueWith (task => { - Application.Invoke ((o, args) => { - if (TreeView == null || definitions.Count == 0) - return; - var fullName = result.Assembly.FullName; - - // filter duplicate assemblies, can happen on opening the same assembly at different locations. - foreach (var d in definitions) { - if (!d.IsLoaded || d == result) - continue; - if (d.Assembly.FullName == fullName) { - definitions = definitions.Remove (result); - LoggingService.LogInfo ("AssemblyBrowser: Loaded duplicate assembly : " + fullName); // Write a log info in case that happens, shouldn't happen often. - return; - } + if (TreeView == null || definitions.Count == 0) + return task.Result; + var fullName = result.Assembly.FullName; + + // filter duplicate assemblies, can happen on opening the same assembly at different locations. + foreach (var d in definitions) { + if (!d.IsLoaded || d == result) + continue; + if (d.Assembly.FullName == fullName) { + definitions = definitions.Remove (result); + LoggingService.LogInfo ("AssemblyBrowser: Loaded duplicate assembly : " + fullName); // Write a log info in case that happens, shouldn't happen often. + return task.Result; } - - try { - ITreeBuilder builder; - if (definitions.Count + projects.Count == 1) { - builder = TreeView.LoadTree (result); - } else { - builder = TreeView.AddChild (result, false); - } - if (TreeView.GetSelectedNode () == null) - builder.Selected = builder.Expanded = expand; - } catch (Exception e) { - LoggingService.LogError ("Error while adding assembly to the assembly list", e); + } + + try { + ITreeBuilder builder; + if (definitions.Count + projects.Count == 1) { + builder = TreeView.LoadTree (result); + } else { + builder = TreeView.AddChild (result, expand); } - }); + if (TreeView.GetSelectedNode () == null) + builder.Selected = builder.Expanded = expand; + } catch (Exception e) { + LoggingService.LogError ("Error while adding assembly to the assembly list", e); + } return task.Result; - } - ); + }, Runtime.MainTaskScheduler); + return result; } @@ -1198,7 +1195,7 @@ namespace MonoDevelop.AssemblyBrowser if (project == null) throw new ArgumentNullException ("project"); - if (projects.Contains (project)) { + if (!projects.Add (project)) { // Select the project. if (selectReference) { ITreeNavigator navigator = TreeView.GetNodeAtObject (project); @@ -1209,14 +1206,16 @@ namespace MonoDevelop.AssemblyBrowser return; } - projects.Add (project); + ITreeBuilder builder; if (definitions.Count + projects.Count == 1) { builder = TreeView.LoadTree (project); } else { - builder = TreeView.AddChild (project); + builder = TreeView.AddChild (project, false); } - builder.Selected = builder.Expanded = selectReference; + + if (TreeView.GetSelectedNode () == null || selectReference) + builder.Selected = builder.Expanded = selectReference; } //MonoDevelop.Components.RoundedFrame popupWidgetFrame; @@ -1258,6 +1257,7 @@ namespace MonoDevelop.AssemblyBrowser foreach (var def in ensuredDefinitions) { if (!definitions.Contains (def)) { definitions = definitions.Add (def); + Application.Invoke ((o, args) => { if (ensuredDefinitions.Count + projects.Count == 1) { TreeView.LoadTree (def.LoadingTask.Result); diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs index 31d7aed461..8baaf723a1 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyLoader.cs @@ -35,6 +35,7 @@ using ICSharpCode.Decompiler.Metadata; using System.Reflection.Metadata; using System.Linq; using ICSharpCode.Decompiler.TypeSystem.Implementation; +using ICSharpCode.Decompiler.CSharp.Transforms; namespace MonoDevelop.AssemblyBrowser { @@ -63,18 +64,13 @@ namespace MonoDevelop.AssemblyBrowser public PEFile Assembly => AssemblyTask.Result; public Task<PEFile> AssemblyTask => assemblyDefinitionTaskSource.Task; - public MetadataReader ModuleDefinition { - get { - return assemblyLoaderTask.Result.Metadata; - } - } - CSharpDecompiler csharpDecompiler; public CSharpDecompiler CSharpDecompiler { get { if (csharpDecompiler == null) { - csharpDecompiler = new CSharpDecompiler (DecompilerTypeSystem, new ICSharpCode.Decompiler.DecompilerSettings ()); + csharpDecompiler = new CSharpDecompiler (DecompilerTypeSystem, new ICSharpCode.Decompiler.DecompilerSettings (LanguageVersion.Latest)); + csharpDecompiler.AstTransforms.Add (new EscapeInvalidIdentifiers ()); } return csharpDecompiler; @@ -84,16 +80,13 @@ namespace MonoDevelop.AssemblyBrowser DecompilerTypeSystem decompilerTypeSystem; public DecompilerTypeSystem DecompilerTypeSystem { get { - LoadTypeSystem (Assembly); + if (decompilerTypeSystem == null) { + decompilerTypeSystem = new DecompilerTypeSystem (Assembly, new AssemblyResolver (Assembly, widget)); + } return decompilerTypeSystem; } } - void LoadTypeSystem (PEFile peFile) - { - decompilerTypeSystem = new DecompilerTypeSystem (peFile, new AssemblyResolver (Assembly, widget)); - } - public Error Error { get; internal set; } public bool IsLoaded { get; private set; } @@ -122,19 +115,7 @@ namespace MonoDevelop.AssemblyBrowser assemblyDefinitionTaskSource.SetResult (null); return null; } finally { IsLoaded = true; } - }); - } - - ICompilation typeSystem; - - public ICompilation GetMinimalTypeSystem () - { - if (typeSystem != null) - return typeSystem; - var assembly = Assembly; - if (assembly == null) - return null; - return typeSystem = new SimpleCompilation (assembly.WithOptions (TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers), MinimalCorlib.Instance); + }, src.Token); } class MyUniversalAssemblyResolver : UniversalAssemblyResolver diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/MetadataExtensions.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/MetadataExtensions.cs index 197fb93c08..df992f6f8c 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/MetadataExtensions.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/MetadataExtensions.cs @@ -40,6 +40,11 @@ namespace MonoDevelop.AssemblyBrowser entity.Accessibility == Accessibility.ProtectedOrInternal || entity.Accessibility == Accessibility.Public; + public static bool IsPublic (this Microsoft.CodeAnalysis.ISymbol entity) => + entity.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Protected || + entity.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.ProtectedOrInternal || + entity.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public; + public static string GetStockIcon (this Accessibility attributes) { switch (attributes) { diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs index 9e9ed85e07..58aa2b9a6f 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyBrowserTypeNodeBuilder.cs @@ -34,6 +34,7 @@ using MonoDevelop.Ide.TypeSystem; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using System.Threading.Tasks; using System.Collections.Generic; +using System.Linq; namespace MonoDevelop.AssemblyBrowser { @@ -79,5 +80,16 @@ namespace MonoDevelop.AssemblyBrowser return treeBuilder.GetParentDataItem (typeof(AssemblyLoader), true) != null; } + protected static void AddFilteredChildren<T> (ITreeBuilder builder, IReadOnlyCollection<T> collection, bool publicApiOnly) where T:IEntity + { + if (collection.Count == 0) + return; + + var children = publicApiOnly + ? collection.Where (x => x.IsPublic ()) + : collection; + + builder.AddChildren (collection); + } } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyReferenceNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyReferenceNodeBuilder.cs index 98d83e6e94..641aa3412d 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyReferenceNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/AssemblyReferenceNodeBuilder.cs @@ -82,7 +82,7 @@ namespace MonoDevelop.AssemblyBrowser if (e2 == null) return -1; - return e1.Name.CompareTo (e2.Name); + return string.Compare(e1.Name, e2.Name, StringComparison.Ordinal); } catch (Exception e) { LoggingService.LogError ("Exception in assembly browser sort function.", e); return -1; diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs index dc17aa56b6..fc1f30d49d 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/BaseTypeFolderNodeBuilder.cs @@ -67,9 +67,7 @@ namespace MonoDevelop.AssemblyBrowser public override int CompareObjects (ITreeNavigator thisNode, ITreeNavigator otherNode) { - var r1 = thisNode.DataItem as ITypeReference; - var r2 = thisNode.DataItem as ITypeReference; - return r1.ToString ().CompareTo (r2.ToString ()); + return string.Compare (thisNode.NodeName, otherNode.NodeName, StringComparison.Ordinal); } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/AssemblyNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/AssemblyNodeBuilder.cs index 0e2c3fd73a..d4ac415ec8 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/AssemblyNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/AssemblyNodeBuilder.cs @@ -100,26 +100,23 @@ namespace MonoDevelop.AssemblyBrowser var resources = new AssemblyResourceFolder (assemblyLoader.Assembly); if (resources.Resources.Any ()) treeBuilder.AddChild (resources); - - var namespaces = new Dictionary<string, NamespaceData> (); - bool publicOnly = Widget.PublicApiOnly; - - foreach (var type in assemblyLoader.GetMinimalTypeSystem ().MainModule.TopLevelTypeDefinitions) { - string namespaceName = string.IsNullOrEmpty (type.Namespace) ? "" : type.Namespace; - if (!namespaces.ContainsKey (namespaceName)) - namespaces [namespaceName] = new NamespaceData (namespaceName); - - var ns = namespaces [namespaceName]; - ns.Types.Add ((type.IsPublic (), type)); - } - treeBuilder.AddChildren (namespaces.Where (ns => ns.Key != "" && (!publicOnly || ns.Value.Types.Any (t => t.isPublic))).Select (n => n.Value)); - if (namespaces.ContainsKey ("")) { - foreach (var child in namespaces [""].Types) { - if (((INamedElement)child.typeObject).Name == "<Module>") - continue; - treeBuilder.AddChild (child); - } + var mainModule = assemblyLoader.DecompilerTypeSystem.MainModule; + var rootData = new NamespaceData (mainModule.RootNamespace); + if (rootData.Types.Length > 0) + treeBuilder.AddChild (rootData); + + var allNamespaces = new List<NamespaceData> (32); + CollectNamespaces (allNamespaces, mainModule.RootNamespace.ChildNamespaces); + treeBuilder.AddChildren (allNamespaces); + } + + void CollectNamespaces (List<NamespaceData> accumulator, IEnumerable<INamespace> namespaces) + { + accumulator.AddRange (namespaces.Select (x => new NamespaceData(x))); + + foreach (var ns in namespaces) { + CollectNamespaces (accumulator, ns.ChildNamespaces); } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/MethodDefinitionNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/MethodDefinitionNodeBuilder.cs index 81b1fa822b..e9b28c36dd 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/MethodDefinitionNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/MethodDefinitionNodeBuilder.cs @@ -124,14 +124,14 @@ namespace MonoDevelop.AssemblyBrowser } - public static async Task<List<ReferenceSegment>> DecompileAsync (TextEditor data, AssemblyLoader assemblyLoader, Func<CSharpDecompiler, SyntaxTree> decompile, DecompilerSettings settings = null, DecompileFlags flags = null) + public static Task<List<ReferenceSegment>> DecompileAsync (TextEditor data, AssemblyLoader assemblyLoader, Func<CSharpDecompiler, SyntaxTree> decompile, DecompilerSettings settings = null, DecompileFlags flags = null) { if (data == null) throw new ArgumentNullException (nameof (data)); if (assemblyLoader == null) throw new ArgumentNullException (nameof (assemblyLoader)); - return await Task.Run (async delegate { + return Task.Run (async delegate { settings = settings ?? GetDecompilerSettings (data, publicOnly: flags.PublicOnly); var csharpDecompiler = assemblyLoader.CSharpDecompiler; try { diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs index b875def8f7..90f83a0e73 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Cecil/TypeDefinitionNodeBuilder.cs @@ -98,46 +98,28 @@ namespace MonoDevelop.AssemblyBrowser public override void BuildChildNodes (ITreeBuilder builder, object dataObject) { var type = (ITypeDefinition)dataObject; - var list = new System.Collections.ArrayList (); if (type.DirectBaseTypes.Any ()) - list.Add (new BaseTypeFolder (type)); - bool publicOnly = Widget.PublicApiOnly; - - foreach (var field in type.Fields.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { - if (publicOnly && !field.IsPublic ()) - continue; - builder.AddChild (field); - } - - foreach (var property in type.Properties.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { - var accessor = property.Getter ?? property.Setter; - if (publicOnly && !accessor.IsPublic ()) - continue; - builder.AddChild (property); - } + builder.AddChild (new BaseTypeFolder (type)); - foreach (var evt in type.Events.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { - var accessor = evt.AddAccessor ?? evt.RemoveAccessor; - if (publicOnly && !accessor.IsPublic ()) - continue; - builder.AddChild (evt); - } + bool publicOnly = Widget.PublicApiOnly; - var accessorMethods = type.GetAccessors (); - foreach (var method in type.Methods.OrderBy (m => m.Name, StringComparer.InvariantCulture)) { - if (publicOnly && !method.IsPublic ()) - continue; - if (!accessorMethods.Contains (method)) { - builder.AddChild (method); - } - } + // PERF: We can take advantage of the fact that AddChildren is faster than AddChild, due to not processing + // sorting of child nodes. Avoid creating additional collection, as TreeBuilder does not optimize for ICollection implementors, + // thus the overhead of creating a IEnumerable is not that big. + AddFilteredChildren (builder, type.Members, publicOnly); + AddFilteredChildren (builder, type.NestedTypes, publicOnly); } public override bool HasChildNodes (ITreeBuilder builder, object dataObject) { return true; } - + + public override int GetSortIndex (ITreeNavigator node) + { + return -50; + } + #region IAssemblyBrowserNodeBuilder internal static void PrintAssembly (StringBuilder result, ITreeNavigator navigator) { diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/ModuleReferenceNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/ModuleReferenceNodeBuilder.cs index e9a6b593a2..9262f9612f 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/ModuleReferenceNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/ModuleReferenceNodeBuilder.cs @@ -70,7 +70,7 @@ namespace MonoDevelop.AssemblyBrowser if (e2 == null) return 1; - return e1.Name.CompareTo (e2.Name); + return string.Compare (e1.Name, e2.Name, StringComparison.Ordinal); } catch (Exception e) { LoggingService.LogError ("Exception in assembly browser sort function.", e); return -1; diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs index 89c848dc27..0307db2738 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceBuilder.cs @@ -64,7 +64,7 @@ namespace MonoDevelop.AssemblyBrowser if (e2 == null) return -1; - return e1.Name.CompareTo (e2.Name); + return string.Compare (e1.Name, e2.Name, StringComparison.Ordinal); } catch (Exception e) { LoggingService.LogError ("Exception in assembly browser sort function.", e); return -1; @@ -87,12 +87,12 @@ namespace MonoDevelop.AssemblyBrowser public override void BuildChildNodes (ITreeBuilder ctx, object dataObject) { NamespaceData ns = (NamespaceData)dataObject; - bool publicOnly = Widget.PublicApiOnly; - foreach (var type in ns.Types) { - if (publicOnly && !type.isPublic) - continue; - ctx.AddChild (type.typeObject); - } + + IEnumerable<object> result = ns.Types; + if (Widget.PublicApiOnly) + result = result.Where (x => NamespaceData.IsPublic (x)); + + ctx.AddChildren (result); } public override bool HasChildNodes (ITreeBuilder builder, object dataObject) diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceData.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceData.cs index 56950b7d79..47a2f0f7f7 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceData.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/NamespaceData.cs @@ -28,42 +28,61 @@ using System; using System.Collections.Generic; +using System.Linq; using ICSharpCode.Decompiler.TypeSystem; +using MonoDevelop.Ide.TypeSystem; namespace MonoDevelop.AssemblyBrowser { class NamespaceData : IDisposable { - List<(bool, object)> types = new List<(bool, object)> (); - - public string Name { - get; - private set; + public string Name { get; } + INamespace decompilerNs; + Microsoft.CodeAnalysis.INamespaceSymbol roslynNamespace; + + public static bool IsPublic (object typeObject) + { + return (typeObject is ITypeDefinition typeDefinition && typeDefinition.IsPublic ()) + || (typeObject is Microsoft.CodeAnalysis.INamedTypeSymbol symbol && symbol.IsPublic ()); } - public List<(bool isPublic, object typeObject)> Types { + object [] types; + public object[] Types { get { + if (types == null) { + types = decompilerNs?.Types.ToArray () + ?? roslynNamespace?.GetTypeMembers ().ToArray () + ?? Array.Empty<object> (); + } return types; } } - - public NamespaceData (string name) + + public NamespaceData(INamespace ns) { - this.Name = name; + Name = ns.FullName; + decompilerNs = ns; + + // Remove <Module> from root namespace. + if (ns.ParentNamespace == null) { + types = decompilerNs.Types.Where (x => x.Name != "<Module>").ToArray (); + } + } + + public NamespaceData(Microsoft.CodeAnalysis.INamespaceSymbol namespaceSymbol) + { + Name = namespaceSymbol.GetFullName (); + roslynNamespace = namespaceSymbol; } public void Dispose () { - if (types != null) { - // types.ForEach (t => t.Dispose ()); - types.Clear (); - types = null; - } + types = null; } public override string ToString () { - return string.Format ("[Namespace: Name={0}, #Types={1}]", Name, Types.Count); + return string.Format ("[Namespace: Name={0}, #Types={1}]", Name, Types.Length); } } } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/ProjectNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/ProjectNodeBuilder.cs index e65bfd01da..15487176c0 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/ProjectNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/ProjectNodeBuilder.cs @@ -58,7 +58,7 @@ namespace MonoDevelop.AssemblyBrowser var project = (Project)dataObject; return project.Name; } - + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { var project = (Project)dataObject; @@ -74,27 +74,27 @@ namespace MonoDevelop.AssemblyBrowser var dom = IdeApp.TypeSystemService.GetCompilationAsync (project).Result; if (dom == null) return; - bool nestedNamespaces = builder.Options ["NestedNamespaces"]; - HashSet<string> addedNames = new HashSet<string> (); - foreach (var ns in dom.Assembly.GlobalNamespace.GetNamespaceMembers ()) { - FillNamespaces (builder, project, ns); + + var data = new List<NamespaceData> (32); + CollectNamespaces (data, dom.Assembly.GlobalNamespace.GetNamespaceMembers ()); + builder.AddChildren (data); + + var types = dom.Assembly.GlobalNamespace.GetTypeMembers (); + if (types.Length > 0) { + var children = publicOnly + ? types.Where (t => t.IsPublic ()) + : types; + + builder.AddChildren (children); } - builder.AddChildren (dom.Assembly.GlobalNamespace.GetTypeMembers () - .Where (type => !publicOnly || type.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public)); } - public static void FillNamespaces (ITreeBuilder builder, Project project, Microsoft.CodeAnalysis.INamespaceSymbol ns) + static void CollectNamespaces (List<NamespaceData> acc, IEnumerable<Microsoft.CodeAnalysis.INamespaceSymbol> namespaces) { - var members = ns.GetTypeMembers (); - //IParserContext ctx = IdeApp.Workspace.ParserDatabase.GetProjectParserContext (project); - if (members.Any ()) { - var data = new NamespaceData (ns.Name); - foreach (var member in members) - data.Types.Add ((member.DeclaredAccessibility == Microsoft.CodeAnalysis.Accessibility.Public, member)); - builder.AddChild (data); - } - foreach (var nSpace in ns.GetNamespaceMembers ()) { - FillNamespaces (builder, project, nSpace); + acc.AddRange (namespaces.Select (x => new NamespaceData (x))); + + foreach (var ns in namespaces) { + CollectNamespaces (acc, ns.GetNamespaceMembers ()); } } @@ -118,7 +118,7 @@ namespace MonoDevelop.AssemblyBrowser if (e2 == null) return 1; - return e1.Name.CompareTo (e2.Name); + return string.Compare (e1.Name, e2.Name, StringComparison.Ordinal); } catch (Exception e) { LoggingService.LogError ("Exception in assembly browser sort function.", e); return -1; diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs index 57b2f79562..3707a94029 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynMemberNodeBuilder.cs @@ -45,39 +45,14 @@ namespace MonoDevelop.AssemblyBrowser public override int CompareObjects (ITreeNavigator thisNode, ITreeNavigator otherNode) { - if (!(otherNode.DataItem is ISymbol)) return 1; - - if (thisNode.Options ["GroupByType"]) { - int v1 = GetTypeSortValue (thisNode.DataItem); - int v2 = GetTypeSortValue (otherNode.DataItem); - if (v1 < v2) return -1; - else if (v1 > v2) return 1; - } - if (thisNode.Options ["GroupByAccess"]) { - int v1 = GetAccessSortValue (((ISymbol)thisNode.DataItem).DeclaredAccessibility); - int v2 = GetAccessSortValue (((ISymbol)otherNode.DataItem).DeclaredAccessibility); - if (v1 < v2) return -1; - else if (v1 > v2) return 1; + try { + if (thisNode == null || otherNode == null) + return -1; + return string.Compare (thisNode.NodeName, otherNode.NodeName, StringComparison.OrdinalIgnoreCase); + } catch (Exception e) { + LoggingService.LogError ("Exception in assembly browser sort function.", e); + return -1; } - return DefaultSort; - } - - int GetTypeSortValue (object member) - { - if (member is IFieldSymbol) return 0; - if (member is IEventSymbol) return 1; - if (member is IPropertySymbol) return 2; - if (member is IMethodSymbol) return 3; - return 4; - } - - int GetAccessSortValue (Accessibility mods) - { - if ((mods & Accessibility.Private) != 0) return 0; - if ((mods & Accessibility.Internal) != 0) return 1; - if ((mods & Accessibility.Protected) != 0) return 2; - if ((mods & Accessibility.Public) != 0) return 3; - return 4; } public Task<List<ReferenceSegment>> DecompileAsync (TextEditor data, ITreeNavigator navigator, DecompileFlags flags) @@ -85,17 +60,17 @@ namespace MonoDevelop.AssemblyBrowser return DisassembleAsync (data, navigator); } - public Task<List<ReferenceSegment>> DisassembleAsync (TextEditor data, ITreeNavigator navigator) + public async Task<List<ReferenceSegment>> DisassembleAsync (TextEditor data, ITreeNavigator navigator) { var symbol = navigator.DataItem as ISymbol; if (symbol == null) { data.Text = "// DataItem is no symbol " + navigator.DataItem; // should never happen LoggingService.LogError ("DataItem is no symbol " + navigator.DataItem); - return AssemblyBrowserTypeNodeBuilder.EmptyReferenceSegmentTask; + return new List<ReferenceSegment> (); } var location = symbol.Locations [0]; if (location.IsInSource) { - var root = location.SourceTree.GetRoot (); + var root = await location.SourceTree.GetRootAsync (); var node = root.FindNode (location.SourceSpan); if (node != null) { data.Text = node.ToFullString (); @@ -106,7 +81,7 @@ namespace MonoDevelop.AssemblyBrowser data.Text = "// Error: Symbol " + symbol.MetadataName + " is not in source."; // should never happen LoggingService.LogError ("Symbol " + symbol.MetadataName + " is not in source."); } - return AssemblyBrowserTypeNodeBuilder.EmptyReferenceSegmentTask; + return new List<ReferenceSegment> (); } diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs index 81b74424aa..b798484b31 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/TreeNodes/Roslyn/RoslynTypeNodeBuilder.cs @@ -88,30 +88,24 @@ namespace MonoDevelop.AssemblyBrowser if (classData.TypeKind == TypeKind.Delegate) return; - builder.AddChildren (classData.GetTypeMembers () - .Where (innerClass => innerClass.DeclaredAccessibility == Accessibility.Public || - (innerClass.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || - !publicOnly)); + Func<ISymbol, bool> filter = symbol => !MethodPropertyFilter (symbol); + if (Widget.PublicApiOnly) + filter = symbol => symbol.IsPublic () && !MethodPropertyFilter (symbol); - builder.AddChildren (classData.GetMembers ().OfType<IMethodSymbol> ().Where (m => m.MethodKind != MethodKind.PropertyGet && m.MethodKind != MethodKind.PropertySet) - .Where (method => method.DeclaredAccessibility == Accessibility.Public || - (method.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || - !publicOnly)); + var typeMembers = classData.GetTypeMembers (); + if (typeMembers.Length > 0) { + builder.AddChildren (Enumerable.Where (typeMembers, filter)); + } - builder.AddChildren (classData.GetMembers ().OfType<IPropertySymbol> () - .Where (property => property.DeclaredAccessibility == Accessibility.Public || - (property.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || - !publicOnly)); + var members = classData.GetMembers (); + if (members.Length > 0) { + builder.AddChildren (members.Where (filter)); + } - builder.AddChildren (classData.GetMembers ().OfType<IFieldSymbol> () - .Where (field => field.DeclaredAccessibility == Accessibility.Public || - (field.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || - !publicOnly)); - - builder.AddChildren (classData.GetMembers ().OfType<IEventSymbol> () - .Where (e => e.DeclaredAccessibility == Accessibility.Public || - (e.DeclaredAccessibility == Accessibility.Protected && publicProtectedOnly) || - !publicOnly)); + bool MethodPropertyFilter (ISymbol symbol) + { + return symbol is IMethodSymbol method && (method.MethodKind == MethodKind.PropertyGet || method.MethodKind == MethodKind.PropertySet); + } } public override bool HasChildNodes (ITreeBuilder builder, object dataObject) diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs index f3f690e450..93058fe629 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs @@ -37,15 +37,17 @@ namespace MonoDevelop.Debugger void TextBuffer_Changed (object sender, TextContentChangedEventArgs e) { foreach (var breakpoint in breakpoints.Values) { - var newSpan = breakpoint.TrackingSpan.GetSpan (e.After); - if (newSpan.IsEmpty) { + var span = breakpoint.TrackingSpan.GetSpan (e.After); + + if (span.IsEmpty || string.IsNullOrWhiteSpace (span.GetText ())) { DebuggingService.Breakpoints.Remove (breakpoint.Breakpoint); continue; } - var newLineNumber = e.After.GetLineFromPosition (newSpan.Start).LineNumber + 1; - if (breakpoint.Breakpoint.Line != newLineNumber) { + + var newLineNumber = e.After.GetLineFromPosition (span.Start).LineNumber + 1; + + if (breakpoint.Breakpoint.Line != newLineNumber) DebuggingService.Breakpoints.UpdateBreakpointLine (breakpoint.Breakpoint, newLineNumber); - } } } @@ -58,38 +60,47 @@ namespace MonoDevelop.Debugger private Dictionary<Breakpoint, ManagerBreakpoint> breakpoints = new Dictionary<Breakpoint, ManagerBreakpoint> (); - private void OnBreakpointsChanged (object sender, EventArgs eventArgs) + private async void OnBreakpointsChanged (object sender, EventArgs eventArgs) { - var snapshot = textBuffer.CurrentSnapshot; var newBreakpoints = new Dictionary<Breakpoint, ManagerBreakpoint> (); - bool needsUpdate = false; + var snapshot = textBuffer.CurrentSnapshot; + var needsUpdate = false; + foreach (var breakpoint in DebuggingService.Breakpoints.GetBreakpointsAtFile (textDocument.FilePath)) { if (breakpoint.Line > snapshot.LineCount) continue; + if (eventArgs is BreakpointEventArgs breakpointEventArgs && breakpointEventArgs.Breakpoint == breakpoint) needsUpdate = true; - var newSpan = snapshot.GetLineFromLineNumber (breakpoint.Line - 1).Extent; + + var line = snapshot.GetLineFromLineNumber (breakpoint.Line - 1); + var position = line.Start.Position + breakpoint.Column; + var span = await DebuggingService.GetBreakpointSpanAsync (textDocument, position); + if (breakpoints.TryGetValue (breakpoint, out var existingBreakpoint)) { newBreakpoints.Add (breakpoint, existingBreakpoint); - if (existingBreakpoint.Span != newSpan.Span) { - // Update if anything was modifed + if (existingBreakpoint.Span != span) { + // Update if anything was modified + existingBreakpoint.Span = span; needsUpdate = true; - existingBreakpoint.Span = newSpan.Span; } } else { // Update if anything was added - needsUpdate = true; - newBreakpoints.Add (breakpoint, new ManagerBreakpoint () { + newBreakpoints.Add (breakpoint, new ManagerBreakpoint { Breakpoint = breakpoint, - TrackingSpan = snapshot.CreateTrackingSpan (newSpan, SpanTrackingMode.EdgeExclusive), - Span = newSpan.Span + TrackingSpan = snapshot.CreateTrackingSpan (span, SpanTrackingMode.EdgeExclusive), + Span = span }); + needsUpdate = true; } } + // Update if anything was removed if (needsUpdate || breakpoints.Keys.Except (newBreakpoints.Keys).Any ()) needsUpdate = true; + breakpoints = newBreakpoints; + if (needsUpdate) BreakpointsChanged?.Invoke (this, new SnapshotSpanEventArgs (new SnapshotSpan (snapshot, 0, snapshot.Length))); } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj index 12b7e203fb..bdecc781e7 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj @@ -174,6 +174,8 @@ <Compile Include="MonoDevelop.Debugger.VSTextView\QuickInfo\IDebugInfoProvider.cs" /> <Compile Include="MonoDevelop.Debugger.VSTextView\BreakpointManagerService.cs" /> <Compile Include="MonoDevelop.Debugger.VSTextView\BreakpointManager.cs" /> + <Compile Include="MonoDevelop.Debugger\IBreakpointSpanResolver.cs" /> + <Compile Include="MonoDevelop.Debugger\DefaultBreakpointSpanResolver.cs" /> </ItemGroup> <ItemGroup Condition="$(OS) != 'Windows_NT'"> <Compile Include="MonoDevelop.Debugger.VSTextView\ExceptionCaught\ExceptionCaughtProvider.cs" /> diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index 950385ee18..9e19487fc4 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -1331,11 +1331,19 @@ namespace MonoDevelop.Debugger static void OnLineCountChanged (object ob, LineCountEventArgs a) { lock (breakpoints) { - foreach (Breakpoint bp in breakpoints.GetBreakpoints ()) { + foreach (var bp in breakpoints.GetBreakpoints ()) { if (bp.FileName == a.TextFile.Name) { if (bp.Line > a.LineNumber) { + var startIndex = a.TextFile.GetPositionFromLineColumn (bp.Line, bp.Column); + var endIndex = a.TextFile.GetPositionFromLineColumn (bp.Line + 1, 0) - 1; + + if (endIndex < startIndex) + endIndex = startIndex; + + var text = a.TextFile.GetText (startIndex, endIndex); + // If the line that has the breakpoint is deleted, delete the breakpoint, otherwise update the line #. - if (bp.Line + a.LineCount >= a.LineNumber) + if (bp.Line + a.LineCount >= a.LineNumber && !string.IsNullOrWhiteSpace (text)) breakpoints.UpdateBreakpointLine (bp, bp.Line + a.LineCount); else breakpoints.Remove (bp); @@ -1482,6 +1490,19 @@ namespace MonoDevelop.Debugger return result; return frame.GetExpressionCompletionData (exp); } + + public static Task<Span> GetBreakpointSpanAsync (ITextDocument document, int position, CancellationToken cancellationToken = default (CancellationToken)) + { + var doc = IdeApp.Workbench.GetDocument (document.FilePath); + IBreakpointSpanResolver resolver = null; + + if (doc != null) + resolver = doc.GetContent<IBreakpointSpanResolver> (); + + resolver = resolver ?? new DefaultBreakpointSpanResolver (); + + return resolver.GetBreakpointSpanAsync (document.TextBuffer, position, cancellationToken); + } } class FeatureCheckerHandlerFactory : IExecutionHandler diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DefaultBreakpointSpanResolver.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DefaultBreakpointSpanResolver.cs new file mode 100644 index 0000000000..19c245fafb --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DefaultBreakpointSpanResolver.cs @@ -0,0 +1,47 @@ +// +// DefaultBreakpointSpanResolver.cs +// +// Author: +// Jeffrey Stedfast <jestedfa@microsoft.com> +// +// Copyright (c) 2019 Microsoft Corp. +// +// 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.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.Text; + +namespace MonoDevelop.Debugger +{ + public class DefaultBreakpointSpanResolver : IBreakpointSpanResolver + { + public Task<Span> GetBreakpointSpanAsync (ITextBuffer buffer, int position, CancellationToken cancellationToken) + { + try { + var line = buffer.CurrentSnapshot.GetLineFromPosition (position); + + return Task.FromResult (Span.FromBounds (line.Start.Position, line.End.Position)); + } catch { + return Task.FromResult (default (Span)); + } + } + } +} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/IBreakpointSpanResolver.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/IBreakpointSpanResolver.cs new file mode 100644 index 0000000000..25f337ac54 --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/IBreakpointSpanResolver.cs @@ -0,0 +1,48 @@ +// +// IBreakpointSpanResolver.cs +// +// Author: +// Jeffrey Stedfast <jestedfa@microsoft.com> +// +// Copyright (c) 2019 Microsoft Corp. +// +// 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.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.Text; + +namespace MonoDevelop.Debugger +{ + /// <summary> + /// An interface for resolving the span of a breakpoint. + /// </summary> + public interface IBreakpointSpanResolver + { + /// <summary> + /// Resolve the span of a breakpoint. + /// </summary> + /// <param name="buffer">The text buffer.</param> + /// <param name="position">The cursor position.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The span of the breakpoint.</returns> + Task<Span> GetBreakpointSpanAsync (ITextBuffer buffer, int position, CancellationToken cancellationToken); + } +} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs index a7f5cb353d..afceba6802 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs @@ -947,15 +947,15 @@ namespace MonoDevelop.Debugger } int numberOfChildren = store.IterNChildren (iter); - Task.Factory.StartNew<ObjectValue[]> (delegate (object arg) { + Task.Run (() => { try { - return ((ObjectValue)arg).GetRangeOfChildren (numberOfChildren - 1, 20); + return value.GetRangeOfChildren (numberOfChildren - 1, 20); } catch (Exception ex) { // Note: this should only happen if someone breaks ObjectValue.GetAllChildren() LoggingService.LogError ("Failed to get ObjectValue children.", ex); return new ObjectValue[0]; } - }, value, cancellationTokenSource.Token).ContinueWith (t => { + }, cancellationTokenSource.Token).ContinueWith (t => { TreeIter it; if (disposed) return; @@ -975,7 +975,7 @@ namespace MonoDevelop.Debugger if (compact) RecalculateWidth (); enumerableLoading.Remove (value); - }, cancellationTokenSource.Token, TaskContinuationOptions.NotOnCanceled, Xwt.Application.UITaskScheduler); + }, cancellationTokenSource.Token, TaskContinuationOptions.NotOnCanceled, Runtime.MainTaskScheduler).Ignore (); } void RefreshRow (TreeIter iter, ObjectValue val) diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs index bd69d29c79..91cc41a8e8 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/SourceCodeLookup.cs @@ -23,15 +23,17 @@ // 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.Core; -using System.Collections.Generic; using System.IO; -using System.Security.Cryptography; using System.Linq; +using System.Collections.Generic; + +using Mono.Debugging.Client; + using MonoDevelop.Ide; +using MonoDevelop.Core; using MonoDevelop.Projects; -using Mono.Debugging.Client; namespace MonoDevelop.Debugger { @@ -120,50 +122,9 @@ namespace MonoDevelop.Debugger return FilePath.Null; } - public static bool CheckFileHash (FilePath file, byte[] hash) + public static bool CheckFileHash (FilePath path, byte[] checksum) { - if (hash == null) - return false; - if (File.Exists (file)) { - using (var fs = File.OpenRead (file)) { - // Roslyn SHA1 checksum always starts with 20 - if (hash.Length > 0 && hash [0] == 20) - using (var sha1 = SHA1.Create ()) { - if (sha1.ComputeHash (fs).Take (15).SequenceEqual (hash.Skip (1))) { - return true; - } - } - if (hash.Length > 0 && hash [0] == 32) - using (var sha1 = SHA256.Create ()) { - if (sha1.ComputeHash (fs).Take (15).SequenceEqual (hash.Skip (1))) { - return true; - } - } - if (hash.Length == 20) { - using (var sha1 = SHA1.Create ()) { - fs.Position = 0; - if (sha1.ComputeHash (fs).SequenceEqual (hash)) { - return true; - } - } - } - if (hash.Length == 32) { - using (var sha256 = SHA256.Create ()) { - fs.Position = 0; - if (sha256.ComputeHash (fs).SequenceEqual (hash)) { - return true; - } - } - } - fs.Position = 0; - using (var md5 = MD5.Create ()) { - if (md5.ComputeHash (fs).SequenceEqual (hash)) { - return true; - } - } - } - } - return false; + return SourceLocation.CheckFileHash (path, checksum); } /// <summary> @@ -223,4 +184,3 @@ namespace MonoDevelop.Debugger } } } - diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs index 984c13692c..df43588e79 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs @@ -58,15 +58,19 @@ namespace MonoDevelop.DotNetCore void FileService_FileChanged (object sender, FileEventArgs e) { - var globalJson = e.FirstOrDefault (x => x.FileName.FileName.IndexOf ("global.json", StringComparison.OrdinalIgnoreCase) == 0 && !x.FileName.IsDirectory); - if (globalJson == null) - return; - - // make sure the global.json file that has been changed is the one we got when loading the project - if (Project.ParentSolution.ExtendedProperties [GlobalJsonPathExtendedPropertyName] is string globalJsonPath - && globalJsonPath.IndexOf (globalJson.FileName, StringComparison.OrdinalIgnoreCase) == 0) { - DetectSDK (restore: true); + foreach (var arg in e) { + if (arg.IsDirectory || !arg.FileName.HasExtension (".json")) + continue; + + // avoid allocation caused by not querying .FileName + string fileName = arg.FileName; + // make sure the global.json file that has been changed is the one we got when loading the project + if (Project.ParentSolution.ExtendedProperties [GlobalJsonPathExtendedPropertyName] is string globalJsonPath + && globalJsonPath.Equals (fileName, StringComparison.OrdinalIgnoreCase)) { + DetectSDK (restore: true); + } } + } protected override bool SupportsObject (WorkspaceObject item) diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/PackagesCommandHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/PackagesCommandHandler.cs index 8c3540eacd..be0378ce09 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/PackagesCommandHandler.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/PackagesCommandHandler.cs @@ -56,38 +56,34 @@ namespace MonoDevelop.PackageManagement.Commands protected DotNetProject GetSelectedDotNetProject ()
{
- return IdeApp.ProjectOperations.CurrentSelectedProject as DotNetProject;
- }
-
- protected bool SelectedDotNetProjectHasPackages ()
- {
- DotNetProject project = GetSelectedDotNetProject ();
- return (project != null) && project.HasPackages ();
+ return CurrentSelectedProject as DotNetProject;
}
protected bool IsDotNetSolutionSelected ()
{
- return IdeApp.ProjectOperations.CurrentSelectedSolution != null;
+ return CurrentSelectedSolution != null;
}
- protected bool SelectedDotNetSolutionHasPackages ()
- {
- Solution solution = IdeApp.ProjectOperations.CurrentSelectedSolution;
- if (solution == null) {
- return false;
+ protected bool CanRestoreSelectedDotNetProjectOrSolution () + { + if (IsDotNetProjectSelected ()) {
+ return CanRestorePackagesForSelectedDotNetProject ();
+ } else if (IsDotNetSolutionSelected ()) {
+ return CanRestorePackagesForSelectedSolution ();
}
+ return false;
+ }
- return solution.HasAnyProjectWithPackages ();
+ protected bool CanRestorePackagesForSelectedDotNetProject ()
+ { + DotNetProject project = GetSelectedDotNetProject ();
+ return project?.CanRestorePackages () == true;
}
- protected bool SelectedDotNetProjectOrSolutionHasPackages ()
+ bool CanRestorePackagesForSelectedSolution ()
{
- if (IsDotNetProjectSelected ()) {
- return SelectedDotNetProjectHasPackages ();
- } else if (IsDotNetSolutionSelected ()) {
- return SelectedDotNetSolutionHasPackages ();
- }
- return false;
+ Solution solution = CurrentSelectedSolution;
+ return solution?.CanRestorePackages () == true;
}
protected Solution GetSelectedSolution ()
@@ -96,7 +92,7 @@ namespace MonoDevelop.PackageManagement.Commands if (project != null) {
return project.ParentSolution;
}
- return IdeApp.ProjectOperations.CurrentSelectedSolution;
+ return CurrentSelectedSolution;
}
protected bool CanUpdatePackagesForSelectedDotNetProject () @@ -107,7 +103,7 @@ namespace MonoDevelop.PackageManagement.Commands bool CanUpdatePackagesForSelectedDotNetSolution ()
{
- Solution solution = IdeApp.ProjectOperations.CurrentSelectedSolution;
+ Solution solution = CurrentSelectedSolution;
return solution?.CanUpdatePackages () == true;
} @@ -120,5 +116,19 @@ namespace MonoDevelop.PackageManagement.Commands }
return false;
}
+
+ /// <summary> + /// Used by unit tests to avoid having to initialize the IDE workspace. + /// </summary>
+ protected virtual Solution CurrentSelectedSolution {
+ get { return IdeApp.ProjectOperations.CurrentSelectedSolution; }
+ } + + /// <summary> + /// Used by unit tests to avoid having to initialize the IDE workspace. + /// </summary> + protected virtual Project CurrentSelectedProject {
+ get { return IdeApp.ProjectOperations.CurrentSelectedProject; }
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesHandler.cs index de29788d4b..0264cae53e 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesHandler.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesHandler.cs @@ -42,7 +42,7 @@ namespace MonoDevelop.PackageManagement.Commands protected override void Update (CommandInfo info) { - info.Enabled = SelectedDotNetProjectOrSolutionHasPackages (); + info.Enabled = CanRestoreSelectedDotNetProjectOrSolution (); } public static void Run (Solution solution) diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs index 73731d3bc4..93dadeff7e 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/RestorePackagesInProjectHandler.cs @@ -40,7 +40,7 @@ namespace MonoDevelop.PackageManagement.Commands protected override void Update (CommandInfo info) { - info.Enabled = SelectedDotNetProjectHasPackages (); + info.Enabled = CanRestorePackagesForSelectedDotNetProject (); } public static void Run (DotNetProject project)
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetAwareProject.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetAwareProject.cs index abe4cce276..33cd30fbb5 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetAwareProject.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeNuGetAwareProject.cs @@ -33,6 +33,11 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers { class FakeNuGetAwareProject : DummyDotNetProject, INuGetAwareProject { + public FakeNuGetAwareProject () + { + Initialize (this); + } + public NuGetProject CreateNuGetProject () { throw new NotImplementedException (); diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableRestorePackagesHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableRestorePackagesHandler.cs new file mode 100644 index 0000000000..dfb6272a16 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableRestorePackagesHandler.cs @@ -0,0 +1,54 @@ +// +// TestableRestorePackagesHandler.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// 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 MonoDevelop.Components.Commands; +using MonoDevelop.PackageManagement.Commands; +using MonoDevelop.Projects; + +namespace MonoDevelop.PackageManagement.Tests.Helpers +{ + class TestableRestorePackagesHandler : RestorePackagesHandler + { + CommandInfo info = new CommandInfo (); + Project project; + Solution solution; + + public bool Enabled { + get { return info.Enabled; } + } + + public void RunUpdate (Solution solution, Project project) + { + this.solution = solution; + this.project = project; + + base.Update (info); + } + + protected override Project CurrentSelectedProject => project; + protected override Solution CurrentSelectedSolution => solution; + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableRestorePackagesInProjectHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableRestorePackagesInProjectHandler.cs new file mode 100644 index 0000000000..64304cb624 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableRestorePackagesInProjectHandler.cs @@ -0,0 +1,54 @@ +// +// TestableRestorePackagesInProjectHandler.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// 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 MonoDevelop.Components.Commands; +using MonoDevelop.PackageManagement.Commands; +using MonoDevelop.Projects; + +namespace MonoDevelop.PackageManagement.Tests.Helpers +{ + class TestableRestorePackagesInProjectHandler : RestorePackagesInProjectHandler + { + CommandInfo info = new CommandInfo (); + Project project; + Solution solution; + + public bool Enabled { + get { return info.Enabled; } + } + + public void RunUpdate (Solution solution, Project project) + { + this.solution = solution; + this.project = project; + + base.Update (info); + } + + protected override Project CurrentSelectedProject => project; + protected override Solution CurrentSelectedSolution => solution; + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj index 25d8ffd69f..db41a064a4 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj @@ -151,6 +151,9 @@ <Compile Include="MonoDevelop.PackageManagement.Tests\FullyQualifiedReferencePathTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\MSBuildPackageSpecCreatorTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\InstallPackageWithAvailableItemNameTests.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests\PackagesCommandHandlerTests.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableRestorePackagesHandler.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableRestorePackagesInProjectHandler.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs index 422d599e35..4843451775 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetProjectExtensionsTests.cs @@ -49,6 +49,12 @@ namespace MonoDevelop.PackageManagement.Tests DotNetProjectExtensions.FileExists = existingFiles.Contains;
}
+ [TearDown]
+ public void TearDown () + { + DotNetProjectExtensions.FileExists = File.Exists; + }
+
void CreateProject (string fileName, string projectName)
{
project = new FakeDotNetProject (fileName.ToNativePath ()) {
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/PackagesCommandHandlerTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/PackagesCommandHandlerTests.cs new file mode 100644 index 0000000000..7cec48b14d --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/PackagesCommandHandlerTests.cs @@ -0,0 +1,257 @@ +// +// PackagesCommandHandlerTests.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// 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.IO; +using System.Linq; +using System.Threading.Tasks; +using MonoDevelop.Core; +using MonoDevelop.PackageManagement.Tests.Helpers; +using MonoDevelop.Projects; +using NUnit.Framework; +using UnitTests; + +namespace MonoDevelop.PackageManagement.Tests +{ + [TestFixture] + public class PackagesCommandHandlerTests : RestoreTestBase + { + TestableRestorePackagesHandler restorePackagesHandler; + TestableRestorePackagesInProjectHandler restorePackagesInProjectHandler; + + [SetUp] + public void Init () + { + restorePackagesHandler = new TestableRestorePackagesHandler (); + restorePackagesInProjectHandler = new TestableRestorePackagesInProjectHandler (); + } + + [Test] + public async Task ProjectWithNoPackages () + { + FilePath solutionFileName = Util.GetSampleProject ("csharp-console", "csharp-console.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsFalse (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled); + } + + [Test] + public async Task ProjectWithPackagesConfig () + { + FilePath solutionFileName = Util.GetSampleProject ("csharp-console", "csharp-console.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + var packagesConfigFileName = project.BaseDirectory.Combine ("packages.config"); + File.WriteAllText (packagesConfigFileName, "<packages />"); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + + [Test] + public async Task SdkProject_PackageReference () + { + FilePath solutionFileName = Util.GetSampleProject ("NetStandardXamarinForms", "NetStandardXamarinForms.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + + [Test] + public async Task SdkProject_NoPackageReferences () + { + FilePath solutionFileName = Util.GetSampleProject ("netstandard-sdk", "netstandard-sdk.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + + [Test] + public async Task SdkProject_NetFramework472 () + { + FilePath solutionFileName = Util.GetSampleProject ("netframework-sdk", "netframework-sdk.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + + [Test] + public async Task PackageReferenceProject_NonSdk () + { + FilePath solutionFileName = Util.GetSampleProject ("package-reference", "package-reference.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + + [Test] + public async Task RestoreProjectStyle_NoPackageReferences () + { + FilePath solutionFileName = Util.GetSampleProject ("RestoreStylePackageReference", "RestoreStylePackageReference.sln"); + + solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); + var project = solution.GetAllDotNetProjects ().Single (); + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + + [Test] + public void NuGetAwareProject () + { + var project = new FakeNuGetAwareProject (); + var solution = new Solution (); + solution.RootFolder.AddItem (project); + + // No packages in project. + project.HasPackagesReturnValue = false; + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsFalse (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + + // Project has packages. + project.HasPackagesReturnValue = true; + + // Project selected. + restorePackagesHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project); + Assert.IsTrue (restorePackagesInProjectHandler.Enabled); + + // Solution only selected + restorePackagesHandler.RunUpdate (solution, project: null); + Assert.IsTrue (restorePackagesHandler.Enabled); + + restorePackagesInProjectHandler.RunUpdate (solution, project: null); + Assert.IsFalse (restorePackagesInProjectHandler.Enabled, "Should be false - no project selected"); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs index dcc1d5a80b..1954713237 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/DotNetProjectExtensions.cs @@ -312,5 +312,16 @@ namespace MonoDevelop.PackageManagement return HasPackages (project.BaseDirectory, project.Name) || project.Items.OfType<ProjectPackageReference> ().Any ();
}
+
+ public static bool CanRestorePackages (this DotNetProject project)
+ {
+ var nugetAwareProject = project as INuGetAwareProject;
+ if (nugetAwareProject != null)
+ return nugetAwareProject.HasPackages ();
+
+ return HasPackages (project.BaseDirectory, project.Name) ||
+ DotNetCoreNuGetProject.CanCreate (project) ||
+ PackageReferenceNuGetProject.CanCreate (project);
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SolutionExtensions.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SolutionExtensions.cs index d63b3831f6..717a1b1e2a 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SolutionExtensions.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SolutionExtensions.cs @@ -48,16 +48,18 @@ namespace MonoDevelop.PackageManagement .Where (project => project.HasPackages ());
}
- public static bool HasAnyProjectWithPackages (this Solution solution)
- {
- return solution.GetAllProjectsWithPackages ().Any ();
- }
-
public static bool CanUpdatePackages (this Solution solution) { return solution .GetAllDotNetProjects () .Any (project => project.CanUpdatePackages ()); }
+
+ public static bool CanRestorePackages (this Solution solution)
+ {
+ return solution
+ .GetAllDotNetProjects ()
+ .Any (project => project.CanRestorePackages ());
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs index 6754d0b0ce..6fb760ace8 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs @@ -1957,7 +1957,7 @@ namespace Mono.TextEditor } } - var metrics = new LineMetrics { + var metrics = new LineMetrics { LineSegment = line, Layout = layout, @@ -1980,18 +1980,26 @@ namespace Mono.TextEditor if (!marker.IsVisible) continue; - if (marker.DrawBackground (textEditor, cr, metrics)) { - isSelectionDrawn |= (marker.Flags & TextLineMarkerFlags.DrawsSelection) == TextLineMarkerFlags.DrawsSelection; + try { + if (marker.DrawBackground (textEditor, cr, metrics)) { + isSelectionDrawn |= (marker.Flags & TextLineMarkerFlags.DrawsSelection) == TextLineMarkerFlags.DrawsSelection; + } + } catch (Exception e) { + LoggingService.LogInternalError ("Error while drawing backround marker " + marker, e); } } var textSegmentMarkers = TextDocument.OrderTextSegmentMarkersByInsertion (Document.GetVisibleTextSegmentMarkersAt (line)).ToList (); foreach (var marker in textSegmentMarkers) { - if (layout.Layout != null) + if (layout.Layout == null) + continue; + try { marker.DrawBackground (textEditor, cr, metrics, offset, offset + length); + } catch (Exception e) { + LoggingService.LogInternalError ("Error while drawing backround marker " + marker, e); + } } - if (DecorateLineBg != null) DecorateLineBg (cr, layout, offset, length, xPos, y, selectionStartOffset, selectionEndOffset); @@ -2175,17 +2183,24 @@ namespace Mono.TextEditor } } } - foreach (TextLineMarker marker in textEditor.Document.GetMarkers (line)) { - if (!marker.IsVisible) + foreach (var marker in textEditor.Document.GetMarkers (line)) { + if (!marker.IsVisible || layout.Layout == null) continue; - - if (layout.Layout != null) + try { marker.Draw (textEditor, cr, metrics); + } catch (Exception e) { + LoggingService.LogInternalError ("Error while drawing line marker " + marker, e); + } } foreach (var marker in textSegmentMarkers) { - if (layout.Layout != null) + if (layout.Layout == null) + continue; + try { marker.Draw (textEditor, cr, metrics, offset, offset + length); + } catch (Exception e) { + LoggingService.LogInternalError ("Error while drawing segment marker " + marker, e); + } } position += System.Math.Floor (layout.LastLineWidth); diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitRepository.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitRepository.cs index 3b22615cab..301d8a6b92 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitRepository.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitRepository.cs @@ -107,8 +107,8 @@ namespace MonoDevelop.VersionControl.Git { if (scheduler == null) { scheduler = new ConcurrentExclusiveSchedulerPair (); - blockingOperationFactory = new TaskFactory (scheduler.ExclusiveScheduler); - readingOperationFactory = new TaskFactory (scheduler.ConcurrentScheduler); + blockingOperationFactory = new TaskFactory (default, TaskCreationOptions.PreferFairness, TaskContinuationOptions.PreferFairness, scheduler.ExclusiveScheduler); + readingOperationFactory = new TaskFactory (default, TaskCreationOptions.PreferFairness, TaskContinuationOptions.PreferFairness, scheduler.ConcurrentScheduler); } } @@ -431,10 +431,12 @@ namespace MonoDevelop.VersionControl.Git var submoduleWriteTime = File.GetLastWriteTimeUtc (RootPath.Combine (".gitmodules")); if (cachedSubmoduleTime != submoduleWriteTime) { cachedSubmoduleTime = submoduleWriteTime; - cachedSubmodules = RootRepository.Submodules.Select (s => { + lock (this) { + cachedSubmodules = RootRepository.Submodules.Select (s => { var fp = new FilePath (Path.Combine (RootRepository.Info.WorkingDirectory, s.Path.Replace ('/', Path.DirectorySeparatorChar))).CanonicalPath; return new Tuple<FilePath, LibGit2Sharp.Repository> (fp, new LibGit2Sharp.Repository (fp)); }).ToArray (); + } } return cachedSubmodules; } diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitSelectRevisionDialog.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitSelectRevisionDialog.cs index 3947f48909..50bf7e6136 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitSelectRevisionDialog.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitSelectRevisionDialog.cs @@ -105,7 +105,9 @@ namespace MonoDevelop.VersionControl.Git };
revisionList.Columns.Add (shaColumn);
- Task.Factory.StartNew (async () => {
+ var token = cts.Token;
+
+ Task.Run (async () => {
const int sliceSize = 150;
var history = repo.GetHistory (repo.RootPath, null);
@@ -114,7 +116,7 @@ namespace MonoDevelop.VersionControl.Git for (int i = 0; i < slices; ++i) {
await Runtime.RunInMainThread (() => {
for (int n = 0; n < sliceSize; ++n) {
- if (cts.IsCancellationRequested)
+ if (token.IsCancellationRequested)
return;
int row = revisionStore.AddRow ();
@@ -128,7 +130,7 @@ namespace MonoDevelop.VersionControl.Git }
});
}
- }, cts.Token);
+ }, token).Ignore ();
revisionList.SelectionChanged += delegate {
CheckSensitive ();
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs index 8ac398f2a8..7ec8059025 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs @@ -281,7 +281,7 @@ namespace MonoDevelop.VersionControl RecursiveDirectoryInfoQuery rq; bool query = false; lock (queryLock) { - rq = recursiveDirectoryQueryQueue.FirstOrDefault (q => q.Directory == path); + rq = recursiveDirectoryQueryQueue.FirstOrDefault (q => q.Directory == path || path.IsChildPathOf (q.Directory)); if (rq == null) { query = true; var mre = new ManualResetEvent (false); |