diff options
author | Lluis Sanchez <llsan@microsoft.com> | 2019-04-02 14:47:19 +0300 |
---|---|---|
committer | Lluis Sanchez <llsan@microsoft.com> | 2019-04-02 14:47:19 +0300 |
commit | 034efec5570d1ca12d7314ce2447692e379f446f (patch) | |
tree | 2ea03cbffcf1167f8b4b931a139729d496f073f6 /main | |
parent | 8fc7d7e202598c31fdbb54d84e3e1fcd1003eb1c (diff) | |
parent | 0331b37ed370c639781c87ac969992f362d34eb0 (diff) |
Merge remote-tracking branch 'xamarin/master-vnext' into new-service-model
Diffstat (limited to 'main')
76 files changed, 1481 insertions, 836 deletions
diff --git a/main/build/MacOSX/BinaryCompatBaseline.txt b/main/build/MacOSX/BinaryCompatBaseline.txt index 66c17fd2ed..3d32a9625c 100755 --- a/main/build/MacOSX/BinaryCompatBaseline.txt +++ b/main/build/MacOSX/BinaryCompatBaseline.txt @@ -8,7 +8,7 @@ Could not load file or assembly 'Microsoft.Build.Utilities.Core, Version=15.1.0. Could not load file or assembly 'Microsoft.Developer.IdentityService.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies
Could not load file or assembly 'Microsoft.ServiceHub.Client, Version=1.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies
Could not load file or assembly 'Microsoft.TestPlatform.Utilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies
-Could not load file or assembly 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies
+Could not load file or assembly 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5caa9e03e69a5abd' or one of its dependencies
Could not load file or assembly 'System.Net.Http.Primitives, Version=1.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies
Could not load file or assembly 'System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies
Could not load file or assembly 'Xamarin.Ide.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies
@@ -118,14 +118,14 @@ In assembly 'MonoDevelop.Core, Version=2.6.0.0, Culture=neutral, PublicKeyToken= In assembly 'MonoDevelop.Debugger.Soft.IPhone, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null': unable to resolve reference to 'Xamarin.MacDev.Ide, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
In assembly 'MonoDevelop.Ide, Version=2.6.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Build, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'MonoDevelop.Ide, Version=2.6.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
-In assembly 'MonoDevelop.IPhone, Version=4.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
+In assembly 'MonoDevelop.IPhone, Version=4.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5caa9e03e69a5abd'
In assembly 'MonoDevelop.IPhone, Version=4.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Xamarin.MacDev.Ide, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
In assembly 'MonoDevelop.MacDev, Version=4.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Xamarin.MacDev.Ide, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
In assembly 'MonoDevelop.MSBuildBuilder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Build, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'MonoDevelop.MSBuildBuilder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'MonoDevelop.MSBuildBuilder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Build.Utilities.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'MonoDevelop.MSBuildResolver, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
-In assembly 'MonoTouch.Design.Client.Mac, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
+In assembly 'MonoTouch.Design.Client.Mac, Version=0.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5caa9e03e69a5abd'
In assembly 'NuGet.Protocol, Version=4.8.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35': Unable to resolve member reference 'System.IdentityModel.Tokens.SecurityToken System.ServiceModel.Security.IWSTrustChannelContract::Issue(System.IdentityModel.Protocols.WSTrust.RequestSecurityToken)' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
In assembly 'NuGet.Protocol, Version=4.8.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35': Unable to resolve member reference 'System.Void System.IdentityModel.Protocols.WSTrust.RequestSecurityToken::.ctor()' from assembly 'System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
In assembly 'NuGet.Protocol, Version=4.8.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35': Unable to resolve member reference 'System.Void System.ServiceModel.Security.WSTrustChannelFactory::.ctor(System.ServiceModel.Channels.Binding,System.String)' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
@@ -144,11 +144,11 @@ In assembly 'System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b In assembly 'System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a': Unable to resolve type reference 'System.Net.ComNetOS' in 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
In assembly 'System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a': Unable to resolve type reference 'System.Net.UnsafeNclNativeMethods/NativePKI' in 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
In assembly 'System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35': unable to resolve reference to 'System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
-In assembly 'Xamarin.Addins.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
-In assembly 'Xamarin.AndroidDesigner.Mac, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
+In assembly 'Xamarin.Addins.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5caa9e03e69a5abd'
+In assembly 'Xamarin.AndroidDesigner.Mac, Version=0.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5caa9e03e69a5abd'
In assembly 'Xamarin.AzureSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Microsoft.Developer.IdentityService.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'Xamarin.AzureSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ead7498f347467b': unable to resolve reference to 'Xamarin.Ide.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
-In assembly 'Xamarin.iOSDesigner.MonoDevelop, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
+In assembly 'Xamarin.iOSDesigner.MonoDevelop, Version=0.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756': unable to resolve reference to 'MonoTouch.Hosting, Version=0.0.0.0, Culture=neutral, PublicKeyToken=5caa9e03e69a5abd'
In assembly 'Xamarin.Mac, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065': Unable to resolve member reference 'System.Net.Http.Headers.HttpHeaderKind System.Net.Http.Headers.HttpHeaders::GetKnownHeaderKind(System.String)' from assembly 'System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'Xamarin.Mac, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065': Unable to resolve type reference 'System.Net.Http.Headers.HttpHeaderKind' in 'System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
In assembly 'XamarinStudio.MSDeploy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null': unable to resolve reference to 'Microsoft.Build, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
diff --git a/main/external/Xamarin.PropertyEditing b/main/external/Xamarin.PropertyEditing -Subproject 9b122cef903d72475c4b58bf51f26060b54994f +Subproject 031d650c59320206ca662753e938a40d473729e diff --git a/main/src/addins/MacPlatform/MacPlatform.cs b/main/src/addins/MacPlatform/MacPlatform.cs index 6dfb61f82f..b0dda5c1ea 100644 --- a/main/src/addins/MacPlatform/MacPlatform.cs +++ b/main/src/addins/MacPlatform/MacPlatform.cs @@ -640,7 +640,7 @@ namespace MonoDevelop.MacIntegration //OpenFiles may pump the mainloop, but can't do that from an AppleEvent GLib.Idle.Add (delegate { Ide.WelcomePage.WelcomePageService.HideWelcomePageOrWindow (); - var trackTTC = IdeApp.StartTimeToCodeLoadTimer (); + var trackTTC = IdeStartupTracker.StartupTracker.StartTimeToCodeLoadTimer (); IdeApp.OpenFiles (e.Documents.Select ( doc => new FileOpenInformation (doc.Key, null, doc.Value, 1, OpenDocumentOptions.DefaultInternal)), null @@ -651,7 +651,7 @@ namespace MonoDevelop.MacIntegration var firstFile = e.Documents.First ().Key; - IdeApp.TrackTimeToCode (GetDocumentTypeFromFilename (firstFile)); + IdeStartupTracker.StartupTracker.TrackTimeToCode (GetDocumentTypeFromFilename (firstFile)); }); return false; }); @@ -660,7 +660,7 @@ namespace MonoDevelop.MacIntegration ApplicationEvents.OpenUrls += delegate (object sender, ApplicationUrlEventArgs e) { GLib.Idle.Add (delegate { - var trackTTC = IdeApp.StartTimeToCodeLoadTimer (); + var trackTTC = IdeStartupTracker.StartupTracker.StartTimeToCodeLoadTimer (); // Open files via the monodevelop:// URI scheme, compatible with the // common TextMate scheme: http://blog.macromates.com/2007/the-textmate-url-scheme/ IdeApp.OpenFiles (e.Urls.Select (url => { @@ -690,7 +690,7 @@ namespace MonoDevelop.MacIntegration } var firstFile = e.Urls.First (); - IdeApp.TrackTimeToCode (GetDocumentTypeFromFilename (firstFile)); + IdeStartupTracker.StartupTracker.TrackTimeToCode (GetDocumentTypeFromFilename (firstFile)); }); return false; }); diff --git a/main/src/addins/MonoDevelop.AspNetCore/MonoDevelop.AspNetCore/AspNetCoreCertificateManager.cs b/main/src/addins/MonoDevelop.AspNetCore/MonoDevelop.AspNetCore/AspNetCoreCertificateManager.cs index 1e5ec4d5ab..d6bcd21706 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/MonoDevelop.AspNetCore/AspNetCoreCertificateManager.cs +++ b/main/src/addins/MonoDevelop.AspNetCore/MonoDevelop.AspNetCore/AspNetCoreCertificateManager.cs @@ -37,7 +37,8 @@ namespace MonoDevelop.AspNetCore static class AspNetCoreCertificateManager { public static bool IsDevelopmentCertificateTrusted { get; private set; } - + static readonly DotNetCoreVersion MinimumCoreRuntime = DotNetCoreVersion.Parse ("2.1"); + static readonly DotNetCoreVersion MinimumCoreSDK = DotNetCoreVersion.Parse ("2.1.300"); /// <summary> /// Only supported projects should be checked. If the development certificate /// was found to be trusted then do not check again for the current IDE session. @@ -52,8 +53,8 @@ namespace MonoDevelop.AspNetCore } /// <summary> - /// Only .NET Core 2.1 projects are supported. - /// Also need .NET Core SDK 2.1 to be installed. + /// Only .NET Core 2.1 projects and higher are supported. + /// Also need .NET Core SDK 2.1 or higher to be installed. /// Also check if the project is using https. /// </summary> public static bool IsProjectSupported (DotNetProject project, SolutionItemRunConfiguration runConfiguration) @@ -63,8 +64,8 @@ namespace MonoDevelop.AspNetCore return false; } - return project.TargetFramework.IsNetCoreApp ("2.1") && - IsNetCoreSdk21Installed () && + return project.TargetFramework.IsNetCoreAppOrHigher (MinimumCoreRuntime) && + IsNetCoreSdk21OrHigherInstalled () && UsingHttps (runConfiguration); } @@ -74,21 +75,15 @@ namespace MonoDevelop.AspNetCore return aspNetCoreRunConfiguration?.UsingHttps () == true; } - static bool IsNetCoreSdk21Installed () - { - return DotNetCoreSdk.Versions.Any (IsNetCoreSdk21); - } + static bool IsNetCoreSdk21OrHigherInstalled () => DotNetCoreSdk.Versions.Any (IsNetCoreSdk21OrHigher); /// <summary> - /// This checks for .NET Core SDK 2.1 to be installed. Note that the - /// .NET Core SDK versions is confusing. Here we want the .NET Core 2.1 + /// This checks for .NET Core SDK 2.1 or higher to be installed. Note that the + /// .NET Core SDK versions is confusing. Here we want at least the .NET Core 2.1 /// SDK that supports .NET Core App 2.1. Not the .NET Core 2.1 SDK that /// supports .NET Core App 2.0 only. /// </summary> - static bool IsNetCoreSdk21 (DotNetCoreVersion version) - { - return version.Major == 2 && version.Minor == 1 && version.Patch >= 300; - } + static bool IsNetCoreSdk21OrHigher (DotNetCoreVersion version) => version >= MinimumCoreSDK; public static async Task TrustDevelopmentCertificate (ProgressMonitor monitor) { diff --git a/main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml b/main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml index a91a288055..f7ce13dbca 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml @@ -79,7 +79,7 @@ For example, .NET Core 1.1 SDK does not have a Razor template so having the 1.1 templates first
would cause the Razor template to be last in the New Project dialog if both 1.1 and 2.x templates
are available. -->
- <Condition id="AspNetCoreSdkInstalled" sdkVersion="3.0">
+ <Condition id="AspNetCoreSdkInstalled" sdkVersion="3.0">
<Template
id="Microsoft.Web.Empty.CSharp"
templateId="Microsoft.Web.Empty.CSharp.3.0"
@@ -169,6 +169,19 @@ category="netcore/app/aspnet"
formatExclude="*.min.css|*.min.js|*.map"
defaultParameters="IncludeLaunchSettings=true" />
+ <Template
+ id="Microsoft.Web.Razor.Library.CSharp"
+ templateId="Microsoft.Web.Razor.Library.CSharp.3.0"
+ _overrideName="Razor Class Library"
+ _overrideDescription="A project for creating a Razor class library that targets .NET Standard"
+ path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
+ icon="md-netcore-empty-project"
+ imageId="md-netcore-empty-project"
+ wizard="MonoDevelop.DotNetCore.ProjectTemplateWizard"
+ condition="UseNetCore30=true"
+ category="netcore/app/aspnet"
+ formatExclude="*.min.css|*.min.js|*.map"
+ defaultParameters="IncludeLaunchSettings=true" />
</Condition>
<Condition id="AspNetCoreSdkInstalled" sdkVersion="2.2">
<Template
@@ -260,6 +273,19 @@ category="netcore/app/aspnet"
formatExclude="*.min.css|*.min.js|*.map"
defaultParameters="IncludeLaunchSettings=true" />
+ <Template
+ id="Microsoft.Web.Razor.Library.CSharp"
+ templateId="Microsoft.Web.Razor.Library.CSharp.2.2"
+ _overrideName="Razor Class Library"
+ _overrideDescription="A project for creating a Razor class library that targets .NET Standard"
+ path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
+ icon="md-netcore-empty-project"
+ imageId="md-netcore-empty-project"
+ wizard="MonoDevelop.DotNetCore.ProjectTemplateWizard"
+ condition="UseNetCore22=true"
+ category="netcore/app/aspnet"
+ formatExclude="*.min.css|*.min.js|*.map"
+ defaultParameters="IncludeLaunchSettings=true" />
</Condition>
<Condition id="AspNetCoreSdkInstalled" sdkVersion="2.1">
<Template
@@ -351,6 +377,19 @@ category="netcore/app/aspnet"
formatExclude="*.min.css|*.min.js|*.map"
defaultParameters="IncludeLaunchSettings=true" />
+ <Template
+ id="Microsoft.Web.Razor.Library.CSharp"
+ templateId="Microsoft.Web.Razor.Library.CSharp.2.1"
+ _overrideName="Razor Class Library"
+ _overrideDescription="A project for creating a Razor class library that targets .NET Standard"
+ path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
+ icon="md-netcore-empty-project"
+ imageId="md-netcore-empty-project"
+ wizard="MonoDevelop.DotNetCore.ProjectTemplateWizard"
+ condition="UseNetCore21=true"
+ category="netcore/app/aspnet"
+ formatExclude="*.min.css|*.min.js|*.map"
+ defaultParameters="IncludeLaunchSettings=true" />
</Condition>
<Condition id="AspNetCoreSdkInstalled" sdkVersion="2.0">
<Template
diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewImportsPage.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewImportsPage.xft.xml index 8393e4a536..9b1002de01 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewImportsPage.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewImportsPage.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore /> + <Or> + <AspNetCore /> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewLayoutPage.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewLayoutPage.xft.xml index 5fbc33c8f3..dd332cb77b 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewLayoutPage.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewLayoutPage.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore /> + <Or> + <AspNetCore /> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewPage.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewPage.xft.xml index 39106f6040..3ba1cd42df 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewPage.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewPage.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore /> + <Or> + <AspNetCore /> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewStartPage.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewStartPage.xft.xml index 9615880eae..b7306129ec 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewStartPage.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/MVCViewStartPage.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore /> + <Or> + <AspNetCore /> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPage.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPage.xft.xml index 68fce01577..30ddd41fc5 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPage.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPage.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore minVersion="2.0"/> + <Or> + <AspNetCore minVersion="2.0"/> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPageWithPageModel.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPageWithPageModel.xft.xml index a2db23ea8e..dcd7c8fc53 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPageWithPageModel.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorPageWithPageModel.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore minVersion="2.0" /> + <Or> + <AspNetCore minVersion="2.0"/> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorTagHelper.xft.xml b/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorTagHelper.xft.xml index 637686310e..42562c570f 100644 --- a/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorTagHelper.xft.xml +++ b/main/src/addins/MonoDevelop.AspNetCore/Templates/RazorTagHelper.xft.xml @@ -14,7 +14,10 @@ </TemplateConfiguration> <Conditions> - <AspNetCore /> + <Or> + <AspNetCore /> + <ProjectCapability Name="DotNetCoreRazor"/> + </Or> </Conditions> <!-- Template Content --> diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/packages.config b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/packages.config deleted file mode 100644 index 5f282702bb..0000000000 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/packages.config +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.DesignerSupport/AddinInfo.cs b/main/src/addins/MonoDevelop.DesignerSupport/AddinInfo.cs index 3fa48ba6ac..60adee7d2d 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/AddinInfo.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/AddinInfo.cs @@ -14,3 +14,8 @@ using Mono.Addins.Description; [assembly:AddinDependency ("Core", MonoDevelop.BuildInfo.Version)] [assembly:AddinDependency ("Ide", MonoDevelop.BuildInfo.Version)] + +#if MAC +[assembly: ImportAddinAssembly ("Xamarin.PropertyEditing.dll")] +[assembly: ImportAddinAssembly ("Xamarin.PropertyEditing.Mac.dll")] +#endif diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/Styles.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/Styles.cs index 6b62628491..0963655a61 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/Styles.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/Styles.cs @@ -1,4 +1,4 @@ -#if MAC
+#if MAC
using AppKit;
using MonoDevelop.Ide; using Xamarin.PropertyEditing.Mac; @@ -29,14 +29,12 @@ namespace MonoDevelop.DesignerSupport public static void LoadStyles ()
{ if (IdeApp.Preferences.UserInterfaceTheme == Theme.Light) { - PropertyEditorPanel.ThemeManager.Theme = Xamarin.PropertyEditing.Themes.PropertyEditorTheme.Light; HeaderBackgroundColor = NSColor.FromRgb (0.98f, 0.98f, 0.98f); HeaderBorderBackgroundColor = NSColor.FromRgb (0.96f, 0.96f, 0.96f);
LabelSelectedForegroundColor = NSColor.Highlight;
ToolbarBackgroundColor = NSColor.White;
CellBackgroundSelectedColor = NSColor.FromRgb (0.36f, 0.54f, 0.90f);
} else {
- PropertyEditorPanel.ThemeManager.Theme = Xamarin.PropertyEditing.Themes.PropertyEditorTheme.Dark;
CellBackgroundSelectedColor = NSColor.FromRgb (0.38f, 0.55f, 0.91f);
HeaderBackgroundColor = NSColor.FromRgb (0.29f, 0.29f, 0.29f);
HeaderBorderBackgroundColor = NSColor.FromRgb (0.29f, 0.29f, 0.29f);
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.csproj b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.csproj index 27cb32a163..86aa268f00 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.csproj +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.csproj @@ -144,6 +144,7 @@ <Compile Include="MonoDevelop.DesignerSupport\NativePropertyEditors\PropertyInfo\DirectoryPathPropertyInfo.cs" /> <Compile Include="MonoDevelop.DesignerSupport\IPropertyPad.cs" /> <Compile Include="MonoDevelop.DesignerSupport\NativePropertyEditors\PropertyInfo\FlagDescriptorPropertyInfo.cs" /> + <Compile Include="MonoDevelop.DesignerSupport\NativePropertyEditors\PropertyPadItem.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.DesignerSupport.addin.xml" /> diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs index de3512f570..29d0001577 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs @@ -116,15 +116,15 @@ namespace MonoDevelop.DesignerSupport } } - Tuple<object, object []> currentSelectedObject; + PropertyPadItem currentSelectedObject; public void SetCurrentObject (object lastComponent, object [] propertyProviders) { if (lastComponent != null) { - var selection = new Tuple<object, object []> (lastComponent, propertyProviders); + var selection = new PropertyPadItem (lastComponent, propertyProviders); if (currentSelectedObject != selection) { propertyEditorPanel.SelectedItems.Clear (); - propertyEditorPanel.SelectedItems.Add (new Tuple<object, object []> (lastComponent, propertyProviders)); + propertyEditorPanel.SelectedItems.Add (selection); currentSelectedObject = selection; } } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/DirectoryPathPropertyInfo.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/DirectoryPathPropertyInfo.cs index 6a070d80c1..6de957d826 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/DirectoryPathPropertyInfo.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/DirectoryPathPropertyInfo.cs @@ -24,7 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#if MAC +#if MAC && false using System; using System.Threading.Tasks; diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/FilePathPropertyInfo.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/FilePathPropertyInfo.cs index 563c5eee6b..393416929a 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/FilePathPropertyInfo.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyInfo/FilePathPropertyInfo.cs @@ -40,11 +40,11 @@ namespace MonoDevelop.DesignerSupport { } - public override Type Type => typeof (FilePath); + public override Type Type => typeof (Xamarin.PropertyEditing.Common.FilePath); internal override void SetValue<T> (object target, T value) { - if (value is FilePath filePath) { + if (value is Xamarin.PropertyEditing.Common.FilePath filePath) { PropertyDescriptor.SetValue (PropertyProvider, new MonoDevelop.Core.FilePath (filePath.Source)); } else { throw new Exception (string.Format ("Value: {0} of type {1} is not a DirectoryPath", value, value.GetType ())); @@ -53,8 +53,8 @@ namespace MonoDevelop.DesignerSupport internal override Task<T> GetValueAsync<T> (object target) { - if (target is Core.FilePath directoryPath) { - T result = (T)(object)new FilePath (directoryPath.FullPath); + if (target is MonoDevelop.Core.FilePath directoryPath) { + T result = (T)(object)new Xamarin.PropertyEditing.Common.FilePath (directoryPath.FullPath); return Task.FromResult (result); } Core.LoggingService.LogWarning ("Value: {0} of type {1} is not a DirectoryPath", target, target.GetType ()); diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadEditorProvider.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadEditorProvider.cs index 1d74baaf33..8752869230 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadEditorProvider.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadEditorProvider.cs @@ -41,19 +41,14 @@ namespace MonoDevelop.DesignerSupport class PropertyPadEditorProvider : IEditorProvider { - Tuple<object, object[]> currentObject; - IObjectEditor currentEditor; - public IReadOnlyDictionary<Type, ITypeInfo> KnownTypes { get; } = new Dictionary<Type, ITypeInfo> (); public Task<IObjectEditor> GetObjectEditorAsync (object item) { - if (item is Tuple<object, object []> tuple) { - this.currentObject = tuple; - this.currentEditor = new PropertyPadObjectEditor (tuple); ; - return Task.FromResult (currentEditor); + if (item is PropertyPadItem propertyPadItem) { + return Task.FromResult<IObjectEditor> (new PropertyPadObjectEditor (propertyPadItem)); } return Task.FromResult<IObjectEditor> (null); } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadItem.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadItem.cs new file mode 100644 index 0000000000..ecd5235fd8 --- /dev/null +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadItem.cs @@ -0,0 +1,69 @@ +// +// PropertyPadItem.cs +// +// Author: +// jmedrano <josmed@microsoft.com> +// +// Copyright (c) 2018 +// +// 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. + +#if MAC + +using System; +using System.Threading.Tasks; +using MonoDevelop.Core; +using Xamarin.PropertyEditing; + +namespace MonoDevelop.DesignerSupport +{ + class PropertyPadItem + { + public PropertyPadItem (object target, object [] providers) + { + Target = target; + Providers = providers; + } + + public object Target { get; } + public object[] Providers { get; } + + public async Task<ValueInfo<T>> GetPropertyValueInfoAsProppyType<T> (DescriptorPropertyInfo propertyInfo) + { + T value = await propertyInfo.GetValueAsync<T> (this); + return new ValueInfo<T> { + Value = value, + Source = ValueSource.Local, + //ValueDescriptor = valueInfoString.ValueDescriptor, + //CustomExpression = valueString + }; + } + + public void SetPropertyValueInfoAsProppyType<T> (DescriptorPropertyInfo info, ValueInfo<T> value, PropertyVariation variations) + { + try { + info.SetValue (this, value.Value); + } catch (Exception ex) { + LoggingService.LogError ("Error setting the value", ex); + } + } + } +} + +#endif
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadObjectEditor.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadObjectEditor.cs index 40fa6dc49a..c272b78a4a 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadObjectEditor.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditors/PropertyPadObjectEditor.cs @@ -39,9 +39,9 @@ using MonoDevelop.Core; namespace MonoDevelop.DesignerSupport { class PropertyPadObjectEditor - : IObjectEditor, INameableObject, IObjectEventEditor + : IObjectEditor, INameableObject { - private readonly object target; + private readonly PropertyPadItem propertyItem; public string Name { get; private set; @@ -51,7 +51,7 @@ namespace MonoDevelop.DesignerSupport private static readonly IObjectEditor [] EmptyDirectChildren = new IObjectEditor [0]; private readonly List<PropertyDescriptorEventInfo> events = new List<PropertyDescriptorEventInfo> (); - public object Target => this.target; + public object Target => this.propertyItem; public ITypeInfo TargetType => ToTypeInfo (Target.GetType ()); @@ -65,7 +65,7 @@ namespace MonoDevelop.DesignerSupport public IReadOnlyDictionary<IPropertyInfo, KnownProperty> KnownProperties => null; - public IObjectEditor Parent => throw new NotImplementedException (); + public IObjectEditor Parent => null; public IReadOnlyList<IObjectEditor> DirectChildren => EmptyDirectChildren; @@ -73,10 +73,10 @@ namespace MonoDevelop.DesignerSupport public event EventHandler<EditorPropertyChangedEventArgs> PropertyChanged; - public PropertyPadObjectEditor (Tuple<object, object []> target) + public PropertyPadObjectEditor (PropertyPadItem propertyItem) { - this.target = target.Item1; - foreach (object propertyProvider in target.Item2) { + this.propertyItem = propertyItem; + foreach (object propertyProvider in propertyItem.Providers) { var props = GetProperties (propertyProvider, null); for (int i = 0; i < props.Count; i++) { @@ -118,10 +118,10 @@ namespace MonoDevelop.DesignerSupport } if (propertyDescriptor.PropertyType.IsAssignableFrom (typeof (Core.FilePath))) { - var isDirectoryPropertyDescriptor = GetPropertyDescriptor (propertyDescriptor.GetChildProperties (), nameof (Core.FilePath.IsDirectory)); - if (isDirectoryPropertyDescriptor != null && (bool)isDirectoryPropertyDescriptor.GetValue (target)) { - return new DirectoryPathPropertyInfo (propertyDescriptor, PropertyProvider, valueSources); - } + //var isDirectoryPropertyDescriptor = GetPropertyDescriptor (propertyDescriptor.GetChildProperties (), nameof (Core.FilePath.IsDirectory)); + //if (isDirectoryPropertyDescriptor != null && (bool)isDirectoryPropertyDescriptor.GetValue (propertyItem)) { + // return new DirectoryPathPropertyInfo (propertyDescriptor, PropertyProvider, valueSources); + //} return new FilePathPropertyInfo (propertyDescriptor, PropertyProvider, valueSources); } @@ -139,20 +139,6 @@ namespace MonoDevelop.DesignerSupport return TypeDescriptor.GetProperties (component); } - #region NOT SUPORTED - - public Task AttachHandlerAsync (IEventInfo ev, string handlerName) - { - throw new NotImplementedException (); - } - - public Task DetachHandlerAsync (IEventInfo ev, string handlerName) - { - throw new NotImplementedException (); - } - - #endregion - public async Task<AssignableTypesResult> GetAssignableTypesAsync (IPropertyInfo property, bool childTypes) { return new AssignableTypesResult (Array.Empty<ITypeInfo> ()); @@ -166,7 +152,7 @@ namespace MonoDevelop.DesignerSupport if (!(ev is Xamarin.PropertyEditing.Reflection.ReflectionEventInfo info)) throw new ArgumentException (); - return Task.FromResult (info.GetHandlers (this.target)); + return Task.FromResult (info.GetHandlers (this.propertyItem)); } public Task<string> GetNameAsync () @@ -184,17 +170,10 @@ namespace MonoDevelop.DesignerSupport if (property == null) throw new ArgumentNullException (nameof (property)); - if (!(property is DescriptorPropertyInfo info)) - throw new ArgumentException (); - - T value = await info.GetValueAsync<T> (this.target); + if (!(property is DescriptorPropertyInfo propertyInfo)) + throw new ArgumentException ("Property should be a DescriptorPropertyInfo", nameof (property)); - return new ValueInfo<T> { - Value = value, - Source = ValueSource.Local, - //ValueDescriptor = valueInfoString.ValueDescriptor, - //CustomExpression = valueString - }; + return await propertyItem.GetPropertyValueInfoAsProppyType<T> (propertyInfo); } public Task RemovePropertyVariantAsync (IPropertyInfo property, PropertyVariation variant) @@ -208,21 +187,19 @@ namespace MonoDevelop.DesignerSupport return Task.FromResult (true); } - public Task SetValueAsync<T> (IPropertyInfo propertyInfo, ValueInfo<T> valueInfo, PropertyVariation variations = null) + public Task SetValueAsync<T> (IPropertyInfo propertyInfo, ValueInfo<T> value, PropertyVariation variations = null) { if (propertyInfo == null) throw new ArgumentNullException (nameof (propertyInfo)); if (propertyInfo is DescriptorPropertyInfo info && info.CanWrite) { - try { - info.SetValue (this.target, valueInfo.Value); - } catch (Exception ex) { - LoggingService.LogError ("Error setting the value", ex); - } + + propertyItem.SetPropertyValueInfoAsProppyType (info, value, variations); OnPropertyChanged (info); + return Task.CompletedTask; } - return Task.CompletedTask; + throw new ArgumentException ($"Property should be a writeable {nameof (DescriptorPropertyInfo)}.", nameof (propertyInfo)); } protected virtual void OnPropertyChanged (IPropertyInfo property) diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateTests.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateTests.cs index 7a51d6a7f7..4522ea3c8e 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateTests.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateTests.cs @@ -271,6 +271,7 @@ namespace MonoDevelop.DotNetCore.Tests [TestCase ("Microsoft.Web.RazorPages.CSharp", "UseNetCore21=true")] [TestCase ("Microsoft.Web.WebApi.CSharp", "UseNetCore21=true")] [TestCase ("Microsoft.Web.WebApi.FSharp", "UseNetCore21=true")] + [TestCase ("Microsoft.Web.Razor.Library.CSharp", "UseNetCore21=true")] public async Task AspNetCore21 (string templateId, string parameters) { if (!IsDotNetCoreSdk21Installed ()) { @@ -287,6 +288,7 @@ namespace MonoDevelop.DotNetCore.Tests [TestCase ("Microsoft.Web.RazorPages.CSharp", "UseNetCore22=true")] [TestCase ("Microsoft.Web.WebApi.CSharp", "UseNetCore22=true")] [TestCase ("Microsoft.Web.WebApi.FSharp", "UseNetCore22=true")] + [TestCase ("Microsoft.Web.Razor.Library.CSharp", "UseNetCore22=true")] public async Task AspNetCore22 (string templateId, string parameters) { if (!IsDotNetCoreSdk22Installed ()) { @@ -304,6 +306,7 @@ namespace MonoDevelop.DotNetCore.Tests [TestCase ("Microsoft.Web.RazorPages.CSharp", "UseNetCore30=true")] [TestCase ("Microsoft.Web.WebApi.CSharp", "UseNetCore30=true")] [TestCase ("Microsoft.Web.WebApi.FSharp", "UseNetCore30=true")] + [TestCase ("Microsoft.Web.Razor.Library.CSharp", "UseNetCore30=true")] public async Task AspNetCore30 (string templateId, string parameters) { if (!IsDotNetCoreSdk30Installed ()) { diff --git a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.csproj b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.csproj index d1da520ca1..bc0bde87a3 100644 --- a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.csproj +++ b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.csproj @@ -7,7 +7,7 @@ <ProjectGuid>{443311BF-766D-4863-B5A1-AFAA7F41DBDA}</ProjectGuid> <TargetFrameworkVersion>$(MDFrameworkVersion)</TargetFrameworkVersion> <OutputPath>..\..\..\build\AddIns\MonoDevelop.Packaging</OutputPath> - <_BuildPackagingVersion>0.2.0</_BuildPackagingVersion> + <_BuildPackagingVersion>0.2.5-dev.8</_BuildPackagingVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " /> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " /> diff --git a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging/DotNetProjectExtensions.cs b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging/DotNetProjectExtensions.cs index 8f6eb98264..b973cfecd6 100644 --- a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging/DotNetProjectExtensions.cs +++ b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging/DotNetProjectExtensions.cs @@ -70,7 +70,7 @@ namespace MonoDevelop.Packaging public static void InstallBuildPackagingNuGetPackage (IEnumerable<Project> projects) { string packagesFolder = GetPackagesFolder (); - var packageReference = new PackageManagementPackageReference ("NuGet.Build.Packaging", "0.2.0"); + var packageReference = new PackageManagementPackageReference ("NuGet.Build.Packaging", "0.2.5-dev.8"); var packageReferences = new [] { packageReference }; diff --git a/main/src/addins/MonoDevelop.Packaging/Templates/CrossPlatformLibrary.xpt.xml b/main/src/addins/MonoDevelop.Packaging/Templates/CrossPlatformLibrary.xpt.xml index 2bfa014101..bbc1befa97 100644 --- a/main/src/addins/MonoDevelop.Packaging/Templates/CrossPlatformLibrary.xpt.xml +++ b/main/src/addins/MonoDevelop.Packaging/Templates/CrossPlatformLibrary.xpt.xml @@ -29,7 +29,7 @@ DefaultNamespace="${ProjectName}" HideGettingStarted="true" /> <Packages> - <Package ID="NuGet.Build.Packaging" Version="0.2.0" directory="../packages" /> + <Package ID="NuGet.Build.Packaging" Version="0.2.5-dev.8" directory="../packages" /> </Packages> <Files> <FileTemplateReference TemplateID="EmptyClass" name="MyClass.cs" /> @@ -50,7 +50,7 @@ <Reference type="Project" refto="${ProjectName}.Shared" /> </References> <Packages> - <Package ID="NuGet.Build.Packaging" Version="0.2.0" directory="../packages" /> + <Package ID="NuGet.Build.Packaging" Version="0.2.5-dev.8" directory="../packages" /> </Packages> <Files> <FileTemplateReference TemplateID="CSharpAssemblyInfo" name="AssemblyInfo.cs" /> @@ -67,7 +67,7 @@ <Reference type="Project" refto="${ProjectName}.Shared" /> </References> <Packages> - <Package ID="NuGet.Build.Packaging" Version="0.2.0" directory="../packages" /> + <Package ID="NuGet.Build.Packaging" Version="0.2.5-dev.8" directory="../packages" /> </Packages> <Files> <FileTemplateReference TemplateID="CSharpAssemblyInfo" name="AssemblyInfo.cs" /> @@ -77,7 +77,7 @@ <Project name="${ProjectName}.NuGet" directory="${ProjectName}.NuGet" type="NuGetPackaging" if="CreateNuGetProject"> <Options TargetFrameworkVersion="4.5" DefaultNamespace="${ProjectName}" HideGettingStarted="true" /> <Packages> - <Package ID="NuGet.Build.Packaging" Version="0.2.0" directory="../packages" /> + <Package ID="NuGet.Build.Packaging" Version="0.2.5-dev.8" directory="../packages" /> </Packages> <References> <Reference type="Project" refto="${ProjectName}.Android" if="CreateAndroidProject" /> diff --git a/main/src/addins/MonoDevelop.Packaging/Templates/PackagingProject.xpt.xml b/main/src/addins/MonoDevelop.Packaging/Templates/PackagingProject.xpt.xml index e5d699884f..82dbbd1fc5 100644 --- a/main/src/addins/MonoDevelop.Packaging/Templates/PackagingProject.xpt.xml +++ b/main/src/addins/MonoDevelop.Packaging/Templates/PackagingProject.xpt.xml @@ -18,7 +18,7 @@ <Project name="${ProjectName}" directory="." type="NuGetPackaging"> <Options TargetFrameworkVersion="4.5" /> <Packages> - <Package ID="NuGet.Build.Packaging" Version="0.2.0" directory="../packages" /> + <Package ID="NuGet.Build.Packaging" Version="0.2.5-dev.8" directory="../packages" /> </Packages> <Files> <File name="readme.txt"><![CDATA[ diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs index 38dd1c42a4..3da12a678e 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeActionEditorExtension.cs @@ -186,7 +186,18 @@ namespace MonoDevelop.CodeActions { Runtime.AssertMainThread (); var caretOffset = Editor.CaretOffset; - return collections.Select (c => FilterOnUIThread (c, workspace)).Where(x => x != null).OrderBy(x => GetDistance (x, caretOffset)).ToImmutableArray (); + var builder = ImmutableArray.CreateBuilder<CodeFixCollection> (collections.Length); + var ids = new HashSet<string> (); + foreach (var c in collections) { + if (!ids.Add (c.FirstDiagnostic.Id)) + continue; + var filtered = FilterOnUIThread (c, workspace); + if (filtered == null) + continue; + builder.Add (filtered); + } + builder.Sort ((x, y) => GetDistance (x, caretOffset).CompareTo (GetDistance (y, caretOffset))); + return builder.ToImmutableArray (); } static int GetDistance (CodeFixCollection fixCollection, int caretOffset) diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs index 6af63e47b2..87836059be 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs @@ -86,14 +86,14 @@ namespace MonoDevelop.SourceEditor.QuickTasks get { return okImage; } - }
-
- Cairo.Color win81Slider;
+ } + + Cairo.Color win81Slider; Cairo.Color win81SliderPrelight; int win81ScrollbarWidth; - protected override void OnStyleSet (Style previous_style)
- {
+ protected override void OnStyleSet (Style previous_style) + { base.OnStyleSet (previous_style); if (Core.Platform.IsWindows) { using (var scrollstyle = Rc.GetStyleByPaths (Settings, null, null, VScrollbar.GType)) { @@ -228,7 +228,7 @@ namespace MonoDevelop.SourceEditor.QuickTasks } void DestroyBackgroundSurface () - {
+ { if (backgroundSurface != null) { backgroundSurface.Dispose (); backgroundSurface = null; @@ -1002,20 +1002,20 @@ namespace MonoDevelop.SourceEditor.QuickTasks } void IndicatorSurfaceTimeoutHandler (bool forceUpdate) - {
- indicatorIdleTimout = 0;
- if (!IsRealized)
- return;
- var allocation = Allocation;
- src?.Cancel ();
- src = new CancellationTokenSource ();
- new IdleUpdater (this, src.Token) { ForceUpdate = forceUpdate }.Start ();
+ { + indicatorIdleTimout = 0; + if (!IsRealized) + return; + var allocation = Allocation; + src?.Cancel (); + src = new CancellationTokenSource (); + new IdleUpdater (this, src.Token) { ForceUpdate = forceUpdate }.Start (); } void RemoveIndicatorIdleHandler () { + src?.Cancel (); if (indicatorIdleTimout > 0) { - src?.Cancel (); GLib.Source.Remove (indicatorIdleTimout); indicatorIdleTimout = 0; } @@ -1415,8 +1415,8 @@ namespace MonoDevelop.SourceEditor.QuickTasks public QuickTaskAccessible (QuickTaskStrip parent, QuickTaskOverviewMode parentMode, QuickTask t) : this (parent, parentMode) { task = t; - usage = null;
-
+ usage = null; + var line = mode.TextEditor.OffsetToLineNumber (t.Location); Accessible.Title = t.Description; Accessible.Help = string.Format (GettextCatalog.GetString ("Jump to line {0}"), line.ToString ()); diff --git a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/MonoDevelop.TextEditor.csproj b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/MonoDevelop.TextEditor.csproj index 30e59d1313..c728874351 100644 --- a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/MonoDevelop.TextEditor.csproj +++ b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/MonoDevelop.TextEditor.csproj @@ -18,7 +18,7 @@ <InternalsVisibleTo Include="MonoDevelop.TextEditor.Wpf" /> <Reference Include="PresentationFramework" /> <Reference Include="PresentationCore" /> - <ProjectReference Include="..\..\MonoDevelop.DesignerSupport\MonoDevelop.DesignerSupport.csproj" /> + <ProjectReference Include="..\..\MonoDevelop.DesignerSupport\MonoDevelop.DesignerSupport.csproj" Private="false" /> </ItemGroup> <ItemGroup> <Compile Include="..\..\..\..\external\vs-editor-core\src\Editor\Text\Impl\BaseViewImpl\VacuousTextDataModel.cs" Link="Util\VacuousTextDataModel.cs" /> diff --git a/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/Commands.cs b/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/Commands.cs index b82de9cb12..852c472a20 100644 --- a/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/Commands.cs +++ b/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/Commands.cs @@ -25,6 +25,12 @@ namespace PerformanceDiagnosticsAddIn public class ProfileFor5SecondsHandler : CommandHandler { + protected override void Update (CommandInfo info) + { + info.DisableOnShellLock = false; + base.Update (info); + } + protected override void Run () { UIThreadMonitor.Instance.Profile (5); @@ -46,6 +52,7 @@ namespace PerformanceDiagnosticsAddIn protected override void Update(CommandInfo info) { info.Checked = UIThreadMonitor.Instance.ToggleProfilingChecked; + info.DisableOnShellLock = false; base.Update(info); } diff --git a/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs b/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs index cfa5c6c497..894fee35d9 100644 --- a/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs +++ b/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs @@ -210,7 +210,11 @@ namespace PerformanceDiagnosticsAddIn internal static void ConvertJITAddressesToMethodNames (string fileName, string profilingType) { + // sample line to replace: + // ??? (in <unknown binary>) [0x103648455] var rx = new Regex (@"\?\?\? \(in <unknown binary>\) \[0x([0-9a-f]+)\]", RegexOptions.Compiled); + + if (File.Exists (fileName) && new FileInfo (fileName).Length > 0) { Directory.CreateDirectory (Options.OutputPath); var outputFilename = Path.Combine (Options.OutputPath, $"{BrandingService.ApplicationName}_{profilingType}_{DateTime.Now:yyyy-MM-dd__HH-mm-ss}.txt"); @@ -219,14 +223,18 @@ namespace PerformanceDiagnosticsAddIn using (var sw = new StreamWriter (outputFilename)) { string line; while ((line = sr.ReadLine ()) != null) { - if (rx.IsMatch (line)) { - var match = rx.Match (line); + + var match = rx.Match (line); + if (match.Success) { var offset = long.Parse (match.Groups [1].Value, NumberStyles.HexNumber); - string pmipMethodName; - if (!methodsCache.TryGetValue (offset, out pmipMethodName)) { + + if (!methodsCache.TryGetValue (offset, out var pmipMethodName)) { pmipMethodName = mono_pmip (offset)?.TrimStart (); + if (pmipMethodName != null) + pmipMethodName = PmipParser.ToSample (pmipMethodName, offset); methodsCache.Add (offset, pmipMethodName); } + if (pmipMethodName != null) { line = line.Remove (match.Index, match.Length); line = line.Insert (match.Index, pmipMethodName); @@ -238,6 +246,95 @@ namespace PerformanceDiagnosticsAddIn } } + static class PmipParser + { + // pmip output: + // (wrapper managed-to-native) Gtk.Application:gtk_main () [{0x7f968e48d1e8} + 0xdf] (0x122577d50 0x122577f28) [0x7f9682702c90 - MonoDevelop.exe] + // MonoDevelop.Startup.MonoDevelopMain:Main (string[]) [{0x7faef5700948} + 0x93] [/Users/therzok/Work/md/monodevelop/main/src/core/MonoDevelop.Startup/MonoDevelop.Startup/MonoDevelopMain.cs :: 39u] (0x10e7609c0 0x10e760aa8) [0x7faef7002d80 - MonoDevelop.exe] + + // sample symbolified line: + // start (in libdyld.dylib) + 1 [0x7fff79c7ded9] + // mono_hit_runtime_invoke (in mono64) + 1619 [0x102f90083] mini-runtime.c:3148 + public static string ToSample (string initialInput, long offset) + { + try { + var input = initialInput.AsSpan (); + var sb = new StringBuilder (); + string filename = null; + + // Cut off wrapper part. + if (input.StartsWith ("(wrapper".AsSpan ())) { + input = input.Slice (input.IndexOf (')') + 1).TrimStart (); + } + + // If it starts with <Module>:, trim it. + if (input.StartsWith ("<Module>:".AsSpan ())) { + input = input.Slice ("<Module>:".Length); + } + + // Usually a generic trampoline marker, don't bother parsing. + if (input[0] == '<') + return input.ToString (); + + // Decode method signature + // Gtk.Application:gtk_main () [{0x7f968e48d1e8} + 0xdf] + var endMethodSignature = input.IndexOf ('{'); + var methodSignature = input.Slice (0, endMethodSignature - 2); // " [" + input = input.Slice (endMethodSignature + 1).TrimStart (); + + // Append chars, escaping what might be unreadable by instruments. + for (int i = 0; i < methodSignature.Length; ++i) { + var ch = methodSignature [i]; + if (ch == ' ') + continue; + + if (ch == ':') { + sb.Append ("::"); + continue; + } + + if (ch == '.') { + sb.Append ("_"); + continue; + } + + if (ch == '[' && methodSignature [i + 1] == ']') { + sb.Append ("*"); + i++; + continue; + } + + sb.Append (ch); + } + + // Add some data to match format, + 0 is because it doesn't matter, we're not looking at native code. + sb.Append (" (in MonoDevelop.exe) + 0 ["); + sb.AppendFormat ("0x{0:x}", offset); + sb.Append ("]"); + + // Skip the rest of the block(s) after the method signature until we get a path. + input = input.Slice (input.IndexOf ('[') + 1).TrimStart (); + + if (input[0] == '/') { + // We have a filename + var end = input.IndexOf (']'); + var filepath = input.Slice (0, end - 1).Trim (); // trim u + filename = filepath.ToString (); + } + + if (filename != null) { + sb.Append (" "); + sb.Append (filename); + } + + return sb.ToString (); + } catch (Exception e) { + LoggingService.LogInternalError ($"Failed to parse line '{initialInput}'", e); + return initialInput; + } + } + } + ConnectionInfo currentConnection; class ConnectionInfo 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 c82a36cb7b..86a4f93b9f 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 @@ -1779,7 +1779,7 @@ namespace MonoDevelop.VersionControl.Git public string GetCurrentBranch () { - return RunOperation (() => RootRepository.Head.FriendlyName); + return RunSafeOperation (() => RootRepository.Head.FriendlyName); } void SwitchBranchInternal (ProgressMonitor monitor, string branch) diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Dialogs/SelectRepositoryDialog.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Dialogs/SelectRepositoryDialog.cs index cd80d2ceac..c65e4434cc 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Dialogs/SelectRepositoryDialog.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Dialogs/SelectRepositoryDialog.cs @@ -402,7 +402,8 @@ namespace MonoDevelop.VersionControl.Dialogs } var vcs = systems [repCombo.Active]; - entryFolder.Text = defaultPath + vcs.GetRelativeCheckoutPathForRemote (edit.RelativePath); + var projectNameFolder = System.IO.Path.DirectorySeparatorChar + System.IO.Path.GetFileName (edit.RelativePath); + entryFolder.Text = defaultPath + vcs.GetRelativeCheckoutPathForRemote (projectNameFolder); } } } diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/CellRendererDiff.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/CellRendererDiff.cs index 54824f5ab1..2c41dd248d 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/CellRendererDiff.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/CellRendererDiff.cs @@ -150,70 +150,8 @@ namespace MonoDevelop.VersionControl.Views // Rendering is done in two steps: // 1) Get a list of blocks to render // 2) render the blocks - - int y = cell_area.Y + 2; - - // cline keeps track of the current source code line (the one to jump to when double clicking) - int cline = 1; - bool inHeader = true; - BlockInfo currentBlock = null; - - List<BlockInfo> blocks = new List<BlockInfo> (); - - for (int n=0; n<lines.Length; n++, y += lineHeight) { - - string line = lines [n]; - if (line.Length == 0) { - currentBlock = null; - y -= lineHeight; - continue; - } - - char tag = line [0]; - - if (line.StartsWith ("---", StringComparison.Ordinal) || - line.StartsWith ("+++", StringComparison.Ordinal)) { - // Ignore this part of the header. - currentBlock = null; - y -= lineHeight; - continue; - } - if (tag == '@') { - int l = ParseCurrentLine (line); - if (l != -1) cline = l - 1; - inHeader = false; - } else if (tag == '+' && !inHeader) - cline++; - BlockType type; - switch (tag) { - case '-': type = BlockType.Removed; break; - case '+': type = BlockType.Added; break; - case '@': type = BlockType.Info; break; - default: type = BlockType.Unchanged; break; - } - - if (currentBlock == null || type != currentBlock.Type) { - if (y > maxy) - break; - - // Starting a new block. Mark section ends between a change block and a normal code block - if (currentBlock != null && IsChangeBlock (currentBlock.Type) && !IsChangeBlock (type)) - currentBlock.SectionEnd = true; - - currentBlock = new BlockInfo () { - YStart = y, - FirstLine = n, - Type = type, - SourceLineStart = cline, - SectionStart = (blocks.Count == 0 || !IsChangeBlock (blocks[blocks.Count - 1].Type)) && IsChangeBlock (type) - }; - blocks.Add (currentBlock); - } - // Include the line in the current block - currentBlock.YEnd = y + lineHeight; - currentBlock.LastLine = n; - } + var blocks = CalculateBlocks (maxy, cell_area.Y + 2); // Now render the blocks @@ -315,7 +253,81 @@ namespace MonoDevelop.VersionControl.Views window.DrawLayout (widget.Style.TextGC (GetState(widget, flags)), cell_area.X, y, layout); } } - + + List<BlockInfo> CalculateBlocks (int maxy, int y) + { + // cline keeps track of the current source code line (the one to jump to when double clicking) + int cline = 1; + + BlockInfo currentBlock = null; + + var result = new List<BlockInfo> (); + int removedLines = 0; + for (int n = 0; n < lines.Length; n++, y += lineHeight) { + + string line = lines [n]; + if (line.Length == 0) { + currentBlock = null; + y -= lineHeight; + continue; + } + + char tag = line [0]; + + if (line.StartsWith ("---", StringComparison.Ordinal) || + line.StartsWith ("+++", StringComparison.Ordinal)) { + // Ignore this part of the header. + currentBlock = null; + y -= lineHeight; + continue; + } + if (tag == '@') { + int l = ParseCurrentLine (line); + if (l != -1) cline = l - 1; + } else + cline++; + + BlockType type; + switch (tag) { + case '-': + type = BlockType.Removed; + removedLines++; + break; + case '+': type = BlockType.Added; break; + case '@': type = BlockType.Info; break; + default: type = BlockType.Unchanged; break; + } + + if (type != BlockType.Removed && removedLines > 0) { + cline -= removedLines; + removedLines = 0; + } + + if (currentBlock == null || type != currentBlock.Type) { + if (y > maxy) + break; + + // Starting a new block. Mark section ends between a change block and a normal code block + if (currentBlock != null && IsChangeBlock (currentBlock.Type) && !IsChangeBlock (type)) + currentBlock.SectionEnd = true; + + currentBlock = new BlockInfo { + YStart = y, + FirstLine = n, + Type = type, + SourceLineStart = cline, + SectionStart = (result.Count == 0 || !IsChangeBlock (result [result.Count - 1].Type)) && IsChangeBlock (type) + }; + result.Add (currentBlock); + } + // Include the line in the current block + currentBlock.YEnd = y + lineHeight; + currentBlock.LastLine = n; + } + + return result; + } + static bool IsChangeBlock (BlockType t) { return t == BlockType.Added || t == BlockType.Removed; diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/StatusView.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/StatusView.cs index ff8d111a1c..30a2dd0338 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/StatusView.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/StatusView.cs @@ -13,6 +13,7 @@ using MonoDevelop.Components.Commands; using MonoDevelop.Projects; using MonoDevelop.Ide; using System.Text; +using System.Threading.Tasks; namespace MonoDevelop.VersionControl.Views { @@ -447,29 +448,50 @@ namespace MonoDevelop.VersionControl.Views showRemoteStatus.Sensitive = false; buttonCommit.Sensitive = false; - ThreadPool.QueueUserWorkItem (delegate { - lock (updateLock) { - if (fileList != null) { - var group = fileList.GroupBy (v => v.IsDirectory || v.WorkspaceObject is SolutionFolderItem); - foreach (var item in group) { - // Is directory. - if (item.Key) { - foreach (var directory in item) - changeSet.AddFiles (vc.GetDirectoryVersionInfo (directory.Path, remoteStatus, true)); - } else - changeSet.AddFiles (item.Select (v => v.VersionInfo).ToArray ()); - } - changeSet.AddFiles (fileList.Where (v => !v.IsDirectory).Select (v => v.VersionInfo).ToArray ()); - fileList = null; + lock (updateLock) { + if (!cancelUpdate.IsCancellationRequested) + cancelUpdate.Cancel (); + cancelUpdate = new CancellationTokenSource (); + var token = cancelUpdate.Token; + updateTask = updateTask.ContinueWith (t => RunUpdate (token), token, TaskContinuationOptions.LazyCancellation, TaskScheduler.Default); + } + } + + CancellationTokenSource cancelUpdate = new CancellationTokenSource (); + Task updateTask = Task.FromResult (true); + + void RunUpdate (CancellationToken cancel) + { + try { + cancel.ThrowIfCancellationRequested (); + if (fileList != null) { + var group = fileList.GroupBy (v => v.IsDirectory || v.WorkspaceObject is SolutionFolderItem); + foreach (var item in group) { + // Is directory. + if (item.Key) { + foreach (var directory in item) + changeSet.AddFiles (vc.GetDirectoryVersionInfo (directory.Path, remoteStatus, true)); + } else + changeSet.AddFiles (item.Select (v => v.VersionInfo).ToArray ()); } - List<VersionInfo> newList = new List<VersionInfo> (); - newList.AddRange (vc.GetDirectoryVersionInfo (filepath, remoteStatus, true)); - Runtime.RunInMainThread (delegate { - if (!disposed) - LoadStatus (newList); - }); + fileList = null; } - }); + + cancel.ThrowIfCancellationRequested (); + var newList = new List<VersionInfo> (); + newList.AddRange (vc.GetDirectoryVersionInfo (filepath, remoteStatus, true)); + + cancel.ThrowIfCancellationRequested (); + Runtime.RunInMainThread (delegate { + // skip status reloading if another update is queued + if (!cancel.IsCancellationRequested && !disposed) + LoadStatus (newList); + }).Ignore (); + } catch (Exception ex) { + if (!(ex is OperationCanceledException)) + LoggingService.LogError ("VCS StatusView update failed", ex); + throw; + } } void LoadStatus (List<VersionInfo> newList) 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 6153628e47..02316c9a1a 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs @@ -106,7 +106,9 @@ namespace MonoDevelop.VersionControl } } - VersionControlService.repositoryCache.TryRemove (RepositoryPath.CanonicalPath, out _); + lock (VersionControlService.repositoryCacheLock) { + VersionControlService.repositoryCache.Remove (RepositoryPath.CanonicalPath); + } infoCache?.Dispose (); infoCache = null; diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs index c5d9b098d2..610af88219 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs @@ -211,7 +211,8 @@ namespace MonoDevelop.VersionControl return String.Empty; } - internal static ConcurrentDictionary<Repository, InternalRepositoryReference> referenceCache = new ConcurrentDictionary<Repository, InternalRepositoryReference> (); + internal static readonly object referenceCacheLock = new object (); + internal static Dictionary<Repository, InternalRepositoryReference> referenceCache = new Dictionary<Repository, InternalRepositoryReference> (); public static Repository GetRepository (WorkspaceObject entry) { if (IsGloballyDisabled) @@ -225,14 +226,20 @@ namespace MonoDevelop.VersionControl InternalRepositoryReference rref = null; if (repo != null) { repo.AddRef (); - rref = referenceCache.GetOrAdd (repo, r => new InternalRepositoryReference (r)); + lock (referenceCacheLock) { + if (!referenceCache.TryGetValue (repo, out rref)) { + rref = new InternalRepositoryReference (repo); + referenceCache [repo] = rref; + } + } } entry.ExtendedProperties [typeof(InternalRepositoryReference)] = rref; return repo; } - internal static readonly ConcurrentDictionary<FilePath,Repository> repositoryCache = new ConcurrentDictionary<FilePath,Repository> (); + internal static readonly object repositoryCacheLock = new object (); + internal static readonly Dictionary<FilePath,Repository> repositoryCache = new Dictionary<FilePath,Repository> (); public static Repository GetRepositoryReference (string path, string id) { VersionControlSystem detectedVCS = null; @@ -256,22 +263,21 @@ namespace MonoDevelop.VersionControl bestMatch = bestMatch.CanonicalPath; - try { - return repositoryCache.GetOrAdd (bestMatch, p => { - var result = detectedVCS?.GetRepositoryReference (p, id); - if (result != null) { + lock (repositoryCacheLock) { + if (repositoryCache.TryGetValue (bestMatch, out var repository)) + return repository; + + try { + var repo = detectedVCS?.GetRepositoryReference (bestMatch, id); + if (repo != null) { + repositoryCache.Add (bestMatch, repo); Instrumentation.Repositories.Inc (new RepositoryMetadata (detectedVCS)); - result.RepositoryPath = p.CanonicalPath; - return result; } - // never add null values - throw new ArgumentNullException ("result"); - }); - } catch (Exception e) { - // ArgumentNullException for "result" is expected when GetRepositoryReference returns null, no need to log - if (!(e is ArgumentNullException ne) || ne.ParamName != "result") - LoggingService.LogInternalError ($"Could not query {detectedVCS.Name} repository reference", e); - return null; + return repo; + } catch (Exception e) { + LoggingService.LogError ($"Could not query {detectedVCS.Name} repository reference", e); + return null; + } } } @@ -839,7 +845,9 @@ namespace MonoDevelop.VersionControl public void Dispose () { - VersionControlService.referenceCache.TryRemove (repo, out _); + lock (VersionControlService.referenceCacheLock) { + VersionControlService.referenceCache.Remove (repo); + } repo.Unref (); } } diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml b/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml index ec2e9bd127..e66d577a8a 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml @@ -204,6 +204,8 @@ <CommandItem id = "MonoDevelop.VersionControl.Commands.Diff"/> <CommandItem id = "MonoDevelop.VersionControl.Commands.Log"/> <CommandItem id = "MonoDevelop.VersionControl.Commands.Annotate"/> + <SeparatorItem/> + <CommandItem id = "MonoDevelop.VersionControl.Commands.Revert"/> </ItemSet> <SeparatorItem/> </Extension> diff --git a/main/src/addins/WindowsPlatform/WindowsPlatform/WindowsPlatform.cs b/main/src/addins/WindowsPlatform/WindowsPlatform/WindowsPlatform.cs index 133db60e95..d5cb477e85 100644 --- a/main/src/addins/WindowsPlatform/WindowsPlatform/WindowsPlatform.cs +++ b/main/src/addins/WindowsPlatform/WindowsPlatform/WindowsPlatform.cs @@ -73,66 +73,66 @@ namespace MonoDevelop.Platform } #region Toolbar implementation - Components.Commands.CommandManager commandManager; - string commandMenuAddinPath; - string appMenuAddinPath; - public override bool SetGlobalMenu (Components.Commands.CommandManager commandManager, string commandMenuAddinPath, string appMenuAddinPath) - { - // Only store this information. Release it when creating the main toolbar. - this.commandManager = commandManager; - this.commandMenuAddinPath = commandMenuAddinPath; - this.appMenuAddinPath = appMenuAddinPath; - - return true; - } - - const int WM_SYSCHAR = 0x0106; - internal override void AttachMainToolbar (Gtk.VBox parent, Components.MainToolbar.IMainToolbarView toolbar) - { - titleBar = new TitleBar (); - var topMenu = new WPFTitlebar (titleBar); - - //commandManager.IncompleteKeyPressed += (sender, e) => { - // if (e.Key == Gdk.Key.Alt_L) { - // Keyboard.Focus(titleBar.DockTitle.Children[0]); - // } - //}; - parent.PackStart (topMenu, false, true, 0); - SetupMenu (); - - parent.PackStart ((WPFToolbar)toolbar, false, true, 0); - } - - void SetupMenu () - { - // TODO: Use this? - CommandEntrySet appCes = commandManager.CreateCommandEntrySet (appMenuAddinPath); - - CommandEntrySet ces = commandManager.CreateCommandEntrySet (commandMenuAddinPath); - var mainMenu = new Menu { - IsMainMenu = true, - FocusVisualStyle = null, - }; - foreach (CommandEntrySet ce in ces) - { - var item = new TitleMenuItem (commandManager, ce, menu: mainMenu); - mainMenu.Items.Add(item); - } - - titleBar.DockTitle.Children.Add (mainMenu); - DockPanel.SetDock (mainMenu, Dock.Left); - - commandManager = null; - commandMenuAddinPath = appMenuAddinPath = null; - } - - TitleBar titleBar; - internal override Components.MainToolbar.IMainToolbarView CreateMainToolbar (Gtk.Window window) - { - return new WPFToolbar { - HeightRequest = 40, - }; - } + //Components.Commands.CommandManager commandManager; + //string commandMenuAddinPath; + //string appMenuAddinPath; + //public override bool SetGlobalMenu (Components.Commands.CommandManager commandManager, string commandMenuAddinPath, string appMenuAddinPath) + //{ + // // Only store this information. Release it when creating the main toolbar. + // this.commandManager = commandManager; + // this.commandMenuAddinPath = commandMenuAddinPath; + // this.appMenuAddinPath = appMenuAddinPath; + + // return true; + //} + + //const int WM_SYSCHAR = 0x0106; + // internal override void AttachMainToolbar (Gtk.VBox parent, Components.MainToolbar.IMainToolbarView toolbar) + //{ + // titleBar = new TitleBar (); + // var topMenu = new WPFTitlebar (titleBar); + + // //commandManager.IncompleteKeyPressed += (sender, e) => { + // // if (e.Key == Gdk.Key.Alt_L) { + // // Keyboard.Focus(titleBar.DockTitle.Children[0]); + // // } + // //}; + // parent.PackStart (topMenu, false, true, 0); + // SetupMenu (); + + // parent.PackStart ((WPFToolbar)toolbar, false, true, 0); + //} + + //void SetupMenu () + //{ + // // TODO: Use this? + // CommandEntrySet appCes = commandManager.CreateCommandEntrySet (appMenuAddinPath); + + // CommandEntrySet ces = commandManager.CreateCommandEntrySet (commandMenuAddinPath); + // var mainMenu = new Menu { + // IsMainMenu = true, + // FocusVisualStyle = null, + // }; + // foreach (CommandEntrySet ce in ces) + // { + // var item = new TitleMenuItem (commandManager, ce, menu: mainMenu); + // mainMenu.Items.Add(item); + // } + + // titleBar.DockTitle.Children.Add (mainMenu); + // DockPanel.SetDock (mainMenu, Dock.Left); + + // commandManager = null; + // commandMenuAddinPath = appMenuAddinPath = null; + //} + + //TitleBar titleBar; + //internal override Components.MainToolbar.IMainToolbarView CreateMainToolbar (Gtk.Window window) + //{ + // return new WPFToolbar { + // HeightRequest = 40, + // }; + //} #endregion public override bool GetIsFullscreen (Components.Window window) diff --git a/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs b/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs index b37fa41673..a645cf49c3 100644 --- a/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs +++ b/main/src/addins/Xml/Editor/BaseXmlEditorExtension.cs @@ -472,7 +472,7 @@ namespace MonoDevelop.Xml.Editor var encoding = Editor.Encoding.WebName; list.Add (new BaseXmlCompletionData($"?xml version=\"1.0\" encoding=\"{encoding}\" ?>")); } - AddCloseTag (list, Tracker.Engine.Nodes); + AddCloseTag (list, Tracker.Engine.Nodes, currentChar); if (tracker.Engine.CurrentState is XmlNameState) if (GetCompletionCommandOffset (out var cpos, out var wlen)) list.TriggerWordLength = wlen; @@ -678,8 +678,8 @@ namespace MonoDevelop.Xml.Editor } return null; } - - protected static void AddCloseTag (CompletionDataList completionList, NodeStack stack) + + protected static void AddCloseTag (CompletionDataList completionList, NodeStack stack, char currentChar) { //FIXME: search forward to see if tag's closed already var elements = new List<XElement> (); @@ -692,7 +692,12 @@ namespace MonoDevelop.Xml.Editor if (elements.Count == 0) { string name = el.Name.FullName; - completionList.Add (new XmlTagCompletionData ("/" + name + ">", 0, true) { + var sb = StringBuilderCache.Allocate (); + if (currentChar != '/') + sb.Append ("/"); + sb.Append (name); + sb.Append (">"); + completionList.Add (new XmlTagCompletionData (StringBuilderCache.ReturnAndFree (sb), 0, true) { Description = GettextCatalog.GetString ("Closing tag for '{0}'", name) }); } else { diff --git a/main/src/addins/Xml/Tests/CodeCompletion/XmlCodeCompletionTests.cs b/main/src/addins/Xml/Tests/CodeCompletion/XmlCodeCompletionTests.cs index 5ffe1c26ce..92afb0b270 100644 --- a/main/src/addins/Xml/Tests/CodeCompletion/XmlCodeCompletionTests.cs +++ b/main/src/addins/Xml/Tests/CodeCompletion/XmlCodeCompletionTests.cs @@ -6,6 +6,7 @@ using MonoDevelop.Ide.Editor.Extension; using MonoDevelop.Xml.Editor; using NUnit.Framework; using MonoDevelop.Ide.CodeCompletion; +using System.Linq; namespace MonoDevelop.Xml.Tests.CodeCompletion { @@ -22,6 +23,46 @@ namespace MonoDevelop.Xml.Tests.CodeCompletion await ext.TriggerCompletion (Ide.CodeCompletion.CompletionTriggerReason.CompletionCommand); ext.KeyPress (KeyDescriptor.FromGtk (Gdk.Key.colon, ':', Gdk.ModifierType.None)); Assert.IsTrue (CompletionWindowManager.IsVisible); + CompletionWindowManager.Wnd.HideWindow (); + } + } + + /// <summary> + /// FeedbackTicket 739349: XAML Editor: When a closing tag already has '</' present, choosing the closing element from the completion window enters an invalid closing tag such as <//ContentView> + /// </summary> + [Test] + public async Task TestVSTS739349 () + { + const string input = @" +<Foo> +</F"; + using (var testCase = await SetupTestCase (input, input.Length)) { + var doc = testCase.Document; + var ext = doc.GetContent<BaseXmlEditorExtension> (); + ext.CompletionWidget = doc.Editor.GetViewContent ().GetContent<ICompletionWidget> (); + await ext.TriggerCompletion (Ide.CodeCompletion.CompletionTriggerReason.CompletionCommand); + Assert.IsTrue (CompletionWindowManager.IsVisible); + + Assert.AreEqual ("Foo>", CompletionWindowManager.Wnd.SelectedItem.DisplayText); + CompletionWindowManager.Wnd.HideWindow (); + } + } + + [Test] + public async Task TestVSTS739349_Case2 () + { + const string input = @" +<Foo> +<"; + using (var testCase = await SetupTestCase (input, input.Length)) { + var doc = testCase.Document; + var ext = doc.GetContent<BaseXmlEditorExtension> (); + ext.CompletionWidget = doc.Editor.GetViewContent ().GetContent<ICompletionWidget> (); + await ext.TriggerCompletion (Ide.CodeCompletion.CompletionTriggerReason.CompletionCommand); + Assert.IsTrue (CompletionWindowManager.IsVisible); + var list = CompletionWindowManager.Wnd.GetFilteredItems (); + Assert.IsTrue (list.Any (i => i.DisplayText == "/Foo>")); + CompletionWindowManager.Wnd.HideWindow (); } } diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml index 2335c3c0ce..1de3bd4529 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml @@ -265,6 +265,8 @@ <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.SplitWindowHorizontally" /> <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.UnsplitWindow" /> <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.SwitchSplitWindow" />--> + <SeparatorItem id = "welcomePageSep" /> + <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.ShowWelcomePage" /> <SeparatorItem id = "contentSep" /> <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.OpenWindowList" /> <SeparatorItem id = "windowDocSep" /> @@ -278,8 +280,6 @@ <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.OpenDocument8" /> <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.OpenDocument9" /> <CommandItem id = "MonoDevelop.Ide.Commands.WindowCommands.OpenDocumentList" /> - <SeparatorItem id = "welcomePageSep" /> - <CommandItem id = "MonoDevelop.Ide.Commands.ViewCommands.ShowWelcomePage" /> </ItemSet> <ItemSet id = "Help" _label = "_Help"> diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml index 7c053a7b9c..3ffd21814c 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml @@ -68,7 +68,7 @@ <ExtensionPoint path = "/MonoDevelop/Ide/InitCompleteHandlers" name = "Post inialization handlers"> <Description>Commands to be automatically executed when the IDE finishes initalization.</Description> - <ExtensionNode name="Class"> + <ExtensionNode name="Class" objectType="MonoDevelop.Components.Commands.CommandHandler"> <Description>A subclass of MonoDevelop.Components.Commands.CommandHandler</Description> </ExtensionNode> </ExtensionPoint> diff --git a/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Projects.AddExternalFileDialog.cs b/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Projects.AddExternalFileDialog.cs index 76e6194b6f..075e8f449a 100644 --- a/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Projects.AddExternalFileDialog.cs +++ b/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Projects.AddExternalFileDialog.cs @@ -242,7 +242,6 @@ namespace MonoDevelop.Ide.Projects this.DefaultHeight = 286; this.radioKeep.Hide (); this.checkApplyAll.Hide (); - this.Show (); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs index 9b63190523..39b63f339e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs @@ -403,50 +403,51 @@ namespace MonoDevelop.Components.Docking internal bool UpdatePlaceholder (DockItem item, Gdk.Size size, bool allowDocking) { - if (!Runtime.IsMainThread) { - var msg = "UpdatePlaceholder called from background thread."; - LoggingService.LogInternalError ($"{msg}\n{Environment.StackTrace}", new InvalidOperationException (msg)); - } + try { + Runtime.AssertMainThread (); - var placeholderWindow = this.placeholderWindow; - var padTitleWindow = this.padTitleWindow; + var placeholderWindow = this.placeholderWindow; + var padTitleWindow = this.padTitleWindow; - if (placeholderWindow == null || padTitleWindow == null) - return false; - - int px, py; - GetPointer (out px, out py); - - placeholderWindow.AllowDocking = allowDocking; - - int ox, oy; - GdkWindow.GetOrigin (out ox, out oy); - - int tw, th; - padTitleWindow.GetSize (out tw, out th); - padTitleWindow.Move (ox + px - tw/2, oy + py - th/2); - padTitleWindow.GdkWindow.KeepAbove = true; - - DockDelegate dockDelegate; - Gdk.Rectangle rect; - if (allowDocking && layout.GetDockTarget (item, px, py, out dockDelegate, out rect)) { - placeholderWindow.Relocate (ox + rect.X, oy + rect.Y, rect.Width, rect.Height, true); - placeholderWindow.Show (); - placeholderWindow.SetDockInfo (dockDelegate, rect); - return true; - } else { - int w,h; - var gi = layout.FindDockGroupItem (item.Id); - if (gi != null) { - w = gi.Allocation.Width; - h = gi.Allocation.Height; + if (placeholderWindow == null || padTitleWindow == null || !IsRealized) + return false; + + int px, py; + GetPointer (out px, out py); + + placeholderWindow.AllowDocking = allowDocking; + + int ox, oy; + GdkWindow.GetOrigin (out ox, out oy); + + int tw, th; + padTitleWindow.GetSize (out tw, out th); + padTitleWindow.Move (ox + px - tw / 2, oy + py - th / 2); + padTitleWindow.GdkWindow.KeepAbove = true; + + DockDelegate dockDelegate; + Gdk.Rectangle rect; + if (allowDocking && layout.GetDockTarget (item, px, py, out dockDelegate, out rect)) { + placeholderWindow.Relocate (ox + rect.X, oy + rect.Y, rect.Width, rect.Height, true); + placeholderWindow.Show (); + placeholderWindow.SetDockInfo (dockDelegate, rect); + return true; } else { - w = item.DefaultWidth; - h = item.DefaultHeight; + int w, h; + var gi = layout.FindDockGroupItem (item.Id); + if (gi != null) { + w = gi.Allocation.Width; + h = gi.Allocation.Height; + } else { + w = item.DefaultWidth; + h = item.DefaultHeight; + } + placeholderWindow.Relocate (ox + px - w / 2, oy + py - h / 2, w, h, false); + placeholderWindow.Show (); + placeholderWindow.AllowDocking = false; } - placeholderWindow.Relocate (ox + px - w / 2, oy + py - h / 2, w, h, false); - placeholderWindow.Show (); - placeholderWindow.AllowDocking = false; + } catch (Exception ex) { + LoggingService.LogInternalError ("Updating the dock container placeholder failed", ex); } return false; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/FoldingTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/FoldingTextEditorExtension.cs index 82eae3fc76..a8c3509b90 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/FoldingTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/FoldingTextEditorExtension.cs @@ -82,73 +82,77 @@ namespace MonoDevelop.Ide.Editor.Extension if (parsedDocument == null || !Editor.Options.ShowFoldMargin || parsedDocument.Flags.HasFlag (ParsedDocumentFlags.SkipFoldings)) return; // don't update parsed documents that contain errors - the foldings from there may be invalid. - if (await parsedDocument.HasErrorsAsync (token)) + if (await parsedDocument.HasErrorsAsync (token).ConfigureAwait (false)) return; + IReadOnlyList<FoldingRegion> foldings = null; try { - var foldSegments = new List<IFoldSegment> (); - - foreach (FoldingRegion region in await parsedDocument.GetFoldingsAsync (token)) { - if (token.IsCancellationRequested) - return; - var type = FoldingType.Unknown; - bool setFolded = false; - bool folded = false; - //decide whether the regions should be folded by default - switch (region.Type) { - case FoldType.Member: - type = FoldingType.TypeMember; - break; - case FoldType.Type: - type = FoldingType.TypeDefinition; - break; - case FoldType.UserRegion: - type = FoldingType.Region; - setFolded = DefaultSourceEditorOptions.Instance.DefaultRegionsFolding; - folded = true; - break; - case FoldType.Comment: - type = FoldingType.Comment; - setFolded = DefaultSourceEditorOptions.Instance.DefaultCommentFolding; - folded = true; - break; - case FoldType.CommentInsideMember: - type = FoldingType.Comment; - setFolded = DefaultSourceEditorOptions.Instance.DefaultCommentFolding; - folded = false; - break; - case FoldType.Undefined: - setFolded = true; - folded = region.IsFoldedByDefault; - break; - } - var start = Editor.LocationToOffset (region.Region.Begin); - var end = Editor.LocationToOffset (region.Region.End); - var marker = Editor.CreateFoldSegment (start, end - start); - foldSegments.Add (marker); - marker.CollapsedText = region.Name; - marker.FoldingType = type; - //and, if necessary, set its fold state - if (marker != null && setFolded && firstTime) { - // only fold on document open, later added folds are NOT folded by default. - marker.IsCollapsed = folded; - continue; - } - if (marker != null && region.Region.Contains (caretLocation.Line, caretLocation.Column)) - marker.IsCollapsed = false; - } - if (firstTime) { - Editor.SetFoldings (foldSegments); - } else { - Application.Invoke ((o, args) => { - if (!token.IsCancellationRequested) - Editor.SetFoldings (foldSegments); - }); - } + foldings = await parsedDocument.GetFoldingsAsync(token).ConfigureAwait (false); } catch (OperationCanceledException) { + return; } catch (Exception ex) { LoggingService.LogError ("Unhandled exception in ParseInformationUpdaterWorkerThread", ex); } + if (token.IsCancellationRequested) + return; + await Runtime.RunInMainThread (delegate { + var foldSegments = new List<IFoldSegment> (); + try { + foreach (var region in foldings) { + if (token.IsCancellationRequested) + return; + var type = FoldingType.Unknown; + bool setFolded = false; + bool folded = false; + //decide whether the regions should be folded by default + switch (region.Type) { + case FoldType.Member: + type = FoldingType.TypeMember; + break; + case FoldType.Type: + type = FoldingType.TypeDefinition; + break; + case FoldType.UserRegion: + type = FoldingType.Region; + setFolded = DefaultSourceEditorOptions.Instance.DefaultRegionsFolding; + folded = true; + break; + case FoldType.Comment: + type = FoldingType.Comment; + setFolded = DefaultSourceEditorOptions.Instance.DefaultCommentFolding; + folded = true; + break; + case FoldType.CommentInsideMember: + type = FoldingType.Comment; + setFolded = DefaultSourceEditorOptions.Instance.DefaultCommentFolding; + folded = false; + break; + case FoldType.Undefined: + setFolded = true; + folded = region.IsFoldedByDefault; + break; + } + var start = Editor.LocationToOffset (region.Region.Begin); + var end = Editor.LocationToOffset (region.Region.End); + var marker = Editor.CreateFoldSegment (start, end - start); + foldSegments.Add (marker); + marker.CollapsedText = region.Name; + marker.FoldingType = type; + //and, if necessary, set its fold state + if (marker != null && setFolded && firstTime) { + // only fold on document open, later added folds are NOT folded by default. + marker.IsCollapsed = folded; + continue; + } + if (marker != null && region.Region.Contains (caretLocation.Line, caretLocation.Column)) + marker.IsCollapsed = false; + } + Editor.SetFoldings (foldSegments); + } catch (OperationCanceledException) { + } catch (Exception ex) { + LoggingService.LogError ("Unhandled exception in ParseInformationUpdaterWorkerThread", ex); + } + }); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorPreferences.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorPreferences.cs index e7de100b4b..eb3bf0f0a6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorPreferences.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorPreferences.cs @@ -90,7 +90,7 @@ namespace MonoDevelop.Ide.Editor OutliningUndoStep = Wrap<bool> ("GenerateFormattingUndoStep", DefaultTextViewOptions.OutliningUndoOptionName); ShowChangeTrackingMargin = Wrap ("EnableQuickDiff", DefaultTextViewHostOptions.ChangeTrackingName, false); ShowGlyphMargin = Wrap<bool> ("ShowGlyphMargin", DefaultTextViewHostOptions.GlyphMarginName); - ShowLineNumberMargin = Wrap<bool> ("ShowLineNumberMargin", DefaultTextViewHostOptions.LineNumberMarginName); + ShowLineNumberMargin = Wrap ("ShowLineNumberMargin", DefaultTextViewHostOptions.LineNumberMarginName, true); ShowOutliningMargin = Wrap<bool> ("ShowFoldMargin", DefaultTextViewHostOptions.OutliningMarginName); TrimTrailingWhitespace = Wrap ("RemoveTrailingWhitespaces", DefaultOptions.TrimTrailingWhiteSpaceOptionName, true); // UseVirtualSpace should be a combination of IndentStyle == MonoDevelop.Ide.Editor.IndentStyle.Smart && RemoveTrailingWhitespaces diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs index 9f737ed3b4..d1b25c2ca9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/ExtensibleTreeView.cs @@ -503,7 +503,7 @@ namespace MonoDevelop.Ide.Gui.Components var rect = text_render.GetStatusIconArea (tree, cellArea); if (cx >= rect.X && cx <= rect.Right) { tree.ConvertBinWindowToWidgetCoords (rect.X, rect.Y, out rect.X, out rect.Y); - ShowStatusMessage (it, rect, info); + ShowStatusMessage (path, rect, info); popupShown = true; } } @@ -514,17 +514,17 @@ namespace MonoDevelop.Ide.Gui.Components } bool statusMessageVisible; - Gtk.TreeIter statusIconIter; + Gtk.TreePath statusIconPath; TooltipPopoverWindow statusPopover; - void ShowStatusMessage (Gtk.TreeIter it, Gdk.Rectangle rect, NodeInfo info) + void ShowStatusMessage (Gtk.TreePath path, Gdk.Rectangle rect, NodeInfo info) { - if (statusMessageVisible && store.GetPath (it).Equals (store.GetPath (statusIconIter))) + if (statusMessageVisible && path.Equals (statusIconPath)) return; if (statusPopover != null) statusPopover.Destroy (); statusMessageVisible = true; - statusIconIter = it; + statusIconPath = path; statusPopover = TooltipPopoverWindow.Create (); statusPopover.ShowArrow = true; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs index f98640dfaf..e3b9988b50 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs @@ -81,6 +81,7 @@ namespace MonoDevelop.Ide.Gui public event EventHandler GuiLocked; public event EventHandler GuiUnlocked; bool fileEventsFrozen; + bool hasEverBeenShown = false; internal void Initialize (ProgressMonitor monitor) { @@ -134,20 +135,22 @@ namespace MonoDevelop.Ide.Gui Counters.Initialization.Trace ("Setting memento"); workbench.Memento = memento; - Counters.Initialization.Trace ("Setting layout"); - workbench.CurrentLayout = "Solution"; - - // now we have an layout set notify it - Counters.Initialization.Trace ("Setting layout"); - if (LayoutChanged != null) - LayoutChanged (this, EventArgs.Empty); - Counters.Initialization.Trace ("Initializing monitors"); monitors.Initialize (); } internal void Show () { + if (!hasEverBeenShown) { + workbench.CurrentLayout = "Solution"; + + // now we have an layout set notify it + if (LayoutChanged != null) + LayoutChanged (this, EventArgs.Empty); + + hasEverBeenShown = true; + } + // Very important: see https://github.com/mono/monodevelop/pull/6064 // Otherwise the editor may not be focused on IDE startup and can't be // focused even by clicking with the mouse. diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MiscellaneousFilesWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MiscellaneousFilesWorkspace.cs index 7e3cecfdc0..247d065aba 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MiscellaneousFilesWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MiscellaneousFilesWorkspace.cs @@ -27,11 +27,16 @@ using System.Linq; using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
+
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
+
using Microsoft.VisualStudio.Composition;
using Microsoft.VisualStudio.Text;
+
+using Mono.Addins; + using MonoDevelop.Ide.Composition;
namespace MonoDevelop.Ide.TypeSystem
@@ -65,7 +70,10 @@ namespace MonoDevelop.Ide.TypeSystem [ImportingConstructor]
public MiscellaneousFilesWorkspace ()
: base (CompositionManager.Instance.HostServices, WorkspaceKind.MiscellaneousFiles)
- {
+ { + foreach (var factory in AddinManager.GetExtensionObjects<Microsoft.CodeAnalysis.Options.IDocumentOptionsProviderFactory> ("/MonoDevelop/Ide/TypeService/OptionProviders"))
+ Services.GetRequiredService<Microsoft.CodeAnalysis.Options.IOptionService> ().RegisterDocumentOptionsProvider (factory.Create (this));
+
defaultProjectId = ProjectId.CreateNewId (DefaultProjectName);
var compilationOptions = new CSharpCompilationOptions (OutputKind.ConsoleApplication);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs index b0a37fe68c..ab63acc39e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs @@ -1,4 +1,4 @@ -// +// // MonoDevelopWorkspace.ProjectSystemHandler.cs // // Author: @@ -104,8 +104,10 @@ namespace MonoDevelop.Ide.TypeSystem var config = IdeApp.IsInitialized ? p.GetConfiguration (IdeApp.Workspace.ActiveConfiguration) as MonoDevelop.Projects.DotNetProjectConfiguration : null; MonoDevelop.Projects.DotNetCompilerParameters cp = config?.CompilationParameters; FilePath fileName = IdeApp.IsInitialized ? p.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration) : (FilePath)""; - if (fileName.IsNullOrEmpty) + + if (fileName.IsNullOrEmpty) { fileName = new FilePath (p.Name + ".dll"); + } if (!hackyCache.TryGetCachedItems (p, workspace.MetadataReferenceManager, projectMap, out var sourceFiles, out var analyzerFiles, out var references, out var projectReferences)) { (references, projectReferences) = await metadataHandler.Value.CreateReferences (p, token).ConfigureAwait (false); @@ -148,7 +150,7 @@ namespace MonoDevelop.Ide.TypeSystem // TODO: Pass in the WorkspaceMetadataFileReferenceResolver var info = ProjectInfo.Create ( projectId, - VersionStamp.Create (), + GetVersionStamp (p), p.Name, fileName.FileNameWithoutExtension, (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp, @@ -169,6 +171,16 @@ namespace MonoDevelop.Ide.TypeSystem return info; } + VersionStamp GetVersionStamp (MonoDevelop.Projects.Project project) + { + try { + return VersionStamp.Create (File.GetLastWriteTimeUtc (project.FileName)); + } catch (Exception e) { + LoggingService.LogInternalError ("Failed to create version stamp", e); + return VersionStamp.Create (); + } + } + async Task<ConcurrentBag<ProjectInfo>> CreateProjectInfos (IEnumerable<MonoDevelop.Projects.Project> mdProjects, CancellationToken token) { var projects = new ConcurrentBag<ProjectInfo> (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index 5b4b8edaa4..91f1f0e7fa 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -4201,6 +4201,7 @@ <Compile Include="MonoDevelop.Components\Mac\NSStackViewExtensions.cs" /> <Compile Include="MonoDevelop.Components\Mac\NSLabel.cs" /> <Compile Include="MonoDevelop.Components\Mac\NSLine.cs" /> + <Compile Include="MonoDevelop.Ide\IdeStartupTracker.cs" /> </ItemGroup> <ItemGroup> <Data Include="options\DefaultEditingLayout.xml" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs index 960da88fd3..ca0bda6546 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs @@ -100,22 +100,8 @@ namespace MonoDevelop.Ide } } - static TimeToCodeMetadata ttcMetadata; - static Stopwatch ttcStopwatch; - static long startupCompletedTicks; - static long ttcDuration = 3 * TimeSpan.TicksPerSecond; // Wait 3 seconds before ignoring TTC events - - internal static void OnStartupCompleted (StartupMetadata startupMetadata, Stopwatch ttcTimer) + internal static void OnStartupCompleted () { - ttcMetadata = new TimeToCodeMetadata { - StartupTime = startupMetadata.CorrectedStartupTime - }; - ttcMetadata.AddProperties (startupMetadata); - - ttcStopwatch = ttcTimer; - startupCompletedTicks = ttcStopwatch.ElapsedTicks; - LoggingService.LogDebug ("TTC starting"); - startupCompleted?.Invoke (null, EventArgs.Empty); } @@ -327,73 +313,6 @@ namespace MonoDevelop.Ide Ide.IdeApp.Workbench.StatusBar.ShowWarning (e.Message); } - internal static void TrackTimeToCode (TimeToCodeMetadata.DocumentType documentType) - { - LoggingService.LogDebug("Tracking TTC"); - if (ttcStopwatch == null || timeToCodeSolutionTimer == null) { - LoggingService.LogDebug("Ignoring TTC"); - return; - } - - ttcStopwatch.Stop (); - timeToCodeSolutionTimer.Stop (); - - if (ttcMetadata == null) { - timeToCodeSolutionTimer = null; - ttcStopwatch = null; - throw new Exception ("SendTimeToCode called before initialisation completed"); - } - - LoggingService.LogDebug ("Processing TTC"); - ttcMetadata.SolutionLoadTime = timeToCodeSolutionTimer.ElapsedMilliseconds; - - ttcMetadata.CorrectedDuration = ttcStopwatch.ElapsedMilliseconds; - ttcMetadata.Type = documentType; - - Counters.TimeToCode.Inc ("SolutionLoaded", ttcMetadata); - - timeToCodeSolutionTimer = null; - timeToCodeWorkspaceTimer = Stopwatch.StartNew (); - - MonoDevelopWorkspace.LoadingFinished += CompleteTimeToIntellisense; - } - - static void CompleteTimeToIntellisense (object sender, EventArgs e) - { - // Reuse ttcMetadata, as it already has other information set. - MonoDevelopWorkspace.LoadingFinished -= CompleteTimeToIntellisense; - - timeToCodeWorkspaceTimer.Stop (); - - ttcMetadata.IntellisenseLoadTime = timeToCodeWorkspaceTimer.ElapsedMilliseconds; - ttcMetadata.CorrectedDuration += timeToCodeWorkspaceTimer.ElapsedMilliseconds; - - Counters.TimeToIntellisense.Inc ("IntellisenseLoaded", ttcMetadata); - } - - - static Stopwatch timeToCodeSolutionTimer = new Stopwatch (); - static Stopwatch timeToCodeWorkspaceTimer = null; - internal static bool StartTimeToCodeLoadTimer () - { - if (ttcStopwatch.ElapsedTicks - startupCompletedTicks > ttcDuration) { - LoggingService.LogDebug ($"Not starting TTC timer: {ttcStopwatch.ElapsedTicks - startupCompletedTicks}"); - return false; - } - LoggingService.LogDebug ("Starting TTC timer"); - timeToCodeSolutionTimer.Start (); - - return true; - } - - public static void BringToFront () - { - Initialized += (sender, e) => { - if (!Ide.WelcomePage.WelcomePageService.HasWindowImplementation) - Workbench.Present (); - }; - } - //this method is MIT/X11, 2009, Michael Hutchinson / (c) Novell public static void OpenFiles (IEnumerable<FileOpenInformation> files) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs index b12343faf4..2a69143203 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs @@ -62,10 +62,6 @@ namespace MonoDevelop.Ide List<AddinError> errorsList = new List<AddinError> (); bool initialized; - static Stopwatch startupTimer = new Stopwatch (); - static Stopwatch startupSectionTimer = new Stopwatch (); - static Stopwatch timeToCodeTimer = new Stopwatch (); - static Dictionary<string, long> sectionTimings = new Dictionary<string, long> (); static bool hideWelcomePage; static TimeToCodeMetadata ttcMetadata; @@ -86,12 +82,10 @@ namespace MonoDevelop.Ide //ensure native libs initialized before we hit anything that p/invokes Platform.Initialize (); - sectionTimings ["PlatformInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("PlatformInitialization"); GettextCatalog.Initialize (); - sectionTimings ["GettextInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("GettextInitialization"); LoggingService.LogInfo ("Operating System: {0}", SystemInformation.GetOperatingSystemDescription ()); @@ -137,8 +131,10 @@ namespace MonoDevelop.Ide LoggingService.LogError ("Error initialising GLib logging.", ex); } - sectionTimings ["GtkInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + var args = options.RemainingArgs.ToArray (); + IdeTheme.InitializeGtk (BrandingService.ApplicationName, ref args); + + IdeStartupTracker.StartupTracker.MarkSection ("GtkInitialization"); LoggingService.LogInfo ("Using GTK+ {0}", IdeVersionInfo.GetGtkVersion ()); // XWT initialization @@ -149,8 +145,7 @@ namespace MonoDevelop.Ide Xwt.Toolkit.CurrentEngine.RegisterBackend<IExtendedTitleBarDialogBackend, GtkExtendedTitleBarDialogBackend> (); IdeTheme.SetupXwtTheme (); - sectionTimings ["XwtInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("XwtInitialization"); //default to Windows IME on Windows if (Platform.IsWindows && GtkWorkarounds.GtkMinorVersion >= 16) { @@ -166,14 +161,12 @@ namespace MonoDevelop.Ide SynchronizationContext.SetSynchronizationContext (DispatchService.SynchronizationContext); Runtime.MainSynchronizationContext = SynchronizationContext.Current; - sectionTimings ["DispatchInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("DispatchInitialization"); // Initialize Roslyn's synchronization context RoslynServices.RoslynService.Initialize (); - sectionTimings ["RoslynInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("RoslynInitialization"); AddinManager.AddinLoadError += OnAddinError; @@ -202,8 +195,7 @@ namespace MonoDevelop.Ide if (!options.NewWindow && startupInfo.HasFiles && instanceConnection.TryConnect (startupInfo)) return 0; - sectionTimings ["RuntimeInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("RuntimeInitialization"); bool restartRequested = PropertyService.Get ("MonoDevelop.Core.RestartRequested", false); startupInfo.Restarted = restartRequested; @@ -215,8 +207,7 @@ namespace MonoDevelop.Ide IdeApp.Customizer.OnCoreInitialized (); - sectionTimings ["ThemeInitialized"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("ThemeInitialized"); IdeApp.IsRunning = true; @@ -254,8 +245,7 @@ namespace MonoDevelop.Ide Counters.Initialization.Trace ("Initializing Platform Service"); await Runtime.GetService<DesktopService> (); - sectionTimings["PlatformInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("PlatformInitialization"); monitor.Step (1); @@ -263,8 +253,7 @@ namespace MonoDevelop.Ide CheckFileWatcher (); - sectionTimings["FileWatcherInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("FileWatcherInitialization"); Exception error = null; int reportedFailures = 0; @@ -274,8 +263,7 @@ namespace MonoDevelop.Ide //force initialisation before the workbench so that it can register stock icons for GTK before they get requested ImageService.Initialize (); - sectionTimings ["ImageInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("ImageInitialization"); // If we display an error dialog before the main workbench window on OS X then a second application menu is created // which is then replaced with a second empty Apple menu. @@ -285,8 +273,7 @@ namespace MonoDevelop.Ide hideWelcomePage = options.NoStartWindow || startupInfo.HasFiles || IdeApp.Preferences.StartupBehaviour.Value != OnStartupBehaviour.ShowStartWindow; await IdeApp.Initialize (monitor); - sectionTimings ["AppInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("AppInitialization"); if (errorsList.Count > 0) { using (AddinLoadErrorDialog dlg = new AddinLoadErrorDialog (errorsList.ToArray (), false)) { @@ -341,8 +328,7 @@ namespace MonoDevelop.Ide errorsList = null; AddinManager.AddinLoadError -= OnAddinError; - sectionTimings["BasicInitializationCompleted"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("BasicInitializationCompleted"); instanceConnection.FileOpenRequested += (sender, a) => { foreach (var e in a) @@ -351,15 +337,13 @@ namespace MonoDevelop.Ide instanceConnection.StartListening (); - sectionTimings["SocketInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("SocketInitialization"); initialized = true; MessageService.RootWindow = IdeApp.Workbench.RootWindow; Xwt.MessageDialog.RootWindow = Xwt.Toolkit.CurrentEngine.WrapWindow (IdeApp.Workbench.RootWindow); - sectionTimings["WindowOpened"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("WindowOpened"); Thread.CurrentThread.Name = "GUI Thread"; Counters.Initialization.Trace ("Running IdeApp"); @@ -374,11 +358,8 @@ namespace MonoDevelop.Ide // we show the main window. DispatchService.RunPendingEvents (); - sectionTimings ["PumpEventLoop"] = startupSectionTimer.ElapsedMilliseconds; - startupTimer.Stop (); - startupSectionTimer.Stop (); - - CreateStartupMetadata (startupInfo, sectionTimings); + IdeStartupTracker.StartupTracker.MarkSection ("PumpEventLoop"); + IdeStartupTracker.StartupTracker.Stop (startupInfo); GLib.Idle.Add (OnIdle); @@ -449,19 +430,6 @@ namespace MonoDevelop.Ide return false; } - void CreateStartupMetadata (StartupInfo si, Dictionary<string, long> timings) - { - var result = IdeServices.DesktopService.PlatformTelemetry; - if (result == null) { - return; - } - - var startupMetadata = GetStartupMetadata (si, result, timings); - Counters.Startup.Inc (startupMetadata); - - IdeApp.OnStartupCompleted (startupMetadata, timeToCodeTimer); - } - static DateTime lastIdle; static bool lockupCheckRunning = true; @@ -565,10 +533,11 @@ namespace MonoDevelop.Ide { if (args.Change == ExtensionChange.Add) { try { - if (typeof(CommandHandler).IsInstanceOfType (args.ExtensionObject)) - typeof(CommandHandler).GetMethod ("Run", System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance, null, Type.EmptyTypes, null).Invoke (args.ExtensionObject, null); - else + if (args.ExtensionObject is CommandHandler handler) { + handler.InternalRun (); + } else { LoggingService.LogError ("Type " + args.ExtensionObject.GetType () + " must be a subclass of MonoDevelop.Components.Commands.CommandHandler"); + } } catch (Exception ex) { LoggingService.LogError (ex.ToString ()); } @@ -706,13 +675,8 @@ namespace MonoDevelop.Ide public static int Main (string[] args, IdeCustomizer customizer = null) { - // Using a Stopwatch instead of a TimerCounter since calling - // TimerCounter.BeginTiming here would occur before any timer - // handlers can be registered. So instead the startup duration is - // set as a metadata property on the Counters.Startup counter. - startupTimer.Start (); - startupSectionTimer.Start (); - timeToCodeTimer.Start (); + + IdeStartupTracker.StartupTracker.Start (); var options = MonoDevelopOptions.Parse (args); if (options.ShowHelp || options.Error != null) @@ -737,8 +701,7 @@ namespace MonoDevelop.Ide exename = exename.ToLower (); Runtime.SetProcessName (exename); - sectionTimings ["mainInitialization"] = startupSectionTimer.ElapsedMilliseconds; - startupSectionTimer.Restart (); + IdeStartupTracker.StartupTracker.MarkSection ("mainInitialization"); var app = new IdeStartup (); ret = app.Run (options); @@ -782,49 +745,6 @@ namespace MonoDevelop.Ide return null; } - enum StartupType - { - Normal = 0x0, - ConfigurationChange = 0x1, - FirstLaunch = 0x2, - DebuggerPresent = 0x10, - CommandExecuted = 0x20, - LaunchedAsDebugger = 0x40, - FirstLaunchSetup = 0x80, - - // Monodevelop specific - FirstLaunchAfterUpgrade = 0x10000 - } - - static StartupMetadata GetStartupMetadata (StartupInfo startupInfo, IPlatformTelemetryDetails platformDetails, Dictionary<string, long> timings) - { - var assetType = StartupAssetType.FromStartupInfo (startupInfo); - StartupType startupType = StartupType.Normal; - - if (startupInfo.Restarted && !IdeApp.IsInitialRunAfterUpgrade) { - startupType = StartupType.ConfigurationChange; // Assume a restart without upgrading was the result of a config change - } else if (IdeApp.IsInitialRun) { - startupType = StartupType.FirstLaunch; - } else if (IdeApp.IsInitialRunAfterUpgrade) { - startupType = StartupType.FirstLaunchAfterUpgrade; - } else if (Debugger.IsAttached) { - startupType = StartupType.DebuggerPresent; - } - - return new StartupMetadata { - CorrectedStartupTime = startupTimer.ElapsedMilliseconds, - StartupType = Convert.ToInt32 (startupType), - AssetTypeId = assetType.Id, - AssetTypeName = assetType.Name, - IsInitialRun = IdeApp.IsInitialRun, - IsInitialRunAfterUpgrade = IdeApp.IsInitialRunAfterUpgrade, - TimeSinceMachineStart = (long)platformDetails.TimeSinceMachineStart.TotalMilliseconds, - TimeSinceLogin = (long)platformDetails.TimeSinceLogin.TotalMilliseconds, - Timings = timings, - StartupBehaviour = IdeApp.Preferences.StartupBehaviour.Value - }; - } - internal static OpenWorkspaceItemMetadata GetOpenWorkspaceOnStartupMetadata () { var metadata = new OpenWorkspaceItemMetadata { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartupTracker.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartupTracker.cs new file mode 100644 index 0000000000..c2579f0346 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartupTracker.cs @@ -0,0 +1,205 @@ +// +// IdeStartupTracker.cs +// +// Author: +// iain <iaholmes@microsoft.com> +// +// Copyright (c) 2019 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using MonoDevelop.Core; + +using MonoDevelop.Ide.Desktop; +using MonoDevelop.Ide.Gui; +using MonoDevelop.Ide.TypeSystem; + +namespace MonoDevelop.Ide +{ + internal class IdeStartupTracker + { + static Lazy<IdeStartupTracker> startupTracker = new Lazy<IdeStartupTracker> (() => new IdeStartupTracker ()); + static internal IdeStartupTracker StartupTracker => startupTracker.Value; + + const long ttcDuration = 3 * TimeSpan.TicksPerSecond; // Wait 3 seconds before ignoring TTC events + + Stopwatch startupTimer = new Stopwatch (); + Stopwatch startupSectionTimer = new Stopwatch (); + Stopwatch timeToCodeTimer = new Stopwatch (); + Stopwatch timeToCodeSolutionTimer = new Stopwatch (); + Stopwatch timeToCodeWorkspaceTimer; + + Dictionary<string, long> sectionTimings = new Dictionary<string, long> (); + StartupInfo startupInfo; + TimeToCodeMetadata ttcMetadata; + + IdeStartupTracker () + { + + } + + internal void Start () + { + // Using a Stopwatch instead of a TimerCounter since calling + // TimerCounter.BeginTiming here would occur before any timer + // handlers can be registered. So instead the startup duration is + // set as a metadata property on the Counters.Startup counter. + startupTimer.Start (); + startupSectionTimer.Start (); + timeToCodeTimer.Start (); + } + + internal void Stop (StartupInfo startupInfo) + { + startupTimer.Stop (); + startupSectionTimer.Stop (); + + this.startupInfo = startupInfo; + + var result = DesktopService.PlatformTelemetry; + if (result == null) { + return; + } + + StartupCompleted (result); + } + + internal void StartupCompleted (IPlatformTelemetryDetails platformTelemetryDetails) + { + var startupMetadata = GetStartupMetadata (platformTelemetryDetails); + Counters.Startup.Inc (startupMetadata); + + // Start TTC timer + ttcMetadata = new TimeToCodeMetadata { + StartupTime = startupMetadata.CorrectedStartupTime + }; + ttcMetadata.AddProperties (startupMetadata); + + LoggingService.LogDebug ("TTC starting"); + } + + internal void MarkSection (string name) + { + sectionTimings [name] = startupSectionTimer.ElapsedMilliseconds; + startupSectionTimer.Restart (); + } + + enum StartupType + { + Normal = 0x0, + ConfigurationChange = 0x1, + FirstLaunch = 0x2, + DebuggerPresent = 0x10, + CommandExecuted = 0x20, + LaunchedAsDebugger = 0x40, + FirstLaunchSetup = 0x80, + + // Monodevelop specific + FirstLaunchAfterUpgrade = 0x10000 + } + + StartupMetadata GetStartupMetadata (IPlatformTelemetryDetails platformDetails) + { + var assetType = StartupAssetType.FromStartupInfo (startupInfo); + StartupType startupType = StartupType.Normal; + + if (startupInfo.Restarted && !IdeApp.IsInitialRunAfterUpgrade) { + startupType = StartupType.ConfigurationChange; // Assume a restart without upgrading was the result of a config change + } else if (IdeApp.IsInitialRun) { + startupType = StartupType.FirstLaunch; + } else if (IdeApp.IsInitialRunAfterUpgrade) { + startupType = StartupType.FirstLaunchAfterUpgrade; + } else if (Debugger.IsAttached) { + startupType = StartupType.DebuggerPresent; + } + + return new StartupMetadata { + CorrectedStartupTime = startupTimer.ElapsedMilliseconds, + StartupType = Convert.ToInt32 (startupType), + AssetTypeId = assetType.Id, + AssetTypeName = assetType.Name, + IsInitialRun = IdeApp.IsInitialRun, + IsInitialRunAfterUpgrade = IdeApp.IsInitialRunAfterUpgrade, + TimeSinceMachineStart = (long)platformDetails.TimeSinceMachineStart.TotalMilliseconds, + TimeSinceLogin = (long)platformDetails.TimeSinceLogin.TotalMilliseconds, + Timings = sectionTimings, + StartupBehaviour = IdeApp.Preferences.StartupBehaviour.Value + }; + } + + internal void TrackTimeToCode (TimeToCodeMetadata.DocumentType documentType) + { + LoggingService.LogDebug ("Tracking TTC"); + if (this.timeToCodeTimer == null || timeToCodeSolutionTimer == null) { + LoggingService.LogDebug ("Ignoring TTC"); + return; + } + + timeToCodeTimer.Stop (); + timeToCodeSolutionTimer.Stop (); + + if (ttcMetadata == null) { + timeToCodeSolutionTimer = null; + timeToCodeTimer = null; + throw new Exception ("SendTimeToCode called before initialisation completed"); + } + + LoggingService.LogDebug ("Processing TTC"); + ttcMetadata.SolutionLoadTime = timeToCodeSolutionTimer.ElapsedMilliseconds; + + ttcMetadata.CorrectedDuration = timeToCodeTimer.ElapsedMilliseconds; + ttcMetadata.Type = documentType; + + Counters.TimeToCode.Inc ("SolutionLoaded", ttcMetadata); + + timeToCodeSolutionTimer = null; + + timeToCodeWorkspaceTimer = Stopwatch.StartNew (); + MonoDevelopWorkspace.LoadingFinished += CompleteTimeToIntellisense; + } + + void CompleteTimeToIntellisense (object sender, EventArgs args) + { + // Reuse ttcMetadata, as it already has other information set. + MonoDevelopWorkspace.LoadingFinished -= CompleteTimeToIntellisense; + + timeToCodeWorkspaceTimer.Stop (); + ttcMetadata.IntellisenseLoadTime = timeToCodeWorkspaceTimer.ElapsedMilliseconds; + ttcMetadata.CorrectedDuration += timeToCodeWorkspaceTimer.ElapsedMilliseconds; + + Counters.TimeToIntellisense.Inc ("IntellisenseLoaded", ttcMetadata); + } + + internal bool StartTimeToCodeLoadTimer () + { + if (timeToCodeTimer.ElapsedTicks - startupTimer.ElapsedTicks > ttcDuration) { + LoggingService.LogDebug ($"Not starting TTC timer: {timeToCodeTimer.ElapsedTicks - startupTimer.ElapsedTicks}"); + return false; + } + LoggingService.LogDebug ("Starting TTC timer"); + timeToCodeSolutionTimer.Start (); + + return true; + } + + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs index ddab4c2070..2a7dc180ab 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs @@ -947,8 +947,8 @@ namespace MonoDevelop.Ide } AlertButton delete = new AlertButton (GettextCatalog.GetString ("Delete from Disk")); - AlertButton result = MessageService.AskQuestion (question, secondaryText, - delete, AlertButton.Cancel, AlertButton.Remove); + AlertButton result = MessageService.GenericAlert (MessageService.RootWindow, Gui.Stock.Question, question, secondaryText, + delete, AlertButton.Cancel, AlertButton.Remove); if (result == delete) { if (!workspace.RequestItemUnload (prj)) return; diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManager.MetadataReferenceCacheTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManager.MetadataReferenceCacheTests.cs index 9c3fdf0c50..acfda97f5d 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManager.MetadataReferenceCacheTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManager.MetadataReferenceCacheTests.cs @@ -19,36 +19,40 @@ namespace MonoDevelop.Ide.TypeSystem using (var sol = (MonoDevelop.Projects.Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - var manager = ws.MetadataReferenceManager; - var cache = new MonoDevelopMetadataReferenceManager.MetadataReferenceCache (); + try { + var manager = ws.MetadataReferenceManager; + var cache = new MonoDevelopMetadataReferenceManager.MetadataReferenceCache (); - // Create one with default assembly properties - var asm = typeof (MonoDevelopMetadataReferenceManagerMetadataReferenceCacheTests).Assembly.Location; - var item = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly); - Assert.IsNotNull (item); + // Create one with default assembly properties + var asm = typeof (MonoDevelopMetadataReferenceManagerMetadataReferenceCacheTests).Assembly.Location; + var item = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly); + Assert.IsNotNull (item); - var item2 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly); - Assert.AreSame (item, item2, "Item that is in cache should be returned"); + var item2 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly); + Assert.AreSame (item, item2, "Item that is in cache should be returned"); - // Create one with custom properties - var item3 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly.WithAliases (new [] { "a" })); - Assert.IsNotNull (item3); + // Create one with custom properties + var item3 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly.WithAliases (new [] { "a" })); + Assert.IsNotNull (item3); - var item4 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly.WithAliases (new [] { "a" })); - Assert.AreSame (item3, item4, "Item that is in cache should be returned"); + var item4 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly.WithAliases (new [] { "a" })); + Assert.AreSame (item3, item4, "Item that is in cache should be returned"); - // Clear the cache, new items should be returned from now on. - cache.ClearCache (); + // Clear the cache, new items should be returned from now on. + cache.ClearCache (); - var item5 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly); - Assert.IsNotNull (item5); + var item5 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly); + Assert.IsNotNull (item5); - Assert.AreNotSame (item, item5, "Cache was cleared, so new item should be returned"); + Assert.AreNotSame (item, item5, "Cache was cleared, so new item should be returned"); - var item6 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly.WithAliases (new [] { "a" })); - Assert.AreNotSame (item3, item6, "Cache was cleared, so new item should be returned"); + var item6 = cache.GetOrCreate (manager, asm, MetadataReferenceProperties.Assembly.WithAliases (new [] { "a" })); + Assert.AreNotSame (item3, item6, "Cache was cleared, so new item should be returned"); - cache.ClearCache (); + cache.ClearCache (); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); + } } } @@ -64,38 +68,42 @@ namespace MonoDevelop.Ide.TypeSystem try { using (var sol = (MonoDevelop.Projects.Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - var manager = ws.MetadataReferenceManager; - var item = manager.GetOrCreateMetadataReference (tempPath, MetadataReferenceProperties.Assembly); - Assert.IsNotNull (item); + try { + var manager = ws.MetadataReferenceManager; + var item = manager.GetOrCreateMetadataReference (tempPath, MetadataReferenceProperties.Assembly); + Assert.IsNotNull (item); - await FileWatcherService.Update (); + await FileWatcherService.Update (); - var initialId = item.CurrentSnapshot.GetMetadataId (); + var initialId = item.CurrentSnapshot.GetMetadataId (); - var taskForNewAsm = WaitForSnapshotChange (item); + var taskForNewAsm = WaitForSnapshotChange (item); - // Replace the assembly with another one. - var newAsm = typeof (MonoDevelopMetadataReference).Assembly.Location; - File.Copy (newAsm, tempPath, true); + // Replace the assembly with another one. + var newAsm = typeof (MonoDevelopMetadataReference).Assembly.Location; + File.Copy (newAsm, tempPath, true); - var argsForNewAsm = await taskForNewAsm; + var argsForNewAsm = await taskForNewAsm; - Assert.AreSame (item.CurrentSnapshot, argsForNewAsm.OldSnapshot); + Assert.AreSame (item.CurrentSnapshot, argsForNewAsm.OldSnapshot); - Assert.AreNotSame (argsForNewAsm.OldSnapshot, argsForNewAsm.NewSnapshot.Value); - // item.CurrentSnapshot is now updated - Assert.AreNotEqual (initialId, item.CurrentSnapshot.GetMetadataId ()); + Assert.AreNotSame (argsForNewAsm.OldSnapshot, argsForNewAsm.NewSnapshot.Value); + // item.CurrentSnapshot is now updated + Assert.AreNotEqual (initialId, item.CurrentSnapshot.GetMetadataId ()); - var taskForOldAsm = WaitForSnapshotChange (item); - File.Copy (oldAsm, tempPath, true); + var taskForOldAsm = WaitForSnapshotChange (item); + File.Copy (oldAsm, tempPath, true); - var argsForOldAsm = await taskForOldAsm; + var argsForOldAsm = await taskForOldAsm; - Assert.AreSame (item.CurrentSnapshot, argsForOldAsm.OldSnapshot); + Assert.AreSame (item.CurrentSnapshot, argsForOldAsm.OldSnapshot); - Assert.AreNotSame (argsForNewAsm.OldSnapshot, argsForNewAsm.NewSnapshot.Value); - // Even though the old assembly was put back, it has a new id this time. - Assert.AreNotEqual (initialId, item.CurrentSnapshot.GetMetadataId ()); + Assert.AreNotSame (argsForNewAsm.OldSnapshot, argsForNewAsm.NewSnapshot.Value); + // Even though the old assembly was put back, it has a new id this time. + Assert.AreNotEqual (initialId, item.CurrentSnapshot.GetMetadataId ()); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); + } } await FileWatcherService.Update (); diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManagerTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManagerTests.cs index 5aadf6ffc2..d9df5c66f7 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManagerTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopMetadataReferenceManagerTests.cs @@ -41,7 +41,11 @@ namespace MonoDevelop.Ide.TypeSystem using (Solution sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - Assert.IsNotNull (ws.MetadataReferenceManager); + try { + Assert.IsNotNull (ws.MetadataReferenceManager); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); + } } } } diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspaceTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspaceTests.cs index ada621fff6..a067f3fef5 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspaceTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspaceTests.cs @@ -46,9 +46,9 @@ namespace MonoDevelop.Ide.TypeSystem FilePath projFile = Util.GetSampleProject ("workspace-metadata-references", "workspace-metadata-references.sln"); - using (var sol = (MonoDevelop.Projects.Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), projFile)) { - using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - + using (var sol = (MonoDevelop.Projects.Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), projFile)) + using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { + try { DotNetProject mainProject = null, libraryProject = null, libraryProject461 = null; foreach (var project in sol.GetAllProjects ()) { if (project.Name == "workspace-metadata-references") @@ -65,6 +65,8 @@ namespace MonoDevelop.Ide.TypeSystem // Also, add test for net461 to net47. await AddAssemblyReference (ws, libraryProject, mainProject, "System.Messaging"); await AddAssemblyReference (ws, libraryProject461, mainProject, "System.ServiceModel"); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); } } } diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/TypeSystemServiceTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/TypeSystemServiceTests.cs index ba6141ad1b..7e724f9c54 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/TypeSystemServiceTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.TypeSystem/TypeSystemServiceTests.cs @@ -57,12 +57,16 @@ namespace MonoDevelop.Ide.TypeSystem using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - var project = sol.GetAllProjects ().Single (); + try { + var project = sol.GetAllProjects ().Single (); - foreach (var file in project.Files) { - Assert.IsNotNull (TypeSystemService.GetDocumentId (project, file.FilePath.ResolveLinks ())); - if (file.FilePath.FileName.EndsWith ("SymlinkedFile.cs", StringComparison.Ordinal)) - Assert.IsNull (TypeSystemService.GetDocumentId (project, file.FilePath)); + foreach (var file in project.Files) { + Assert.IsNotNull (TypeSystemService.GetDocumentId (project, file.FilePath.ResolveLinks ())); + if (file.FilePath.FileName.EndsWith ("SymlinkedFile.cs", StringComparison.Ordinal)) + Assert.IsNull (TypeSystemService.GetDocumentId (project, file.FilePath)); + } + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); } } } diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide/RoslynSearchCategoryTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide/RoslynSearchCategoryTests.cs index 07066e6a71..3c76bda394 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide/RoslynSearchCategoryTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide/RoslynSearchCategoryTests.cs @@ -55,21 +55,25 @@ namespace MonoDevelop.Ide using (Solution sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile))
using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) {
- var results = await Search ("");
- Assert.AreEqual (0, results.Count);
-
- results = await Search ("M");
- Assert.AreEqual (6, results.Count);
-
- results = await Search ("type:M");
- Assert.AreEqual (3, results.Count);
-
- results = await Search ("My"); - // Should be 4: https://github.com/dotnet/roslyn/issues/29031 - Assert.AreEqual (2, results.Count);
-
- results = await Search ("MC");
- Assert.AreEqual (5, results.Count);
+ try { + var results = await Search (""); + Assert.AreEqual (0, results.Count); + + results = await Search ("M"); + Assert.AreEqual (6, results.Count); + + results = await Search ("type:M"); + Assert.AreEqual (3, results.Count); + + results = await Search ("My"); + // Should be 4: https://github.com/dotnet/roslyn/issues/29031 + Assert.AreEqual (2, results.Count); + + results = await Search ("MC"); + Assert.AreEqual (5, results.Count); + } finally {
+ TypeSystemServiceTestExtensions.UnloadSolution (sol);
+ }
}
}
diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs index dc65094cbf..3f1224824d 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs @@ -36,10 +36,13 @@ using System.Threading.Tasks; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.IncrementalCaches; +using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.SolutionSize; using System.IO; using System.Collections.Immutable; +using System.Text; namespace MonoDevelop.Ide { @@ -106,8 +109,12 @@ namespace MonoDevelop.Ide using (Solution sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - var storageLocationService = (MonoDevelopPersistentStorageLocationService)ws.Services.GetService<IPersistentStorageLocationService> (); - Assert.That (storageLocationService.TryGetStorageLocation (ws.CurrentSolution.Id), Is.Not.Null.Or.Empty); + try { + var storageLocationService = (MonoDevelopPersistentStorageLocationService)ws.Services.GetService<IPersistentStorageLocationService> (); + Assert.That (storageLocationService.TryGetStorageLocation (ws.CurrentSolution.Id), Is.Not.Null.Or.Empty); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); + } } } @@ -118,35 +125,202 @@ namespace MonoDevelop.Ide using (Solution sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - var storageLocationService = (MonoDevelopPersistentStorageLocationService)ws.Services.GetService<IPersistentStorageLocationService> (); - var storageLocation = System.IO.Path.Combine ( - storageLocationService.TryGetStorageLocation (ws.CurrentSolution.Id), - "sqlite3", - "storage.ide"); - - if (System.IO.File.Exists (storageLocation)) - System.IO.File.Delete (storageLocation); - - var solutionSizeTracker = (IIncrementalAnalyzerProvider)Composition.CompositionManager.GetExportedValue<ISolutionSizeTracker> (); - - // This will return the tracker, since it's a singleton. - var analyzer = solutionSizeTracker.CreateIncrementalAnalyzer (ws); - - // We need this hack because we can't guess when the work coordinator will run the incremental analyzers. - await analyzer.NewSolutionSnapshotAsync (ws.CurrentSolution, CancellationToken.None); - - foreach (var projectFile in sol.GetAllProjects ().SelectMany (x => x.Files.Where (file => file.BuildAction == BuildAction.Compile))) { - var projectId = ws.GetProjectId (projectFile.Project); - var docId = ws.GetDocumentId (projectId, projectFile.FilePath); - var doc = ws.GetDocument (docId); - if (!doc.SupportsSyntaxTree) - continue; - - await Microsoft.CodeAnalysis.FindSymbols.SyntaxTreeIndex.PrecalculateAsync (doc, CancellationToken.None); + try { + var storageLocationService = (MonoDevelopPersistentStorageLocationService)ws.Services.GetService<IPersistentStorageLocationService> (); + var storageLocation = System.IO.Path.Combine ( + storageLocationService.TryGetStorageLocation (ws.CurrentSolution.Id), + "sqlite3", + "storage.ide"); + + if (System.IO.File.Exists (storageLocation)) + System.IO.File.Delete (storageLocation); + + var solutionSizeTracker = (IIncrementalAnalyzerProvider)Composition.CompositionManager.GetExportedValue<ISolutionSizeTracker> (); + + // This will return the tracker, since it's a singleton. + var analyzer = solutionSizeTracker.CreateIncrementalAnalyzer (ws); + + // We need this hack because we can't guess when the work coordinator will run the incremental analyzers. + await analyzer.NewSolutionSnapshotAsync (ws.CurrentSolution, CancellationToken.None); + + foreach (var projectFile in sol.GetAllProjects ().SelectMany (x => x.Files.Where (file => file.BuildAction == BuildAction.Compile))) { + var projectId = ws.GetProjectId (projectFile.Project); + var docId = ws.GetDocumentId (projectId, projectFile.FilePath); + var doc = ws.GetDocument (docId); + if (!doc.SupportsSyntaxTree) + continue; + + await Microsoft.CodeAnalysis.FindSymbols.SyntaxTreeIndex.PrecalculateAsync (doc, CancellationToken.None); + } + + var fi = new System.IO.FileInfo (storageLocation); + Assert.That (fi.Length, Is.GreaterThan (0)); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); } + } + } - var fi = new System.IO.FileInfo (storageLocation); - Assert.That (fi.Length, Is.GreaterThan (0)); + [Test] + public async Task TestWorkspacePersistentStorageImplementation () + { + string solFile = Util.GetSampleProject ("console-project", "ConsoleProject.sln"); + var streamName1 = "PersistentService_Solution_WriteReadDifferentInstances1"; + var streamName2 = "PersistentService_Solution_WriteReadDifferentInstances2"; + + using (Solution sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) + using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { + try { + var persistentStorageService = ws.Services.GetService<IPersistentStorageService> (); + Assert.That (persistentStorageService, Is.TypeOf (typeof (Microsoft.CodeAnalysis.SQLite.SQLitePersistentStorageService))); + + if (!(persistentStorageService is Microsoft.CodeAnalysis.SQLite.SQLitePersistentStorageService sqlitePersistentStorageService)) + return; + + var solutionSizeTracker = (IIncrementalAnalyzerProvider)Composition.CompositionManager.GetExportedValue<ISolutionSizeTracker> (); + // This will return the tracker, since it's a singleton. + var analyzer = solutionSizeTracker.CreateIncrementalAnalyzer (ws); + + // We need this hack because we can't guess when the work coordinator will run the incremental analyzers. + await analyzer.NewSolutionSnapshotAsync (ws.CurrentSolution, CancellationToken.None); + + // Due to the nature of roslyn returning a new wrapper every time we request the storage, do a reflection check. + const System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; + var fieldInfo = sqlitePersistentStorageService.GetType ().BaseType.GetField ("_currentPersistentStorage", flags); + + using (var persistentStorage = sqlitePersistentStorageService.GetStorage (ws.CurrentSolution, checkBranchId: false)) { + Assert.That (persistentStorage, Is.Not.TypeOf (typeof (NoOpPersistentStorage))); + + Assert.True (await persistentStorage.WriteStreamAsync (streamName1, EncodeString ("MyString"))); + Assert.True (await persistentStorage.WriteStreamAsync (streamName2, EncodeString ("MyString2"))); + } + + var initialFieldValue = fieldInfo.GetValue (sqlitePersistentStorageService); + + using (var persistentStorage = sqlitePersistentStorageService.GetStorage (ws.CurrentSolution, checkBranchId: false)) { + Assert.That (persistentStorage, Is.Not.TypeOf (typeof (NoOpPersistentStorage))); + } + + var secondFieldValue = fieldInfo.GetValue (sqlitePersistentStorageService); + + Assert.AreSame (initialFieldValue, secondFieldValue); + + using (var persistentStorage = sqlitePersistentStorageService.GetStorage (ws.CurrentSolution, checkBranchId: false)) { + Assert.AreEqual ("MyString", ReadStringToEnd (await persistentStorage.ReadStreamAsync (streamName1))); + Assert.AreEqual ("MyString2", ReadStringToEnd (await persistentStorage.ReadStreamAsync (streamName2))); + } + + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); + } + } + + Stream EncodeString (string text) + { + var bytes = Encoding.UTF8.GetBytes (text); + var stream = new MemoryStream (bytes); + return stream; + } + + string ReadStringToEnd (Stream stream) + { + using (stream) { + var bytes = new byte [stream.Length]; + int count = 0; + while (count < stream.Length) { + count = stream.Read (bytes, count, (int)stream.Length - count); + } + + return Encoding.UTF8.GetString (bytes); + } + } + } + + [Test] + public async Task TestStorageDataIsNotRecomputed () + { + string solFile = Util.GetSampleProject ("console-project", "ConsoleProject.sln"); + + var checkSum1 = await RunTest (usedCache: false); + var checkSum2 = await RunTest (usedCache: true); + + Assert.AreEqual (checkSum1, checkSum2); + + async Task<Microsoft.CodeAnalysis.Checksum> RunTest (bool usedCache) + { + var initial = Logger.GetLogger (); + + using (Solution sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) + using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { + try { + var persistentStorageService = ws.Services.GetService<IPersistentStorageService> (); + Assert.That (persistentStorageService, Is.TypeOf (typeof (Microsoft.CodeAnalysis.SQLite.SQLitePersistentStorageService))); + + if (!(persistentStorageService is Microsoft.CodeAnalysis.SQLite.SQLitePersistentStorageService sqlitePersistentStorageService)) + return null; + + var solutionSizeTracker = (IIncrementalAnalyzerProvider)Composition.CompositionManager.GetExportedValue<ISolutionSizeTracker> (); + // This will return the tracker, since it's a singleton. + var analyzer = solutionSizeTracker.CreateIncrementalAnalyzer (ws); + + // We need this hack because we can't guess when the work coordinator will run the incremental analyzers. + await analyzer.NewSolutionSnapshotAsync (ws.CurrentSolution, CancellationToken.None); + + var storageLogger = new StorageCheckingLogger (); + var aggregateLogger = AggregateLogger.Create (initial, storageLogger); + Logger.SetLogger (aggregateLogger); + + var provider = new SymbolTreeInfoIncrementalAnalyzerProvider (); + var cacheService = (Microsoft.CodeAnalysis.FindSymbols.SymbolTree.ISymbolTreeInfoCacheService)provider.CreateService (ws.Services); + + var incrementalAnalyzer = provider.CreateIncrementalAnalyzer (ws); + + var project = sol.GetAllProjects ().Single (); + var roslynProject = TypeSystemService.GetProject (project); + + await incrementalAnalyzer.AnalyzeProjectAsync (roslynProject, default, default, CancellationToken.None); + + Assert.That (storageLogger.QueriedCount, Is.GreaterThan (0)); + if (usedCache) { + Assert.AreEqual (storageLogger.QueriedCount, storageLogger.UsedCacheCount); + } else + Assert.AreEqual (storageLogger.QueriedCount, storageLogger.CreatedCount); + + Assert.IsNotNull (await cacheService.TryGetSourceSymbolTreeInfoAsync (roslynProject, CancellationToken.None)); + + return await Microsoft.CodeAnalysis.FindSymbols.SymbolTreeInfo.GetSourceSymbolsChecksumAsync (roslynProject, CancellationToken.None); + } finally { + Logger.SetLogger (initial); + TypeSystemServiceTestExtensions.UnloadSolution (sol); + } + } + } + } + + class StorageCheckingLogger : ILogger + { + public int QueriedCount { get; private set; } + public int CreatedCount { get; private set; } + public int UsedCacheCount => QueriedCount - CreatedCount; + + public bool IsEnabled (FunctionId functionId) => true; + + public void Log (FunctionId functionId, LogMessage logMessage) + { + // nothing + } + + public void LogBlockEnd (FunctionId functionId, LogMessage logMessage, int uniquePairId, int delta, CancellationToken cancellationToken) + { + // nothing + } + + public void LogBlockStart (FunctionId functionId, LogMessage logMessage, int uniquePairId, CancellationToken cancellationToken) + { + if (functionId == FunctionId.SymbolTreeInfo_TryLoadOrCreate) + QueriedCount++; + else if (functionId == FunctionId.SymbolTreeInfo_Create) + CreatedCount++; } } @@ -292,9 +466,9 @@ namespace MonoDevelop.Ide newParsers.Add (projectionParser); TypeSystemService.Parsers = newParsers; - using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) { - using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { - + using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) + using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { + try { var source1 = new CancellationTokenSource (); var source2 = new CancellationTokenSource (); @@ -314,6 +488,8 @@ namespace MonoDevelop.Ide Assert.IsNotNull (result2); Assert.IsNull (result1); + } finally { + TypeSystemServiceTestExtensions.UnloadSolution (sol); } } } finally { diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj index 49f66ee13b..0dd4c0da4d 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj @@ -119,6 +119,7 @@ <Compile Include="MonoDevelop.Core\FileServiceEventQueueTests.cs" /> <Compile Include="MonoDevelop.Core\FileServiceEventStateMachineTests.cs" /> <Compile Include="MonoDevelop.Projects\AsyncCriticalSectionTests.cs" /> + <Compile Include="MonoDevelop.Core\UserProfileTests.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/UserProfileTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/UserProfileTests.cs new file mode 100644 index 0000000000..8a92b9fe5d --- /dev/null +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/UserProfileTests.cs @@ -0,0 +1,39 @@ +// +// UserProfileTests.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2019 Microsoft Inc. +// +// 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 NUnit.Framework; +namespace MonoDevelop.Core +{ + [TestFixture] + public class UserProfileTests + { + [Test] + public void TestCurrentVersionWillMigrate () + { + Assert.That (UserProfile.ProfileVersions, Contains.Item (BuildInfo.CompatVersion)); + } + } +} diff --git a/main/tests/StressTest/MonoDevelop.StressTest/IEditorTestScenario.cs b/main/tests/StressTest/MonoDevelop.StressTest/IEditorTestScenario.cs new file mode 100644 index 0000000000..180512c019 --- /dev/null +++ b/main/tests/StressTest/MonoDevelop.StressTest/IEditorTestScenario.cs @@ -0,0 +1,16 @@ +using System; +namespace MonoDevelop.StressTest +{ + public enum EditorTestRun + { + Legacy, + VSEditor, + Both, + Default = Legacy, + } + + public interface IEditorTestScenario : ITestScenario + { + EditorTestRun EditorRunConfiguration { get; } + } +} diff --git a/main/tests/StressTest/MonoDevelop.StressTest/Profiler/LeakProcessor.cs b/main/tests/StressTest/MonoDevelop.StressTest/Profiler/LeakProcessor.cs index 30c5cc5de6..fe7e720522 100644 --- a/main/tests/StressTest/MonoDevelop.StressTest/Profiler/LeakProcessor.cs +++ b/main/tests/StressTest/MonoDevelop.StressTest/Profiler/LeakProcessor.cs @@ -47,7 +47,7 @@ namespace MonoDevelop.StressTest if (heapshot == null) return; - // TODO: Make this async. + // TODO: Make this async. Each heapshot will add time to the current test run. var previousData = result.Iterations.LastOrDefault (); var leakedObjects = DetectLeakedObjects (heapshot, isCleanup, previousData, iterationName); diff --git a/main/tests/StressTest/MonoDevelop.StressTest/Profiler/TestScenarioLeakExtensions.cs b/main/tests/StressTest/MonoDevelop.StressTest/Profiler/TestScenarioLeakExtensions.cs index 578a4c2fe8..4066e0d091 100644 --- a/main/tests/StressTest/MonoDevelop.StressTest/Profiler/TestScenarioLeakExtensions.cs +++ b/main/tests/StressTest/MonoDevelop.StressTest/Profiler/TestScenarioLeakExtensions.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.StressTest var attributes = member.GetCustomAttributes<NoLeakAttribute> (true).ToDictionary (x => x.TypeName, x => x); - // TODO: Ensure that we don't leak, so add GtkWidgetResult results, as they can cause retention of UI widgets. + // Ensure that we don't leak, so add GtkWidgetResult results, as they can cause retention of UI widgets. attributes.Add (autoTest, new NoLeakAttribute (autoTest)); return attributes; diff --git a/main/tests/StressTest/MonoDevelop.StressTest/Scenarios/DefaultScenario.cs b/main/tests/StressTest/MonoDevelop.StressTest/Scenarios/DefaultScenario.cs index aebfc6c729..af00f9f26d 100644 --- a/main/tests/StressTest/MonoDevelop.StressTest/Scenarios/DefaultScenario.cs +++ b/main/tests/StressTest/MonoDevelop.StressTest/Scenarios/DefaultScenario.cs @@ -34,7 +34,7 @@ using UserInterfaceTests; namespace MonoDevelop.StressTest { [NoLeak(typeof (Projects.Solution))] - public class DefaultScenario : ITestScenario + public class DefaultScenario : IEditorTestScenario { List<FilePath> filesToOpen; @@ -64,6 +64,8 @@ namespace MonoDevelop.StressTest bool firstRun = true; [NoLeak ("MonoDevelop.SourceEditor.ExtensibleTextEditor")] + [NoLeak ("Microsoft.VisualStudio.Text.Editor.Implementation.CocoaTextViewControl")] // vs-editor-core part of the chain + [NoLeak ("MonoDevelop.TextEditor.CocoaTextViewContent")] // MD part of the chain. public void Run () { if (firstRun) { @@ -109,5 +111,7 @@ namespace MonoDevelop.StressTest // Close all documents. WorkbenchExtensions.CloseAllOpenFiles (); } + + public EditorTestRun EditorRunConfiguration { get; } = EditorTestRun.Default; } } diff --git a/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs b/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs index 488c6824f6..4834ff3260 100644 --- a/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs +++ b/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs @@ -84,14 +84,47 @@ namespace MonoDevelop.StressTest leakProcessor = new LeakProcessor (scenario, ProfilerOptions); - ReportMemoryUsage (setupIteration); + ReportMemoryUsage ("Setup"); + RunTestScenario (); + + UserInterfaceTests.Ide.CloseAll (exit: false); + ReportMemoryUsage ("Cleanup"); + } + + void RunTestScenario () + { + string suffix = string.Empty; + if (scenario is IEditorTestScenario editorTestScenario) { + var configuration = editorTestScenario.EditorRunConfiguration; + switch (configuration) { + case EditorTestRun.Legacy: + Properties.UseNewEditor = false; + suffix = "_Legacy"; + break; + case EditorTestRun.VSEditor: + Properties.UseNewEditor = true; + suffix = "_VSEditor"; + break; + case EditorTestRun.Both: + // Run once with legacy + Properties.UseNewEditor = false; + RunScenarioIterations ("_Legacy"); + + Properties.UseNewEditor = true; + suffix = "_VSEditor"; + break; + } + } + + RunScenarioIterations (suffix); + } + + void RunScenarioIterations(string suffix) + { for (int i = 0; i < Iterations; ++i) { scenario.Run (); - ReportMemoryUsage (i); + ReportMemoryUsage ($"Run_{i}_{suffix}"); } - - UserInterfaceTests.Ide.CloseAll (exit: false); - ReportMemoryUsage (cleanupIteration); } bool StartWithProfiler (string profilePath, string logFile) @@ -177,7 +210,7 @@ namespace MonoDevelop.StressTest } } - void ReportMemoryUsage (int iteration) + void ReportMemoryUsage (string iterationName) { //Make sure IDE stops doing what it was doing UserInterfaceTests.Ide.WaitForIdeIdle (); @@ -191,15 +224,6 @@ namespace MonoDevelop.StressTest var memoryStats = TestService.Session.MemoryStats; - string iterationName; - if (iteration == cleanupIteration) { - iterationName = "Cleanup"; - } else if (iteration == setupIteration) { - iterationName = "Setup"; - } else { - iterationName = string.Format ("Run_{0}", iteration + 1); - } - Console.WriteLine (iterationName); Console.WriteLine (" NonPagedSystemMemory: " + memoryStats.NonPagedSystemMemory); @@ -212,7 +236,7 @@ namespace MonoDevelop.StressTest Console.WriteLine (); - leakProcessor.Process (heapshot, iteration == cleanupIteration, iterationName, memoryStats); + leakProcessor.Process (heapshot, iterationName == "Cleanup", iterationName, memoryStats); } } } diff --git a/main/tests/StressTest/StressTest.csproj b/main/tests/StressTest/StressTest.csproj index 8023693313..4db6a8945f 100644 --- a/main/tests/StressTest/StressTest.csproj +++ b/main/tests/StressTest/StressTest.csproj @@ -95,6 +95,8 @@ <Compile Include="MonoDevelop.StressTest\Profiler\LeakProcessor.cs" /> <Compile Include="MonoDevelop.StressTest\Profiler\TestScenarioLeakExtensions.cs" /> <Compile Include="MonoDevelop.StressTest\Profiler\GraphExtensions.cs" /> + <Compile Include="MonoDevelop.StressTest\IEditorTestScenario.cs" /> + <Compile Include="UserInterfaceTests\Properties.cs" /> </ItemGroup> <ItemGroup> <Folder Include="MonoDevelop.StressTest.Attributes\" /> diff --git a/main/tests/StressTest/UserInterfaceTests/Properties.cs b/main/tests/StressTest/UserInterfaceTests/Properties.cs new file mode 100644 index 0000000000..62eea0c8c2 --- /dev/null +++ b/main/tests/StressTest/UserInterfaceTests/Properties.cs @@ -0,0 +1,15 @@ +using System; +using UserInterfaceTests; + +namespace MonoDevelop.StressTest +{ + public static class Properties + { + const string useNewEditorProperty = "MonoDevelop.Ide.Editor.DefaultSourceEditorOptions.Instance.EnableNewEditor"; + + public static bool UseNewEditor { + get => TestService.Session.GetGlobalValue<bool> (useNewEditorProperty); + set => TestService.Session.SetGlobalValue (useNewEditorProperty, value); + } + } +} diff --git a/main/tests/StressTest/UserInterfaceTests/WorkbenchExtensions.cs b/main/tests/StressTest/UserInterfaceTests/WorkbenchExtensions.cs index aa7ac36224..7cdb9217a3 100644 --- a/main/tests/StressTest/UserInterfaceTests/WorkbenchExtensions.cs +++ b/main/tests/StressTest/UserInterfaceTests/WorkbenchExtensions.cs @@ -69,7 +69,7 @@ namespace UserInterfaceTests return isPass == Workbench.IsBuildSuccessful (timeoutInSecs); } - public static bool Debug (int timeoutSeconds = 20, int pollStepSecs = 5) + public static bool Debug (int timeoutSeconds = 20, int pollStepSecs = 1) { Session.ExecuteCommand ("MonoDevelop.Debugger.DebugCommands.Debug"); try { diff --git a/main/tests/UserInterfaceTests/Ide.cs b/main/tests/UserInterfaceTests/Ide.cs index 45f4d5dc67..ef60b49e90 100644 --- a/main/tests/UserInterfaceTests/Ide.cs +++ b/main/tests/UserInterfaceTests/Ide.cs @@ -71,17 +71,12 @@ namespace UserInterfaceTests public static void WaitUntil (Func<bool> done, int timeout = 20000, int pollStep = 200, Func<string> timeoutMessage = null) { - do { - if (done ()) - return; - timeout -= pollStep; - Thread.Sleep (pollStep); - } while (timeout > 0); - - if (timeoutMessage != null) { - throw new TimeoutException ("Timed out waiting for Function: " + done.Method.Name + " Message: " + timeoutMessage ()); - } else { - throw new TimeoutException ("Timed out waiting for Function: " + done.Method.Name); + try { + PollTimer (timeout, pollStep, done); + } catch (TimeoutException) { + // Replace the exception with another one. + var suffix = timeoutMessage != null ? " Message: " + timeoutMessage () : string.Empty; + throw new TimeoutException ("Timed out waiting for Function: " + done.Method.Name + suffix); } } @@ -122,7 +117,7 @@ namespace UserInterfaceTests public readonly static Action EmptyAction = delegate { }; - static string[] waitForNuGetMessages = { + static readonly string[] waitForNuGetMessages = { "Package updates are available.", "Packages are up to date.", "No updates found but warnings were reported.", @@ -132,21 +127,21 @@ namespace UserInterfaceTests "Packages updated with warnings."}; public readonly static Action WaitForPackageUpdate = delegate { - WaitForStatusMessage (waitForNuGetMessages, timeoutInSecs: 180, pollStepInSecs: 5); + WaitForStatusMessage (waitForNuGetMessages, timeoutInSecs: 180, pollStepInSecs: 1); }; public static void WaitForPackageUpdateExtra (List<string> otherMessages) { otherMessages.AddRange (waitForNuGetMessages); - WaitForStatusMessage (otherMessages.ToArray (), timeoutInSecs: 180, pollStepInSecs: 5); + WaitForStatusMessage (otherMessages.ToArray (), timeoutInSecs: 180, pollStepInSecs: 1); } public readonly static Action WaitForSolutionCheckedOut = delegate { - WaitForStatusMessage (new [] {"Solution checked out", "Solution Loaded."}, timeoutInSecs: 360, pollStepInSecs: 5); + WaitForStatusMessage (new [] {"Solution checked out", "Solution Loaded."}, timeoutInSecs: 360, pollStepInSecs: 1); }; public readonly static Action WaitForSolutionLoaded = delegate { - WaitForStatusMessage (new [] {"Project saved.", "Solution loaded."}, timeoutInSecs: 120, pollStepInSecs: 5); + WaitForStatusMessage (new [] {"Project saved.", "Solution loaded."}, timeoutInSecs: 120, pollStepInSecs: 1); }; public static void WaitForStatusMessage (string[] statusMessage, int timeoutInSecs = 240, int pollStepInSecs = 1) @@ -177,7 +172,7 @@ namespace UserInterfaceTests timeoutMessage: () => "GetStatusMessage=" + Workbench.GetStatusMessage ()); } - static readonly List<string> ignoreStatusMessgaes = new List<string> { + static readonly List<string> globalIgnoreStatusMessages = new List<string> { "Saving...", "Restoring packages for solution...", "Restoring packages before update...", @@ -186,46 +181,58 @@ namespace UserInterfaceTests "Updating packages in project..." }; - public static void WaitForIdeIdle (uint totalTimeoutInSecs = 100, uint idlePeriodInSecs = 10, string[] ignoreMessages = null) + public static void WaitForIdeIdle (uint totalTimeoutInSecs = 100, uint idlePeriodInSecs = 1, string[] ignoreMessages = null) { - uint retriesLeft = (uint)Math.Ceiling ((double)totalTimeoutInSecs/(double)idlePeriodInSecs); - ManualResetEvent resetEvent = new ManualResetEvent (false); + var ignoreStatusMessages = globalIgnoreStatusMessages.ToList (); if (ignoreMessages != null) - ignoreStatusMessgaes.AddRange (ignoreMessages); - - var timer = new System.Timers.Timer { - Interval = idlePeriodInSecs * 1000, - AutoReset = true - }; - bool didTimeout = false; + ignoreStatusMessages.AddRange (ignoreMessages); var initialStatusMessage = Workbench.GetStatusMessage (waitForNonEmpty: false); - timer.Elapsed += (sender, e) => { - if (retriesLeft == 0) { - didTimeout = true; - resetEvent.Set (); - } - + PollTimer ((int)totalTimeoutInSecs * 1000, (int)idlePeriodInSecs * 1000, () => { var finalStatusMessage = Workbench.GetStatusMessage (waitForNonEmpty: false); - var isIdle = string.Equals (initialStatusMessage, finalStatusMessage) && !ignoreStatusMessgaes.Contains (finalStatusMessage); + var isIdle = string.Equals (initialStatusMessage, finalStatusMessage) && !ignoreStatusMessages.Contains (finalStatusMessage); - if (!isIdle) { - retriesLeft--; + if (!isIdle) initialStatusMessage = finalStatusMessage; - } - if (isIdle) { - resetEvent.Set (); - } - }; + return isIdle; + }); + } - timer.Start (); - resetEvent.WaitOne (); - timer.Stop (); - timer.AutoReset = false; - timer.Dispose (); + static void PollTimer (int timeout, int interval, Func<bool> checkIsDone) + { + uint retriesLeft = (uint)Math.Ceiling ((double)timeout / interval); + var resetEvent = new ManualResetEvent (false); - if (didTimeout) - throw new TimeoutException ("Timeout waiting for IDE to be ready and idle"); + var timer = new System.Timers.Timer { + Interval = interval, + AutoReset = true + }; + + using (timer) + using (resetEvent) { + bool didTimeout = false; + + timer.Elapsed += (sender, e) => { + if (retriesLeft == 0) { + didTimeout = true; + resetEvent.Set (); + return; + } + + bool done = checkIsDone (); + if (!done) { + retriesLeft--; + } else + resetEvent.Set (); + }; + + timer.Start (); + resetEvent.WaitOne (); + timer.Stop (); + + if (didTimeout) + throw new TimeoutException ("Timeout waiting for IDE to be ready and idle"); + } } } diff --git a/main/tests/UserInterfaceTests/IdeQuery.cs b/main/tests/UserInterfaceTests/IdeQuery.cs index dc1a4a6548..317b7f34e6 100644 --- a/main/tests/UserInterfaceTests/IdeQuery.cs +++ b/main/tests/UserInterfaceTests/IdeQuery.cs @@ -31,14 +31,6 @@ namespace UserInterfaceTests { public static class IdeQuery { - readonly static Func<AppQuery, AppQuery> _defaultWorkbench = c => c.Window ().Marked ("MonoDevelop.Ide.Gui.DefaultWorkbench"); - readonly static Func<AppQuery, AppQuery> _newFileDialog = c => c.Window ().Marked ("MonoDevelop.Ide.Projects.NewFileDialog"); - readonly static Func<AppQuery, AppQuery> _gitConfigurationDialog = c => c.Window ().Marked ("MonoDevelop.VersionControl.Git.GitConfigurationDialog"); - readonly static Func<AppQuery, AppQuery> _editRemoteDialog = c => c.Window ().Marked ("MonoDevelop.VersionControl.Git.EditRemoteDialog"); - readonly static Func<AppQuery, AppQuery> _editBranchDialog = c => c.Window ().Marked ("MonoDevelop.VersionControl.Git.EditBranchDialog"); - readonly static Func<AppQuery, AppQuery> _textArea = c => c.Window ().Children ().Marked ("Mono.TextEditor.TextArea"); - readonly static Func<AppQuery, AppQuery> _xamarinUpdate = c => c.Marked ("Visual Studio Update"); - readonly static Func<AppQuery, AppQuery> _macRunButton = c => c.Marked ("MonoDevelop.MacIntegration.MainToolbar.RunButton"); public static Func<AppQuery, AppQuery> RunButton @@ -50,54 +42,20 @@ namespace UserInterfaceTests } } - public static Func<AppQuery, AppQuery> DefaultWorkbench - { - get { - return _defaultWorkbench; - } - } + public static Func<AppQuery, AppQuery> DefaultWorkbench { get; } = c => c.Window ().Marked ("MonoDevelop.Ide.Gui.DefaultWorkbench"); - public static Func<AppQuery, AppQuery> NewFileDialog - { - get { - return _newFileDialog; - } - } + public static Func<AppQuery, AppQuery> NewFileDialog { get; } = c => c.Window ().Marked ("MonoDevelop.Ide.Projects.NewFileDialog"); - public static Func<AppQuery, AppQuery> GitConfigurationDialog - { - get { - return _gitConfigurationDialog; - } - } + public static Func<AppQuery, AppQuery> GitConfigurationDialog { get; } = c => c.Window ().Marked ("MonoDevelop.VersionControl.Git.GitConfigurationDialog"); - public static Func<AppQuery, AppQuery> EditRemoteDialog - { - get { - return _editRemoteDialog; - } - } + public static Func<AppQuery, AppQuery> EditRemoteDialog { get; } = c => c.Window ().Marked ("MonoDevelop.VersionControl.Git.EditRemoteDialog"); - public static Func<AppQuery, AppQuery> EditBranchDialog - { - get { - return _editBranchDialog; - } - } + public static Func<AppQuery, AppQuery> EditBranchDialog { get; } = c => c.Window ().Marked ("MonoDevelop.VersionControl.Git.EditBranchDialog"); - public static Func<AppQuery, AppQuery> TextArea - { - get { - return _textArea; - } - } + // TODO: Implement this for the cocoa editor, as we aren't going through a TextArea. + public static Func<AppQuery, AppQuery> TextArea { get; } = c => c.Window ().Children ().Marked ("Mono.TextEditor.TextArea"); - public static Func<AppQuery, AppQuery> XamarinUpdate - { - get { - return _xamarinUpdate; - } - } + public static Func<AppQuery, AppQuery> XamarinUpdate { get; } = c => c.Marked ("Visual Studio Update"); } } diff --git a/main/tests/UserInterfaceTests/Workbench.cs b/main/tests/UserInterfaceTests/Workbench.cs index bae6f11a74..d59a6757ab 100644 --- a/main/tests/UserInterfaceTests/Workbench.cs +++ b/main/tests/UserInterfaceTests/Workbench.cs @@ -45,20 +45,22 @@ namespace UserInterfaceTests public static string GetStatusMessage (int timeout = 20000, bool waitForNonEmpty = true) { if (Platform.IsMac) { + const string macStatusTextField = "MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.text"; if (waitForNonEmpty) { Ide.WaitUntil ( - () => Session.GetGlobalValue<string> ("MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.text") != string.Empty, + () => Session.GetGlobalValue<string> (macStatusTextField) != string.Empty, timeout ); } - return (string)Session.GetGlobalValue ("MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.text"); + return (string)Session.GetGlobalValue (macStatusTextField); } if (waitForNonEmpty) { + const string gtkStatusMessageCount = "MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.messageQueue.Count"; Ide.WaitUntil ( - () => Session.GetGlobalValue<int> ("MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.messageQueue.Count") == 0, + () => Session.GetGlobalValue<int> (gtkStatusMessageCount) == 0, timeout, - timeoutMessage: ()=> "MessageQueue.Count="+Session.GetGlobalValue<int> ("MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.messageQueue.Count") + timeoutMessage: ()=> "MessageQueue.Count=" + Session.GetGlobalValue<int> (gtkStatusMessageCount) ); } return (string) Session.GetGlobalValue ("MonoDevelop.Ide.IdeApp.Workbench.RootWindow.StatusBar.renderArg.CurrentText"); @@ -85,7 +87,7 @@ namespace UserInterfaceTests ); } - // TODO: + throw new NotImplementedException ("Gtk backend not implemented"); } public static bool IsBuildSuccessful (int timeoutInSecs) @@ -115,7 +117,7 @@ namespace UserInterfaceTests return isBuildSuccessful; } - public static bool Run (int timeoutSeconds = 20, int pollStepSecs = 5) + public static bool Run (int timeoutSeconds = 20, int pollStepSecs = 1) { Session.ExecuteCommand (ProjectCommands.Run); try { @@ -130,8 +132,7 @@ namespace UserInterfaceTests public static void OpenWorkspace (string solutionPath, UITestBase testContext = null) { - if (testContext != null) - testContext.ReproStep (string.Format ("Open solution path '{0}'", solutionPath)); + testContext?.ReproStep (string.Format ("Open solution path '{0}'", solutionPath)); Action<string> takeScreenshot = GetScreenshotAction (testContext); Session.GlobalInvoke ("MonoDevelop.Ide.IdeApp.Workspace.OpenWorkspaceItem", new FilePath (solutionPath), true); Ide.WaitForIdeIdle (); @@ -140,8 +141,7 @@ namespace UserInterfaceTests public static void CloseWorkspace (UITestBase testContext = null) { - if (testContext != null) - testContext.ReproStep ("Close current workspace"); + testContext?.ReproStep ("Close current workspace"); Action<string> takeScreenshot = GetScreenshotAction (testContext); takeScreenshot ("About-To-Close-Workspace"); Session.ExecuteCommand (FileCommands.CloseWorkspace); @@ -150,8 +150,7 @@ namespace UserInterfaceTests public static void CloseDocument (UITestBase testContext = null) { - if (testContext != null) - testContext.ReproStep ("Close current workspace"); + testContext?.ReproStep ("Close current workspace"); Action<string> takeScreenshot = GetScreenshotAction (testContext); takeScreenshot ("About-To-Close-Workspace"); Session.ExecuteCommand (FileCommands.CloseFile); |