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

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/main/src
diff options
context:
space:
mode:
authorLluis Sanchez <llsan@microsoft.com>2019-02-20 18:10:48 +0300
committerLluis Sanchez <llsan@microsoft.com>2019-02-20 18:10:48 +0300
commit552d55b304b6abe3cf7e410389daf64e26b75188 (patch)
tree0ad30a67c3d6d580b17fbbfa5026561ff016f939 /main/src
parent7cfc4085396702861c4000d8f1d36e9ba53e3cea (diff)
parentf803f4f8ae5f1df72134d2a3d4e0e157e19b1507 (diff)
Merge branch 'new-service-model' into new-doc-model
Diffstat (limited to 'main/src')
-rw-r--r--main/src/addins/CSharpBinding/CSharpBinding.addin.xml5
-rw-r--r--main/src/addins/CSharpBinding/CSharpBinding.csproj1
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs3
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs16
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextPasteHandler.cs2
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs33
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs393
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs7
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs107
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs2
-rw-r--r--main/src/addins/Deployment/MonoDevelop.Deployment/MonoDevelop.Deployment/DefaultDeployServiceExtension.cs2
-rw-r--r--main/src/addins/GnomePlatform/GnomePlatform.cs6
-rw-r--r--main/src/addins/MacPlatform/BasicAuthenticationHandler.cs112
-rw-r--r--main/src/addins/MacPlatform/Dialogs/HttpClientOptionsPanel.cs (renamed from main/src/core/MonoDevelop.Core/MonoDevelop.FSW/Mono/FileSystemWatcher.cs)66
-rw-r--r--main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.UI.cs76
-rw-r--r--main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.cs57
-rw-r--r--main/src/addins/MacPlatform/Dialogs/MacCommonFileDialogHandler.cs5
-rw-r--r--main/src/addins/MacPlatform/MacHttpMessageHandlerProvider.cs66
-rw-r--r--main/src/addins/MacPlatform/MacInterop/ProcessManager.cs37
-rw-r--r--main/src/addins/MacPlatform/MacPlatform.addin.xml10
-rw-r--r--main/src/addins/MacPlatform/MacPlatform.cs48
-rw-r--r--main/src/addins/MacPlatform/MacPlatform.csproj10
-rw-r--r--main/src/addins/MacPlatform/MacPlatformSettings.cs36
-rw-r--r--main/src/addins/MacPlatform/MainToolbar/SelectorView.cs79
-rw-r--r--main/src/addins/MacPlatform/NSUrlSessionCredentialsHandler.cs70
-rw-r--r--main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml66
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj2
-rw-r--r--main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/packages.config2
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Converters/DebugValueConverter.cs7
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs8
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs57
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs8
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs16
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs21
-rw-r--r--main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/TextEntryWithCodeCompletion.cs61
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/CodeTemplateToolboxProvider.cs3
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxProvider.cs10
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs1
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs30
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs4
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs3
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs42
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs7
-rw-r--r--main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxService.cs29
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreMSBuildProjectTests.cs35
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreMSBuildProject.cs18
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs30
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs2
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs9
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdkPaths.cs31
-rw-r--r--main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs2
-rw-r--r--main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/ToolboxProvider.cs8
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/LambdaMessageHandler.cs32
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/RestoreTestBase.cs3
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestProxy.cs27
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj21
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/NuGetHttpSourceAuthenticationHandlerTests.cs406
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/UpdateXamarinFormsBuildTest.cs18
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/packages.config2
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj4
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/HttpClientFactory.cs39
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopHttpHandlerResourceV3Provider.cs118
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopServerWarningLogHandler.cs32
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/NuGetHttpSourceAuthenticationHandler.cs230
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RepositoryProviderFactoryExtensions.cs74
-rw-r--r--main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SourceRepositoryProvider.cs3
-rw-r--r--main/src/addins/MonoDevelop.Packaging/Gui/MonoDevelop.Packaging.Gui.GtkNuGetPackageMetadataOptionsPanelWidget.cs4
-rw-r--r--main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkNuGetPackageMetadataOptionsPanelWidget.cs159
-rw-r--r--main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkProjectNuGetBuildOptionsPanelWidget.cs19
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs207
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs8
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringPreviewTooltipWindow.cs5
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/IconMargin.cs57
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs1
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs27
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs9
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ClipboardRingService.cs3
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs20
-rw-r--r--main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs26
-rw-r--r--main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/ExternalTestRunner.cs3
-rw-r--r--main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/NUnitAssemblyTestSuite.cs3
-rw-r--r--main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs15
-rw-r--r--main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml3
-rw-r--r--main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs150
-rw-r--r--main/src/addins/MonoDevelop.UnitTesting/Services/SolutionFolderTestGroup.cs2
-rw-r--r--main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.Dialogs/WebReferenceDialog.cs48
-rw-r--r--main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.csproj1
-rw-r--r--main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/Library.cs6
-rw-r--r--main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/MonoDevelopDiscoveryClientProtocol.cs65
-rw-r--r--main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/WebServiceEngine.cs36
-rw-r--r--main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs2
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/EditRemoteDialog.cs19
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitCommitDialogExtension.cs16
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitConfigurationDialog.cs20
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitRepository.cs12
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Dialogs/SelectRepositoryDialog.cs38
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs27
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameWidget.cs59
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/DiffView.cs7
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogView.cs5
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs83
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/StatusView.cs39
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj8
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs163
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs236
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/UrlBasedRepositoryEditor.cs7
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs15
-rw-r--r--main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config4
-rw-r--r--main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs11
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/ClipboardColoredText.cs6
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs11
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs22
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs29
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Styles.cs2
-rw-r--r--main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs39
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs103
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs38
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs286
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs20
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessExecutionCommand.cs11
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs13
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs5
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/AmbientAuthenticationState.cs33
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialResponse.cs48
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialService.cs73
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialStatus.cs18
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpClientHandler.cs42
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpMessageHandlerProvider.cs38
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientProvider.cs81
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientSettings.cs39
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpMessageHandlerProvider.cs36
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceAuthenticationHandler.cs180
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceCredentials.cs88
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IAsyncCredentialProvider.cs25
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ICredentialService.cs35
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IHttpCredentialsHandler.cs39
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IProxyCredentialCache.cs28
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyAuthenticationHandler.cs172
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyCache.cs58
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml5
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj43
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs44
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs9
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Core/WebRequestHelper.cs4
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/FileSystemWatcher.cs580
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.OSX.cs484
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.cs603
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.CoreFoundation.cs110
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Error.cs209
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.EventStream.cs209
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.IOError.cs164
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Libraries.cs41
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.PathConf.cs70
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RealPath.cs23
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RunLoop.cs80
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Sync.cs21
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.CaseSensitivity.cs53
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.Unix.cs107
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.cs187
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PatternMatcher.cs473
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SR.cs100
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeCreateHandle.OSX.cs41
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeEventStreamHandle.OSX.cs44
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs52
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs12
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs13
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs25
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs10
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects/FileWatcherService.cs631
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs12
-rw-r--r--main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs3
-rw-r--r--main/src/core/MonoDevelop.Core/packages.config4
-rw-r--r--main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml6
-rw-r--r--main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml1
-rw-r--r--main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Gui.OptionPanels.LoadSavePanelWidget.cs162
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs231
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkNotebookResult.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkTreeModelResult.cs7
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkWidgetResult.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/NSObjectResult.cs8
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/ObjectResult.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppQuery.cs10
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs15
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestClientSession.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestService.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestSession.cs39
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs16
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs16
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/BaseFileEntry.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageLoader.cs50
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/InformationPopoverWidget.cs22
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/Messaging.cs3
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs7
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs7
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs10
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs14
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs8
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs7
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs155
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs139
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs26
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/EditorFormattingServiceTextEditorExtension.cs10
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs68
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorTheme.cs5
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs5
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs5
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Caret.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/OSXEditor.cs9
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs31
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs12
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ExternalTools/ExternalToolPanel.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs28
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs48
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/CommonAboutDialog.cs8
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs221
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/PolicyOptionsPanel.cs21
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.OptionPanels/LoadSavePanel.cs19
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs26
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectPadContextMenu.addin.xml5
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs24
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/RunConfigurationsList.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs10
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs34
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs1
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportSolutionDialog.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs6
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.cs56
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/LanguageCellRenderer.cs12
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewSolutionRunConfigurationDialog.cs98
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/MonoDevelopProjectCacheHostServiceFactory.cs63
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs30
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs5
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/HackyWorkspaceFilesCache.cs237
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs119
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs20
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs48
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs146
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs132
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs11
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs13
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/IWelcomeWindowProvider.cs10
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageCommands.cs5
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj8
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs19
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs11
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs19
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs107
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs112
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs37
-rw-r--r--main/src/core/MonoDevelop.Ide/gtkrc7
-rw-r--r--main/src/core/MonoDevelop.Ide/gtkrc.mac9
-rw-r--r--main/src/core/MonoDevelop.Ide/gtkrc.mac-dark6
-rw-r--r--main/src/core/MonoDevelop.Ide/gtkrc.win327
-rw-r--r--main/src/core/MonoDevelop.Ide/gtkrc.win32-dark4
-rw-r--r--main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs2
-rw-r--r--main/src/core/MonoDevelop.Startup/app.config2
-rw-r--r--main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs144
-rw-r--r--main/src/tools/ExtensionTools/AddinInfo.cs15
-rw-r--r--main/src/tools/ExtensionTools/AddinListWidget.cs68
-rw-r--r--main/src/tools/ExtensionTools/AddinRegistryExtensions.cs64
-rw-r--r--main/src/tools/ExtensionTools/Application.cs97
-rw-r--r--main/src/tools/ExtensionTools/ExtensionNodesWidget.cs120
-rw-r--r--main/src/tools/ExtensionTools/ExtensionPointsWidget.cs97
-rw-r--r--main/src/tools/ExtensionTools/ExtensionTools.csproj56
-rw-r--r--main/src/tools/ExtensionTools/HashSetExtensions.cs41
-rw-r--r--main/src/tools/ExtensionTools/LazyNotebook.cs71
-rw-r--r--main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml7
-rw-r--r--main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs51
281 files changed, 8349 insertions, 6017 deletions
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.addin.xml b/main/src/addins/CSharpBinding/CSharpBinding.addin.xml
index 2eac3cb5fb..be59585792 100644
--- a/main/src/addins/CSharpBinding/CSharpBinding.addin.xml
+++ b/main/src/addins/CSharpBinding/CSharpBinding.addin.xml
@@ -60,6 +60,7 @@
type="array"
_label = "Refactory Operations" />
<Command id = "MonoDevelop.CSharp.Refactoring.Commands.SortAndRemoveImports"
+ defaultHandler = "MonoDevelop.CSharp.Refactoring.RemoveAndSortUsingsHandler"
_label = "R_emove and Sort Usings"
_displayName = "Remove Unused and Sort (Usings)"
/>
@@ -193,6 +194,10 @@
<Parser class="MonoDevelop.CSharp.Parser.TypeSystemParser" mimeType = "text/x-csharp" />
</Extension>
+ <Extension path = "/MonoDevelop/TypeSystem/FoldingParser">
+ <Parser class = "MonoDevelop.CSharp.Parser.CSharpFoldingParser" mimeType="text/x-csharp" />
+ </Extension>
+
<Extension path = "/MonoDevelop/TypeSystem/CodeGenerators">
<Generator class="MonoDevelop.CSharp.Refactoring.CSharpCodeGenerator" mimeType = "text/x-csharp" />
</Extension>
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj
index e1911f1b85..5013b87b4d 100644
--- a/main/src/addins/CSharpBinding/CSharpBinding.csproj
+++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj
@@ -159,6 +159,7 @@
<Compile Include="MonoDevelop.CSharp.Completion\CSharpCompletionTextEditorExtension.cs" />
<Compile Include="MonoDevelop.CSharp.Refactoring\CSharpCodeGenerator.cs" />
<Compile Include="MonoDevelop.CSharp.Refactoring\HelperMethods.cs" />
+ <Compile Include="MonoDevelop.CSharp.Parser\CSharpFoldingParser.cs" />
<Compile Include="MonoDevelop.CSharp.CodeGeneration\AbstractGenerateAction.cs" />
<Compile Include="MonoDevelop.CSharp.CodeGeneration\CodeGenerationCommands.cs" />
<Compile Include="MonoDevelop.CSharp.CodeGeneration\CodeGenerationOptions.cs" />
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs
index 7a11695290..9ff63bcbe9 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs
@@ -372,7 +372,8 @@ namespace MonoDevelop.CSharp.Completion.Provider
{
(string beforeText, string afterText, string newMethod) = await GetInsertText (item.Properties);
TextChange change;
- if (newMethod != null && CompletionWindowManager.IsVisible) { // check for completion window manager to prevent the insertion cursor popup when the changes are queried by code diagnostics.
+
+ if (newMethod != null && RoslynCompletionData.RequestInsertText) { // check for completion window manager to prevent the insertion cursor popup when the changes are queried by code diagnostics.
change = new TextChange (new TextSpan (item.Span.Start, item.Span.Length), item.Properties [MethodNameKey] + ";");
var semanticModel = await doc.GetSemanticModelAsync (cancellationToken);
if (!doc.IsOpen () || await doc.IsForkedDocumentWithSyntaxChangesAsync (cancellationToken))
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs
index 88d34e8340..68c67dd1fa 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs
@@ -594,22 +594,6 @@ namespace MonoDevelop.CSharp.Formatting
#endregion
- #region Code Style options
- bool placeSystemDirectiveFirst = true;
- [Obsolete("Not used anymore.")]
- [ItemProperty]
- public bool PlaceSystemDirectiveFirst {
- get {
- return placeSystemDirectiveFirst;
- }
-
- set {
- placeSystemDirectiveFirst = value;
- }
- }
-
- #endregion
-
public CSharpFormattingPolicy ()
{
this.options = IdeApp.TypeSystemService.Workspace?.Options;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextPasteHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextPasteHandler.cs
index c1288027a1..9dce8b43f9 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextPasteHandler.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextPasteHandler.cs
@@ -106,6 +106,8 @@ namespace MonoDevelop.CSharp.Formatting
indent.Editor.Options.IndentStyle == IndentStyle.Auto)
return;
var doc = indent.DocumentContext.AnalysisDocument;
+ if (doc == null)
+ return;
var options = await doc.GetOptionsAsync ();
if (!options.GetOption (FeatureOnOffOptions.FormatOnPaste, doc.Project.Language))
return;
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs
index 3a547a2d07..1e65e6345b 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs
@@ -39,10 +39,10 @@ namespace MonoDevelop.CSharp.Navigation
{
class FindImplementingMembersHandler : CommandHandler
{
- protected override async void Update (CommandInfo info)
+ protected async override Task UpdateAsync (CommandInfo info, CancellationToken cancelToken)
{
var sym = await GetNamedTypeAtCaret (IdeApp.Workbench.ActiveDocument);
- info.Enabled = sym != null;
+ info.Enabled = TryGetInterfaceType (sym, out var interfaceType, out var implementingType);
info.Bypass = !info.Enabled;
}
@@ -76,25 +76,18 @@ namespace MonoDevelop.CSharp.Navigation
if (info.Node?.Parent.IsKind (SyntaxKind.SimpleBaseType) != true)
return null;
-
+
return info;
}
Task FindImplementingSymbols (Compilation compilation, RefactoringSymbolInfo info, CancellationTokenSource cancellationTokenSource)
{
- var interfaceType = info.Symbol as ITypeSymbol;
- if (interfaceType == null)
+ if (!TryGetInterfaceType (info, out var interfaceType, out var implementingType))
return Task.FromResult (0);
return Task.Run (delegate {
var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true);
using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) {
- var parentTypeNode = info.Node?.Parent?.Parent?.Parent;
- if (parentTypeNode == null)
- return;
- var implementingType = info.Model.GetDeclaredSymbol (parentTypeNode) as INamedTypeSymbol;
- if (implementingType == null)
- return;
foreach (var interfaceMember in interfaceType.GetMembers ()) {
if (monitor.CancellationToken.IsCancellationRequested)
return;
@@ -105,7 +98,7 @@ namespace MonoDevelop.CSharp.Navigation
searchMonitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length));
}
foreach (var iFace in interfaceType.AllInterfaces) {
-
+
foreach (var interfaceMember in iFace.GetMembers ()) {
if (monitor.CancellationToken.IsCancellationRequested)
return;
@@ -117,9 +110,23 @@ namespace MonoDevelop.CSharp.Navigation
searchMonitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length));
}
}
-
+
}
});
}
+
+ static bool TryGetInterfaceType (RefactoringSymbolInfo sym, out ITypeSymbol interfaceType, out INamedTypeSymbol implementingType)
+ {
+ interfaceType = null;
+ implementingType = null;
+ if (sym == null)
+ return false;
+ interfaceType = sym.Symbol as ITypeSymbol;
+ var parentTypeNode = sym.Node?.Parent?.Parent?.Parent;
+ if (parentTypeNode == null || interfaceType.TypeKind != TypeKind.Interface)
+ return false;
+ implementingType = sym.Model.GetDeclaredSymbol (parentTypeNode) as INamedTypeSymbol;
+ return implementingType != null;
+ }
}
} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs
new file mode 100644
index 0000000000..cf6ba5fb22
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs
@@ -0,0 +1,393 @@
+//
+// CSharpFoldingParser.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin 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 System.Collections.Generic;
+using MonoDevelop.Ide.TypeSystem;
+using MonoDevelop.Ide.Editor;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CSharp.Parser
+{
+ unsafe class CSharpFoldingParser : IFoldingParser
+ {
+ #region IFoldingParser implementation
+
+ static unsafe bool StartsIdentifier (char* ptr, char* endPtr, string identifier)
+ {
+ fixed (char* startId = identifier) {
+ char* idPtr = startId;
+ char* endId = startId + identifier.Length;
+ while (idPtr < endId) {
+ if (ptr >= endPtr)
+ return false;
+ if (*idPtr != *ptr)
+ return false;
+ idPtr++;
+ ptr++;
+ }
+ return true;
+ }
+ }
+
+ static unsafe void SkipWhitespaces (ref char* ptr, char* endPtr, ref int column)
+ {
+ while (ptr < endPtr) {
+ char ch = *ptr;
+ if (ch != ' ' && ch != '\t')
+ return;
+ column++;
+ ptr++;
+ }
+ }
+
+ static unsafe string ReadToEol (string content, ref char* ptr, char* endPtr, ref int line, ref int column)
+ {
+ char* lineBeginPtr = ptr;
+ char* lineEndPtr = lineBeginPtr;
+
+ while (ptr < endPtr) {
+ switch (*ptr) {
+ case '\n':
+ if (lineEndPtr == lineBeginPtr)
+ lineEndPtr = ptr;
+ line++;
+ column = 1;
+ ptr++;
+ fixed (char* startPtr = content) {
+ return content.Substring ((int)(lineBeginPtr - startPtr), (int)(lineEndPtr - lineBeginPtr));
+ }
+ case '\r':
+ lineEndPtr = ptr;
+ if (ptr + 1 < endPtr && *(ptr + 1) == '\n')
+ ptr++;
+ goto case '\n';
+ }
+ column++;
+ ptr++;
+ }
+ return "";
+ }
+
+ public unsafe ParsedDocument Parse (string fileName, string content)
+ {
+ var regionStack = new Stack<Tuple<string, DocumentLocation>> ();
+ var result = new DefaultParsedDocument (fileName);
+ bool inSingleComment = false, inMultiLineComment = false;
+ bool inString = false, inVerbatimString = false;
+ bool inChar = false;
+ bool inLineStart = true, hasStartedAtLine = false;
+ int line = 1, column = 1;
+ int bracketDepth = 0;
+ var startLoc = DocumentLocation.Empty;
+
+ fixed (char* startPtr = content) {
+ char* endPtr = startPtr + content.Length;
+ char* ptr = startPtr;
+ char* beginPtr = ptr;
+ while (ptr < endPtr) {
+ switch (*ptr) {
+ case '{':
+ if (inString || inChar || inVerbatimString || inMultiLineComment || inSingleComment)
+ break;
+ bracketDepth++;
+ break;
+ case '}':
+ if (inString || inChar || inVerbatimString || inMultiLineComment || inSingleComment)
+ break;
+ bracketDepth--;
+ break;
+ case '#':
+ if (!inLineStart)
+ break;
+ inLineStart = false;
+ ptr++;
+
+ if (StartsIdentifier (ptr, endPtr, "region")) {
+ var regionLocation = new DocumentLocation (line, column);
+ column++;
+ ptr += "region".Length;
+ column += "region".Length;
+ SkipWhitespaces (ref ptr, endPtr, ref column);
+ regionStack.Push (Tuple.Create (ReadToEol (content, ref ptr, endPtr, ref line, ref column), regionLocation));
+ continue;
+ } else if (StartsIdentifier (ptr, endPtr, "endregion")) {
+ column++;
+ ptr += "endregion".Length;
+ column += "endregion".Length;
+ if (regionStack.Count > 0) {
+ var beginRegion = regionStack.Pop ();
+ result.Add (new FoldingRegion (
+ beginRegion.Item1,
+ new DocumentRegion (beginRegion.Item2.Line, beginRegion.Item2.Column, line, column),
+ FoldType.UserRegion,
+ true));
+ }
+ continue;
+ } else {
+ column++;
+ }
+ break;
+ case '/':
+ if (inString || inChar || inVerbatimString || inMultiLineComment || inSingleComment) {
+ inLineStart = false;
+ break;
+ }
+ if (ptr + 1 < endPtr) {
+ char nextCh = *(ptr + 1);
+ if (nextCh == '/') {
+ hasStartedAtLine = inLineStart;
+ beginPtr = ptr + 2;
+ startLoc = new DocumentLocation (line, column);
+ ptr++;
+ column++;
+ inSingleComment = true;
+ } else if (nextCh == '*') {
+ hasStartedAtLine = inLineStart;
+ beginPtr = ptr + 2;
+ startLoc = new DocumentLocation (line, column);
+ ptr++;
+ column++;
+ inMultiLineComment = true;
+ }
+ }
+ inLineStart = false;
+ break;
+ case '*':
+ inLineStart = false;
+ if (inString || inChar || inVerbatimString || inSingleComment)
+ break;
+ if (inMultiLineComment && ptr + 1 < endPtr) {
+ if (ptr + 1 < endPtr && *(ptr + 1) == '/') {
+ ptr += 2;
+ column += 2;
+ inMultiLineComment = false;
+ if (bracketDepth <= 1) {
+ result.Add (new MonoDevelop.Ide.TypeSystem.Comment () {
+ Region = new DocumentRegion (startLoc, new DocumentLocation (line, column)),
+ OpenTag = "/*",
+ CommentType = MonoDevelop.Ide.TypeSystem.CommentType.Block,
+ Text = content.Substring ((int)(beginPtr - startPtr), (int)(ptr - beginPtr)),
+ CommentStartsLine = hasStartedAtLine
+ });
+ }
+ continue;
+ }
+ }
+ break;
+ case '@':
+ inLineStart = false;
+ if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment)
+ break;
+ if (ptr + 1 < endPtr && *(ptr + 1) == '"') {
+ ptr++;
+ column++;
+ inVerbatimString = true;
+ }
+ break;
+ case '\n':
+ if (inSingleComment && hasStartedAtLine) {
+ bool isDocumentation = *beginPtr == '/';
+ if (isDocumentation)
+ beginPtr++;
+ if (isDocumentation || bracketDepth <= 1) {
+ // Doesn't matter much that some comments are not correctly recognized - they'll get added later
+ // It's important that header comments are in.
+ result.Add (new MonoDevelop.Ide.TypeSystem.Comment () {
+ Region = new DocumentRegion (startLoc, new DocumentLocation (line, column)),
+ CommentType = MonoDevelop.Ide.TypeSystem.CommentType.SingleLine,
+ OpenTag = "//",
+ Text = content.Substring ((int)(beginPtr - startPtr), (int)(ptr - beginPtr)),
+ CommentStartsLine = hasStartedAtLine,
+ IsDocumentation = isDocumentation
+ });
+ }
+ inSingleComment = false;
+ }
+ inString = false;
+ inChar = false;
+ inLineStart = true;
+ line++;
+ column = 1;
+ ptr++;
+ continue;
+ case '\r':
+ if (ptr + 1 < endPtr && *(ptr + 1) == '\n')
+ ptr++;
+ goto case '\n';
+ case '\\':
+ if (inString || inChar)
+ ptr++;
+ break;
+ case '"':
+ if (inSingleComment || inMultiLineComment || inChar)
+ break;
+ if (inVerbatimString) {
+ if (ptr + 1 < endPtr && *(ptr + 1) == '"') {
+ ptr++;
+ column++;
+ break;
+ }
+ inVerbatimString = false;
+ break;
+ }
+ inString = !inString;
+ break;
+ case '\'':
+ if (inSingleComment || inMultiLineComment || inString || inVerbatimString)
+ break;
+ inChar = !inChar;
+ break;
+ default:
+ inLineStart &= *ptr == ' ' || *ptr == '\t';
+ break;
+ }
+
+ column++;
+ ptr++;
+ }
+ }
+ foreach (var fold in ToFolds (result.GetCommentsAsync().Result)) {
+ result.Add (fold);
+ }
+ return result;
+ }
+ #endregion
+
+ static IEnumerable<FoldingRegion> ToFolds (IReadOnlyList<Comment> comments)
+ {
+ for (int i = 0; i < comments.Count; i++) {
+ Comment comment = comments [i];
+
+ if (comment.CommentType == CommentType.Block) {
+ int startOffset = 0;
+ if (comment.Region.BeginLine == comment.Region.EndLine)
+ continue;
+ while (startOffset < comment.Text.Length) {
+ char ch = comment.Text [startOffset];
+ if (!char.IsWhiteSpace (ch) && ch != '*')
+ break;
+ startOffset++;
+ }
+ int endOffset = startOffset;
+ while (endOffset < comment.Text.Length) {
+ char ch = comment.Text [endOffset];
+ if (ch == '\r' || ch == '\n' || ch == '*')
+ break;
+ endOffset++;
+ }
+
+ string txt;
+ if (endOffset > startOffset) {
+ txt = "/* " + GetFirstLine (comment.Text) + " ...";
+ } else {
+ txt = "/* */";
+ }
+ yield return new FoldingRegion (txt, comment.Region, FoldType.Comment);
+ continue;
+ }
+
+ if (!comment.CommentStartsLine)
+ continue;
+ int j = i;
+ int curLine = comment.Region.BeginLine - 1;
+ var end = comment.Region.End;
+ var commentText = StringBuilderCache.Allocate ();
+ for (; j < comments.Count; j++) {
+ Comment curComment = comments [j];
+ if (curComment == null || !curComment.CommentStartsLine
+ || curComment.CommentType != comment.CommentType
+ || curLine + 1 != curComment.Region.BeginLine)
+ break;
+ commentText.Append (curComment.Text);
+ end = curComment.Region.End;
+ curLine = curComment.Region.BeginLine;
+ }
+
+ if (j - i > 1 || (comment.IsDocumentation && comment.Region.BeginLine < comment.Region.EndLine)) {
+ string txt = null;
+ if (comment.IsDocumentation) {
+ string cmtText = commentText.ToString ();
+ int idx = cmtText.IndexOf ("<summary>", StringComparison.Ordinal);
+ if (idx >= 0) {
+ int maxOffset = cmtText.IndexOf ("</summary>", StringComparison.Ordinal);
+ while (maxOffset > 0 && cmtText [maxOffset - 1] == ' ')
+ maxOffset--;
+ if (maxOffset < 0)
+ maxOffset = cmtText.Length;
+ int startOffset = idx + "<summary>".Length;
+ while (startOffset < maxOffset) {
+ char ch = cmtText [startOffset];
+ if (!char.IsWhiteSpace (ch) && ch != '/')
+ break;
+ startOffset++;
+ }
+ int endOffset = startOffset;
+ while (endOffset < maxOffset) {
+ char ch = cmtText [endOffset];
+ if (ch == '\r' || ch == '\n')
+ break;
+ endOffset++;
+ }
+ if (endOffset > startOffset)
+ txt = "/// <summary> " + cmtText.Substring (startOffset, endOffset - startOffset).Trim () + " ...";
+ }
+ if (txt == null)
+ txt = "/// " + comment.Text.Trim () + " ...";
+ } else {
+ txt = "// " + comment.Text.Trim () + " ...";
+ }
+ StringBuilderCache.Free (commentText);
+ yield return new FoldingRegion (txt,
+ new DocumentRegion (comment.Region.Begin, end),
+ FoldType.Comment);
+ i = j - 1;
+ }
+ }
+ }
+
+ static string GetFirstLine (string text)
+ {
+ int start = 0;
+ while (start < text.Length) {
+ char ch = text [start];
+ if (ch != ' ' && ch != '\t')
+ break;
+ start++;
+ }
+ int end = start;
+
+ while (end < text.Length) {
+ char ch = text [end];
+ if (MonoDevelop.Core.Text.NewLine.IsNewLine (ch))
+ break;
+ end++;
+ }
+ if (end <= start)
+ return "";
+ return text.Substring (start, end - start);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs
index 4b12ce6b09..3ae26b0d8c 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs
@@ -187,11 +187,10 @@ namespace MonoDevelop.CSharp.Project
var items = warnings.Split (new [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct ();
foreach (string warning in items) {
- if (warning.StartsWith ("CS", StringComparison.OrdinalIgnoreCase)) {
- yield return warning;
- } else {
+ if (int.TryParse (warning, out _))
yield return "CS" + warning;
- }
+ else
+ yield return warning;
}
}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs
index bf25ab7901..91301f0eb2 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs
@@ -58,7 +58,70 @@ namespace MonoDevelop.CSharp.Refactoring
SortAndRemoveImports,
}
- sealed class CurrentRefactoryOperationsHandler : CommandHandler
+ abstract class RefactoringHandler : CommandHandler
+ {
+ protected bool TryGetDocument (out Document analysisDocument, out Ide.Gui.Document doc)
+ {
+ doc = IdeApp.Workbench.ActiveDocument;
+ if (doc == null || doc.FileName == null) {
+ analysisDocument = null;
+ return false;
+ }
+
+ analysisDocument = doc.DocumentContext?.AnalysisDocument;
+ return doc != null;
+ }
+ }
+
+ sealed class RemoveAndSortUsingsHandler : RefactoringHandler
+ {
+ protected override void Update (CommandInfo info)
+ {
+ info.Enabled = TryGetDocument (out var doc, out var _) && IsSortAndRemoveImportsSupported (doc);
+ }
+
+ protected override void Run ()
+ {
+ if (TryGetDocument (out var doc, out var _))
+ SortAndRemoveUnusedImports (doc, CancellationToken.None).Ignore ();
+ }
+
+ internal static bool IsSortAndRemoveImportsSupported (Document document)
+ {
+ var workspace = document.Project.Solution.Workspace;
+
+ if (!workspace.CanApplyChange (ApplyChangesKind.ChangeDocument)) {
+ return false;
+ }
+
+ if (workspace.Kind == WorkspaceKind.MiscellaneousFiles) {
+ return false;
+ }
+
+ return workspace.Services.GetService<IDocumentSupportsFeatureService> ().SupportsRefactorings (document);
+ }
+
+ internal static async Task SortAndRemoveUnusedImports (Document originalDocument, CancellationToken cancellationToken)
+ {
+ if (originalDocument == null)
+ return;
+
+ var workspace = originalDocument.Project.Solution.Workspace;
+
+ var unnecessaryImportsService = originalDocument.GetLanguageService<IRemoveUnnecessaryImportsService> ();
+
+ // Remove unnecessary imports and sort them
+ var removedImportsDocument = await unnecessaryImportsService.RemoveUnnecessaryImportsAsync (originalDocument, cancellationToken);
+ var resultDocument = await OrganizeImportsService.OrganizeImportsAsync (removedImportsDocument, cancellationToken);
+
+ // Apply the document change if needed
+ if (resultDocument != originalDocument) {
+ workspace.ApplyDocumentChanges (resultDocument, cancellationToken);
+ }
+ }
+ }
+
+ sealed class CurrentRefactoryOperationsHandler : RefactoringHandler
{
protected override void Run (object dataItem)
{
@@ -69,9 +132,7 @@ namespace MonoDevelop.CSharp.Refactoring
protected override async Task UpdateAsync (CommandArrayInfo ainfo, CancellationToken cancelToken)
{
- var doc = IdeApp.Workbench.ActiveDocument;
- var analysisDocument = doc.DocumentContext.AnalysisDocument;
- if (doc == null || doc.FileName == FilePath.Null || analysisDocument == null)
+ if (!TryGetDocument (out var analysisDocument, out var doc))
return;
var semanticModel = await analysisDocument.GetSemanticModelAsync (cancelToken);
if (semanticModel == null)
@@ -87,12 +148,12 @@ namespace MonoDevelop.CSharp.Refactoring
}));
}
- bool isSortAndRemoveUsingsSupported = IsSortAndRemoveImportsSupported (analysisDocument);
+ bool isSortAndRemoveUsingsSupported = RemoveAndSortUsingsHandler.IsSortAndRemoveImportsSupported (analysisDocument);
if (isSortAndRemoveUsingsSupported) {
var sortAndRemoveImportsInfo = IdeApp.CommandService.GetCommandInfo (Commands.SortAndRemoveImports);
sortAndRemoveImportsInfo.Enabled = true;
ainfo.Add (sortAndRemoveImportsInfo, new Action (async delegate {
- await SortAndRemoveUnusedImports (analysisDocument, cancelToken);
+ await RemoveAndSortUsingsHandler.SortAndRemoveUnusedImports (analysisDocument, cancelToken);
}));
}
@@ -140,40 +201,6 @@ namespace MonoDevelop.CSharp.Refactoring
}
}
- static bool IsSortAndRemoveImportsSupported (Document document)
- {
- var workspace = document.Project.Solution.Workspace;
-
- if (!workspace.CanApplyChange (ApplyChangesKind.ChangeDocument)) {
- return false;
- }
-
- if (workspace.Kind == WorkspaceKind.MiscellaneousFiles) {
- return false;
- }
-
- return workspace.Services.GetService<IDocumentSupportsFeatureService> ().SupportsRefactorings (document);
- }
-
- static async Task SortAndRemoveUnusedImports (Document originalDocument, CancellationToken cancellationToken)
- {
- if (originalDocument == null)
- return;
-
- var workspace = originalDocument.Project.Solution.Workspace;
-
- var unnecessaryImportsService = originalDocument.GetLanguageService<IRemoveUnnecessaryImportsService> ();
-
- // Remove unnecessary imports and sort them
- var removedImportsDocument = await unnecessaryImportsService.RemoveUnnecessaryImportsAsync (originalDocument, cancellationToken);
- var resultDocument = await OrganizeImportsService.OrganizeImportsAsync (removedImportsDocument, cancellationToken);
-
- // Apply the document change if needed
- if (resultDocument != originalDocument) {
- workspace.ApplyDocumentChanges (resultDocument, cancellationToken);
- }
- }
-
static string FormatFileName (string fileName)
{
if (fileName == null)
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs
index 29fa4a5f5d..28f5ec22b8 100644
--- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs
@@ -167,7 +167,7 @@ namespace MonoDevelop.CSharp
}
}
- if (alreadyAddedReference.Any (reference => SystemAssemblyService.ContainsReferenceToSystemRuntime (reference))) {
+ if (alreadyAddedReference.Any (reference => SystemAssemblyService.RequiresFacadeAssembliesAsync (reference).WaitAndGetResult (monitor.CancellationToken))) {
LoggingService.LogInfo ("Found PCLv2 assembly.");
var facades = runtime.FindFacadeAssembliesForPCL (project.TargetFramework);
foreach (var facade in facades)
diff --git a/main/src/addins/Deployment/MonoDevelop.Deployment/MonoDevelop.Deployment/DefaultDeployServiceExtension.cs b/main/src/addins/Deployment/MonoDevelop.Deployment/MonoDevelop.Deployment/DefaultDeployServiceExtension.cs
index 4cec8f48b9..ad47d39987 100644
--- a/main/src/addins/Deployment/MonoDevelop.Deployment/MonoDevelop.Deployment/DefaultDeployServiceExtension.cs
+++ b/main/src/addins/Deployment/MonoDevelop.Deployment/MonoDevelop.Deployment/DefaultDeployServiceExtension.cs
@@ -26,7 +26,7 @@ namespace MonoDevelop.Deployment
evalCtx.ItemsToEvaluate.Add ("AllPublishItemsFullPathWithTargetPath");
if (project.MSBuildProject.UseMSBuildEngine) {
- var result = project.RunTarget (null, "GetCopyToPublishDirectoryItems", configuration, evalCtx).Result;
+ var result = project.RunTarget (new ProgressMonitor (), "GetCopyToPublishDirectoryItems", configuration, evalCtx).Result;
foreach (var item in result.Items) {
if (item.Name == "AllPublishItemsFullPathWithTargetPath") {
var fromPath = MSBuildProjectService.FromMSBuildPath (project.ItemDirectory, item.Include);
diff --git a/main/src/addins/GnomePlatform/GnomePlatform.cs b/main/src/addins/GnomePlatform/GnomePlatform.cs
index b6d044a59c..1fea9cd7de 100644
--- a/main/src/addins/GnomePlatform/GnomePlatform.cs
+++ b/main/src/addins/GnomePlatform/GnomePlatform.cs
@@ -175,8 +175,10 @@ namespace MonoDevelop.Platform
CreateNoWindow = true,
UseShellExecute = false,
};
- foreach (var env in environmentVariables)
- psi.EnvironmentVariables [env.Key] = env.Value;
+ if (environmentVariables != null) {
+ foreach (var env in environmentVariables)
+ psi.EnvironmentVariables [env.Key] = env.Value;
+ }
ProcessWrapper proc = new ProcessWrapper ();
if (terminal_command.Contains ("gnome-terminal")) {
diff --git a/main/src/addins/MacPlatform/BasicAuthenticationHandler.cs b/main/src/addins/MacPlatform/BasicAuthenticationHandler.cs
new file mode 100644
index 0000000000..ce01118728
--- /dev/null
+++ b/main/src/addins/MacPlatform/BasicAuthenticationHandler.cs
@@ -0,0 +1,112 @@
+//
+// BasicAuthenticationHandler.cs
+//
+// Based on based on Mono's mono/mcs/class/System/System.Net/BasicClient.cs
+//
+// Authors:
+// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// Matt Ward <matt.ward@microsoft.com>
+//
+// (C) 2003 Ximian, Inc (http://www.ximian.com)
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+
+namespace MacPlatform
+{
+ /// <summary>
+ /// NSUrlSessionHandler does not handle all WWW-Authenticate basic auth responses. VSTS NuGet packages sources return
+ /// WWW-Authenticate: Bearer authorization_uri=https://login.windows.net/, Basic realm="https://pkg.visualstudio.com/", TFS-Federated
+ /// This basic auth challenge is not delivered to NSUrlSessionHandlerDelegate's DidReceiveChallenge.
+ /// WWW-Authenticate headers that start with 'Basic' do seem to be passed to DidReceiveChallenge.
+ /// </summary>
+ static class BasicAuthenticationHandler
+ {
+ internal static bool Authenticate (HttpRequestMessage request, HttpResponseMessage response, ICredentials credentials)
+ {
+ if (credentials == null)
+ return false;
+
+ if (!IsBasicAuthentication (response))
+ return false;
+
+ return AddBasicAuthenticationHeader (request, credentials);
+ }
+
+ static bool IsBasicAuthentication (HttpResponseMessage response)
+ {
+ foreach (string authHeader in response.Headers.GetValues ("WWW-Authenticate")) {
+ if (string.IsNullOrEmpty (authHeader))
+ continue;
+ if (authHeader.IndexOf ("basic", StringComparison.OrdinalIgnoreCase) >= 0)
+ return true;
+ }
+ return false;
+ }
+
+ static bool AddBasicAuthenticationHeader (HttpRequestMessage request, ICredentials credentials)
+ {
+ string authHeader = GetBasicAuthenticationHeader (request.RequestUri, credentials);
+ if (authHeader == null)
+ return false;
+
+ request.Headers.Authorization = new AuthenticationHeaderValue ("Basic", authHeader);
+
+ return true;
+ }
+
+ static string GetBasicAuthenticationHeader (Uri requestUri, ICredentials credentials)
+ {
+ var foundCredential = credentials.GetCredential (requestUri, "basic");
+ if (foundCredential == null)
+ return null;
+
+ string userName = foundCredential.UserName;
+ if (string.IsNullOrEmpty (userName))
+ return null;
+
+ string password = foundCredential.Password;
+ string domain = foundCredential.Domain;
+ byte [] bytes;
+
+ // If domain is set, MS sends "domain\user:password".
+ if (string.IsNullOrEmpty (domain) || domain.Trim () == "")
+ bytes = GetBytes (userName + ":" + password);
+ else
+ bytes = GetBytes (domain + "\\" + userName + ":" + password);
+
+ return Convert.ToBase64String (bytes);
+ }
+
+ static byte [] GetBytes (string str)
+ {
+ int i = str.Length;
+ byte [] result = new byte [i];
+ for (--i; i >= 0; i--)
+ result [i] = (byte)str [i];
+
+ return result;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/Mono/FileSystemWatcher.cs b/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsPanel.cs
index efee8951aa..77a7c3eac8 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/Mono/FileSystemWatcher.cs
+++ b/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsPanel.cs
@@ -1,10 +1,10 @@
-//
-// FileSystemWatcher.cs
+//
+// HttpClientOptionsPanel.cs
//
// Author:
-// ludovic <ludovic.henry@xamarin.com>
+// Matt Ward <matt.ward@microsoft.com>
//
-// Copyright (c) 2017 ludovic
+// Copyright (c) 2018 Microsoft
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -24,53 +24,39 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-namespace MonoDevelop.FSW.Mono
+using MonoDevelop.Components;
+using MonoDevelop.Ide.Gui.Dialogs;
+
+namespace MonoDevelop.MacIntegration
{
- internal class FileSystemWatcher : System.IO.FileSystemWatcher
+ class HttpClientOptionsPanel : OptionsPanel
{
- public FileSystemWatcher ()
- : base ()
- {
- }
-
- public FileSystemWatcher (string path)
- : base (path)
- {
- }
-
- public FileSystemWatcher (string path, string filter)
- : base (path, filter)
- {
- }
+ Control control;
+ HttpClientOptionsWidget widget;
- protected internal new void OnChanged (System.IO.FileSystemEventArgs e)
+ public override void ApplyChanges ()
{
- base.OnChanged (e);
+ widget.ApplyChanges ();
}
- protected internal new void OnCreated (System.IO.FileSystemEventArgs e)
+ public override Control CreatePanelWidget ()
{
- base.OnCreated (e);
- }
-
- protected internal new void OnDeleted (System.IO.FileSystemEventArgs e)
- {
- base.OnDeleted (e);
- }
+ if (control == null) {
+ widget = new HttpClientOptionsWidget ();
+ control = new XwtControl (widget);
+ }
- protected internal new void OnError (System.IO.ErrorEventArgs e)
- {
- base.OnError (e);
- }
-
- protected internal new void OnRenamed (System.IO.RenamedEventArgs e)
- {
- base.OnRenamed (e);
+ return control;
}
- protected internal new void Dispose (bool disposing)
+ public override void Dispose ()
{
- base.Dispose (disposing);
+ if (control != null) {
+ // No need to dispose Control. This is done automatically.
+ // XwtControl does not dispose its widget though.
+ widget.Dispose ();
+ }
+ base.Dispose ();
}
}
}
diff --git a/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.UI.cs b/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.UI.cs
new file mode 100644
index 0000000000..d100b35f5e
--- /dev/null
+++ b/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.UI.cs
@@ -0,0 +1,76 @@
+//
+// HttpClientOptionsWidget.UI.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using MonoDevelop.Components.AtkCocoaHelper;
+using MonoDevelop.Core;
+using Xwt;
+
+namespace MonoDevelop.MacIntegration
+{
+ partial class HttpClientOptionsWidget : Widget
+ {
+ ComboBox httpClientHandlerComboBox;
+ HttpClientImplementation nsUrlSessionHttpClientImplementation = new HttpClientImplementation {
+ Name = GettextCatalog.GetString ("NSUrlSession (default)")
+ };
+ HttpClientImplementation managedHttpClientImplementation = new HttpClientImplementation {
+ Name = GettextCatalog.GetString ("Managed")
+ };
+
+ void Build ()
+ {
+ var mainHBox = new HBox ();
+
+ var httpClientHandlerLabel = new Label ();
+ httpClientHandlerLabel.Name = nameof (httpClientHandlerLabel);
+ httpClientHandlerLabel.Text = GettextCatalog.GetString ("HttpClient implementation:");
+ mainHBox.PackStart (httpClientHandlerLabel);
+
+ httpClientHandlerComboBox = new ComboBox ();
+ httpClientHandlerComboBox.Name = nameof (httpClientHandlerComboBox);
+ httpClientHandlerComboBox.SetCommonAccessibilityAttributes (
+ nameof (httpClientHandlerComboBox),
+ httpClientHandlerLabel,
+ GettextCatalog.GetString ("Select HttpClient implementation"));
+ mainHBox.PackStart (httpClientHandlerComboBox);
+
+ httpClientHandlerComboBox.Items.Add (nsUrlSessionHttpClientImplementation);
+ httpClientHandlerComboBox.Items.Add (managedHttpClientImplementation);
+
+ Content = mainHBox;
+ }
+
+ class HttpClientImplementation
+ {
+ public string Name;
+
+ public override string ToString ()
+ {
+ return Name;
+ }
+ }
+ }
+}
diff --git a/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.cs b/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.cs
new file mode 100644
index 0000000000..b33c2a1344
--- /dev/null
+++ b/main/src/addins/MacPlatform/Dialogs/HttpClientOptionsWidget.cs
@@ -0,0 +1,57 @@
+//
+// HttpClientOptionsWidget.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.MacIntegration
+{
+ partial class HttpClientOptionsWidget
+ {
+ public HttpClientOptionsWidget ()
+ {
+ Build ();
+ SelectHttpClientImplementation ();
+ }
+
+ /// <summary>
+ /// NSUrlSession is used by default.
+ /// </summary>
+ void SelectHttpClientImplementation ()
+ {
+ if (MacPlatformSettings.UseNSUrlSessionHandler.Value) {
+ httpClientHandlerComboBox.SelectedItem = nsUrlSessionHttpClientImplementation;
+ } else {
+ httpClientHandlerComboBox.SelectedItem = managedHttpClientImplementation;
+ }
+ }
+
+ public void ApplyChanges ()
+ {
+ bool nsUrlSessionSelected = httpClientHandlerComboBox.SelectedItem == nsUrlSessionHttpClientImplementation;
+ MacPlatformSettings.UseNSUrlSessionHandler.Value = nsUrlSessionSelected;
+ }
+ }
+}
diff --git a/main/src/addins/MacPlatform/Dialogs/MacCommonFileDialogHandler.cs b/main/src/addins/MacPlatform/Dialogs/MacCommonFileDialogHandler.cs
index 132474886f..12c03059c9 100644
--- a/main/src/addins/MacPlatform/Dialogs/MacCommonFileDialogHandler.cs
+++ b/main/src/addins/MacPlatform/Dialogs/MacCommonFileDialogHandler.cs
@@ -30,6 +30,7 @@ using Foundation;
using MonoDevelop.Components;
using MonoDevelop.Components.Extensions;
using MonoDevelop.Core;
+using MonoDevelop.Ide;
namespace MonoDevelop.MacIntegration
{
@@ -91,7 +92,9 @@ namespace MonoDevelop.MacIntegration
if (!string.IsNullOrEmpty (data.CurrentFolder))
panel.DirectoryUrl = new NSUrl (data.CurrentFolder, true);
- panel.ParentWindow = NSApplication.SharedApplication.KeyWindow ?? NSApplication.SharedApplication.MainWindow;
+ var parent = IdeServices.DesktopService.GetFocusedTopLevelWindow ();
+ if (parent != null)
+ panel.ParentWindow = parent;
if (panel is NSOpenPanel openPanel) {
openPanel.AllowsMultipleSelection = data.SelectMultiple;
diff --git a/main/src/addins/MacPlatform/MacHttpMessageHandlerProvider.cs b/main/src/addins/MacPlatform/MacHttpMessageHandlerProvider.cs
new file mode 100644
index 0000000000..6601c8e4ca
--- /dev/null
+++ b/main/src/addins/MacPlatform/MacHttpMessageHandlerProvider.cs
@@ -0,0 +1,66 @@
+
+//
+// MacHttpMessageHandlerProvider.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Net.Http;
+using MonoDevelop.Core.Web;
+using Foundation;
+
+namespace MonoDevelop.MacIntegration
+{
+ class MacHttpMessageHandlerProvider : HttpMessageHandlerProvider
+ {
+ readonly DefaultHttpMessageHandlerProvider defaultProvider = new DefaultHttpMessageHandlerProvider ();
+
+ public override HttpMessageHandler CreateHttpMessageHandler (Uri uri, HttpClientSettings settings)
+ {
+ if (MacPlatformSettings.UseNSUrlSessionHandler) {
+ return CreateNSUrlSessionHandler (uri, settings);
+ }
+
+ return defaultProvider.CreateHttpMessageHandler (uri, settings);
+ }
+
+ HttpMessageHandler CreateNSUrlSessionHandler (Uri uri, HttpClientSettings settings)
+ {
+ var config = NSUrlSessionConfiguration.DefaultSessionConfiguration;
+ config.RequestCachePolicy = NSUrlRequestCachePolicy.ReloadIgnoringLocalCacheData;
+ config.URLCache = null;
+
+ var sessionHandler = new NSUrlSessionCredentialsHandler (config) {
+ DisableCaching = true,
+ AllowAutoRedirect = settings.AllowAutoRedirect,
+ };
+
+ if (!settings.SourceAuthenticationRequired) {
+ return sessionHandler;
+ }
+
+ return new HttpSourceAuthenticationHandler (uri, sessionHandler, sessionHandler);
+ }
+ }
+}
diff --git a/main/src/addins/MacPlatform/MacInterop/ProcessManager.cs b/main/src/addins/MacPlatform/MacInterop/ProcessManager.cs
index 6861c33696..565ed251a1 100644
--- a/main/src/addins/MacPlatform/MacInterop/ProcessManager.cs
+++ b/main/src/addins/MacPlatform/MacInterop/ProcessManager.cs
@@ -33,28 +33,6 @@ namespace MonoDevelop.MacInterop
{
public static class ProcessManager
{
- const string CARBON = "/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon";
-
- [DllImport (CARBON)]
- static extern OSStatus GetProcessPID (ref ProcessSerialNumber psn, out int pid);
-
- [DllImport (CARBON)]
- static extern OSStatus KillProcess (ref ProcessSerialNumber process);
-
- public static int GetProcessPid (ProcessSerialNumber psn)
- {
- int pid;
- if (GetProcessPID (ref psn, out pid) == OSStatus.Ok)
- return pid;
- return -1;
- }
-
- [Obsolete ("Use KillProcess (int pid) instead")]
- public static bool KillProcess (ProcessSerialNumber psn)
- {
- return KillProcess (ref psn) == OSStatus.Ok;
- }
-
public static bool KillProcess (int pid)
{
NSRunningApplication runningApp = NSRunningApplication.GetRunningApplication (pid);
@@ -74,20 +52,5 @@ namespace MonoDevelop.MacInterop
Ok = 0
}
}
-
- public struct ProcessSerialNumber
- {
- uint high;
- uint low;
-
- public ProcessSerialNumber (uint high, uint low)
- {
- this.high = high;
- this.low = low;
- }
-
- public uint High { get { return high; } }
- public uint Low { get { return low; } }
- }
}
diff --git a/main/src/addins/MacPlatform/MacPlatform.addin.xml b/main/src/addins/MacPlatform/MacPlatform.addin.xml
index 72a1fc3287..96915b12c9 100644
--- a/main/src/addins/MacPlatform/MacPlatform.addin.xml
+++ b/main/src/addins/MacPlatform/MacPlatform.addin.xml
@@ -89,4 +89,14 @@
<StockIcon stockid = "project" resource = "project.png" size="Menu" />
<StockIcon stockid = "status-stop" resource = "status-stop-16.png" size="Menu" />
</Extension>
+
+ <Extension path = "/MonoDevelop/Ide/GlobalOptionsDialog/Other">
+ <Section id = "HttpClientImplementation"
+ _label = "Network"
+ class = "MonoDevelop.MacIntegration.HttpClientOptionsPanel" />
+ </Extension>
+
+ <Extension path = "/MonoDevelop/Core/HttpMessageHandlerProviders">
+ <Class id = "NSUrlSessionHandlerProvider" class = "MonoDevelop.MacIntegration.MacHttpMessageHandlerProvider" />
+ </Extension>
</ExtensionModel>
diff --git a/main/src/addins/MacPlatform/MacPlatform.cs b/main/src/addins/MacPlatform/MacPlatform.cs
index 4ffaffa7a7..8cefaa6858 100644
--- a/main/src/addins/MacPlatform/MacPlatform.cs
+++ b/main/src/addins/MacPlatform/MacPlatform.cs
@@ -266,6 +266,7 @@ namespace MonoDevelop.MacIntegration
e.Reply = NSApplicationTerminateReply.Now;
}
};
+ appDelegate.ShowDockMenu += AppDelegate_ShowDockMenu;
}
// Listen to the AtkCocoa notification for the presence of VoiceOver
@@ -298,6 +299,27 @@ namespace MonoDevelop.MacIntegration
return loaded;
}
+ void AppDelegate_ShowDockMenu (object sender, ShowDockMenuArgs e)
+ {
+ if (((FilePath)NSBundle.MainBundle.BundlePath).Extension != ".app")
+ return;
+ var menu = new NSMenu ();
+ var newInstanceMenuItem = new NSMenuItem ();
+ newInstanceMenuItem.Title = GettextCatalog.GetString ("New Instance");
+ newInstanceMenuItem.Activated += NewInstanceMenuItem_Activated;
+ menu.AddItem (newInstanceMenuItem);
+ e.DockMenu = menu;
+ }
+
+ static void NewInstanceMenuItem_Activated (object sender, EventArgs e)
+ {
+ var bundlePath = NSBundle.MainBundle.BundlePath;
+ NSWorkspace.SharedWorkspace.LaunchApplication (NSUrl.FromFilename (bundlePath), NSWorkspaceLaunchOptions.NewInstance, new NSDictionary (), out NSError error);
+ if (error != null)
+ LoggingService.LogError ($"Failed to start new instance: {error.LocalizedDescription}");
+ }
+
+
const string EnabledKey = "com.monodevelop.AccessibilityEnabled";
static void ShowVoiceOverNotice ()
{
@@ -934,6 +956,16 @@ namespace MonoDevelop.MacIntegration
NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);
}
+ public override Window GetParentForModalWindow ()
+ {
+ return NSApplication.SharedApplication.KeyWindow ?? NSApplication.SharedApplication.MainWindow;
+ }
+
+ public override Window GetFocusedTopLevelWindow ()
+ {
+ return NSApplication.SharedApplication.KeyWindow;
+ }
+
public override void FocusWindow (Window window)
{
try {
@@ -1325,6 +1357,14 @@ namespace MonoDevelop.MacIntegration
public class ThemedMacDialogBackend : Xwt.Mac.DialogBackend
{
+ public ThemedMacDialogBackend ()
+ {
+ }
+
+ public ThemedMacDialogBackend (IntPtr ptr) : base (ptr)
+ {
+ }
+
public override void InitializeBackend (object frontend, Xwt.Backends.ApplicationContext context)
{
base.InitializeBackend (frontend, context);
@@ -1334,6 +1374,14 @@ namespace MonoDevelop.MacIntegration
public class ThemedMacAlertDialogBackend : Xwt.Mac.AlertDialogBackend
{
+ public ThemedMacAlertDialogBackend ()
+ {
+ }
+
+ public ThemedMacAlertDialogBackend (IntPtr ptr) : base (ptr)
+ {
+ }
+
public override void Initialize (Xwt.Backends.ApplicationContext actx)
{
base.Initialize (actx);
diff --git a/main/src/addins/MacPlatform/MacPlatform.csproj b/main/src/addins/MacPlatform/MacPlatform.csproj
index 15b8e10c59..d1a4fea78e 100644
--- a/main/src/addins/MacPlatform/MacPlatform.csproj
+++ b/main/src/addins/MacPlatform/MacPlatform.csproj
@@ -26,6 +26,7 @@
<HintPath>..\..\..\packages\System.ValueTuple.4.4.0\lib\net47\System.ValueTuple.dll</HintPath>
<Private>False</Private>
</Reference>
+ <Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj">
@@ -111,6 +112,15 @@
<Compile Include="MacTelemetryDetails.cs" />
<Compile Include="Interop.cs" />
<Compile Include="KernelInterop.cs" />
+ <Compile Include="Dialogs\HttpClientOptionsPanel.cs" />
+ <Compile Include="Dialogs\HttpClientOptionsWidget.cs" />
+ <Compile Include="Dialogs\HttpClientOptionsWidget.UI.cs">
+ <DependentUpon>HttpClientOptionsWidget.cs</DependentUpon>
+ </Compile>
+ <Compile Include="MacPlatformSettings.cs" />
+ <Compile Include="MacHttpMessageHandlerProvider.cs" />
+ <Compile Include="NSUrlSessionCredentialsHandler.cs" />
+ <Compile Include="BasicAuthenticationHandler.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
diff --git a/main/src/addins/MacPlatform/MacPlatformSettings.cs b/main/src/addins/MacPlatform/MacPlatformSettings.cs
new file mode 100644
index 0000000000..8f465304bd
--- /dev/null
+++ b/main/src/addins/MacPlatform/MacPlatformSettings.cs
@@ -0,0 +1,36 @@
+//
+// MacPlatformSettings.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using MonoDevelop.Core;
+
+namespace MonoDevelop.MacIntegration
+{
+ static class MacPlatformSettings
+ {
+ public static readonly ConfigurationProperty<bool> UseNSUrlSessionHandler
+ = ConfigurationProperty.Create ("MonoDevelop.MacIntegration.UseNSUrlSessionHandler", true);
+ }
+}
diff --git a/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs b/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs
index ffaace2c12..6eb52e52e5 100644
--- a/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs
+++ b/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs
@@ -124,22 +124,10 @@ namespace MonoDevelop.MacIntegration.MainToolbar
internal void OnSizeChanged ()
{
- if (SizeChanged != null) {
- SizeChanged (this, EventArgs.Empty);
- }
+ SizeChanged?.Invoke (this, EventArgs.Empty);
}
- public override bool BecomeFirstResponder()
- {
- if (Window.FirstResponder != RealSelectorView)
- return Window.MakeFirstResponder(RealSelectorView);
- return false;
- }
-
- public override bool AcceptsFirstResponder()
- {
- return Window.FirstResponder != RealSelectorView;
- }
+ public override bool AcceptsFirstResponder () => false;
#region PathSelectorView
[Register]
@@ -574,6 +562,28 @@ namespace MonoDevelop.MacIntegration.MainToolbar
int focusedCellIndex = 0;
NSPathComponentCellFocusable focusedItem;
+ bool UpdatePreviousCellForResponderChain (int fromPosition)
+ {
+ for (focusedCellIndex = fromPosition; focusedCellIndex >= 0; focusedCellIndex--) {
+ var cell = Cells [focusedCellIndex].Cell;
+ if (PathComponentCells.Contains (cell) && cell.Enabled) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool UpdateNextCellForResponderChain (int fromPosition)
+ {
+ for (focusedCellIndex = fromPosition; focusedCellIndex < Cells.Length; focusedCellIndex++) {
+ var cell = Cells [focusedCellIndex].Cell;
+ if (PathComponentCells.Contains (cell) && cell.Enabled) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public override void KeyDown (NSEvent theEvent)
{
if (theEvent.KeyCode == (ushort) KeyCodes.Space) {
@@ -585,28 +595,30 @@ namespace MonoDevelop.MacIntegration.MainToolbar
// 0x30 is Tab
if (theEvent.KeyCode == (ushort)KeyCodes.Tab) {
if ((theEvent.ModifierFlags & NSEventModifierMask.ShiftKeyMask) == NSEventModifierMask.ShiftKeyMask) {
- focusedCellIndex--;
- if (focusedCellIndex < 0) {
+ if (focusedCellIndex <= 0) {
if (PreviousKeyView != null) {
SetSelection ();
focusedCellIndex = 0;
focusedItem = null;
}
} else {
- SetSelection ();
- return;
+ if (UpdatePreviousCellForResponderChain (focusedCellIndex - 1)) {
+ SetSelection ();
+ return;
+ }
}
} else {
- focusedCellIndex++;
- if (focusedCellIndex >= VisibleCellIds.Length) {
+ if (focusedCellIndex >= VisibleCellIds.Length - 1) {
if (NextKeyView != null) {
SetSelection ();
focusedCellIndex = 0;
focusedItem = null;
}
} else {
- SetSelection ();
- return;
+ if (UpdateNextCellForResponderChain (focusedCellIndex + 1)) {
+ SetSelection ();
+ return;
+ }
}
}
}
@@ -616,15 +628,20 @@ namespace MonoDevelop.MacIntegration.MainToolbar
void SetSelection ()
{
- if (focusedItem != null) {
- focusedItem.HasFocus = false;
- }
+ //ensures our cells are in the correct enabled state
if (focusedCellIndex >= 0 && focusedCellIndex < Cells.Length) {
- var item = Cells [focusedCellIndex].Cell as NSPathComponentCellFocusable;
- focusedItem = item;
- if (item != null)
- item.HasFocus = true;
+ focusedItem = Cells [focusedCellIndex].Cell as NSPathComponentCellFocusable;
+ if (focusedItem != null)
+ focusedItem.HasFocus = true;
+ }
+
+ //we want ensure our state is correct in other elements
+ for (int i = 0; i < Cells.Length; i++) {
+ if (i != focusedCellIndex && Cells [i].Cell is NSPathComponentCellFocusable focusable) {
+ focusable.HasFocus = false;
+ }
}
+
SetNeedsDisplay ();
}
@@ -639,9 +656,9 @@ namespace MonoDevelop.MacIntegration.MainToolbar
if (currentEvent.Type == NSEventType.KeyDown) {
if (currentEvent.KeyCode == (ushort) KeyCodes.Tab) {
if ((currentEvent.ModifierFlags & NSEventModifierMask.ShiftKeyMask) == NSEventModifierMask.ShiftKeyMask) {
- focusedCellIndex = Cells.Length - 1;
+ UpdatePreviousCellForResponderChain (Cells.Length - 1);
} else {
- focusedCellIndex = 0;
+ UpdateNextCellForResponderChain (0);
}
}
}
diff --git a/main/src/addins/MacPlatform/NSUrlSessionCredentialsHandler.cs b/main/src/addins/MacPlatform/NSUrlSessionCredentialsHandler.cs
new file mode 100644
index 0000000000..5931326060
--- /dev/null
+++ b/main/src/addins/MacPlatform/NSUrlSessionCredentialsHandler.cs
@@ -0,0 +1,70 @@
+//
+// NSUrlSessionCredentialsHandler.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Foundation;
+using MacPlatform;
+using MonoDevelop.Core.Web;
+
+namespace MonoDevelop.MacIntegration
+{
+ class NSUrlSessionCredentialsHandler : NSUrlSessionHandler, IHttpCredentialsHandler
+ {
+ public NSUrlSessionCredentialsHandler (NSUrlSessionConfiguration configuration)
+ : base (configuration)
+ {
+ }
+
+ /// <summary>
+ /// Not supported.
+ /// </summary>
+ public bool UseDefaultCredentials { get; set; }
+
+ /// <summary>
+ /// Not all WWW-Authenticate basic auth responses are handled by the NSUrlSessionHandler, such as those
+ /// from VSTS NuGet package sources, so an Authorization header is added to the request and re-sent
+ /// if basic auth credentials can be found.
+ /// </summary>
+ protected override async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ bool retry = false;
+ while (true) {
+ var response = await base.SendAsync (request, cancellationToken).ConfigureAwait (false);
+
+ if (retry || response.StatusCode != HttpStatusCode.Unauthorized)
+ return response;
+
+ if (!BasicAuthenticationHandler.Authenticate (request, response, Credentials))
+ return response;
+
+ retry = true;
+ }
+ }
+ }
+}
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 6e0428798e..a91a288055 100644
--- a/main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml
+++ b/main/src/addins/MonoDevelop.AspNetCore/Properties/MonoDevelop.AspNetCore.addin.xml
@@ -83,7 +83,7 @@
<Template
id="Microsoft.Web.Empty.CSharp"
templateId="Microsoft.Web.Empty.CSharp.3.0"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -95,7 +95,7 @@
<Template
id="Microsoft.Web.Empty.FSharp"
templateId="Microsoft.Web.Empty.FSharp.3.0"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -107,7 +107,7 @@
<Template
id="Microsoft.Web.WebApi.CSharp"
templateId="Microsoft.Web.WebApi.CSharp.3.0"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -119,7 +119,7 @@
<Template
id="Microsoft.Web.WebApi.FSharp"
templateId="Microsoft.Web.WebApi.FSharp.3.0"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -132,7 +132,7 @@
<Template
id="Microsoft.Web.RazorPages.CSharp"
templateId="Microsoft.Web.RazorPages.CSharp.3.0"
- _overrideName="ASP.NET Core Web App"
+ _overrideName="Web Application"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Razor Pages content."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -146,7 +146,7 @@
<Template
id="Microsoft.Web.Mvc.CSharp"
templateId="Microsoft.Web.Mvc.CSharp.3.0"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -159,7 +159,7 @@
<Template
id="Microsoft.Web.Mvc.FSharp"
templateId="Microsoft.Web.Mvc.FSharp.3.0"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="${DotNetCoreSdk.3.0.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -174,7 +174,7 @@
<Template
id="Microsoft.Web.Empty.CSharp"
templateId="Microsoft.Web.Empty.CSharp.2.2"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -186,7 +186,7 @@
<Template
id="Microsoft.Web.Empty.FSharp"
templateId="Microsoft.Web.Empty.FSharp.2.2"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -198,7 +198,7 @@
<Template
id="Microsoft.Web.WebApi.CSharp"
templateId="Microsoft.Web.WebApi.CSharp.2.2"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -210,7 +210,7 @@
<Template
id="Microsoft.Web.WebApi.FSharp"
templateId="Microsoft.Web.WebApi.FSharp.2.2"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -223,7 +223,7 @@
<Template
id="Microsoft.Web.RazorPages.CSharp"
templateId="Microsoft.Web.RazorPages.CSharp.2.2"
- _overrideName="ASP.NET Core Web App"
+ _overrideName="Web Application"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Razor Pages content."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -237,7 +237,7 @@
<Template
id="Microsoft.Web.Mvc.CSharp"
templateId="Microsoft.Web.Mvc.CSharp.2.2"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -250,7 +250,7 @@
<Template
id="Microsoft.Web.Mvc.FSharp"
templateId="Microsoft.Web.Mvc.FSharp.2.2"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="${DotNetCoreSdk.2.2.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -265,7 +265,7 @@
<Template
id="Microsoft.Web.Empty.CSharp"
templateId="Microsoft.Web.Empty.CSharp.2.1"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -277,7 +277,7 @@
<Template
id="Microsoft.Web.Empty.FSharp"
templateId="Microsoft.Web.Empty.FSharp.2.1"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -289,7 +289,7 @@
<Template
id="Microsoft.Web.WebApi.CSharp"
templateId="Microsoft.Web.WebApi.CSharp.2.1"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -301,7 +301,7 @@
<Template
id="Microsoft.Web.WebApi.FSharp"
templateId="Microsoft.Web.WebApi.FSharp.2.1"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -314,7 +314,7 @@
<Template
id="Microsoft.Web.RazorPages.CSharp"
templateId="Microsoft.Web.RazorPages.CSharp.2.1"
- _overrideName="ASP.NET Core Web App"
+ _overrideName="Web Application"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Razor Pages content."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -328,7 +328,7 @@
<Template
id="Microsoft.Web.Mvc.CSharp"
templateId="Microsoft.Web.Mvc.CSharp.2.1"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -341,7 +341,7 @@
<Template
id="Microsoft.Web.Mvc.FSharp"
templateId="Microsoft.Web.Mvc.FSharp.2.1"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="${DotNetCoreSdk.2.1.Templates.Web.ProjectTemplates.nupkg}"
icon="md-netcore-empty-project"
@@ -356,7 +356,7 @@
<Template
id="Microsoft.Web.Empty.CSharp"
templateId="Microsoft.Web.Empty.CSharp.2.0"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -368,7 +368,7 @@
<Template
id="Microsoft.Web.Empty.FSharp"
templateId="Microsoft.Web.Empty.FSharp.2.0"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -380,7 +380,7 @@
<Template
id="Microsoft.Web.WebApi.CSharp"
templateId="Microsoft.Web.WebApi.CSharp.2.0"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -392,7 +392,7 @@
<Template
id="Microsoft.Web.WebApi.FSharp"
templateId="Microsoft.Web.WebApi.FSharp.2.0"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -405,7 +405,7 @@
<Template
id="Microsoft.Web.RazorPages.CSharp"
templateId="Microsoft.Web.RazorPages.CSharp.2.0"
- _overrideName="ASP.NET Core Web App"
+ _overrideName="Web Application"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Razor Pages content."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -419,7 +419,7 @@
<Template
id="Microsoft.Web.Mvc.CSharp"
templateId="Microsoft.Web.Mvc.CSharp.2.0"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -432,7 +432,7 @@
<Template
id="Microsoft.Web.Mvc.FSharp"
templateId="Microsoft.Web.Mvc.FSharp.2.0"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.2.0.1.0.0-beta2-20170727-301.nupkg"
icon="md-netcore-empty-project"
@@ -446,7 +446,7 @@
<Condition id="AspNetCoreSdkInstalled" sdkVersion="1.*">
<Template
id="Microsoft.Web.Empty.CSharp"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.1.x.1.0.0-beta2-20170430-208.nupkg"
icon="md-netcore-empty-project"
@@ -457,7 +457,7 @@
defaultParameters="IncludeLaunchSettings=true" />
<Template
id="Microsoft.Web.Empty.FSharp"
- _overrideName="ASP.NET Core Empty"
+ _overrideName="Empty"
_overrideDescription="An empty project template for creating an ASP.NET Core application. This template does not have any content in it."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.1.x.1.0.0-beta2-20170430-208.nupkg"
icon="md-netcore-empty-project"
@@ -468,7 +468,7 @@
defaultParameters="IncludeLaunchSettings=true" />
<Template
id="Microsoft.Web.WebApi.CSharp"
- _overrideName="ASP.NET Core Web API"
+ _overrideName="API"
_overrideDescription="A project template for creating an ASP.NET Core application with an example Controller for a RESTful HTTP service. This template can also be used for ASP.NET Core MVC Views and Controllers."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.1.x.1.0.0-beta2-20170430-208.nupkg"
icon="md-netcore-empty-project"
@@ -479,7 +479,7 @@
defaultParameters="IncludeLaunchSettings=true" />
<Template
id="Microsoft.Web.Mvc.CSharp"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.1.x.1.0.0-beta2-20170430-208.nupkg"
icon="md-netcore-empty-project"
@@ -491,7 +491,7 @@
defaultParameters="IncludeLaunchSettings=true" />
<Template
id="Microsoft.Web.Mvc.FSharp"
- _overrideName="ASP.NET Core Web App (MVC)"
+ _overrideName="Web Application (Model-View-Controller)"
_overrideDescription="A project template for creating an ASP.NET Core application with example ASP.NET Core MVC Views and Controllers. This template can also be used for RESTful HTTP services."
path="Templates/Microsoft.DotNet.Web.ProjectTemplates.1.x.1.0.0-beta2-20170430-208.nupkg"
icon="md-netcore-empty-project"
diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj
index 25833d91f9..cced3d764b 100644
--- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj
+++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol.csproj
@@ -16,7 +16,7 @@
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.Shared.VSCodeDebugProtocol">
- <HintPath>..\..\..\..\packages\Microsoft.VisualStudio.Shared.VsCodeDebugProtocol.15.0.10815.1\lib\net45\Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.dll</HintPath>
+ <HintPath>..\..\..\..\packages\Microsoft.VisualStudio.Shared.VsCodeDebugProtocol.15.8.20719.1\lib\net45\Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
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
index 89dde6f38d..9a5ab42712 100644
--- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/packages.config
+++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/packages.config
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Microsoft.VisualStudio.Shared.VsCodeDebugProtocol" version="15.0.10815.1" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Shared.VsCodeDebugProtocol" version="15.8.20719.1" targetFramework="net471" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Converters/DebugValueConverter.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Converters/DebugValueConverter.cs
index d66bfa7f65..f93edacefe 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Converters/DebugValueConverter.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Converters/DebugValueConverter.cs
@@ -24,6 +24,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
+using System.Threading;
+using System.Threading.Tasks;
using Mono.Debugging.Client;
namespace MonoDevelop.Debugger
@@ -34,6 +36,11 @@ namespace MonoDevelop.Debugger
public abstract T GetValue (ObjectValue val);
+ public virtual Task<T> GetValueAsync (ObjectValue val, CancellationToken token = default (CancellationToken))
+ {
+ return Task.FromResult (GetValue (val));
+ }
+
public virtual bool CanSetValue (ObjectValue val)
{
return false;
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs
index e4a233e40a..b4182ffadc 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs
@@ -830,7 +830,7 @@ namespace MonoDevelop.Debugger
void UpdateDebugSessionCounter ()
{
- var metadata = new Dictionary<string, string> ();
+ var metadata = new Dictionary<string, object> ();
metadata ["Success"] = (!SessionError).ToString ();
metadata ["DebuggerType"] = Engine.Id;
@@ -843,7 +843,7 @@ namespace MonoDevelop.Debugger
}
}
- Counters.DebugSession.Inc (metadata);
+ Counters.DebugSession.Inc (1, null, metadata);
}
void UpdateEvaluationStatsCounter ()
@@ -853,7 +853,7 @@ namespace MonoDevelop.Debugger
return;
}
- var metadata = new Dictionary<string, string> ();
+ var metadata = new Dictionary<string, object> ();
metadata ["DebuggerType"] = Engine.Id;
metadata ["AverageDuration"] = Session.EvaluationStats.AverageTime.ToString ();
metadata ["MaximumDuration"] = Session.EvaluationStats.MaxTime.ToString ();
@@ -861,7 +861,7 @@ namespace MonoDevelop.Debugger
metadata ["FailureCount"] = Session.EvaluationStats.FailureCount.ToString ();
metadata ["SuccessCount"] = Session.EvaluationStats.TimingsCount.ToString ();
- Counters.EvaluationStats.Inc (metadata);
+ Counters.EvaluationStats.Inc (1, null, metadata);
}
bool ExceptionHandler (Exception ex)
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
index dc24c1e85b..456df5df01 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs
@@ -41,6 +41,8 @@ using MonoDevelop.Ide.Gui.Content;
using MonoDevelop.Ide.Editor.Extension;
using MonoDevelop.Ide.Fonts;
using System.Collections.Generic;
+using System.Drawing;
+using System.Text;
namespace MonoDevelop.Debugger
{
@@ -765,7 +767,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
Context = ctx;
}
- string GetMethodMarkup (bool selected)
+ string GetMethodMarkup (bool selected, string foregroundColor)
{
if (Markup != null)
return $"<span foreground='{Styles.ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false)}'>{Markup}</span>";
@@ -777,30 +779,28 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
var markup = $"<b>{GLib.Markup.EscapeText (methodName)}</b> {GLib.Markup.EscapeText (parameters)}";
- if (selected)
- markup = $"<span foreground='#FFFFFF'>{markup}</span>";
- else {
- var textColor = IsUserCode ? Ide.Gui.Styles.BaseForegroundColor.ToHexString (false) : Styles.ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false);
- markup = $"<span foreground='{textColor}'>{markup}</span>";
+ if (string.IsNullOrEmpty (foregroundColor)) {
+ return markup;
}
-
- return markup;
+ return $"<span foreground='{foregroundColor}'>{markup}</span>";
}
- string GetFileMarkup (bool selected)
+ string GetFileMarkup (bool selected, string foregroundColor)
{
if (Frame == null || string.IsNullOrEmpty (Frame.File)) {
return "";
}
- var markup = string.Format ("<span foreground='{0}'>{1}", selected ? "#FFFFFF" : Styles.ExceptionCaughtDialog.LineNumberTextColor.ToHexString (false), GLib.Markup.EscapeText (Path.GetFileName (Frame.File)));
+ var markup = GLib.Markup.EscapeText (Path.GetFileName (Frame.File));
if (Frame.Line > 0) {
markup += ":" + Frame.Line;
if (Frame.Column > 0)
markup += "," + Frame.Column;
}
- markup += "</span>";
- return markup;
+ if (string.IsNullOrEmpty (foregroundColor)) {
+ return markup;
+ }
+ return $"<span foreground='{foregroundColor}'>{markup}</span>";
}
public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
@@ -808,7 +808,11 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
using (var layout = new Pango.Layout (Context)) {
Pango.Rectangle ink, logical;
layout.FontDescription = font;
- layout.SetMarkup (GetMethodMarkup (false));
+
+ var selected = false;
+ var foregroundColor = Styles.GetStackFrameForegroundHexColor (selected, IsUserCode);
+
+ layout.SetMarkup (GetMethodMarkup (selected, foregroundColor));
layout.GetPixelExtents (out ink, out logical);
height = logical.Height;
@@ -821,10 +825,20 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
protected override void Render (Gdk.Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags)
{
using (var cr = Gdk.CairoHelper.Create (window)) {
+ if (!widget.HasFocus) {
+ cr.Rectangle (background_area.ToCairoRect ());
+ cr.SetSourceColor (Styles.ObjectValueTreeDisabledBackgroundColor);
+ cr.Fill ();
+ }
+
Pango.Rectangle ink, logical;
using (var layout = new Pango.Layout (Context)) {
layout.FontDescription = font;
- layout.SetMarkup (GetFileMarkup ((flags & CellRendererState.Selected) != 0));
+
+ var selected = (flags & CellRendererState.Selected) != 0;
+ var foregroundColor = Styles.GetStackFrameForegroundHexColor (selected, IsUserCode);
+
+ layout.SetMarkup (GetFileMarkup (selected, foregroundColor));
layout.GetPixelExtents (out ink, out logical);
var width = widget.Allocation.Width;
cr.Translate (width - logical.Width - 10, cell_area.Y);
@@ -832,7 +846,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
cr.IdentityMatrix ();
- layout.SetMarkup (GetMethodMarkup ((flags & CellRendererState.Selected) != 0));
+ layout.SetMarkup (GetMethodMarkup (selected, foregroundColor));
layout.Width = (int)((width - logical.Width - 35) * Pango.Scale.PangoScale);
layout.Ellipsize = Pango.EllipsizeMode.Middle;
cr.Translate (cell_area.X + 10, cell_area.Y);
@@ -1105,12 +1119,19 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander""
{
public override bool KeyPress (KeyDescriptor descriptor)
{
- if (descriptor.SpecialKey == SpecialKey.Escape && DebuggingService.ExceptionCaughtMessage != null &&
+ if (DebuggingService.ExceptionCaughtMessage != null &&
!DebuggingService.ExceptionCaughtMessage.IsMinimized &&
DebuggingService.ExceptionCaughtMessage.File.CanonicalPath == new FilePath (DocumentContext.Name).CanonicalPath) {
- DebuggingService.ExceptionCaughtMessage.ShowMiniButton ();
- return true;
+ if (descriptor.SpecialKey == SpecialKey.Escape) {
+ DebuggingService.ExceptionCaughtMessage.ShowMiniButton ();
+ return true;
+ }
+
+ if (descriptor.SpecialKey == SpecialKey.Return) {
+ DebuggingService.ExceptionCaughtMessage.ShowDialog ();
+ return false;
+ }
}
return base.KeyPress (descriptor);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs
index b1a2498285..1364ab3939 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs
@@ -65,6 +65,10 @@ namespace MonoDevelop.Debugger
public static AsyncOperation DebugApplication (this ProjectOperations opers, string executableFile, string args, string workingDir, IDictionary<string,string> envVars)
{
+ if (!IdeApp.Workbench.RootWindow.Visible) {
+ IdeApp.Workbench.RootWindow.Show ();
+ }
+
var monitor = IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor (System.IO.Path.GetFileName (executableFile));
var oper = DebuggingService.Run (executableFile, args, workingDir, envVars, monitor.Console);
@@ -79,6 +83,10 @@ namespace MonoDevelop.Debugger
public static AsyncOperation AttachToProcess (this ProjectOperations opers, DebuggerEngine debugger, ProcessInfo proc)
{
+ if (!IdeApp.Workbench.RootWindow.Visible) {
+ IdeApp.Workbench.RootWindow.Show ();
+ }
+
var oper = DebuggingService.AttachToProcess (debugger, proc);
opers.AddRunOperation (oper);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
index c2998c9c8b..e08e49ff1c 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValueTreeView.cs
@@ -930,7 +930,7 @@ namespace MonoDevelop.Debugger
ExpandRow (store.GetPath (it), false);
}
} else {
- RefreshRow (it);
+ RefreshRow (it, val);
}
}
@@ -977,10 +977,11 @@ namespace MonoDevelop.Debugger
enumerableLoading.Remove (value);
}, cancellationTokenSource.Token, TaskContinuationOptions.NotOnCanceled, Xwt.Application.UITaskScheduler);
}
-
- void RefreshRow (TreeIter iter)
+
+ void RefreshRow (TreeIter iter, ObjectValue val)
{
- var val = (ObjectValue) store.GetValue (iter, ObjectColumn);
+ if (val == null)
+ return;
UnregisterValue (val);
RemoveChildren (iter);
@@ -1013,8 +1014,7 @@ namespace MonoDevelop.Debugger
while (store.IterChildren (out citer, iter)) {
var val = (ObjectValue) store.GetValue (citer, ObjectColumn);
- if (val != null)
- UnregisterValue (val);
+ UnregisterValue (val);
RemoveChildren (citer);
store.Remove (ref citer);
}
@@ -1030,6 +1030,8 @@ namespace MonoDevelop.Debugger
void UnregisterValue (ObjectValue val)
{
+ if (val == null)
+ return;
val.ValueChanged -= OnValueUpdated;
nodes.Remove (val);
}
@@ -1904,7 +1906,7 @@ namespace MonoDevelop.Debugger
var val = (ObjectValue)store.GetValue (it, ObjectColumn);
if (DebuggingService.ShowValueVisualizer (val)) {
UpdateParentValue (it);
- RefreshRow (it);
+ RefreshRow (it, val);
}
} else if (cr == crtExp && !PreviewWindowManager.IsVisible && ValidObjectForPreviewIcon (it)) {
var val = (ObjectValue)store.GetValue (it, ObjectColumn);
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs
index 411e8c5eda..e65c855147 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs
@@ -26,6 +26,7 @@
using MonoDevelop.Ide;
using Xwt.Drawing;
+using MonoDevelop.Components;
namespace MonoDevelop.Debugger
{
@@ -94,7 +95,13 @@ namespace MonoDevelop.Debugger
ExceptionCaughtDialog.ValueTreeBackgroundColor = Color.FromName ("#525252");
}
+ //Disabled state
+ ObjectValueTreeDisabledBackgroundColor = new Cairo.Color (0.64f, 0.64f, 0.64f);
+
// Shared
+ ObjectValueTreeSelectedTextColor = Ide.Gui.Styles.BaseSelectionTextColor.ToHexString (false);
+ ObjectValueTreeForegroundTextColor = Ide.Gui.Styles.BaseSelectionTextColor.ToHexString (false);
+ ObjectValueTreeExternalCodeForegroundTextColor = ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false);
ObjectValueTreeValueErrorText = Ide.Gui.Styles.WarningForegroundColor;
@@ -102,6 +109,20 @@ namespace MonoDevelop.Debugger
PreviewVisualizerTextColor = Ide.Gui.Styles.PopoverWindow.DefaultTextColor;
PreviewVisualizerHeaderTextColor = Ide.Gui.Styles.PopoverWindow.DefaultTextColor;
}
+
+ public static string ObjectValueTreeSelectedTextColor { get; private set; }
+ public static string ObjectValueTreeForegroundTextColor { get; private set; }
+ public static string ObjectValueTreeExternalCodeForegroundTextColor { get; private set; }
+
+ public static Cairo.Color ObjectValueTreeDisabledBackgroundColor { get; private set; }
+
+ internal static string GetStackFrameForegroundHexColor (bool selected, bool isUserCode)
+ {
+ if (selected) {
+ return ObjectValueTreeSelectedTextColor;
+ }
+ return isUserCode ? ObjectValueTreeForegroundTextColor : ObjectValueTreeExternalCodeForegroundTextColor;
+ }
}
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/TextEntryWithCodeCompletion.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/TextEntryWithCodeCompletion.cs
index ceb0633283..b68f8e5762 100644
--- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/TextEntryWithCodeCompletion.cs
+++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/TextEntryWithCodeCompletion.cs
@@ -34,21 +34,18 @@ namespace MonoDevelop.Debugger
class TextEntryWithCodeCompletion : TextEntry, ICompletionWidget
{
CodeCompletionContext ctx;
- Gtk.Entry gtkEntry;
- Gdk.ModifierType modifier;
+ Xwt.ModifierKeys modifier;
bool keyHandled = false;
uint keyValue;
char keyChar;
- Gdk.Key key;
+ Key key;
public TextEntryWithCodeCompletion ()
{
- gtkEntry = Xwt.Toolkit.CurrentEngine.GetNativeWidget (this) as Gtk.Entry;
- if (gtkEntry == null)
- throw new NotImplementedException ();
- gtkEntry.KeyReleaseEvent += HandleKeyReleaseEvent;
- gtkEntry.KeyPressEvent += HandleKeyPressEvent;
+ KeyPressed += HandleKeyPressEvent;
+ KeyReleased += HandleKeyReleaseEvent;
+
CompletionWindowManager.WindowClosed += HandleWindowClosed;
}
@@ -65,32 +62,40 @@ namespace MonoDevelop.Debugger
CompletionContextChanged (this, EventArgs.Empty);
}
+ char CharFromKey (Xwt.Key xwtKey)
+ {
+ if (xwtKey >= Key.Exclamation && xwtKey <= Key.Tilde) {
+ return (char)xwtKey;
+ }
+
+ return '\0';
+ }
+
[GLib.ConnectBeforeAttribute]
- void HandleKeyPressEvent (object o, Gtk.KeyPressEventArgs args)
+ void HandleKeyPressEvent (object o, KeyEventArgs args)
{
keyHandled = false;
- keyChar = (char)args.Event.Key;
- keyValue = args.Event.KeyValue;
- modifier = args.Event.State;
- key = args.Event.Key;
+ keyChar = CharFromKey (args.Key);
+ modifier = args.Modifiers;
+ key = args.Key;
- if ((args.Event.Key == Gdk.Key.Down || args.Event.Key == Gdk.Key.Up)) {
+ if ((args.Key == Key.Down || args.Key == Key.Up)) {
keyChar = '\0';
}
if (list != null)
- args.RetVal = keyHandled = CompletionWindowManager.PreProcessKeyEvent (KeyDescriptor.FromGtk (key, keyChar, modifier));
+ args.Handled = keyHandled = CompletionWindowManager.PreProcessKeyEvent (KeyDescriptor.FromXwt (key, keyChar, modifier));
}
- void HandleKeyReleaseEvent (object o, Gtk.KeyReleaseEventArgs args)
+ void HandleKeyReleaseEvent (object o, KeyEventArgs args)
{
if (keyHandled)
return;
string text = ctx == null ? Text : Text.Substring (Math.Max (0, Math.Min (ctx.TriggerOffset, Text.Length)));
CompletionWindowManager.UpdateWordSelection (text);
- CompletionWindowManager.PostProcessKeyEvent (KeyDescriptor.FromGtk (key, keyChar, modifier));
+ CompletionWindowManager.PostProcessKeyEvent (KeyDescriptor.FromXwt (key, keyChar, modifier));
PopupCompletion ();
}
@@ -121,7 +126,7 @@ namespace MonoDevelop.Debugger
/// </summary>
class NullDotKeyHandler : ICompletionKeyHandler
{
- #region ICompletionKeyHandler implementation
+#region ICompletionKeyHandler implementation
public bool PreProcessKey (CompletionListWindow listWindow, KeyDescriptor descriptor, out KeyActions keyAction)
{
@@ -141,10 +146,10 @@ namespace MonoDevelop.Debugger
return false;
}
- #endregion
+#endregion
}
- #region ICompletionWidget implementation
+#region ICompletionWidget implementation
public event EventHandler CompletionContextChanged;
@@ -186,11 +191,11 @@ namespace MonoDevelop.Debugger
public CodeCompletionContext CreateCodeCompletionContext (int triggerOffset)
{
- var height = gtkEntry.SizeRequest ().Height;
+ var height = Size.Height;
var location = ConvertToScreenCoordinates (new Point (0, height));
return new CodeCompletionContext (
- (int)location.X, (int)location.Y, height,
+ (int)location.X, (int)location.Y, (int)height,
triggerOffset, 0, triggerOffset, CaretOffset
);
}
@@ -203,13 +208,13 @@ namespace MonoDevelop.Debugger
public void SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word)
{
Text = complete_word;
- gtkEntry.Position = complete_word.Length;
+ CursorPosition = complete_word.Length;
}
public void SetCompletionText (CodeCompletionContext ctx, string partial_word, string complete_word, int completeWordOffset)
{
Text = complete_word;
- gtkEntry.Position = complete_word.Length;
+ CursorPosition = complete_word.Length;
}
public CodeCompletionContext CurrentCodeCompletionContext {
@@ -220,10 +225,10 @@ namespace MonoDevelop.Debugger
public int CaretOffset {
get {
- return gtkEntry.Position;
+ return CursorPosition;
}
set {
- gtkEntry.Position = value;
+ CursorPosition = value;
}
}
@@ -241,7 +246,7 @@ namespace MonoDevelop.Debugger
public Gtk.Style GtkStyle {
get {
- return gtkEntry.Style;
+ return null;
}
}
@@ -250,7 +255,7 @@ namespace MonoDevelop.Debugger
return 1;
}
}
- #endregion
+#endregion
}
}
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/CodeTemplateToolboxProvider.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/CodeTemplateToolboxProvider.cs
index fded6a4a62..ee50ac79f5 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/CodeTemplateToolboxProvider.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/CodeTemplateToolboxProvider.cs
@@ -41,7 +41,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
{
static string category = MonoDevelop.Core.GettextCatalog.GetString ("Text Snippets");
-
public System.Collections.Generic.IEnumerable<ItemToolboxNode> GetDynamicItems (IToolboxConsumer consumer)
{
// TOTEST
@@ -60,7 +59,7 @@ namespace MonoDevelop.DesignerSupport.Toolbox
};
}
}
-
+
public event EventHandler ItemsChanged {
add { CodeTemplateService.TemplatesChanged += value; }
remove { CodeTemplateService.TemplatesChanged -= value; }
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxProvider.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxProvider.cs
index 3498dccd20..041c9cf194 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxProvider.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxProvider.cs
@@ -35,7 +35,13 @@ using System.Collections.Generic;
namespace MonoDevelop.DesignerSupport.Toolbox
{
-
+ public interface IToolboxDynamicProviderDeleteSupport
+ {
+ bool DeleteDynamicItem (ItemToolboxNode node);
+
+ bool CanDeleteDynamicItem (ItemToolboxNode node);
+ }
+
//Used to fetch or generate the default toolbox items at the beginning of each MD session
public interface IToolboxDefaultProvider
{
@@ -53,7 +59,7 @@ namespace MonoDevelop.DesignerSupport.Toolbox
//This method will be called each time the consumer changes. Return null if not
//returning any items for a specific consumer.
IEnumerable<ItemToolboxNode> GetDynamicItems (IToolboxConsumer consumer);
-
+
event EventHandler ItemsChanged;
}
}
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs
index 23045937ef..ae61656bc9 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs
@@ -40,7 +40,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
IEnumerable<ToolboxWidgetCategory> Categories { get; }
IEnumerable<ToolboxWidgetItem> AllItems { get; }
- event EventHandler SelectedItemChanged;
event EventHandler ActivateSelectedItem;
void AddCategory (ToolboxWidgetCategory category);
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs
index f96b00e16f..4a3633ed96 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs
@@ -62,7 +62,7 @@ namespace MonoDevelop.DesignerSupport.Toolbox
public event EventHandler<Gtk.TargetEntry []> DragSourceSet;
public event EventHandler ContentFocused;
- public ItemToolboxNode selectedNode;
+ public ItemToolboxNode SelectedNode => toolboxWidget.SelectedItem?.Node;
NativeViews.ToggleButton catToggleButton;
NativeViews.ToggleButton compactModeToggleButton;
@@ -180,7 +180,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
filterEntry.Changed += FilterEntry_Changed;
- toolboxWidget.SelectedItemChanged += ToolboxWidget_SelectedItemChanged;
toolboxWidget.DragBegin += ToolboxWidget_DragBegin;
toolboxWidget.MouseDownActivated += ToolboxWidget_MouseDownActivated;
toolboxWidget.ActivateSelectedItem += ToolboxWidget_ActivateSelectedItem;
@@ -205,15 +204,13 @@ namespace MonoDevelop.DesignerSupport.Toolbox
Refresh ();
}
- void ToolboxWidget_SelectedItemChanged (object sender, EventArgs e)
- {
- selectedNode = this.toolboxWidget.SelectedItem != null ? this.toolboxWidget.SelectedItem.Tag as ItemToolboxNode : null;
- toolboxService.SelectItem (selectedNode);
- }
-
void ToolboxWidget_ActivateSelectedItem (object sender, EventArgs e)
{
- toolboxService.UseSelectedItem ();
+ var selectedNode = SelectedNode;
+ if (selectedNode != null) {
+ DesignerSupport.Service.ToolboxService.SelectItem (selectedNode);
+ toolboxService.UseSelectedItem ();
+ }
}
void FilterEntry_Changed (object sender, EventArgs e)
@@ -438,16 +435,20 @@ namespace MonoDevelop.DesignerSupport.Toolbox
[CommandHandler (MonoDevelop.Ide.Commands.EditCommands.Delete)]
internal void OnDeleteItem ()
{
- if (MessageService.Confirm (GettextCatalog.GetString ("Are you sure you want to remove the selected Item?"), AlertButton.Delete))
- toolboxService.RemoveUserItem (selectedNode);
+ var selectedNode = SelectedNode;
+ if (selectedNode != null) {
+ if (MessageService.Confirm (GettextCatalog.GetString ("Are you sure you want to remove the selected Item?"), AlertButton.Delete))
+ toolboxService.RemoveUserItem (SelectedNode);
+ }
}
[CommandUpdateHandler (MonoDevelop.Ide.Commands.EditCommands.Delete)]
internal void OnUpdateDeleteItem (CommandInfo info)
{
+ var selectedNode = SelectedNode;
// Hack manually filter out gtk# widgets & container since they cannot be re added
// because they're missing the toolbox attributes.
- info.Enabled = selectedNode != null
+ info.Enabled = selectedNode != null && toolboxService.CanRemoveUserItem (selectedNode)
&& (selectedNode.ItemDomain != GtkWidgetDomain
|| (selectedNode.Category != "Widgets" && selectedNode.Category != "Container"));
}
@@ -540,7 +541,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
toolboxAddButton.Activated -= ToolboxAddButton_Clicked;
toolboxAddButton.Focused -= ToolboxAddButton_Focused;
- toolboxWidget.SelectedItemChanged -= ToolboxWidget_SelectedItemChanged;
toolboxWidget.ActivateSelectedItem -= ToolboxWidget_ActivateSelectedItem;
toolboxWidget.MenuOpened -= ToolboxWidget_MenuOpened;
toolboxWidget.MouseDownActivated -= ToolboxWidget_MouseDownActivated;
@@ -560,12 +560,12 @@ namespace MonoDevelop.DesignerSupport.Toolbox
object IPropertyPadProvider.GetActiveComponent ()
{
- return selectedNode;
+ return SelectedNode;
}
object IPropertyPadProvider.GetProvider ()
{
- return selectedNode;
+ return SelectedNode;
}
void IPropertyPadProvider.OnEndEditing (object obj)
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs
index 361798aca1..e52c52eb18 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs
@@ -56,7 +56,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
public event EventHandler Focused;
public event EventHandler DragBegin;
public event EventHandler<CGPoint> MenuOpened;
- public event EventHandler SelectedItemChanged;
public event EventHandler ActivateSelectedItem;
public Action<NSEvent> MouseDownActivated { get; set; }
@@ -74,8 +73,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
internal void PerformActivateSelectedItem () => OnActivateSelectedItem (EventArgs.Empty);
void OnActivateSelectedItem (EventArgs args) => ActivateSelectedItem?.Invoke (this, args);
-
- void OnSelectedItemChanged (EventArgs args) => SelectedItemChanged?.Invoke (this, args);
NSIndexPath selectedIndexPath;
public NSIndexPath SelectedIndexPath {
@@ -85,7 +82,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox
set {
if (selectedIndexPath != value) {
selectedIndexPath = value;
- OnSelectedItemChanged (EventArgs.Empty);
}
}
}
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs
index 18d9ca7f3d..70a7f4462c 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs
@@ -1149,8 +1149,7 @@ namespace MonoDevelop.DesignerSupport.Toolbox
}
}
- [Obsolete("This class should never have been public")]
- public class Item : IComparable<Item>
+ class Item : IComparable<Item>
{
ToolboxWidgetItem inner;
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs
index 5af30a213a..10f6e33171 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs
@@ -36,12 +36,11 @@ using System;
using System.Collections;
using Mono.Addins;
+using MonoDevelop.Core;
using MonoDevelop.Ide;
namespace MonoDevelop.DesignerSupport
{
-
-
public class DesignerSupportService
{
PropertyPad propertyPad = null;
@@ -78,10 +77,17 @@ namespace MonoDevelop.DesignerSupport
propertyPad.PropertyGrid.Changed += OnPropertyGridChanged;
}
else if (lastCustomProvider != null) {
- propertyPad.UseCustomWidget (lastCustomProvider.GetCustomPropertyWidget ());
- var customizer = lastCustomProvider as IPropertyPadCustomizer;
- if (customizer != null)
- customizer.Customize (pad.PadWindow, null);
+ try {
+ var currentCustomWidget = lastCustomProvider.GetCustomPropertyWidget ();
+ if (currentCustomWidget != null) {
+ propertyPad.UseCustomWidget (currentCustomWidget);
+ if (lastCustomProvider is IPropertyPadCustomizer customizer)
+ customizer.Customize (pad.PadWindow, null);
+ }
+ } catch (Exception ex) {
+ LoggingService.LogInternalError ($"There was an error trying to GetCustomPropertyWidget from '{lastCustomProvider.GetType ()}' provider", ex);
+ ReSetPad ();
+ }
}
}
}
@@ -174,12 +180,24 @@ namespace MonoDevelop.DesignerSupport
lastCustomProvider = provider;
if (propertyPad != null) {
- propertyPad.UseCustomWidget (provider.GetCustomPropertyWidget ());
- propertyPad.CommandRouteOrigin = commandRouteOrigin;
-
- var customizer = provider as IPropertyPadCustomizer;
- if (customizer != null)
- customizer.Customize (propertyPad.PadWindow, null);
+ try {
+ var customWidget = provider.GetCustomPropertyWidget ();
+ if (customWidget != null) {
+ propertyPad.UseCustomWidget (customWidget);
+ propertyPad.CommandRouteOrigin = commandRouteOrigin;
+
+ var customizer = provider as IPropertyPadCustomizer;
+ if (customizer != null)
+ customizer.Customize (propertyPad.PadWindow, null);
+ } else {
+ propertyPad?.BlankPad ();
+ return;
+ }
+ } catch (Exception ex) {
+ LoggingService.LogInternalError ($"There was an error trying to GetCustomPropertyWidget from '{lastCustomProvider.GetType ()}' provider", ex);
+ propertyPad?.BlankPad ();
+ return;
+ }
}
}
else {
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs
index 1be634b3a0..b69333c078 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs
@@ -82,14 +82,17 @@ namespace MonoDevelop.DesignerSupport
targets.AddTable (e);
};
toolbox.DragBegin += (object sender, EventArgs e) => {
- if (!isDragging) {
+ var selectedNode = toolbox.SelectedNode;
+ if (!isDragging && selectedNode != null) {
+
+ DesignerSupport.Service.ToolboxService.SelectItem (selectedNode);
Gtk.Drag.SourceUnset (widget);
// Gtk.Application.CurrentEvent and other copied gdk_events seem to have a problem
// when used as they use gdk_event_copy which seems to crash on de-allocating the private slice.
IntPtr currentEvent = GtkWorkarounds.GetCurrentEventHandle ();
- Gtk.Drag.Begin (widget, targets, Gdk.DragAction.Copy | Gdk.DragAction.Move, 1, new Gdk.Event (currentEvent));
+ Gtk.Drag.Begin (widget, targets, Gdk.DragAction.Copy | Gdk.DragAction.Move, 1, new Gdk.Event (currentEvent, false));
// gtk_drag_begin does not store the event, so we're okay
GtkWorkarounds.FreeEvent (currentEvent);
diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxService.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxService.cs
index 2a53440dcd..a50f32043d 100644
--- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxService.cs
+++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxService.cs
@@ -152,8 +152,16 @@ namespace MonoDevelop.DesignerSupport
public void RemoveUserItem (ItemToolboxNode node)
{
- Configuration.ItemList.Remove (node);
- SaveConfiguration ();
+ if (Configuration.ItemList.Remove (node)) {
+ SaveConfiguration ();
+ } else {
+ //we need check in the dynamic providers
+ foreach (var prov in dynamicProviders) {
+ if (prov is IToolboxDynamicProviderDeleteSupport provDelSupport && provDelSupport.DeleteDynamicItem (node)) {
+ break;
+ }
+ }
+ }
OnToolboxContentsChanged ();
}
@@ -525,7 +533,22 @@ namespace MonoDevelop.DesignerSupport
//we assume permitted, so only return false when blocked by a filter
return true;
}
-
+
+ internal bool CanRemoveUserItem (ItemToolboxNode node)
+ {
+ if (Configuration.ItemList.Contains (node)) {
+ return true;
+ } else {
+ //we need check in the dynamic providers
+ foreach (var prov in dynamicProviders) {
+ if (prov is IToolboxDynamicProviderDeleteSupport provDelSupport && provDelSupport.CanDeleteDynamicItem (node)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
//evaluate a filter attribute against a list, and check whether permitted
private bool FilterPermitted (ItemToolboxNode node, ToolboxItemFilterAttribute desFa,
ICollection<ToolboxItemFilterAttribute> filterAgainst, IToolboxConsumer consumer)
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreMSBuildProjectTests.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreMSBuildProjectTests.cs
index 75efb748ab..bd7cf6e50d 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreMSBuildProjectTests.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreMSBuildProjectTests.cs
@@ -88,11 +88,10 @@ namespace MonoDevelop.DotNetCore.Tests
"</Project>");
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
Assert.AreEqual ("15.0", project.ToolsVersion);
Assert.IsFalse (project.IsOutputTypeDefined);
- Assert.AreEqual ("Microsoft.NET.Sdk", project.Sdk);
Assert.IsTrue (project.HasSdk);
}
@@ -115,6 +114,24 @@ namespace MonoDevelop.DotNetCore.Tests
}
[Test]
+ public void ReadProject_ExplicityReferences ()
+ {
+ CreateMSBuildProject (
+ "<Project ToolsVersion=\"15.0\">\r\n" +
+ " <PropertyGroup>\r\n" +
+ " <OutputType>Exe</OutputType>\r\n" +
+ " <TargetFramework>netcoreapp1.0</TargetFramework>\r\n" +
+ " </PropertyGroup>\r\n" +
+ " <Import Sdk=\"Microsoft.NET.Sdk\" Project=\"Sdk.targets\" />" +
+ "</Project>");
+ msbuildProject.Evaluate ();
+
+ ReadProject ();
+
+ Assert.That (msbuildProject.GetReferencedSDKs (), Is.Not.Empty);
+ }
+
+ [Test]
public void WriteProject_ProjectGuidAddedAndToolsVersionChanged_ProjectGuidIsRemovedAndToolsVersionReset ()
{
CreateMSBuildProject (
@@ -313,7 +330,7 @@ namespace MonoDevelop.DotNetCore.Tests
" </PropertyGroup>\r\n" +
"</Project>");
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
msbuildProject.ToolsVersion = "4.0";
WriteProject ();
@@ -332,7 +349,7 @@ namespace MonoDevelop.DotNetCore.Tests
" </PropertyGroup>\r\n" +
"</Project>");
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
var projectReferenceItem = msbuildProject.AddNewItem ("ProjectReference", @"Lib\Lib.csproj");
projectReferenceItem.Metadata.SetValue ("Name", "Lib");
projectReferenceItem.Metadata.SetValue ("Project", "{F109E7DF-F561-4CD6-A46E-CFB27A8B6F2C}");
@@ -359,7 +376,7 @@ namespace MonoDevelop.DotNetCore.Tests
"</Project>");
msbuildProject.Evaluate ();
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
WriteProject (".NETCoreApp,Version=v1.1");
@@ -380,7 +397,7 @@ namespace MonoDevelop.DotNetCore.Tests
"</Project>");
msbuildProject.Evaluate ();
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
WriteProject (".NETCoreApp,Version=v1.1");
WriteProject (".NETCoreApp,Version=v1.0");
@@ -402,7 +419,7 @@ namespace MonoDevelop.DotNetCore.Tests
"</Project>");
msbuildProject.Evaluate ();
ReadProject (".NET Standard,Version=v1.0");
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
WriteProject (".NETStandard,Version=v1.6");
@@ -423,7 +440,7 @@ namespace MonoDevelop.DotNetCore.Tests
"</Project>");
msbuildProject.Evaluate ();
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
WriteProject (".NETFramework,Version=v4.6");
@@ -444,7 +461,7 @@ namespace MonoDevelop.DotNetCore.Tests
"</Project>");
msbuildProject.Evaluate ();
ReadProject ();
- project.Sdk = "Microsoft.NET.Sdk";
+ project.HasSdk = true;
WriteProject (".NETCoreApp,Version=v1.1");
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreMSBuildProject.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreMSBuildProject.cs
index da870db57f..0034e373fd 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreMSBuildProject.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreMSBuildProject.cs
@@ -45,24 +45,14 @@ namespace MonoDevelop.DotNetCore
public string ToolsVersion { get; private set; }
public bool IsOutputTypeDefined { get; private set; }
- public string Sdk { get; set; }
- public IEnumerable<string> TargetFrameworks {
- get { return targetFrameworks; }
- }
+ public IEnumerable<string> TargetFrameworks => targetFrameworks;
- public bool HasSdk {
- get { return Sdk != null; }
- }
+ public bool HasSdk { get; set; }
- public bool HasToolsVersion ()
- {
- return !string.IsNullOrEmpty (ToolsVersion);
- }
+ public bool HasToolsVersion () => !string.IsNullOrEmpty (ToolsVersion);
- public CompileTarget DefaultCompileTarget {
- get { return defaultCompileTarget; }
- }
+ public CompileTarget DefaultCompileTarget => defaultCompileTarget;
/// <summary>
/// Ensure MSBuildProject has ToolsVersion set to 15.0 so the correct
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs
index b70088877a..a005019c12 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectExtension.cs
@@ -93,7 +93,7 @@ namespace MonoDevelop.DotNetCore
/// </summary>
bool IsSdkProject (DotNetProject project)
{
- return project.MSBuildProject.GetReferencedSDKs ().Any ();
+ return project.MSBuildProject.GetReferencedSDKs ().Length > 0;
}
protected override bool OnGetCanReferenceProject (DotNetProject targetProject, out string reason)
@@ -409,7 +409,12 @@ namespace MonoDevelop.DotNetCore
if (ProjectNeedsRestore ()) {
return CreateNuGetRestoreRequiredBuildResult ();
}
- if ((HasSdk && !IsDotNetCoreSdkInstalled ()) || sdkPaths.IsUnsupportedSdkVersion) {
+
+ if (!Project.TargetFramework.Id.IsNetStandardOrNetCoreApp ()) {
+ return null;
+ }
+
+ if ((HasSdk && !IsDotNetCoreSdkInstalled ()) || (sdkPaths != null && sdkPaths.IsUnsupportedSdkVersion)) {
return CreateDotNetCoreSdkRequiredBuildResult ();
}
return null;
@@ -468,13 +473,10 @@ namespace MonoDevelop.DotNetCore
protected override void OnBeginLoad ()
{
- dotNetCoreMSBuildProject.Sdk = Project.MSBuildProject.Sdk;
base.OnBeginLoad ();
}
- public bool HasSdk {
- get { return dotNetCoreMSBuildProject.HasSdk; }
- }
+ public bool HasSdk => Project.MSBuildProject.GetReferencedSDKs ().Length > 0;
protected bool IsWebProject (DotNetProject project)
{
@@ -491,10 +493,11 @@ namespace MonoDevelop.DotNetCore
if (!HasSdk)
return;
-
- sdkPaths = DotNetCoreSdk.FindSdkPaths (dotNetCoreMSBuildProject.Sdk);
- }
+ var referencedSdks = project.GetReferencedSDKs ();
+ sdkPaths = DotNetCoreSdk.FindSdkPaths (referencedSdks);
+ dotNetCoreMSBuildProject.HasSdk = referencedSdks.Length > 0;
+ }
protected override async Task<ImmutableArray<ProjectFile>> OnGetSourceFiles (ProgressMonitor monitor, ConfigurationSelector configuration)
{
var sourceFiles = await base.OnGetSourceFiles (monitor, configuration);
@@ -608,7 +611,14 @@ namespace MonoDevelop.DotNetCore
bool IsFSharpSdkProject ()
{
- return HasSdk && dotNetCoreMSBuildProject.Sdk.Contains ("FSharp");
+ if (HasSdk) {
+ var sdks = Project.MSBuildProject.GetReferencedSDKs ();
+ for (var i = 0; i < sdks.Length; i++) {
+ if (sdks [i].Contains ("FSharp"))
+ return true;
+ }
+ }
+ return false;
}
/// <summary>
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs
index 97be6b2649..9863575fb1 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs
@@ -94,7 +94,7 @@ namespace MonoDevelop.DotNetCore
public static IEnumerable<TargetFramework> GetNetCoreAppTargetFrameworks ()
{
foreach (Version runtimeVersion in GetMajorRuntimeVersions ()) {
- if (runtimeVersion.Major == 3 && runtimeVersion.Minor > 0) {
+ if (runtimeVersion.Major > 3 || (runtimeVersion.Major == 3 && runtimeVersion.Minor > 0)) {
// Skip versions > 3.0 since this is not currently supported.
continue;
}
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs
index df0ef2a820..534c810f88 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs
@@ -36,11 +36,10 @@ namespace MonoDevelop.DotNetCore
public static class DotNetCoreSdk
{
static readonly Version DotNetCoreVersion2_1 = new Version (2, 1, 0);
- static readonly DotNetCoreSdkPaths sdkPaths;
static DotNetCoreSdk ()
{
- sdkPaths = new DotNetCoreSdkPaths ();
+ var sdkPaths = new DotNetCoreSdkPaths ();
sdkPaths.ResolveSDK ();
Update (sdkPaths);
}
@@ -82,9 +81,11 @@ namespace MonoDevelop.DotNetCore
{
}
- internal static DotNetCoreSdkPaths FindSdkPaths (string sdk)
+ internal static DotNetCoreSdkPaths FindSdkPaths (string[] sdks)
{
- sdkPaths.FindSdkPaths (sdk);
+ var sdkPaths = new DotNetCoreSdkPaths ();
+ sdkPaths.ResolveSDK ();
+ sdkPaths.FindSdkPaths (sdks);
return sdkPaths;
}
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdkPaths.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdkPaths.cs
index 87122fbd1a..b41d895b9c 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdkPaths.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdkPaths.cs
@@ -119,12 +119,12 @@ namespace MonoDevelop.DotNetCore
IsUnsupportedSdkVersion = false;
}
- public void FindSdkPaths (string sdk)
+ public void FindSdkPaths (string [] sdks)
{
if (string.IsNullOrEmpty (MSBuildSDKsPath))
return;
- Exist = CheckSdksExist (sdk);
+ Exist = CheckSdksExist (sdks);
if (Exist) {
IsUnsupportedSdkVersion = !CheckIsSupportedSdkVersion (SdksParentDirectory);
@@ -149,18 +149,13 @@ namespace MonoDevelop.DotNetCore
string SdksParentDirectory { get; set; }
- static IEnumerable<string> SplitSdks (string sdk) => sdk.Split (new [] { ';' }, StringSplitOptions.RemoveEmptyEntries);
-
- bool CheckSdksExist (string sdk)
+ bool CheckSdksExist (string[] sdks)
{
- if (sdk.Contains (';')) {
- foreach (string sdkItem in SplitSdks (sdk)) {
- if (!SdkPathExists (sdkItem))
- return false;
- }
- return true;
+ foreach (string sdkItem in sdks) {
+ if (!SdkPathExists (sdkItem))
+ return false;
}
- return SdkPathExists (sdk);
+ return true;
}
bool SdkPathExists (string sdk)
@@ -238,10 +233,14 @@ namespace MonoDevelop.DotNetCore
using (var r = new StreamReader (GlobalJsonPath)) {
try {
var token = JObject.Parse (r.ReadToEnd ());
- if (token == null)
- return string.Empty;
- var version = (string)token.SelectToken ("sdk").SelectToken ("version");
- return version;
+
+ if (token != null && token.TryGetValue ("sdk", out var sdkToken)) {
+ var version = sdkToken ["version"];
+ if (version != null)
+ return version.Value<string>();
+ }
+
+ return string.Empty;
} catch (Exception e) {
LoggingService.LogWarning ($"Unable to parse {GlobalJsonPath}.", e);
return string.Empty;
diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs
index f336abfb60..2e9a399908 100644
--- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs
+++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs
@@ -34,7 +34,7 @@ namespace MonoDevelop.DotNetCore
static readonly Version MonoVersion5_4 = new Version (5, 4, 0);
static readonly Version DotNetCore2_1 = new Version (2, 1);
- internal static Version CurrentRuntimeVersion { get; set; } = MonoRuntimeInfo.FromCurrentRuntime ().RuntimeVersion;
+ internal static Version CurrentRuntimeVersion { get; set; } = MonoRuntimeInfo.FromCurrentRuntime ()?.RuntimeVersion ?? new Version ();
public static bool SupportsNetStandard20 (this Version monoVersion)
{
diff --git a/main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/ToolboxProvider.cs b/main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/ToolboxProvider.cs
index 1616424340..2df3434088 100644
--- a/main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/ToolboxProvider.cs
+++ b/main/src/addins/MonoDevelop.GtkCore/MonoDevelop.GtkCore.GuiBuilder/ToolboxProvider.cs
@@ -14,7 +14,7 @@ using MonoDevelop.Components;
namespace MonoDevelop.GtkCore.GuiBuilder
{
- public class ToolboxProvider: IToolboxDynamicProvider, IToolboxDefaultProvider
+ public class ToolboxProvider: IToolboxDynamicProvider, IToolboxDefaultProvider, IToolboxDynamicProviderDeleteSupport
{
internal static ToolboxProvider Instance;
@@ -82,7 +82,11 @@ namespace MonoDevelop.GtkCore.GuiBuilder
{
yield return typeof(Stetic.Wrapper.Widget).Assembly.Location;
}
-
+
+ public virtual bool DeleteDynamicItem (ItemToolboxNode node) => false;
+
+ public virtual bool CanDeleteDynamicItem (ItemToolboxNode node) => false;
+
public event EventHandler ItemsChanged;
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/LambdaMessageHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/LambdaMessageHandler.cs
new file mode 100644
index 0000000000..fc9c82c06a
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/LambdaMessageHandler.cs
@@ -0,0 +1,32 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+// test/NuGet.Core.Tests/NuGet.Protocol.Tests/HttpSource/LambdaMessageHandler.cs
+
+using System;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MonoDevelop.PackageManagement.Tests
+{
+ internal class LambdaMessageHandler : HttpMessageHandler
+ {
+ private readonly Func<HttpRequestMessage, HttpResponseMessage> _delegate;
+
+ public LambdaMessageHandler (Func<HttpRequestMessage, HttpResponseMessage> @delegate)
+ {
+ if (@delegate == null) {
+ throw new ArgumentNullException (nameof (@delegate));
+ }
+
+ _delegate = @delegate;
+ }
+
+ protected override Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ return Task.FromResult (_delegate (request));
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/RestoreTestBase.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/RestoreTestBase.cs
index 6500fb434b..1877922a3c 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/RestoreTestBase.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/RestoreTestBase.cs
@@ -30,6 +30,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MonoDevelop.Core;
+using MonoDevelop.Ide;
using MonoDevelop.Projects;
using NuGet.Configuration;
using NuGet.PackageManagement;
@@ -42,7 +43,7 @@ using UnitTests;
namespace MonoDevelop.PackageManagement.Tests.Helpers
{
- public abstract class RestoreTestBase : TestBase
+ public abstract class RestoreTestBase : IdeTestBase
{
protected Solution solution;
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestProxy.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestProxy.cs
new file mode 100644
index 0000000000..9e0e626fde
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestProxy.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+// test/NuGet.Core.Tests/NuGet.Protocol.Tests/HttpSource/TestProxy.cs
+
+using System;
+using System.Net;
+
+namespace MonoDevelop.PackageManagement.Tests
+{
+ internal class TestProxy : IWebProxy
+ {
+ readonly Uri proxyAddress;
+
+ public TestProxy (Uri proxyAddress)
+ {
+ this.proxyAddress = proxyAddress;
+ }
+
+ public ICredentials Credentials { get; set; }
+
+ public Uri GetProxy (Uri destination) => proxyAddress;
+
+ public bool IsBypassed (Uri host) => false;
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj
index f3197f3c68..8e7ea6b49c 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj
@@ -1,5 +1,6 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Import Project="..\..\..\..\MonoDevelop.props" />
+ <Import Project="$(ReferencesGtk)" />
<PropertyGroup>
<ProjectGuid>{2645C9F3-9ED5-4806-AB09-DAD9BE90C67B}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -70,6 +71,14 @@
<Reference Include="NuGet.Commands">
<HintPath>..\..\..\..\packages\NuGet.Commands.4.8.0\lib\net46\NuGet.Commands.dll</HintPath>
</Reference>
+ <Reference Include="Castle.Core">
+ <HintPath>..\..\..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Configuration" />
+ <Reference Include="Moq">
+ <HintPath>..\..\..\..\packages\Moq.4.7.145\lib\net45\Moq.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -183,6 +192,9 @@
<Compile Include="MonoDevelop.PackageManagement.Tests\NuGetPackageAssetSourceFilesTests.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeNuGetAwareProject.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests\NuGetSdkResolverTests.cs" />
+ <Compile Include="MonoDevelop.PackageManagement.Tests\NuGetHttpSourceAuthenticationHandlerTests.cs" />
+ <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\LambdaMessageHandler.cs" />
+ <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestProxy.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests\AssemblyReferenceWithConditionTests.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests\ProjectReferenceMaintainerTests.cs" />
<Compile Include="MonoDevelop.PackageManagement.Tests\FullyQualifiedReferencePathTests.cs" />
@@ -213,6 +225,15 @@
<Name>UnitTests</Name>
<Private>False</Private>
</ProjectReference>
+ <ProjectReference Include="..\..\..\..\external\xwt\Xwt\Xwt.csproj">
+ <Project>{92494904-35FA-4DC9-BDE9-3A3E87AC49D3}</Project>
+ <Name>Xwt</Name>
+ <Private>False</Private>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\..\tests\IdeUnitTests\IdeUnitTests.csproj">
+ <Project>{F7B2B155-7CF4-42C4-B5AF-63C0667D2E4F}</Project>
+ <Name>IdeUnitTests</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs
index 13e91f8a09..b501bd500e 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs
@@ -63,7 +63,7 @@ namespace MonoDevelop.PackageManagement.Tests
}
[Test]
- public async Task OfflineRestore_NetCore22Project ()
+ public async Task OfflineRestore_NetCore21Project ()
{
FilePath solutionFileName = Util.GetSampleProject ("restore-netcore-offline", "dotnetcoreconsole.sln");
solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName);
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/NuGetHttpSourceAuthenticationHandlerTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/NuGetHttpSourceAuthenticationHandlerTests.cs
new file mode 100644
index 0000000000..61898383cb
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/NuGetHttpSourceAuthenticationHandlerTests.cs
@@ -0,0 +1,406 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+// Based on: NuGet.Core.Tests/NuGet.Protocol.Tests/HttpSource/HttpSourceAuthenticationHandlerTests.cs
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using MonoDevelop.Core.Web;
+using Moq;
+using NuGet.Configuration;
+using NuGet.Protocol;
+using NUnit.Framework;
+
+namespace MonoDevelop.PackageManagement.Tests
+{
+ [TestFixture]
+ public class NuGetHttpSourceAuthenticationHandlerTests
+ {
+ [Test]
+ public void Constructor_WithSourceCredentials_InitializesClientHandler ()
+ {
+ var packageSource = new PackageSource ("http://package.source.net", "source") {
+ Credentials = new PackageSourceCredential ("source", "user", "password", isPasswordClearText: true)
+ };
+ var clientHandler = new TestHttpClientHandler ();
+ var credentialService = Mock.Of<ICredentialService> ();
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService);
+
+ Assert.NotNull (clientHandler.Credentials);
+
+ var actualCredentials = clientHandler.Credentials.GetCredential (packageSource.SourceUri, "Basic");
+ Assert.NotNull (actualCredentials);
+ Assert.AreEqual ("user", actualCredentials.UserName);
+ Assert.AreEqual ("password", actualCredentials.Password);
+ }
+
+ [Test]
+ public async Task SendAsync_WithUnauthenticatedSource_PassesThru ()
+ {
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = new Mock<ICredentialService> (MockBehavior.Strict);
+ credentialService.SetupGet (x => x.HandlesDefaultCredentials)
+ .Returns (false);
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService.Object) {
+ InnerHandler = GetLambdaMessageHandler (HttpStatusCode.OK)
+ };
+
+ var response = await SendAsync (handler);
+
+ Assert.NotNull (response);
+ Assert.AreEqual (HttpStatusCode.OK, response.StatusCode);
+ }
+
+ [Test]
+ public async Task SendAsync_WithAcquiredCredentialsOn401_RetriesRequest ()
+ {
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+ Mock.Get (credentialService)
+ .Setup (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()))
+ .Returns (() => Task.FromResult<ICredentials> (new NetworkCredential ()));
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService) {
+ InnerHandler = GetLambdaMessageHandler (
+ HttpStatusCode.Unauthorized, HttpStatusCode.OK)
+ };
+
+ var response = await SendAsync (handler);
+
+ Assert.NotNull (response);
+ Assert.AreEqual (HttpStatusCode.OK, response.StatusCode);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Once ());
+ }
+
+ [Test]
+ public async Task SendAsync_WithAcquiredCredentialsOn403_RetriesRequest ()
+ {
+ // Arrange
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+ Mock.Get (credentialService)
+ .Setup (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Forbidden,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()))
+ .Returns (() => Task.FromResult<ICredentials> (new NetworkCredential ()));
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService) {
+ InnerHandler = GetLambdaMessageHandler (
+ HttpStatusCode.Forbidden, HttpStatusCode.OK)
+ };
+
+ // Act
+ var response = await SendAsync (handler);
+
+ // Assert
+ Assert.NotNull (response);
+ Assert.AreEqual (HttpStatusCode.OK, response.StatusCode);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Forbidden,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Once ());
+ }
+
+ [Test]
+ public async Task SendAsync_WhenTaskCanceledExceptionThrownDuringAcquiringCredentials_Throws ()
+ {
+ // Arrange
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+ Mock.Get (credentialService)
+ .Setup (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()))
+ .ThrowsAsync (new TaskCanceledException ());
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService);
+
+ int retryCount = 0;
+ var innerHandler = new LambdaMessageHandler (
+ _ => {
+ retryCount++;
+ return new HttpResponseMessage (HttpStatusCode.Unauthorized);
+ });
+ handler.InnerHandler = innerHandler;
+
+ // Act & Assert
+ await AssertThrowsAsync<TaskCanceledException> (
+ () => SendAsync (handler));
+
+ Assert.AreEqual (1, retryCount);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Once);
+ }
+
+ [Test]
+ public async Task SendAsync_WhenOperationCanceledExceptionThrownDuringAcquiringCredentials_Throws ()
+ {
+ // Arrange
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var cts = new CancellationTokenSource ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+ Mock.Get (credentialService)
+ .Setup (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()))
+ .ThrowsAsync (new OperationCanceledException ())
+ .Callback (() => cts.Cancel ());
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService);
+
+ int retryCount = 0;
+ var innerHandler = new LambdaMessageHandler (
+ _ => {
+ retryCount++;
+ return new HttpResponseMessage (HttpStatusCode.Unauthorized);
+ });
+ handler.InnerHandler = innerHandler;
+
+ // Act & Assert
+ await AssertThrowsAsync<OperationCanceledException> (
+ () => SendAsync (handler));
+
+ Assert.AreEqual (1, retryCount);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Once);
+ }
+
+ [Test]
+ public async Task SendAsync_WithWrongCredentials_StopsRetryingAfter3Times ()
+ {
+ // Arrange
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+ Mock.Get (credentialService)
+ .Setup (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()))
+ .Returns (() => Task.FromResult<ICredentials> (new NetworkCredential ()));
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService);
+
+ int retryCount = 0;
+ var innerHandler = new LambdaMessageHandler (
+ _ => {
+ retryCount++;
+ return new HttpResponseMessage (HttpStatusCode.Unauthorized);
+ });
+ handler.InnerHandler = innerHandler;
+
+ // Act
+ var response = await SendAsync (handler);
+
+ // Assert
+ Assert.NotNull (response);
+ Assert.AreEqual (HttpStatusCode.Unauthorized, response.StatusCode);
+
+ Assert.AreEqual (NuGetHttpSourceAuthenticationHandler.MaxAuthRetries + 1, retryCount);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Exactly (NuGetHttpSourceAuthenticationHandler.MaxAuthRetries));
+ }
+
+ [Test]
+ public async Task SendAsync_WithMissingCredentials_Returns401 ()
+ {
+ // Arrange
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService);
+
+ int retryCount = 0;
+ var innerHandler = new LambdaMessageHandler (
+ _ => {
+ retryCount++;
+ return new HttpResponseMessage (HttpStatusCode.Unauthorized);
+ });
+ handler.InnerHandler = innerHandler;
+
+ // Act
+ var response = await SendAsync (handler);
+
+ // Assert
+ Assert.NotNull (response);
+ Assert.AreEqual (HttpStatusCode.Unauthorized, response.StatusCode);
+
+ Assert.AreEqual (1, retryCount);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Once ());
+ }
+
+ [Test]
+ public async Task SendAsync_WhenCredentialServiceThrows_Returns401 ()
+ {
+ // Arrange
+ var packageSource = new PackageSource ("http://package.source.net");
+ var clientHandler = new TestHttpClientHandler ();
+
+ var credentialService = Mock.Of<ICredentialService> ();
+ Mock.Get (credentialService)
+ .Setup (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()))
+ .Throws (new InvalidOperationException ("Credential service failed acquring user credentials"));
+
+ var handler = new NuGetHttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService);
+
+ int retryCount = 0;
+ var innerHandler = new LambdaMessageHandler (
+ _ => {
+ retryCount++;
+ return new HttpResponseMessage (HttpStatusCode.Unauthorized);
+ });
+ handler.InnerHandler = innerHandler;
+
+ // Act
+ var response = await SendAsync (handler);
+
+ // Assert
+ Assert.NotNull (response);
+ Assert.AreEqual (HttpStatusCode.Unauthorized, response.StatusCode);
+
+ Assert.AreEqual (1, retryCount);
+
+ Mock.Get (credentialService)
+ .Verify (
+ x => x.GetCredentialsAsync (
+ packageSource.SourceUri,
+ It.IsAny<IWebProxy> (),
+ CredentialRequestType.Unauthorized,
+ It.IsAny<string> (),
+ It.IsAny<CancellationToken> ()),
+ Times.Once ());
+ }
+
+ static LambdaMessageHandler GetLambdaMessageHandler (HttpStatusCode statusCode)
+ {
+ return new LambdaMessageHandler (
+ _ => new HttpResponseMessage (statusCode));
+ }
+
+ static LambdaMessageHandler GetLambdaMessageHandler (params HttpStatusCode [] statusCodes)
+ {
+ var responses = new Queue<HttpStatusCode> (statusCodes);
+ return new LambdaMessageHandler (
+ _ => new HttpResponseMessage (responses.Dequeue ()));
+ }
+
+ static async Task<HttpResponseMessage> SendAsync (
+ HttpMessageHandler handler,
+ HttpRequestMessage request = null,
+ CancellationToken cancellationToken = default (CancellationToken))
+ {
+ using (var client = new HttpClient (handler)) {
+ return await client.SendAsync (request ?? new HttpRequestMessage (HttpMethod.Get, "http://foo"), cancellationToken);
+ }
+ }
+
+ static async Task AssertThrowsAsync<T> (Func<Task> handler)
+ {
+ try {
+ await handler ();
+ Assert.Fail ("Exception not thrown");
+ } catch (Exception ex) {
+ Assert.IsInstanceOf<T> (ex);
+ }
+ }
+
+ class TestHttpClientHandler : HttpClientHandler, IHttpCredentialsHandler
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/UpdateXamarinFormsBuildTest.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/UpdateXamarinFormsBuildTest.cs
index 6b20e6a00c..1f333ad4cd 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/UpdateXamarinFormsBuildTest.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/UpdateXamarinFormsBuildTest.cs
@@ -27,6 +27,7 @@
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
+using MonoDevelop.Core;
using MonoDevelop.PackageManagement.Tests.Helpers;
using MonoDevelop.Projects;
using NuGet.Versioning;
@@ -38,6 +39,23 @@ namespace MonoDevelop.PackageManagement.Tests
[TestFixture]
public class UpdateXamarinFormsBuildTest : RestoreTestBase
{
+ static readonly string UseNSUrlSessionHandlerProperty = "MonoDevelop.MacIntegration.UseNSUrlSessionHandler";
+ static bool originalSessionHandlerPropertyValue;
+
+ [TestFixtureSetUp]
+ public void TestFixtureSetup ()
+ {
+ // Disable use of Xamarin.Mac's NSUrlSessionHandler for tests. Xamarin.Mac is not initialized.
+ originalSessionHandlerPropertyValue = PropertyService.Get (UseNSUrlSessionHandlerProperty, true);
+ PropertyService.Set (UseNSUrlSessionHandlerProperty, false);
+ }
+
+ [TestFixtureTearDown]
+ public void TestFixtureTearDown ()
+ {
+ PropertyService.Set (UseNSUrlSessionHandlerProperty, originalSessionHandlerPropertyValue);
+ }
+
/// <summary>
/// Tests that a build error due to a mismatched Xamarin.Forms NuGet package used
/// in two projects can be fixed by updating the NuGet package to match without
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/packages.config b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/packages.config
index ee51c23738..b6e3ec09f6 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/packages.config
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/packages.config
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
+ <package id="Castle.Core" version="4.2.1" targetFramework="net471" />
+ <package id="Moq" version="4.7.145" targetFramework="net471" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj
index fe47de2d96..f37a483a88 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj
@@ -370,6 +370,10 @@
<Compile Include="MonoDevelop.PackageManagement\ItemTemplateNuGetPackageInstaller.cs" />
<Compile Include="Gui\MonoDevelop.PackageManagement.PackageManagementOptionsWidget.cs" />
<Compile Include="Gui\MonoDevelop.PackageManagement.PackageSourcesWidget.cs" />
+ <Compile Include="MonoDevelop.PackageManagement\RepositoryProviderFactoryExtensions.cs" />
+ <Compile Include="MonoDevelop.PackageManagement\MonoDevelopHttpHandlerResourceV3Provider.cs" />
+ <Compile Include="MonoDevelop.PackageManagement\MonoDevelopServerWarningLogHandler.cs" />
+ <Compile Include="MonoDevelop.PackageManagement\NuGetHttpSourceAuthenticationHandler.cs" />
<Compile Include="MonoDevelop.PackageManagement\ProjectReferenceMaintainer.cs" />
<Compile Include="MonoDevelop.PackageManagement\IProjectReferenceMaintainer.cs" />
<Compile Include="MonoDevelop.PackageManagement\IHasProjectReferenceMaintainer.cs" />
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/HttpClientFactory.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/HttpClientFactory.cs
index 0222a6a372..d8d260abfc 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/HttpClientFactory.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/HttpClientFactory.cs
@@ -25,14 +25,15 @@
// THE SOFTWARE.
using System;
+using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
+using NuGet.Common;
using NuGet.Configuration;
+using NuGet.Credentials;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
-using NuGet.Common;
-using NuGet.Credentials;
namespace MonoDevelop.PackageManagement
{
@@ -47,37 +48,23 @@ namespace MonoDevelop.PackageManagement
{
var httpClient = new HttpClient (CreateHttpMessageHandler (packageSource, credentialService));
UserAgent.SetUserAgent (httpClient);
+ SetAcceptLanguage (httpClient);
return httpClient;
}
- static HttpMessageHandler CreateHttpMessageHandler (PackageSource packageSource, ICredentialService credentialService)
+ static void SetAcceptLanguage (HttpClient httpClient)
{
- var proxy = ProxyCache.Instance.GetProxy (packageSource.SourceUri);
-
- var clientHandler = new HttpClientHandler {
- Proxy = proxy,
- AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate)
- };
-
- HttpMessageHandler messageHandler = clientHandler;
-
- if (proxy != null) {
- messageHandler = new ProxyAuthenticationHandler (clientHandler, credentialService, ProxyCache.Instance);
+ string acceptLanguage = CultureInfo.CurrentUICulture.ToString ();
+ if (!string.IsNullOrEmpty (acceptLanguage)) {
+ httpClient.DefaultRequestHeaders.AcceptLanguage.ParseAdd (acceptLanguage);
}
+ }
- HttpMessageHandler innerHandler = messageHandler;
- messageHandler = new StsAuthenticationHandler (packageSource, TokenStore.Instance) {
- InnerHandler = messageHandler
- };
-
- innerHandler = messageHandler;
-
- messageHandler = new HttpSourceAuthenticationHandler (packageSource, clientHandler, credentialService) {
- InnerHandler = innerHandler
- };
-
- return messageHandler;
+ static HttpMessageHandler CreateHttpMessageHandler (PackageSource packageSource, ICredentialService credentialService)
+ {
+ HttpHandlerResourceV3 resource = MonoDevelopHttpHandlerResourceV3Provider.CreateResource (packageSource, credentialService, nonInteractive: true);
+ return resource.MessageHandler;
}
public static ICredentialService CreateNonInteractiveCredentialService ()
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopHttpHandlerResourceV3Provider.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopHttpHandlerResourceV3Provider.cs
new file mode 100644
index 0000000000..2776eac384
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopHttpHandlerResourceV3Provider.cs
@@ -0,0 +1,118 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// Based on HttpHandlerResourceV3Provider
+// From: https://github.com/NuGet/NuGet.Client/
+
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using MonoDevelop.Core.Web;
+using NuGet.Configuration;
+using NuGet.Protocol;
+using NuGet.Protocol.Core.Types;
+
+namespace MonoDevelop.PackageManagement
+{
+ class MonoDevelopHttpHandlerResourceV3Provider : ResourceProvider
+ {
+ public MonoDevelopHttpHandlerResourceV3Provider ()
+ : base (typeof (HttpHandlerResource),
+ nameof (MonoDevelopHttpHandlerResourceV3Provider),
+ NuGetResourceProviderPositions.Last)
+ {
+ }
+
+ public override Task<Tuple<bool, INuGetResource>> TryCreate (SourceRepository source, CancellationToken token)
+ {
+ Debug.Assert (source.PackageSource.IsHttp, "HTTP handler requested for a non-http source.");
+
+ HttpHandlerResourceV3 curResource = null;
+
+ if (source.PackageSource.IsHttp) {
+ curResource = CreateResource (source.PackageSource);
+ }
+
+ return Task.FromResult (new Tuple<bool, INuGetResource> (curResource != null, curResource));
+ }
+
+ static HttpHandlerResourceV3 CreateResource (PackageSource packageSource)
+ {
+ return CreateResource (packageSource, HttpHandlerResourceV3.CredentialService.Value);
+ }
+
+ internal static HttpHandlerResourceV3 CreateResource (PackageSource packageSource, ICredentialService credentialService, bool nonInteractive = false)
+ {
+ var sourceUri = packageSource.SourceUri;
+ var settings = new HttpClientSettings {
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
+ SourceAuthenticationRequired = false,
+ NonInteractive = nonInteractive
+ };
+ var rootHandler = HttpClientProvider.CreateHttpMessageHandler (sourceUri, settings);
+
+ // HTTP handler pipeline can be injected here, around the client handler
+ HttpMessageHandler messageHandler = new MonoDevelopServerWarningLogHandler (rootHandler);
+
+ var innerHandler = messageHandler;
+
+ messageHandler = new StsAuthenticationHandler (packageSource, TokenStore.Instance) {
+ InnerHandler = messageHandler
+ };
+
+ innerHandler = messageHandler;
+ var credentialsHandler = GetHttpCredentialsHandler (rootHandler);
+
+ messageHandler = new NuGetHttpSourceAuthenticationHandler (packageSource, credentialsHandler, credentialService) {
+ InnerHandler = innerHandler
+ };
+
+ // Have to pass a dummy HttpClientProvider since it may not be used by a native implementation, such as on the Mac.
+ // It looks like the only place this is used in NuGet is with the DownloadResourcePluginProvider in order to pass the
+ // HttpClientHandler's Proxy to the GetCredentialsRequestHandler. There is no plugin support yet in MonoDevelop but
+ // this may be a problem in the future. Possibly a custom DownloadResourcePluginProvider would be required or possibly
+ // a HttpClientProvider created with just its Proxy property set based on the current proxy for that url.
+ var clientHandler = GetOrCreateHttpClientHandler (rootHandler);
+
+ // Get the proxy from NuGet's ProxyCache which has support for proxy information define in the NuGet.Config file.
+ var proxy = ProxyCache.Instance.GetUserConfiguredProxy ();
+ if (proxy != null) {
+ clientHandler.Proxy = proxy;
+ }
+ var resource = new HttpHandlerResourceV3 (clientHandler, messageHandler);
+
+ return resource;
+ }
+
+ static IHttpCredentialsHandler GetHttpCredentialsHandler (HttpMessageHandler handler)
+ {
+ do {
+ if (handler is IHttpCredentialsHandler credentialsHandler) {
+ return credentialsHandler;
+ }
+
+ var delegatingHandler = handler as DelegatingHandler;
+ handler = delegatingHandler?.InnerHandler;
+ } while (handler != null);
+
+ return null;
+ }
+
+ static HttpClientHandler GetOrCreateHttpClientHandler (HttpMessageHandler handler)
+ {
+ do {
+ if (handler is HttpClientHandler clientHandler) {
+ return clientHandler;
+ }
+
+ var delegatingHandler = handler as DelegatingHandler;
+ handler = delegatingHandler?.InnerHandler;
+ } while (handler != null);
+
+ return new HttpClientHandler ();
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopServerWarningLogHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopServerWarningLogHandler.cs
new file mode 100644
index 0000000000..98a7d5629f
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/MonoDevelopServerWarningLogHandler.cs
@@ -0,0 +1,32 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// Based on ServerWarningLogHandler.
+// From: https://github.com/NuGet/NuGet.Client/From: https://github.com/NuGet/NuGet.Client/
+
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using NuGet.Protocol;
+
+namespace MonoDevelop.PackageManagement
+{
+ class MonoDevelopServerWarningLogHandler : DelegatingHandler
+ {
+ public MonoDevelopServerWarningLogHandler (HttpMessageHandler innerHandler)
+ : base (innerHandler)
+ {
+ }
+
+ protected override async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ var configuration = request.GetOrCreateConfiguration ();
+
+ var response = await base.SendAsync (request, cancellationToken).ConfigureAwait (false);
+
+ response.LogServerWarning (configuration.Logger);
+
+ return response;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/NuGetHttpSourceAuthenticationHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/NuGetHttpSourceAuthenticationHandler.cs
new file mode 100644
index 0000000000..88b0b8fa24
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/NuGetHttpSourceAuthenticationHandler.cs
@@ -0,0 +1,230 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// Based on HttpSourceAuthenticationHandler
+// From: https://github.com/NuGet/NuGet.Client/
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using MonoDevelop.Core.Web;
+using NuGet.Common;
+using NuGet.Configuration;
+
+namespace NuGet.Protocol
+{
+ class NuGetHttpSourceAuthenticationHandler : DelegatingHandler
+ {
+ public static readonly int MaxAuthRetries = 4;
+
+ // Only one source may prompt at a time
+ readonly static SemaphoreSlim credentialPromptLock = new SemaphoreSlim (1, 1);
+
+ readonly PackageSource packageSource;
+ readonly IHttpCredentialsHandler credentialsHandler;
+ readonly ICredentialService credentialService;
+
+ readonly SemaphoreSlim httpClientLock = new SemaphoreSlim (1, 1);
+ Dictionary<string, AmbientAuthenticationState> authStates = new Dictionary<string, AmbientAuthenticationState> ();
+ HttpSourceCredentials credentials;
+
+ public NuGetHttpSourceAuthenticationHandler (
+ PackageSource packageSource,
+ IHttpCredentialsHandler credentialsHandler,
+ ICredentialService credentialService)
+ {
+ if (packageSource == null) {
+ throw new ArgumentNullException (nameof (packageSource));
+ }
+
+ this.packageSource = packageSource;
+
+ if (credentialsHandler == null) {
+ throw new ArgumentNullException (nameof (credentialsHandler));
+ }
+
+ this.credentialsHandler = credentialsHandler;
+
+ // credential service is optional as credentials may be attached to a package source
+ this.credentialService = credentialService;
+
+ // Create a new wrapper for ICredentials that can be modified
+
+ if (credentialService == null || !credentialService.HandlesDefaultCredentials) {
+ // This is used to match the value of HttpClientHandler.UseDefaultCredentials = true
+ credentials = new HttpSourceCredentials (CredentialCache.DefaultNetworkCredentials);
+ } else {
+ credentials = new HttpSourceCredentials ();
+ }
+
+ if (packageSource.Credentials != null &&
+ packageSource.Credentials.IsValid ()) {
+ var sourceCredentials = new NetworkCredential (packageSource.Credentials.Username, packageSource.Credentials.Password);
+ credentials.Credentials = sourceCredentials;
+ }
+
+ this.credentialsHandler.Credentials = credentials;
+ // Always take the credentials from the helper.
+ this.credentialsHandler.UseDefaultCredentials = false;
+ }
+
+ protected override async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ HttpResponseMessage response = null;
+ ICredentials promptCredentials = null;
+
+ var configuration = request.GetOrCreateConfiguration ();
+
+ // Authorizing may take multiple attempts
+ while (true) {
+ // Clean up any previous responses
+ if (response != null) {
+ response.Dispose ();
+ }
+
+ // store the auth state before sending the request
+ var beforeLockVersion = credentials.Version;
+
+ response = await base.SendAsync (request, cancellationToken).ConfigureAwait (false);
+
+ if (credentialService == null) {
+ return response;
+ }
+
+ if (response.StatusCode == HttpStatusCode.Unauthorized ||
+ (configuration.PromptOn403 && response.StatusCode == HttpStatusCode.Forbidden)) {
+ promptCredentials = await AcquireCredentialsAsync (
+ response.StatusCode,
+ beforeLockVersion,
+ configuration.Logger,
+ cancellationToken);
+
+ if (promptCredentials == null) {
+ return response;
+ }
+
+ continue;
+ }
+
+ if (promptCredentials != null) {
+ CredentialsSuccessfullyUsed (packageSource.SourceUri, promptCredentials);
+ }
+
+ return response;
+ }
+ }
+
+ private async Task<ICredentials> AcquireCredentialsAsync (HttpStatusCode statusCode, Guid credentialsVersion, ILogger log, CancellationToken cancellationToken)
+ {
+ try {
+ // Only one request may prompt and attempt to auth at a time
+ await httpClientLock.WaitAsync ();
+
+ cancellationToken.ThrowIfCancellationRequested ();
+
+ // Auth may have happened on another thread, if so just continue
+ if (credentialsVersion != credentials.Version) {
+ return credentials.Credentials;
+ }
+
+ var authState = GetAuthenticationState ();
+
+ if (authState.IsBlocked) {
+ cancellationToken.ThrowIfCancellationRequested ();
+
+ return null;
+ }
+
+ // Construct a reasonable message for the prompt to use.
+ CredentialRequestType type;
+ if (statusCode == HttpStatusCode.Unauthorized) {
+ type = CredentialRequestType.Unauthorized;
+ } else {
+ type = CredentialRequestType.Forbidden;
+ }
+
+ var promptCredentials = await PromptForCredentialsAsync (
+ type,
+ authState,
+ log,
+ cancellationToken);
+
+ if (promptCredentials == null) {
+ return null;
+ }
+
+ credentials.Credentials = promptCredentials;
+
+ return promptCredentials;
+ } finally {
+ httpClientLock.Release ();
+ }
+ }
+
+ AmbientAuthenticationState GetAuthenticationState ()
+ {
+ var correlationId = ActivityCorrelationId.Current;
+
+ AmbientAuthenticationState authState;
+ if (!authStates.TryGetValue (correlationId, out authState)) {
+ authState = new AmbientAuthenticationState ();
+ authStates [correlationId] = authState;
+ }
+
+ return authState;
+ }
+
+ async Task<ICredentials> PromptForCredentialsAsync (
+ CredentialRequestType type,
+ AmbientAuthenticationState authState,
+ ILogger log,
+ CancellationToken token)
+ {
+ ICredentials promptCredentials;
+
+ try {
+ // Only one prompt may display at a time.
+ await credentialPromptLock.WaitAsync ();
+
+ // Get the proxy for this URI so we can pass it to the credentialService methods
+ // this lets them use the proxy if they have to hit the network.
+ var proxyCache = ProxyCache.Instance;
+ var proxy = proxyCache?.GetProxy (packageSource.SourceUri);
+
+ promptCredentials = await credentialService
+ .GetCredentialsAsync (packageSource.SourceUri, proxy, type, string.Empty, token);
+
+ if (promptCredentials == null) {
+ // If this is the case, this means none of the credential providers were able to
+ // handle the credential request or no credentials were available for the
+ // endpoint.
+ authState.Block ();
+ } else {
+ authState.Increment ();
+ }
+ } catch (OperationCanceledException) {
+ // This indicates a non-human cancellation.
+ throw;
+ } catch (Exception e) {
+ // If this is the case, this means there was a fatal exception when interacting
+ // with the credential service (or its underlying credential providers). Either way,
+ // block asking for credentials for the live of this operation.
+ log.LogError (ExceptionUtilities.DisplayMessage (e));
+ promptCredentials = null;
+ authState.Block ();
+ } finally {
+ credentialPromptLock.Release ();
+ }
+
+ return promptCredentials;
+ }
+
+ void CredentialsSuccessfullyUsed (Uri uri, ICredentials usedCredentials)
+ {
+ HttpHandlerResourceV3.CredentialsSuccessfullyUsed?.Invoke (uri, usedCredentials);
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RepositoryProviderFactoryExtensions.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RepositoryProviderFactoryExtensions.cs
new file mode 100644
index 0000000000..c106bcf839
--- /dev/null
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RepositoryProviderFactoryExtensions.cs
@@ -0,0 +1,74 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// Based on FactoryExtensionsV3 and FactoryExtensionsVS
+// From: https://github.com/NuGet/NuGet.Client/
+
+using System;
+using System.Collections.Generic;
+using NuGet.Protocol;
+using NuGet.Protocol.Core.Types;
+using NuGet.Protocol.LocalRepositories;
+
+namespace MonoDevelop.PackageManagement
+{
+ static class RepositoryProviderFactoryExtensions
+ {
+ public static IEnumerable<Lazy<INuGetResourceProvider>> GetMonoDevelop (this Repository.ProviderFactory factory)
+ {
+ foreach (Lazy<INuGetResourceProvider> item in Repository.Provider.GetMonoDevelopCoreV3 ()) {
+ yield return item;
+ }
+ }
+
+ /// <summary>
+ /// Includes a custom HttpHandlerResourceV3Provider which can use native HttpMessageHandlers defined by MonoDevelop.
+ /// </summary>
+ public static IEnumerable<Lazy<INuGetResourceProvider>> GetMonoDevelopCoreV3 (this Repository.ProviderFactory factory)
+ {
+ yield return new Lazy<INuGetResourceProvider> (() => new FeedTypeResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new DependencyInfoResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new DownloadResourcePluginProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new DownloadResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new MetadataResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new RawSearchResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new RegistrationResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new ReportAbuseResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new ServiceIndexResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new ODataServiceDocumentResourceV2Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new MonoDevelopHttpHandlerResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new HttpSourceResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PluginFindPackageByIdResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new HttpFileSystemBasedFindPackageByIdResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new RemoteV3FindPackageByIdResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new RemoteV2FindPackageByIdResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalV3FindPackageByIdResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalV2FindPackageByIdResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PackageUpdateResourceV2Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PackageUpdateResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new DependencyInfoResourceV2FeedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new DownloadResourceV2FeedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new MetadataResourceV2FeedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new V3FeedListResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new V2FeedListResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalPackageListResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PackageSearchResourceV2FeedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PackageSearchResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PackageMetadataResourceV2FeedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PackageMetadataResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new AutoCompleteResourceV2FeedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new AutoCompleteResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new PluginResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new FindLocalPackagesResourceUnzippedProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new FindLocalPackagesResourceV2Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new FindLocalPackagesResourceV3Provider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new FindLocalPackagesResourcePackagesConfigProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalAutoCompleteResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalDependencyInfoResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalDownloadResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalMetadataResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalPackageMetadataResourceProvider ());
+ yield return new Lazy<INuGetResourceProvider> (() => new LocalPackageSearchResourceProvider ());
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SourceRepositoryProvider.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SourceRepositoryProvider.cs
index c1264c3bb8..3bc25eef1a 100644
--- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SourceRepositoryProvider.cs
+++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SourceRepositoryProvider.cs
@@ -26,7 +26,6 @@
using NuGet.Configuration;
using NuGet.Protocol.Core.Types;
-using NuGet.Protocol.VisualStudio;
namespace MonoDevelop.PackageManagement
{
@@ -41,7 +40,7 @@ namespace MonoDevelop.PackageManagement
public static ISourceRepositoryProvider CreateSourceRepositoryProvider (ISettings settings)
{
var packageSourceProvider = new MonoDevelopPackageSourceProvider (settings);
- return new SourceRepositoryProvider (packageSourceProvider, Repository.Provider.GetVisualStudio ());
+ return new SourceRepositoryProvider (packageSourceProvider, Repository.Provider.GetMonoDevelop ());
}
}
}
diff --git a/main/src/addins/MonoDevelop.Packaging/Gui/MonoDevelop.Packaging.Gui.GtkNuGetPackageMetadataOptionsPanelWidget.cs b/main/src/addins/MonoDevelop.Packaging/Gui/MonoDevelop.Packaging.Gui.GtkNuGetPackageMetadataOptionsPanelWidget.cs
index 49a3bd30d5..7af75f47b5 100644
--- a/main/src/addins/MonoDevelop.Packaging/Gui/MonoDevelop.Packaging.Gui.GtkNuGetPackageMetadataOptionsPanelWidget.cs
+++ b/main/src/addins/MonoDevelop.Packaging/Gui/MonoDevelop.Packaging.Gui.GtkNuGetPackageMetadataOptionsPanelWidget.cs
@@ -48,7 +48,7 @@ namespace MonoDevelop.Packaging.Gui
private global::Gtk.HBox packageLanguageHBox;
- private global::Gtk.ComboBoxEntry packageLanguageComboBox;
+ private global::Gtk.ComboBox packageLanguageComboBox;
private global::Gtk.Label packageLanguageLabel;
@@ -321,7 +321,7 @@ namespace MonoDevelop.Packaging.Gui
this.packageLanguageHBox.Name = "packageLanguageHBox";
this.packageLanguageHBox.Spacing = 6;
// Container child packageLanguageHBox.Gtk.Box+BoxChild
- this.packageLanguageComboBox = global::Gtk.ComboBoxEntry.NewText();
+ this.packageLanguageComboBox = global::Gtk.ComboBox.NewText();
this.packageLanguageComboBox.Name = "packageLanguageComboBox";
this.packageLanguageHBox.Add(this.packageLanguageComboBox);
global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.packageLanguageHBox[this.packageLanguageComboBox]));
diff --git a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkNuGetPackageMetadataOptionsPanelWidget.cs b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkNuGetPackageMetadataOptionsPanelWidget.cs
index 3f6f091de5..8e2ab47e58 100644
--- a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkNuGetPackageMetadataOptionsPanelWidget.cs
+++ b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkNuGetPackageMetadataOptionsPanelWidget.cs
@@ -42,6 +42,8 @@ namespace MonoDevelop.Packaging.Gui
NuGetPackageMetadata metadata;
bool projectOriginallyHadMetadata;
bool hasPackageId;
+ List<CultureInfo> languages;
+ ListStore languagesListStore;
public GtkNuGetPackageMetadataOptionsPanelWidget ()
{
@@ -58,85 +60,68 @@ namespace MonoDevelop.Packaging.Gui
packageReleaseNotesPaddingLabel.Accessible.Role = Atk.Role.Filler;
packageIdTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.ID",
- GettextCatalog.GetString ("ID"),
- GettextCatalog.GetString ("Enter the ID of the NuGet package"));
- packageIdTextBox.SetAccessibilityLabelRelationship (packageIdLabel);
+ packageIdLabel,
+ GettextCatalog.GetString ("Enter the ID of the NuGet package"));
packageVersionTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Version",
- GettextCatalog.GetString ("Version"),
- GettextCatalog.GetString ("Enter the version of the NuGet package"));
- packageVersionTextBox.SetAccessibilityLabelRelationship (packageVersionLabel);
+ packageVersionLabel,
+ GettextCatalog.GetString ("Enter the version of the NuGet package"));
packageAuthorsTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Authors",
- GettextCatalog.GetString ("Authors"),
- GettextCatalog.GetString ("Enter the authors of the NuGet package"));
- packageAuthorsTextBox.SetAccessibilityLabelRelationship (packageAuthorsLabel);
+ packageAuthorsLabel,
+ GettextCatalog.GetString ("Enter the authors of the NuGet package"));
packageDescriptionTextView.SetCommonAccessibilityAttributes ("NuGetMetadata.Description",
- GettextCatalog.GetString ("Description"),
- GettextCatalog.GetString ("Enter the description of the NuGet package"));
- packageDescriptionTextView.SetAccessibilityLabelRelationship (packageDescriptionLabel);
+ packageDescriptionLabel,
+ GettextCatalog.GetString ("Enter the description of the NuGet package"));
packageOwnersTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Owners",
- GettextCatalog.GetString ("Owners"),
- GettextCatalog.GetString ("Enter the owners of the NuGet package"));
- packageOwnersTextBox.SetAccessibilityLabelRelationship (packageOwnersLabel);
+ packageOwnersLabel,
+ GettextCatalog.GetString ("Enter the owners of the NuGet package"));
packageCopyrightTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Copyright",
- GettextCatalog.GetString ("Copyright"),
- GettextCatalog.GetString ("Enter the copyright statement for the NuGet package"));
- packageCopyrightTextBox.SetAccessibilityLabelRelationship (packageCopyrightLabel);
+ packageCopyrightLabel,
+ GettextCatalog.GetString ("Enter the copyright statement for the NuGet package"));
packageTitleTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Title",
- GettextCatalog.GetString ("Title"),
- GettextCatalog.GetString ("Enter the title of the NuGet package"));
- packageTitleTextBox.SetAccessibilityLabelRelationship (packageTitleLabel);
+ packageTitleLabel,
+ GettextCatalog.GetString ("Enter the title of the NuGet package"));
packageSummaryTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Summary",
- GettextCatalog.GetString ("Summary"),
- GettextCatalog.GetString ("Enter the summary for the NuGet package"));
- packageSummaryTextBox.SetAccessibilityLabelRelationship (packageSummaryLabel);
+ packageSummaryLabel,
+ GettextCatalog.GetString ("Enter the summary for the NuGet package"));
packageProjectUrlTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.URL",
- GettextCatalog.GetString ("Project URL"),
- GettextCatalog.GetString ("Enter the project URL for the NuGet package"));
- packageProjectUrlTextBox.SetAccessibilityLabelRelationship (packageProjectUrlLabel);
+ packageProjectUrlLabel,
+ GettextCatalog.GetString ("Enter the project URL for the NuGet package"));
packageIconUrlTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Icon",
- GettextCatalog.GetString ("Icon URL"),
- GettextCatalog.GetString ("Enter the URL for the NuGet package's icon"));
- packageIconUrlTextBox.SetAccessibilityLabelRelationship (packageIconUrlLabel);
+ packageIconUrlLabel,
+ GettextCatalog.GetString ("Enter the URL for the NuGet package's icon"));
packageLicenseUrlTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.licence",
- GettextCatalog.GetString ("License URL"),
- GettextCatalog.GetString ("Enter the URL for the NuGet package's license"));
- packageLicenseUrlTextBox.SetAccessibilityLabelRelationship (packageLicenseUrlLabel);
+ packageLicenseUrlLabel,
+ GettextCatalog.GetString ("Enter the URL for the NuGet package's license"));
packageRequireLicenseAcceptanceCheckBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Acceptance",
- GettextCatalog.GetString ("Require License Acceptance"),
- GettextCatalog.GetString ("Check to require the user to accept the NuGet package's license"));
- packageRequireLicenseAcceptanceCheckBox.SetAccessibilityLabelRelationship (packageRequireLicenseAcceptanceLabel);
+ packageRequireLicenseAcceptanceLabel,
+ GettextCatalog.GetString ("Check to require the user to accept the NuGet package's license"));
packageDevelopmentDependencyCheckBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Development",
- GettextCatalog.GetString ("Development Dependency"),
- GettextCatalog.GetString ("Check to indicate that this is a development dependency"));
- packageDevelopmentDependencyCheckBox.SetAccessibilityLabelRelationship (packageDevelopmentDependencyLabel);
+ packageDevelopmentDependencyLabel,
+ GettextCatalog.GetString ("Check to indicate that this is a development dependency"));
packageTagsTextBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Tags",
- GettextCatalog.GetString ("Tags"),
- GettextCatalog.GetString ("Enter the tags for this NuGet package"));
- packageTagsTextBox.SetAccessibilityLabelRelationship (packageTagsLabel);
+ packageTagsLabel,
+ GettextCatalog.GetString ("Enter the tags for this NuGet package"));
packageLanguageComboBox.SetCommonAccessibilityAttributes ("NuGetMetadata.Language",
- GettextCatalog.GetString ("Language"),
- GettextCatalog.GetString ("Select the language for this NuGet package"));
- packageLanguageComboBox.SetAccessibilityLabelRelationship (packageLanguageLabel);
+ packageLanguageLabel,
+ GettextCatalog.GetString ("Select the language for this NuGet package"));
packageReleaseNotesTextView.SetCommonAccessibilityAttributes ("NuGetMetadata.ReleaseNotes",
- GettextCatalog.GetString ("Release Notes"),
- GettextCatalog.GetString ("Enter the release notes for this NuGet package"));
- packageReleaseNotesTextView.SetAccessibilityLabelRelationship (packageReleaseNotesLabel);
-
+ packageReleaseNotesLabel,
+ GettextCatalog.GetString ("Enter the release notes for this NuGet package"));
}
internal static System.Action<bool> OnProjectHasMetadataChanged;
@@ -168,7 +153,7 @@ namespace MonoDevelop.Packaging.Gui
packageCopyrightTextBox.Text = GetTextBoxText (metadata.Copyright);
packageDevelopmentDependencyCheckBox.Active = metadata.DevelopmentDependency;
packageIconUrlTextBox.Text = GetTextBoxText (metadata.IconUrl);
- packageLanguageComboBox.Entry.Text = GetTextBoxText (metadata.Language);
+ LoadLanguage (metadata.Language);
packageLicenseUrlTextBox.Text = GetTextBoxText (metadata.LicenseUrl);
packageOwnersTextBox.Text = GetTextBoxText (metadata.Owners);
packageProjectUrlTextBox.Text = GetTextBoxText (metadata.ProjectUrl);
@@ -184,6 +169,35 @@ namespace MonoDevelop.Packaging.Gui
return text ?? string.Empty;
}
+ void LoadLanguage (string language)
+ {
+ if (string.IsNullOrEmpty (language)) {
+ packageLanguageComboBox.Active = 0;
+ return;
+ }
+
+ int index = GetLanguageIndex (language);
+ if (index >= 0) {
+ packageLanguageComboBox.Active = index + 1;
+ return;
+ }
+
+ // Language does not match so we need to add it to the combo box.
+ TreeIter iter = languagesListStore.AppendValues (language);
+ packageLanguageComboBox.SetActiveIter (iter);
+ }
+
+ int GetLanguageIndex (string language)
+ {
+ for (int i = 0; i < languages.Count; ++i) {
+ CultureInfo culture = languages [i];
+ if (string.Equals (culture.Name, language, StringComparison.OrdinalIgnoreCase)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
internal void Save (PackagingProject project)
{
UpdateMetadata ();
@@ -220,7 +234,7 @@ namespace MonoDevelop.Packaging.Gui
metadata.Copyright = packageCopyrightTextBox.Text;
metadata.DevelopmentDependency = packageDevelopmentDependencyCheckBox.Active;
metadata.IconUrl = packageIconUrlTextBox.Text;
- metadata.Language = packageLanguageComboBox.Entry.Text;
+ metadata.Language = GetSelectedLanguage ();
metadata.LicenseUrl = packageLicenseUrlTextBox.Text;
metadata.Owners = packageOwnersTextBox.Text;
metadata.ProjectUrl = packageProjectUrlTextBox.Text;
@@ -231,20 +245,49 @@ namespace MonoDevelop.Packaging.Gui
metadata.Title = packageTitleTextBox.Text;
}
+ string GetSelectedLanguage ()
+ {
+ if (packageLanguageComboBox.Active == 0) {
+ // 'None' selected.
+ return string.Empty;
+ }
+
+ int languageIndex = packageLanguageComboBox.Active - 1;
+ if (languageIndex < languages.Count) {
+ return languages [languageIndex].Name;
+ }
+
+ // No match for language so just return the combo box text.
+ return packageLanguageComboBox.ActiveText;
+ }
+
void PopulateLanguages ()
{
- var languagesListStore = new ListStore (typeof (string));
+ languagesListStore = new ListStore (typeof (string));
packageLanguageComboBox.Model = languagesListStore;
- List<string> languages = CultureInfo.GetCultures(CultureTypes.AllCultures)
- .Select (c => c.Name)
- .ToList ();
+ languages = new List<CultureInfo> ();
- languages.Sort ();
+ foreach (CultureInfo culture in CultureInfo.GetCultures (CultureTypes.AllCultures)) {
+ if (!string.IsNullOrEmpty (culture.Name)) {
+ languages.Add (culture);
+ }
+ }
- foreach (string language in languages) {
- languagesListStore.AppendValues (language);
+ languages.Sort (CompareLanguages);
+
+ languagesListStore.AppendValues (GettextCatalog.GetString ("None"));
+
+ foreach (CultureInfo language in languages) {
+ languagesListStore.AppendValues (language.DisplayName);
}
+
+ packageLanguageComboBox.Active = 0;
+ }
+
+ static int CompareLanguages (CultureInfo x, CultureInfo y)
+ {
+ return string.Compare (x.DisplayName, y.DisplayName, StringComparison.CurrentCulture);
}
bool ProjectHasMetadata ()
diff --git a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkProjectNuGetBuildOptionsPanelWidget.cs b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkProjectNuGetBuildOptionsPanelWidget.cs
index 3645bd12ac..38468f2f14 100644
--- a/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkProjectNuGetBuildOptionsPanelWidget.cs
+++ b/main/src/addins/MonoDevelop.Packaging/MonoDevelop.Packaging.Gui/GtkProjectNuGetBuildOptionsPanelWidget.cs
@@ -44,9 +44,13 @@ namespace MonoDevelop.Packaging.Gui
SetupAccessibility ();
}
- void SetupAccessibility ()
+ void SetupAccessibility (bool includeMissingMetadataLabelText = false)
{
- packOnBuildButton.SetCommonAccessibilityAttributes ("NugetBuildOptionsPanel.PackOnBuild", "",
+ string accessibilityLabel = packOnBuildButton.Label;
+ if (includeMissingMetadataLabelText) {
+ accessibilityLabel += " " + missingMetadataLabel.Text;
+ }
+ packOnBuildButton.SetCommonAccessibilityAttributes ("NugetBuildOptionsPanel.PackOnBuild", accessibilityLabel,
GettextCatalog.GetString ("Check to create a NuGet package when building"));
}
@@ -73,10 +77,15 @@ namespace MonoDevelop.Packaging.Gui
void UpdateMissingMetadataLabelVisibility ()
{
- if (packOnBuildButton.Active) {
- missingMetadataLabel.Visible = !ProjectHasMetadata;
+ bool visible = packOnBuildButton.Active && !ProjectHasMetadata;
+ missingMetadataLabel.Visible = visible;
+
+ // Refresh accessibility information so missing metadata label text is available to Voice Over
+ // when the check box is selected.
+ if (visible) {
+ SetupAccessibility (includeMissingMetadataLabelText: true);
} else {
- missingMetadataLabel.Visible = false;
+ SetupAccessibility ();
}
}
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs
index 1a8be1f307..b2768902cb 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs
@@ -90,19 +90,27 @@ namespace MonoDevelop.AnalysisCore.Gui
return;
enabled = false;
diagService.DiagnosticsUpdated -= OnDiagnosticsUpdated;
+ diagService = null;
CancelUpdateTimout ();
AnalysisOptions.AnalysisEnabled.Changed -= AnalysisOptionsChanged;
- foreach (var queue in markers) {
- foreach (var marker in queue.Value)
- Editor.RemoveMarker (marker);
- queue.Value.Clear ();
- }
+ RemoveAllMarkers ();
disposed = true;
base.Dispose ();
}
+
+ void RemoveAllMarkers ()
+ {
+ foreach (var markerQueue in markers) {
+ foreach (var marker in markerQueue.Value) {
+ Editor.RemoveMarker (marker);
+ }
+ PutBackCachedList (markerQueue.Value);
+ }
+ markers.Clear ();
+ }
bool enabled;
-
+
public bool Enabled {
get { return enabled; }
set {
@@ -248,40 +256,39 @@ namespace MonoDevelop.AnalysisCore.Gui
}
}
+ const int MaxCacheSize = 10;
+ Queue<List<IGenericTextSegmentMarker>> listCache = new Queue<List<IGenericTextSegmentMarker>> ();
- class ResultsUpdater
+ List<IGenericTextSegmentMarker> GetCachedList ()
+ {
+ if (listCache.Count == 0)
+ return new List<IGenericTextSegmentMarker> ();
+ return listCache.Dequeue ();
+ }
+
+ void PutBackCachedList (List<IGenericTextSegmentMarker> list)
+ {
+ list.Clear ();
+ if (listCache.Count < MaxCacheSize)
+ listCache.Enqueue (list);
+ }
+
+ class ResultsUpdater
{
readonly ResultsEditorExtension ext;
readonly CancellationToken cancellationToken;
-
+
//the number of markers at the head of the queue that need tp be removed
int oldMarkerIndex;
- readonly List<IGenericTextSegmentMarker> oldMarkers;
+ List<IGenericTextSegmentMarker> oldMarkers;
int curResult = 0;
IReadOnlyList<Result> results;
- private List<IGenericTextSegmentMarker> newMarkers;
+ List<IGenericTextSegmentMarker> newMarkers;
ImmutableArray<QuickTask>.Builder builder;
object id;
- const int MaxCacheSize = 200;
- readonly static Queue<List<IGenericTextSegmentMarker>> listCache = new Queue<List<IGenericTextSegmentMarker>> ();
-
- static List<IGenericTextSegmentMarker> GetCachedList ()
- {
- if (listCache.Count == 0)
- return new List<IGenericTextSegmentMarker> ();
- return listCache.Dequeue ();
- }
-
- static void PutBackCachedList (List<IGenericTextSegmentMarker> list)
- {
- list.Clear ();
- if (listCache.Count < MaxCacheSize)
- listCache.Enqueue (list);
- }
-
public ResultsUpdater (ResultsEditorExtension ext, IReadOnlyList<Result> results, object resultsId, CancellationToken cancellationToken)
{
if (ext == null)
@@ -293,13 +300,13 @@ namespace MonoDevelop.AnalysisCore.Gui
this.cancellationToken = cancellationToken;
if (resultsId != null) {
- if (!ext.markers.TryGetValue (id, out oldMarkers))
- ext.markers [id] = oldMarkers = GetCachedList ();
+ ext.markers.TryGetValue (id, out oldMarkers);
}
builder = ImmutableArray<QuickTask>.Empty.ToBuilder ();
this.results = results;
- newMarkers = GetCachedList ();
+ newMarkers = ext.GetCachedList ();
+ Debug.Assert (newMarkers != null);
}
public void Update ()
@@ -342,91 +349,95 @@ namespace MonoDevelop.AnalysisCore.Gui
//in order to to block the GUI thread, we batch them in UPDATE_COUNT
bool IdleHandler ()
{
- var editor = ext.Editor;
- if (editor == null)
- return false;
- if (id == null) {
- foreach (var markerQueue in ext.markers) {
- foreach (var marker in markerQueue.Value) {
- editor.RemoveMarker (marker);
- }
- PutBackCachedList (markerQueue.Value);
+ try {
+ var editor = ext.Editor;
+ if (editor == null)
+ return false;
+ if (id == null) {
+ ext.RemoveAllMarkers ();
+ lock (ext.tasks)
+ ext.tasks.Clear ();
+ ext.OnTasksUpdated (EventArgs.Empty);
+ return false;
}
- ext.markers.Clear ();
- lock (ext.tasks)
- ext.tasks.Clear ();
- ext.OnTasksUpdated (EventArgs.Empty);
- return false;
- }
- if (cancellationToken.IsCancellationRequested) {
- FinishUpdateRun ();
- return false;
- }
+ if (cancellationToken.IsCancellationRequested) {
+ FinishUpdateRun ();
+ return false;
+ }
- //clear the old results out at the same rate we add in the new ones
- for (int i = 0; oldMarkerIndex < oldMarkers.Count && i < UPDATE_COUNT; i++) {
- var oldMarker = oldMarkers [oldMarkerIndex++];
-
- var oldResult = (Result)oldMarker.Tag;
- if (curResult < results.Count) {
- Result currentResult = results [curResult];
- if (currentResult.Equals (oldResult, oldMarker.Offset)) {
- oldMarker.Tag = currentResult;
- newMarkers.Add (oldMarker);
- if (oldResult.QuickTask != null) {
- currentResult.QuickTask = oldResult.QuickTask;
- builder.Add (currentResult.QuickTask);
+ //clear the old results out at the same rate we add in the new ones
+ if (oldMarkers != null) {
+ for (int i = 0; oldMarkerIndex < oldMarkers.Count && i < UPDATE_COUNT; i++) {
+ var oldMarker = oldMarkers [oldMarkerIndex++];
+
+ var oldResult = (Result)oldMarker.Tag;
+ if (oldResult != null && curResult < results.Count) {
+ Result currentResult = results [curResult];
+ if (currentResult.Equals (oldResult, oldMarker.Offset)) {
+ oldMarker.Tag = currentResult;
+ newMarkers.Add (oldMarker);
+ if (oldResult.QuickTask != null) {
+ currentResult.QuickTask = oldResult.QuickTask;
+ builder.Add (currentResult.QuickTask);
+ }
+ curResult++;
+ continue;
+ }
}
- curResult++;
- continue;
+ editor.RemoveMarker (oldMarker);
}
}
- editor.RemoveMarker (oldMarker);
- }
- //add in the new markers
- for (int i = 0; i < UPDATE_COUNT; i++) {
- if (curResult >= results.Count) {
- FinishUpdateRun ();
- return false;
- }
- var currentResult = results [curResult++];
- if (currentResult.InspectionMark != IssueMarker.None) {
- int start = currentResult.Region.Start;
- int end = currentResult.Region.End;
- if (start > end)
- continue;
-
- // In case a diagnostic has a 0 length span, force it to 1.
- if (start == end)
- end = end + 1;
-
- var marker = TextMarkerFactory.CreateGenericTextSegmentMarker (editor, GetSegmentMarkerEffect (currentResult.InspectionMark), TextSegment.FromBounds (start, end));
- marker.Tag = currentResult;
- marker.IsVisible = currentResult.Underline;
-
- if (currentResult.InspectionMark != IssueMarker.GrayOut) {
- marker.Color = GetColor (editor, currentResult);
- marker.IsVisible &= currentResult.Level != DiagnosticSeverity.Hidden;
+ //add in the new markers
+ for (int i = 0; i < UPDATE_COUNT; i++) {
+ if (curResult >= results.Count) {
+ FinishUpdateRun ();
+ return false;
+ }
+ var currentResult = results [curResult++];
+ if (currentResult.InspectionMark != IssueMarker.None) {
+ int start = currentResult.Region.Start;
+ int end = currentResult.Region.End;
+ if (start > end)
+ continue;
+
+ // In case a diagnostic has a 0 length span, force it to 1.
+ if (start == end)
+ end = end + 1;
+
+ var marker = TextMarkerFactory.CreateGenericTextSegmentMarker (editor, GetSegmentMarkerEffect (currentResult.InspectionMark), TextSegment.FromBounds (start, end));
+ marker.Tag = currentResult;
+ marker.IsVisible = currentResult.Underline;
+
+ if (currentResult.InspectionMark != IssueMarker.GrayOut) {
+ marker.Color = GetColor (editor, currentResult);
+ marker.IsVisible &= currentResult.Level != DiagnosticSeverity.Hidden;
+ }
+ editor.AddMarker (marker);
+ newMarkers.Add (marker);
}
- editor.AddMarker (marker);
- newMarkers.Add (marker);
+ builder.Add (currentResult.QuickTask = new QuickTask (currentResult.Message, currentResult.Region.Start, currentResult.Level));
}
- builder.Add (currentResult.QuickTask = new QuickTask (currentResult.Message, currentResult.Region.Start, currentResult.Level));
+ return true;
+ } catch (Exception ex) {
+ LoggingService.LogInternalError ("Error while ResutsUpdater.IdleHandler", ex);
+ return false;
}
- return true;
}
void FinishUpdateRun ()
{
var editor = ext.Editor;
// remove remaining old markers
- while (oldMarkerIndex < oldMarkers.Count) {
- editor.RemoveMarker (oldMarkers [oldMarkerIndex]);
- oldMarkerIndex++;
+ if (oldMarkers != null) {
+ while (oldMarkerIndex < oldMarkers.Count) {
+ editor.RemoveMarker (oldMarkers [oldMarkerIndex]);
+ oldMarkerIndex++;
+ }
+ ext.PutBackCachedList (oldMarkers);
+ oldMarkers = null;
}
- PutBackCachedList (oldMarkers);
ext.markers [id] = newMarkers;
lock (ext.tasks)
ext.tasks [id] = builder.ToImmutable ();
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs
index f309fa2a76..bb43e7b75f 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs
@@ -186,11 +186,11 @@ namespace MonoDevelop.CodeActions
var item = new CodeFixMenuEntry (label, async delegate {
// Task.Run here so we don't end up binding the whole document on popping the menu, also there is no cancellation token support
- var fix = await Task.Run (() => {
- var context = fixState.CreateFixAllContext (new RoslynProgressTracker (), token);
- return provider.GetFixAsync (context);
- });
+
Microsoft.CodeAnalysis.Text.TextChange[] result = await Task.Run (async () => {
+ var context = fixState.CreateFixAllContext (new RoslynProgressTracker (), token);
+ var fix = await provider.GetFixAsync (context);
+
var previewOperations = await fix.GetPreviewOperationsAsync (token);
return await Runtime.RunInMainThread (() => {
var engine = Xwt.Toolkit.CurrentEngine; // NativeEngine
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringPreviewTooltipWindow.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringPreviewTooltipWindow.cs
index 69270698f2..705bb142ae 100644
--- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringPreviewTooltipWindow.cs
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringPreviewTooltipWindow.cs
@@ -135,6 +135,7 @@ namespace MonoDevelop.Refactoring
if (temp.LineKind == LineKind.Normal && temp.TextMarkup == null) {
var newText = editor.GetMarkup (temp.Offset, temp.Length, new MarkupOptions (MarkupFormat.Pango, false));
diff.LineResults [i] = new LineResult (newText, temp.Offset, temp.Length, temp.LineKind, temp.XNeedsMeasure);
+ QueueResize ();
}
}
@@ -353,7 +354,7 @@ namespace MonoDevelop.Refactoring
return;
}
- drawingLayout.SetMarkup (lineResult.TextMarkup);
+ drawingLayout.SetMarkup (lineResult.TextMarkup ?? "");
drawingLayout.GetPixelSize (out int w, out int h);
x = Math.Max (x, w);
y += lineHeight;
@@ -398,7 +399,7 @@ namespace MonoDevelop.Refactoring
{
using (var drawingLayout = new Pango.Layout (PangoContext)) {
drawingLayout.FontDescription = fontDescription;
- drawingLayout.SetMarkup (lineResult.TextMarkup);
+ drawingLayout.SetMarkup (lineResult.TextMarkup ?? "");
g.Save ();
g.Translate (textBorder, y);
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/IconMargin.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/IconMargin.cs
index a0db793606..cfdd1730b0 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/IconMargin.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/IconMargin.cs
@@ -43,16 +43,21 @@ namespace Mono.TextEditor
public IconMargin (MonoTextEditor editor)
{
- this.editor = editor;
-
- editor.Document.MarkerAdded += OnMarkerAdded;
- editor.Document.MarkerRemoved += OnMarkerRemoved;
+ this.editor = editor;
+
+ if (IdeTheme.AccessibilityEnabled) {
+ editor.Document.MarkerAdded += OnMarkerAdded;
+ editor.Document.MarkerRemoved += OnMarkerRemoved;
+ markerToAccessible = new Dictionary<TextLineMarker, AccessibilityMarkerProxy> ();
+ }
}
public override void Dispose ()
{
- editor.Document.MarkerAdded -= OnMarkerAdded;
- editor.Document.MarkerRemoved -= OnMarkerRemoved;
+ if (IdeTheme.AccessibilityEnabled) {
+ editor.Document.MarkerAdded -= OnMarkerAdded;
+ editor.Document.MarkerRemoved -= OnMarkerRemoved;
+ }
if (markerToAccessible != null) {
foreach (var proxy in markerToAccessible.Values) {
@@ -187,44 +192,30 @@ namespace Mono.TextEditor
}
}
- Dictionary<TextLineMarker, AccessibilityMarkerProxy> markerToAccessible = null;
+ Dictionary<TextLineMarker, AccessibilityMarkerProxy> markerToAccessible;
+
void OnMarkerAdded (object sender, TextMarkerEvent e)
{
- if (!IdeTheme.AccessibilityEnabled) {
- return;
+ lock (markerToAccessible) {
+ var proxy = new AccessibilityMarkerProxy (e.TextMarker, editor, this);
+ Accessible.AddAccessibleChild (proxy.Accessible);
+ markerToAccessible [e.TextMarker] = proxy;
}
- if (markerToAccessible == null) {
- markerToAccessible = new Dictionary<TextLineMarker, AccessibilityMarkerProxy> ();
- }
-
- var proxy = new AccessibilityMarkerProxy (e.TextMarker, editor, this);
- Accessible.AddAccessibleChild (proxy.Accessible);
-
- markerToAccessible [e.TextMarker] = proxy;
-
if (focusMarkers != null) {
UpdateMarkers ();
- }
+ }
}
void OnMarkerRemoved (object sender, TextMarkerEvent e)
{
- if (!IdeTheme.AccessibilityEnabled) {
- return;
+ lock (markerToAccessible) {
+ if (!markerToAccessible.TryGetValue (e.TextMarker, out var proxy)) {
+ return;
+ }
+ Accessible.RemoveAccessibleChild (proxy.Accessible);
+ markerToAccessible.Remove (e.TextMarker);
}
-
- if (markerToAccessible == null) {
- return;
- }
-
- var proxy = markerToAccessible [e.TextMarker];
- if (proxy == null) {
- throw new Exception ("No accessible found for marker");
- }
-
- Accessible.RemoveAccessibleChild (proxy.Accessible);
- markerToAccessible.Remove (e.TextMarker);
if (focusMarkers != null) {
UpdateMarkers ();
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs
index aa4875782a..df50904421 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/MdTextViewLineCollection.MdTextViewLine.cs
@@ -50,6 +50,7 @@ namespace Mono.TextEditor
/// 1-based
/// </summary>
public int LineNumber { get; private set; }
+ public bool HasDrawn { get; set; }
public MdTextViewLine(MdTextViewLineCollection collection, MonoTextEditor textEditor, DocumentLine line, int lineNumber, TextViewMargin.LayoutWrapper layoutWrapper)
{
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs
index 874a18c389..f6dafc25f0 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextArea.cs
@@ -522,18 +522,26 @@ namespace Mono.TextEditor
preeditOffset = Caret.Offset;
preeditLine = Caret.Line;
}
- if (UpdatePreeditLineHeight ())
+ if (UpdatePreeditLineHeight ()) {
QueueDraw ();
+ } else {
+ this.textViewMargin.ForceInvalidateLine (preeditLine);
+ this.textEditorData.Document.CommitLineUpdate (preeditLine);
+ }
} else {
+ if (preeditOffset < 0)
+ return;
preeditOffset = -1;
preeditString = null;
preeditAttrs = null;
preeditCursorCharIndex = 0;
- if (UpdatePreeditLineHeight ())
+ if (UpdatePreeditLineHeight ()) {
QueueDraw ();
+ } else {
+ this.textViewMargin.ForceInvalidateLine (preeditLine);
+ this.textEditorData.Document.CommitLineUpdate (preeditLine);
+ }
}
- this.textViewMargin.ForceInvalidateLine (preeditLine);
- this.textEditorData.Document.CommitLineUpdate (preeditLine);
}
internal bool UpdatePreeditLineHeight ()
@@ -1117,8 +1125,6 @@ namespace Mono.TextEditor
if (isDisposed || logicalLine > LineCount || logicalLine < DocumentLocation.MinLine)
return;
- textViewMargin.RemoveCachedLine(logicalLine);
-
double y = LineToY (logicalLine) - this.textEditorData.VAdjustment.Value;
double h = GetLineHeight (logicalLine);
@@ -1414,8 +1420,13 @@ namespace Mono.TextEditor
return true;
}
}
- if (margin != null)
- margin.MousePressed (new MarginMouseEventArgs (textEditorData.Parent, e, e.Button, e.X - startPos, e.Y, e.State));
+ if (margin != null) {
+ try {
+ margin.MousePressed (new MarginMouseEventArgs (textEditorData.Parent, e, e.Button, e.X - startPos, e.Y, e.State));
+ } catch (Exception ex) {
+ LoggingService.LogInternalError ("Exception while margin mouse press.", ex);
+ }
+ }
}
return result;
}
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs
index 20f8c18b96..6cb5586a22 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs
@@ -670,18 +670,23 @@ namespace Mono.TextEditor
#if MAC
try {
lineHeight = System.Math.Ceiling (0.5 + OSXEditor.GetLineHeight(font.ToString ()));
+ if (lineHeight < 0)
+ lineHeight = GetLineHeight (metrics);
} catch (Exception e) {
LoggingService.LogError ("Error while getting the macOS font metrics for " + font, e);
- lineHeight = System.Math.Ceiling (0.5 + (metrics.Ascent + metrics.Descent) / Pango.Scale.PangoScale);
+ lineHeight = GetLineHeight (metrics);
}
#else
- lineHeight = System.Math.Ceiling(0.5 + (metrics.Ascent + metrics.Descent) / Pango.Scale.PangoScale);
+ lineHeight = GetLineHeight (metrics);
#endif
underlinePosition = metrics.UnderlinePosition;
underLineThickness = metrics.UnderlineThickness;
charWidth = metrics.ApproximateCharWidth / Pango.Scale.PangoScale;
}
}
+
+ static double GetLineHeight (Pango.FontMetrics metrics) => System.Math.Ceiling (0.5 + (metrics.Ascent + metrics.Descent) / Pango.Scale.PangoScale);
+
public override void Dispose ()
{
CancelCodeSegmentTooltip ();
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ClipboardRingService.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ClipboardRingService.cs
index e5cbe4f179..3e1b8a9248 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ClipboardRingService.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ClipboardRingService.cs
@@ -31,6 +31,7 @@ using MonoDevelop.Core;
using MonoDevelop.DesignerSupport.Toolbox;
using MonoDevelop.Ide;
using MonoDevelop.Ide.Gui;
+using System.Linq;
namespace MonoDevelop.SourceEditor
{
@@ -102,6 +103,8 @@ namespace MonoDevelop.SourceEditor
return clipboardRing;
}
+ internal static bool DeleteItem (ItemToolboxNode node) => clipboardRing.Remove (node as ClipboardToolboxNode);
+
class ClipboardToolboxNode : ItemToolboxNode, ITextToolboxNode, ICustomTooltipToolboxNode
{
static readonly ToolboxItemFilterAttribute filterAtt = new ToolboxItemFilterAttribute ("text/plain", ToolboxItemFilterType.Allow);
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs
index cb7b112912..75478307b3 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs
@@ -1,4 +1,4 @@
-// SourceEditorView.cs
+// SourceEditorView.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
@@ -68,7 +68,7 @@ using MonoDevelop.Ide.Gui.Documents;
namespace MonoDevelop.SourceEditor
{
partial class SourceEditorView : IBookmarkBuffer, IClipboardHandler, ITextFile,
- ICompletionWidget2, ISplittable, IFoldable, IToolboxDynamicProvider,
+ ICompletionWidget2, ISplittable, IFoldable, IToolboxDynamicProvider, IToolboxDynamicProviderDeleteSupport,
ICustomFilteringToolboxConsumer, IZoomable, ITextEditorResolver, ITextEditorDataProvider,
ICodeTemplateHandler, ICodeTemplateContextProvider, IPrintable,
ITextEditorImpl, ITextMarkerFactory, IUndoHandler
@@ -3111,14 +3111,20 @@ namespace MonoDevelop.SourceEditor
string ITextEditorImpl.GetMarkup (int offset, int length, MarkupOptions options)
{
+ return GetMarkupAsync (offset, length, options, default).WaitAndGetResult (default);
+ }
+
+ public async Task<string> GetMarkupAsync (int offset, int length, MarkupOptions options, CancellationToken cancellationToken = default)
+ {
+ Runtime.AssertMainThread ();
var data = TextEditor.GetTextEditorData ();
switch (options.MarkupFormat) {
case MarkupFormat.Pango:
- return data.GetMarkup (offset, length, false, replaceTabs: false, fitIdeStyle: options.FitIdeStyle);
+ return await data.GetMarkupAsync (offset, length, false, replaceTabs: false, fitIdeStyle: options.FitIdeStyle, cancellationToken: cancellationToken);
case MarkupFormat.Html:
- return HtmlWriter.GenerateHtml (ClipboardColoredText.GetChunks (data, new TextSegment (offset, length)).WaitAndGetResult (default (System.Threading.CancellationToken)), data.ColorStyle, data.Options);
+ return HtmlWriter.GenerateHtml (await ClipboardColoredText.GetChunks (data, new TextSegment (offset, length), cancellationToken), data.ColorStyle, data.Options);
case MarkupFormat.RichText:
- return RtfWriter.GenerateRtf (ClipboardColoredText.GetChunks (data, new TextSegment (offset, length)).WaitAndGetResult (default (System.Threading.CancellationToken)), data.ColorStyle, data.Options);
+ return RtfWriter.GenerateRtf (await ClipboardColoredText.GetChunks (data, new TextSegment (offset, length), cancellationToken), data.ColorStyle, data.Options);
default:
throw new ArgumentOutOfRangeException ();
}
@@ -3413,6 +3419,10 @@ namespace MonoDevelop.SourceEditor
return TextEditor.GetLineHeight (line);
}
+ public bool DeleteDynamicItem (ItemToolboxNode node) => ClipboardRingService.DeleteItem (node);
+
+ public bool CanDeleteDynamicItem (ItemToolboxNode node) => ClipboardRingService.GetToolboxItems ().Contains (node);
+
public bool HasFocus {
get {
return this.TextEditor.HasFocus;
diff --git a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs
index 9ef7217efa..bcb9df0d6b 100644
--- a/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs
+++ b/main/src/addins/MonoDevelop.SourceEditor2/VSEditor/TagBasedSyntaxHighlighting.cs
@@ -198,19 +198,19 @@ namespace Microsoft.VisualStudio.Platform
}
}
- private void OnClassificationChanged(object sender, ClassificationChangedEventArgs args)
- {
- var handler = _highlightingStateChanged;
- if (handler != null)
- {
- foreach (Mono.TextEditor.MdTextViewLineCollection.MdTextViewLine line in textView.TextViewLines)
- {
- if (line.Start.Position > args.ChangeSpan.End.Position || line.End.Position < args.ChangeSpan.Start)
- continue;
- handler(this, new LineEventArgs(line.line));
- }
- }
- }
+ private void OnClassificationChanged (object sender, ClassificationChangedEventArgs args)
+ {
+ var handler = _highlightingStateChanged;
+ if (handler != null) {
+ foreach (Mono.TextEditor.MdTextViewLineCollection.MdTextViewLine line in textView.TextViewLines) {
+ if (!line.HasDrawn) {
+ line.HasDrawn = true;
+ handler(this, new LineEventArgs(line.line));
+ continue;
+ }
+ }
+ }
+ }
private ScopeStack GetScopeStackFromClassificationType (IClassificationType classificationType)
{
diff --git a/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/ExternalTestRunner.cs b/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/ExternalTestRunner.cs
index 51fa5342d2..2cbf788fe0 100644
--- a/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/ExternalTestRunner.cs
+++ b/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/ExternalTestRunner.cs
@@ -48,6 +48,8 @@ namespace MonoDevelop.UnitTesting.NUnit.External
IRemoteEventListener listener;
readonly string assemblyDirectory;
+ public ProcessExecutionArchitecture ProcessExecutionArchitecture { get; set; }
+
public ExternalTestRunner ()
{
}
@@ -61,6 +63,7 @@ namespace MonoDevelop.UnitTesting.NUnit.External
{
var exePath = Path.Combine (Path.GetDirectoryName (GetType ().Assembly.Location), version.ToString (), "NUnitRunner.exe");
connection = new RemoteProcessConnection (exePath, assemblyDirectory, executionHandler, console, Runtime.MainSynchronizationContext);
+ connection.ProcessExecutionArchitecture = ProcessExecutionArchitecture;
connection.AddListener (this);
return connection.Connect ();
}
diff --git a/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/NUnitAssemblyTestSuite.cs b/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/NUnitAssemblyTestSuite.cs
index b75a993743..5b3e57bbed 100644
--- a/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/NUnitAssemblyTestSuite.cs
+++ b/main/src/addins/MonoDevelop.UnitTesting.NUnit/MonoDevelop.UnitTesting.NUnit/NUnitAssemblyTestSuite.cs
@@ -44,6 +44,7 @@ using System.Xml.Linq;
using System.Linq;
using System.Globalization;
using System.Threading.Tasks;
+using MonoDevelop.Core.Assemblies;
namespace MonoDevelop.UnitTesting.NUnit
{
@@ -333,6 +334,7 @@ namespace MonoDevelop.UnitTesting.NUnit
try {
if (File.Exists (ld.Path)) {
runner = new ExternalTestRunner (Path.GetDirectoryName (ld.Path));
+ runner.ProcessExecutionArchitecture = AssemblyUtilities.GetProcessExecutionArchitectureForAssembly (ld.Path);
runner.Connect (ld.NUnitVersion).Wait ();
var supportAssemblies = new List<string> (ld.SupportAssemblies.Result);
ld.Info = runner.GetTestInfo (ld.Path, supportAssemblies).Result;
@@ -398,6 +400,7 @@ namespace MonoDevelop.UnitTesting.NUnit
OperationConsoleFactory.CreateConsoleOptions.Default.WithTitle (GettextCatalog.GetString ("Unit Tests")));
ExternalTestRunner runner = new ExternalTestRunner (Path.GetDirectoryName (AssemblyPath));
+ runner.ProcessExecutionArchitecture = AssemblyUtilities.GetProcessExecutionArchitectureForAssembly (AssemblyPath);
runner.Connect (NUnitVersion, testContext.ExecutionContext.ExecutionHandler, console).Wait ();
LocalTestMonitor localMonitor = new LocalTestMonitor (testContext, test, suiteName, testName != null);
diff --git a/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs b/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs
index 5f776ccc60..6d0a84150d 100644
--- a/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs
+++ b/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs
@@ -44,7 +44,7 @@ namespace MonoDevelop.UnitTesting.Commands
GoToFailure,
RerunTest,
}
-
+
public enum TestChartCommands
{
ShowResults,
@@ -55,13 +55,20 @@ namespace MonoDevelop.UnitTesting.Commands
ShowFailedTests,
ShowIgnoredTests
}
-
+
public enum NUnitProjectCommands
{
AddAssembly
}
-
- class RunAllTestsHandler: CommandHandler
+
+ public enum TextEditorCommands
+ {
+ RunTestAtCaret,
+ DebugTestAtCaret,
+ SelectTestAtCaret
+ }
+
+ class RunAllTestsHandler : CommandHandler
{
protected override void Run ()
{
diff --git a/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml b/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml
index c2158343b2..4fd591a678 100644
--- a/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml
+++ b/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml
@@ -75,6 +75,9 @@
<Command id = "MonoDevelop.UnitTesting.Commands.TestChartCommands.ShowSuccessfulTests" _label = "Show successful tests" type="check"/>
<Command id = "MonoDevelop.UnitTesting.Commands.TestChartCommands.ShowFailedTests" _label = "Show failed tests" type="check"/>
<Command id = "MonoDevelop.UnitTesting.Commands.TestChartCommands.ShowIgnoredTests" _label = "Show ignored tests" type="check"/>
+ <Command id = "MonoDevelop.UnitTesting.Commands.TextEditorCommands.RunTestAtCaret" _label = "Run Test at Caret"/>
+ <Command id = "MonoDevelop.UnitTesting.Commands.TextEditorCommands.DebugTestAtCaret" _label = "Debug Test at Caret"/>
+ <Command id = "MonoDevelop.UnitTesting.Commands.TextEditorCommands.SelectTestAtCaret" _label = "Select Test at Caret"/>
</Category>
</Extension>
diff --git a/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs b/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs
index 58449a06a8..27464ad5b2 100644
--- a/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs
+++ b/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs
@@ -37,6 +37,8 @@ using MonoDevelop.Ide.Editor.Extension;
using MonoDevelop.Ide.Editor;
using MonoDevelop.Projects;
using Mono.Addins;
+using MonoDevelop.Components.Commands;
+using MonoDevelop.UnitTesting.Commands;
namespace MonoDevelop.UnitTesting
{
@@ -137,6 +139,36 @@ namespace MonoDevelop.UnitTesting
List<IUnitTestMarker> currentMarker = new List<IUnitTestMarker>();
+ TestRunner GetTestRunnerAtCaret (bool debug)
+ {
+ var line = Editor.GetLine (Editor.CaretLine);
+ if (line == null)
+ return null;
+ foreach (var marker in Editor.GetLineMarkers (line)) {
+ if (marker is IUnitTestMarker result)
+ return new TestRunner (result.UnitTest.UnitTestIdentifier, DocumentContext.Project, debug);
+ }
+ return null;
+ }
+
+ [CommandHandler (TextEditorCommands.RunTestAtCaret)]
+ protected void OnRunTestAtCaret ()
+ {
+ GetTestRunnerAtCaret (false)?.Run (this, EventArgs.Empty);
+ }
+
+ [CommandHandler (TextEditorCommands.DebugTestAtCaret)]
+ protected void OnDebugTestAtCaret ()
+ {
+ GetTestRunnerAtCaret (true)?.Run (this, EventArgs.Empty);
+ }
+
+ [CommandHandler (TextEditorCommands.SelectTestAtCaret)]
+ protected void OnSelectTestAtCaret ()
+ {
+ GetTestRunnerAtCaret (false)?.Select (this, EventArgs.Empty);
+ }
+
class UnitTestMarkerHostImpl : UnitTestMarkerHost
{
readonly AbstractUnitTestTextEditorExtension ext;
@@ -277,82 +309,70 @@ namespace MonoDevelop.UnitTesting
#endregion
- class TestRunner
- {
- readonly string testCase;
- readonly bool debug;
- IBuildTarget project;
-
- public TestRunner (string testCase, IBuildTarget project, bool debug)
- {
- this.testCase = testCase;
- this.debug = debug;
- this.project = project;
- }
-
- bool TimeoutHandler ()
- {
- var test = UnitTestService.SearchTestByDocumentId (testCase);
- if (test != null) {
- RunTest (test);
- timeoutHandler = 0;
- } else {
- return true;
- }
- return false;
- }
+ }
+ class TestRunner
+ {
+ readonly string testCase;
+ readonly bool debug;
+ IBuildTarget project;
- internal async void Run (object sender, EventArgs e)
- {
- if (IdeApp.ProjectOperations.IsBuilding (IdeApp.ProjectOperations.CurrentSelectedSolution) ||
- IdeApp.ProjectOperations.IsRunning (IdeApp.ProjectOperations.CurrentSelectedSolution))
- return;
+ public TestRunner (string testCase, IBuildTarget project, bool debug)
+ {
+ this.testCase = testCase;
+ this.debug = debug;
+ this.project = project;
+ }
- var foundTest = UnitTestService.SearchTestByDocumentId (testCase);
- if (foundTest != null) {
- RunTest (foundTest);
- return;
- }
+ internal async void Run (object sender, EventArgs e)
+ {
+ if (IdeApp.ProjectOperations.IsBuilding (IdeApp.ProjectOperations.CurrentSelectedSolution) ||
+ IdeApp.ProjectOperations.IsRunning (IdeApp.ProjectOperations.CurrentSelectedSolution))
+ return;
- bool buildBeforeExecuting = IdeApp.Preferences.BuildBeforeRunningTests;
+ var foundTest = UnitTestService.SearchTestByDocumentId (testCase);
+ if (foundTest != null) {
+ RunTest (foundTest);
+ return;
+ }
- if (buildBeforeExecuting) {
- await IdeApp.ProjectOperations.Build (project).Task;
- await UnitTestService.RefreshTests (CancellationToken.None);
- }
+ bool buildBeforeExecuting = IdeApp.Preferences.BuildBeforeRunningTests;
- foundTest = UnitTestService.SearchTestByDocumentId (testCase);
- if (foundTest != null)
- RunTest (foundTest);
- else
- UnitTestService.ReportExecutionError (GettextCatalog.GetString ("Unit test '{0}' could not be loaded.", testCase));
+ if (buildBeforeExecuting) {
+ await IdeApp.ProjectOperations.Build (project).Task;
+ await UnitTestService.RefreshTests (CancellationToken.None);
}
- internal void Select (object sender, EventArgs e)
- {
- var test = UnitTestService.SearchTestByDocumentId (testCase);
- if (test == null)
- return;
- UnitTestService.CurrentSelectedTest = test;
- }
+ foundTest = UnitTestService.SearchTestByDocumentId (testCase);
+ if (foundTest != null)
+ RunTest (foundTest);
+ else
+ UnitTestService.ReportExecutionError (GettextCatalog.GetString ("Unit test '{0}' could not be loaded.", testCase));
+ }
- void RunTest (UnitTest test)
- {
- var debugModeSet = Runtime.ProcessService.GetDebugExecutionMode ();
- Core.Execution.IExecutionHandler ctx = null;
- if (debug && debugModeSet != null) {
- foreach (var executionMode in debugModeSet.ExecutionModes) {
- if (test.CanRun (executionMode.ExecutionHandler)) {
- ctx = executionMode.ExecutionHandler;
- break;
- }
+ internal void Select (object sender, EventArgs e)
+ {
+ var test = UnitTestService.SearchTestByDocumentId (testCase);
+ if (test == null)
+ return;
+ UnitTestService.CurrentSelectedTest = test;
+ }
+
+ void RunTest (UnitTest test)
+ {
+ var debugModeSet = Runtime.ProcessService.GetDebugExecutionMode ();
+ Core.Execution.IExecutionHandler ctx = null;
+ if (debug && debugModeSet != null) {
+ foreach (var executionMode in debugModeSet.ExecutionModes) {
+ if (test.CanRun (executionMode.ExecutionHandler)) {
+ ctx = executionMode.ExecutionHandler;
+ break;
}
}
- // NUnitService.Instance.RunTest (test, ctx);
- var pad = IdeApp.Workbench.GetPad<TestPad> ();
- var content = (TestPad)pad.Content;
- content.RunTest (test, ctx);
}
+ // NUnitService.Instance.RunTest (test, ctx);
+ var pad = IdeApp.Workbench.GetPad<TestPad> ();
+ var content = (TestPad)pad.Content;
+ content.RunTest (test, ctx);
}
}
}
diff --git a/main/src/addins/MonoDevelop.UnitTesting/Services/SolutionFolderTestGroup.cs b/main/src/addins/MonoDevelop.UnitTesting/Services/SolutionFolderTestGroup.cs
index 5346301540..bcf3ebbe39 100644
--- a/main/src/addins/MonoDevelop.UnitTesting/Services/SolutionFolderTestGroup.cs
+++ b/main/src/addins/MonoDevelop.UnitTesting/Services/SolutionFolderTestGroup.cs
@@ -64,7 +64,7 @@ namespace MonoDevelop.UnitTesting
public override void Dispose ()
{
folder.NameChanged -= OnCombineRenamed;
- if (folder.IsRoot) {
+ if (folder.IsRoot && folder.ParentSolution != null) {
folder.ParentSolution.SolutionItemAdded -= OnEntryChanged;
folder.ParentSolution.SolutionItemRemoved -= OnEntryChanged;
}
diff --git a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.Dialogs/WebReferenceDialog.cs b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.Dialogs/WebReferenceDialog.cs
index 234deb1883..b05fc834f8 100644
--- a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.Dialogs/WebReferenceDialog.cs
+++ b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.Dialogs/WebReferenceDialog.cs
@@ -150,7 +150,7 @@ namespace MonoDevelop.WebReferences.Dialogs
#endregion
#region Member Variables
- const string homeUrl = "https://www.w3schools.com/xml/tempconvert.asmx?WSDL";
+ const string homeUrl = "https://www.w3schools.com/xml/tempconvert.asmx";
WebServiceDiscoveryResult selectedService;
// protected Gtk.Alignment frmBrowserAlign;
#endregion
@@ -639,50 +639,4 @@ namespace MonoDevelop.WebReferences.Dialogs
base.OnDestroyed ();
}
}
-
- class AskCredentials: GuiSyncObject, ICredentials
- {
- static readonly Dictionary<string,NetworkCredential> credentials = new Dictionary<string, NetworkCredential> ();
-
- readonly Dictionary<string,NetworkCredential> tempCredentials = new Dictionary<string, NetworkCredential> ();
-
- public bool Canceled;
-
- public void Reset ()
- {
- tempCredentials.Clear ();
- }
-
- public void Store ()
- {
- foreach (var creds in tempCredentials)
- credentials [creds.Key] = creds.Value;
- }
-
- public NetworkCredential GetCredential (Uri uri, string authType)
- {
- NetworkCredential nc;
- if (tempCredentials.TryGetValue (uri.Host + uri.AbsolutePath, out nc))
- return nc; // Exact match
-
- var dlg = new UserPasswordDialog (uri.Host);
- if (tempCredentials.TryGetValue (uri.Host, out nc) || credentials.TryGetValue (uri.Host, out nc)) {
- dlg.User = nc.UserName;
- dlg.Password = nc.Password;
- }
- try {
- if (MessageService.RunCustomDialog (dlg) == (int)ResponseType.Ok) {
- nc = new NetworkCredential (dlg.User, dlg.Password);
- tempCredentials [uri.Host + uri.AbsolutePath] = nc;
- tempCredentials [uri.Host] = nc;
- return nc;
- }
- Canceled = true;
- return null;
- } finally {
- dlg.Destroy ();
- dlg.Dispose ();
- }
- }
- }
}
diff --git a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.csproj b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.csproj
index fcc52a16fa..dd917ce6e6 100644
--- a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.csproj
+++ b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences.csproj
@@ -86,6 +86,7 @@
<Compile Include="Gui\MonoDevelop.WebReferences.Dialogs.WCFConfigWidget.cs" />
<Compile Include="Gui\MonoDevelop.WebReferences.Dialogs.WebReferenceDialog.cs" />
<Compile Include="MonoDevelop.WebReferences\WebReferencesProjectExtension.cs" />
+ <Compile Include="MonoDevelop.WebReferences\MonoDevelopDiscoveryClientProtocol.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MonoDevelop.WebReferences.addin.xml" />
diff --git a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/Library.cs b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/Library.cs
index 14c9492527..4ed5a6f709 100644
--- a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/Library.cs
+++ b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/Library.cs
@@ -18,14 +18,14 @@ namespace MonoDevelop.WebReferences
/// <summary>Read the service description for a specified uri.</summary>
/// <param name="uri">A string containing the unique reference identifier for the service.</param>
/// <returns>A ServiceDescription for the specified uri.</returns>
+ [Obsolete]
public static ServiceDescription ReadServiceDescription(string uri)
{
var desc = new ServiceDescription();
try
{
- var request = (HttpWebRequest)WebRequest.Create(uri);
- WebResponse response = request.GetResponse();
-
+ WebResponse response = WebRequestHelper.GetResponse (() => (HttpWebRequest)WebRequest.Create (uri));
+
desc = ServiceDescription.Read(response.GetResponseStream());
response.Close();
desc.RetrievalUrl = uri;
diff --git a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/MonoDevelopDiscoveryClientProtocol.cs b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/MonoDevelopDiscoveryClientProtocol.cs
new file mode 100644
index 0000000000..0688c67394
--- /dev/null
+++ b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/MonoDevelopDiscoveryClientProtocol.cs
@@ -0,0 +1,65 @@
+//
+// MonoDevelopDiscoveryClientProtocol.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Net;
+using System.Web.Services.Discovery;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.WebReferences
+{
+ /// <summary>
+ /// Integrates with the MonoDevelop credential provider.
+ /// </summary>
+ class MonoDevelopDiscoveryClientProtocol : DiscoveryClientProtocol
+ {
+ protected override WebResponse GetWebResponse (WebRequest request)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete. Have to use WebRequest with the DiscoveryClientProtocol
+ return WebRequestHelper.GetResponse (() => CreateWebRequest (request.RequestUri));
+#pragma warning restore CS0618 // Type or member is obsolete.
+ }
+
+ /// <summary>
+ /// Need to create a new web request just in case the request is not authorized and the
+ /// WebRequestHelper is retrying. Otherwise the second attempt will fail since the request
+ /// has already been started and will throw an System.InvalidOperationException: request started
+ /// when properties such as the Proxy are changed by the WebRequestHelper. The only method used
+ /// is the DiscoveryClientProtocol's Download method which creates the web request and then gets
+ /// the response immediately afterwards.
+ /// </summary>
+ HttpWebRequest CreateWebRequest (Uri uri)
+ {
+ var request = (HttpWebRequest)base.GetWebRequest (uri);
+ // Method is set in base.Download so set it here.
+ request.Method = "GET";
+ // Allow redirects. This allows the Web Reference dialog to show information about the
+ // the https://www.w3schools.com/xml/tempconvert.asmx web service.
+ request.AllowAutoRedirect = true;
+ return request;
+ }
+ }
+}
diff --git a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/WebServiceEngine.cs b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/WebServiceEngine.cs
index 62ced2eb08..edc362d24f 100644
--- a/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/WebServiceEngine.cs
+++ b/main/src/addins/MonoDevelop.WebReferences/MonoDevelop.WebReferences/WebServiceEngine.cs
@@ -27,8 +27,6 @@
using MonoDevelop.Projects;
using System.Collections.Generic;
using System.Web.Services.Discovery;
-using MonoDevelop.WebReferences.Dialogs;
-using System.Net;
using MonoDevelop.Core;
namespace MonoDevelop.WebReferences
@@ -50,36 +48,14 @@ namespace MonoDevelop.WebReferences
protected DiscoveryClientProtocol DiscoResolve (string url)
{
// Checks the availablity of any services
- var protocol = new DiscoveryClientProtocol ();
- var creds = new AskCredentials ();
- protocol.Credentials = creds;
- bool unauthorized;
-
- do {
- unauthorized = false;
- creds.Reset ();
-
- try {
- protocol.DiscoverAny (url);
- } catch (WebException wex) {
- var wr = wex.Response as HttpWebResponse;
- if (!creds.Canceled && wr != null && wr.StatusCode == HttpStatusCode.Unauthorized) {
- unauthorized = true;
- continue;
- }
- throw;
- }
- } while (unauthorized);
-
- if (protocol != null) {
- creds.Store ();
- if (protocol.References.Count == 0)
- return null;
- }
+ var protocol = new MonoDevelopDiscoveryClientProtocol ();
+ protocol.DiscoverAny (url);
+
+ if (protocol.References.Count == 0)
+ return null;
+
return protocol;
}
}
-
-
}
diff --git a/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs b/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs
index e383d6192b..cfa5c6c497 100644
--- a/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs
+++ b/main/src/addins/PerformanceDiagnostics/PerformanceDiagnostics/UIThreadMonitor.cs
@@ -212,7 +212,9 @@ namespace PerformanceDiagnosticsAddIn
{
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");
+
using (var sr = new StreamReader (fileName))
using (var sw = new StreamWriter (outputFilename)) {
string line;
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/EditRemoteDialog.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/EditRemoteDialog.cs
index 5e05821a6e..591007a30f 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/EditRemoteDialog.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/EditRemoteDialog.cs
@@ -24,6 +24,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+using System;
+using System.Linq;
using LibGit2Sharp;
using MonoDevelop.Components;
@@ -31,18 +33,22 @@ namespace MonoDevelop.VersionControl.Git
{
partial class EditRemoteDialog : Gtk.Dialog
{
+ GitRepository repo;
+
// TODO: Add user possibility to choose refspecs.
- public EditRemoteDialog () : this (null)
+ public EditRemoteDialog () : this (null, null)
{
}
bool sameUrls;
- public EditRemoteDialog (Remote remote)
+ public EditRemoteDialog (GitRepository repository, Remote remote)
{
this.Build ();
this.UseNativeContextMenus ();
+ repo = repository;
+
if (remote != null) {
entryName.Text = remote.Name;
entryUrl.Text = remote.Url ?? "";
@@ -80,7 +86,7 @@ namespace MonoDevelop.VersionControl.Git
void UpdateButtons ()
{
- buttonOk.Sensitive = entryName.Text.Length > 0 && entryUrl.Text.Length > 0 && entryPushUrl.Text.Length > 0;
+ buttonOk.Sensitive = entryName.Text.Length > 0 && IsValidUrl (entryUrl.Text) && IsValidUrl(entryPushUrl.Text);
}
protected virtual void OnEntryNameChanged (object sender, System.EventArgs e)
@@ -107,5 +113,12 @@ namespace MonoDevelop.VersionControl.Git
UpdateButtons ();
}
+
+ bool IsValidUrl (string url)
+ {
+ if (repo == null)
+ return url.Length > 0;
+ return repo.IsUrlValid (url);
+ }
}
}
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitCommitDialogExtension.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitCommitDialogExtension.cs
index 2c0cff0a13..55278dbfa6 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitCommitDialogExtension.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitCommitDialogExtension.cs
@@ -30,11 +30,14 @@ using System;
using MonoDevelop.Core;
using MonoDevelop.Projects;
using MonoDevelop.Ide;
+using System.Text.RegularExpressions;
namespace MonoDevelop.VersionControl.Git
{
sealed class GitCommitDialogExtension: CommitDialogExtension
{
+ public static readonly Regex MailRegex = new Regex (@"[\w\d._%+-]+@[\w\d.-]+\.\w+", RegexOptions.Compiled);
+
GitCommitDialogExtensionWidget widget;
Gtk.TextView textView;
@@ -49,7 +52,8 @@ namespace MonoDevelop.VersionControl.Git
widget.Show ();
Show ();
widget.Changed += delegate {
- AllowCommit = widget.CommitterIsAuthor || widget.AuthorName.Length > 0;
+ AllowCommit = widget.CommitterIsAuthor ||
+ (widget.AuthorName.Length > 0 && IsValidMail(widget.AuthorMail));
};
return true;
}
@@ -202,5 +206,15 @@ namespace MonoDevelop.VersionControl.Git
textView.HasTooltip = false;
}
}
+
+ bool IsValidMail (string mail)
+ {
+ if (string.IsNullOrEmpty (mail))
+ return false;
+
+ var match = MailRegex.Match (mail);
+
+ return match.Success;
+ }
}
}
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitConfigurationDialog.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitConfigurationDialog.cs
index 66c265820b..3df491f6eb 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitConfigurationDialog.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git/MonoDevelop.VersionControl.Git/GitConfigurationDialog.cs
@@ -236,7 +236,7 @@ namespace MonoDevelop.VersionControl.Git
protected virtual void OnButtonAddRemoteClicked (object sender, EventArgs e)
{
- var dlg = new EditRemoteDialog ();
+ var dlg = new EditRemoteDialog (repo, null);
try {
if (MessageService.RunCustomDialog (dlg) == (int) ResponseType.Ok) {
repo.AddRemote (dlg.RemoteName, dlg.RemoteUrl, dlg.ImportTags);
@@ -258,7 +258,7 @@ namespace MonoDevelop.VersionControl.Git
if (remote == null)
return;
- var dlg = new EditRemoteDialog (remote);
+ var dlg = new EditRemoteDialog (repo, remote);
try {
if (MessageService.RunCustomDialog (dlg) == (int) ResponseType.Ok) {
if (remote.Url != dlg.RemoteUrl)
@@ -364,12 +364,20 @@ namespace MonoDevelop.VersionControl.Git
protected async void OnButtonFetchClicked (object sender, EventArgs e)
{
- TreeIter it;
- if (!treeRemotes.Selection.GetSelected (out it))
+ if (!treeRemotes.Selection.GetSelected (out var it))
return;
- string remoteName = (string) storeRemotes.GetValue (it, 4);
- if (remoteName == null)
+ bool toplevel = !storeRemotes.IterParent (out var parent, it);
+
+ string remoteName = string.Empty;
+
+ if (toplevel) {
+ remoteName = (string)storeRemotes.GetValue (it, 4);
+ } else {
+ remoteName = (string)storeRemotes.GetValue (parent, 4);
+ }
+
+ if (string.IsNullOrEmpty(remoteName))
return;
await System.Threading.Tasks.Task.Run (() => repo.Fetch (VersionControlService.GetProgressMonitor (GettextCatalog.GetString ("Fetching remote...")), remoteName));
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 f209d7046b..71981d13a4 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
@@ -478,9 +478,12 @@ namespace MonoDevelop.VersionControl.Git
static void GetFilesVersionInfoCore (LibGit2Sharp.Repository repo, GitRevision rev, List<FilePath> localPaths, List<VersionInfo> versions)
{
- foreach (var file in repo.ToGitPath (localPaths)) {
- var status = repo.RetrieveStatus (file);
- AddStatus (repo, rev, file, versions, status, null);
+ foreach (var localPath in localPaths) {
+ if (!localPath.IsDirectory) {
+ var file = repo.ToGitPath (localPath);
+ var status = repo.RetrieveStatus (file);
+ AddStatus (repo, rev, file, versions, status, null);
+ }
}
}
@@ -518,6 +521,7 @@ namespace MonoDevelop.VersionControl.Git
{
var relativePath = repo.ToGitPath (directory);
var status = repo.RetrieveStatus (new StatusOptions {
+ DisablePathSpecMatch = true,
PathSpec = relativePath != "." ? new [] { relativePath } : null,
IncludeUnaltered = true,
});
@@ -664,11 +668,13 @@ namespace MonoDevelop.VersionControl.Git
{
monitor.BeginTask (GettextCatalog.GetString ("Fetching"), 1);
monitor.Log.WriteLine (GettextCatalog.GetString ("Fetching from '{0}'", remote));
+
int progress = 0;
RetryUntilSuccess (monitor, credType => RootRepository.Fetch (remote, new FetchOptions {
CredentialsProvider = (url, userFromUrl, types) => GitCredentials.TryGet (url, userFromUrl, types, credType),
OnTransferProgress = tp => OnTransferProgress (tp, monitor, ref progress),
}));
+
monitor.Step (1);
monitor.EndTask ();
}
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 8ad5a5e087..9df102723f 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
@@ -69,6 +69,7 @@ namespace MonoDevelop.VersionControl.Dialogs
defaultPath = VersionControlDefaultPath;
entryFolder.Text = defaultPath;
buttonOk.Label = GettextCatalog.GetString ("_Checkout");
+ UpdateCheckoutButton ();
} else {
labelTargetDir.Visible = false;
boxFolder.Visible = false;
@@ -111,6 +112,16 @@ namespace MonoDevelop.VersionControl.Dialogs
}
}
+ protected override void OnDestroyed ()
+ {
+ UrlBasedRepositoryEditor edit = currentEditor as UrlBasedRepositoryEditor;
+ if (edit != null) {
+ edit.UrlChanged -= OnEditUrlChanged;
+ edit.PathChanged -= OnPathChanged;
+ }
+ base.OnDestroyed ();
+ }
+
protected virtual void OnRepComboChanged(object sender, System.EventArgs e)
{
if (repoContainer.Child != null)
@@ -125,11 +136,19 @@ namespace MonoDevelop.VersionControl.Dialogs
repoContainer.Add (currentEditor.Widget);
currentEditor.Show ();
UrlBasedRepositoryEditor edit = currentEditor as UrlBasedRepositoryEditor;
- if (edit != null)
+ if (edit != null) {
+ edit.UrlChanged += OnEditUrlChanged;
edit.PathChanged += OnPathChanged;
+ }
UpdateRepoDescription ();
}
-
+
+ protected virtual void OnRepositoryServerEntryChanged (object sender, System.EventArgs e)
+ {
+ if (mode == SelectRepositoryMode.Checkout)
+ buttonOk.Sensitive = entryFolder.Text.Length > 0;
+ }
+
public void LoadRepositories ()
{
store.Clear ();
@@ -348,6 +367,21 @@ namespace MonoDevelop.VersionControl.Dialogs
Respond (ResponseType.Ok);
}
+ protected virtual void OnEditUrlChanged (object sender, EventArgs e)
+ {
+ if (mode == SelectRepositoryMode.Checkout) {
+ UpdateCheckoutButton ();
+ }
+ }
+
+ void UpdateCheckoutButton()
+ {
+ UrlBasedRepositoryEditor edit = currentEditor as UrlBasedRepositoryEditor;
+ if (edit == null)
+ return;
+ buttonOk.Sensitive = !string.IsNullOrWhiteSpace (edit.RepositoryServer);
+ }
+
protected virtual void OnPathChanged (object sender, EventArgs e)
{
AppendRelativePath ();
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs
index 559c24fa3c..695ebdbb7d 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs
@@ -28,6 +28,8 @@ using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Content;
using Mono.TextEditor;
using MonoDevelop.Ide.Gui.Documents;
+using System;
+using System.Linq;
namespace MonoDevelop.VersionControl.Views
{
@@ -60,19 +62,30 @@ namespace MonoDevelop.VersionControl.Views
var buffer = info.Controller.GetContent<MonoDevelop.Ide.Editor.TextEditor> ();
if (buffer != null) {
- var loc = buffer.CaretLocation;
- int line = loc.Line < 1 ? 1 : loc.Line;
- int column = loc.Column < 1 ? 1 : loc.Column;
- widget.Editor.SetCaretTo (line, column, highlight: false, centerCaret: false);
+ if (!(buffer.TextView is MonoTextEditor)) {
+ //compatibility for other not MonoTextEditor editors
+ var loc = buffer.CaretLocation;
+ int line = loc.Line < 1 ? 1 : loc.Line;
+ int column = loc.Column < 1 ? 1 : loc.Column;
+ widget.Editor.SetCaretTo (line, column, highlight: false, centerCaret: false);
+ }
}
}
protected override void OnDeselected ()
{
- var buffer = info.Controller.GetContent<MonoDevelop.Ide.Editor.TextEditor> ();
+ var buffer = info.Controller.GetContent<MonoDevelop.Ide.Editor.TextEditor> () ;
if (buffer != null) {
- buffer.SetCaretLocation (widget.Editor.Caret.Line, widget.Editor.Caret.Column, usePulseAnimation: false, centerCaret: false);
- buffer.ScrollTo (new Ide.Editor.DocumentLocation (widget.Editor.YToLine (widget.Editor.VAdjustment.Value), 1));
+ if (buffer.TextView is MonoTextEditor exEditor) {
+ if (widget.Revision == null)
+ exEditor.Document.UpdateFoldSegments (widget.Editor.Document.FoldSegments.Select (f => new Mono.TextEditor.FoldSegment (f)));
+ exEditor.SetCaretTo (widget.Editor.Caret.Line, widget.Editor.Caret.Column);
+ exEditor.VAdjustment.Value = widget.Editor.VAdjustment.Value;
+ } else {
+ //compatibility for other not MonoTextEditor editors
+ buffer.ScrollTo (new Ide.Editor.DocumentLocation (widget.Editor.YToLine (widget.Editor.VAdjustment.Value), 1));
+ buffer.SetCaretLocation (widget.Editor.Caret.Line, widget.Editor.Caret.Column, usePulseAnimation: false, centerCaret: false);
+ }
}
}
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameWidget.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameWidget.cs
index 162abcb171..e3d5623ed8 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameWidget.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameWidget.cs
@@ -36,6 +36,7 @@ using MonoDevelop.Core;
using MonoDevelop.Components;
using MonoDevelop.Components.Commands;
using MonoDevelop.Ide.Fonts;
+using MonoDevelop.Ide.Editor;
namespace MonoDevelop.VersionControl.Views
{
@@ -49,6 +50,13 @@ namespace MonoDevelop.VersionControl.Views
class BlameWidget : Bin
{
Revision revision;
+
+ public Revision Revision {
+ get {
+ return revision;
+ }
+ }
+
Adjustment vAdjustment;
Gtk.VScrollbar vScrollBar;
@@ -131,7 +139,11 @@ namespace MonoDevelop.VersionControl.Views
IsReadOnly = true,
MimeType = sourceEditor.TextEditor.Document.MimeType,
};
- editor = new MonoTextEditor (doc, sourceEditor.TextEditor.Options);
+ var options = new CustomEditorOptions (DefaultSourceEditorOptions.Instance);
+ options.TabsToSpaces = false;
+
+ editor = new MonoTextEditor (doc, new SourceEditor.StyledSourceEditorOptions (options));
+
AddChild (editor);
editor.SetScrollAdjustments (hAdjustment, vAdjustment);
@@ -412,17 +424,14 @@ namespace MonoDevelop.VersionControl.Views
WidthRequest = newWidthRequest;
QueueResize ();
}
- int startLine = widget.Editor.YToLine (widget.Editor.VAdjustment.Value + evnt.Y);
- var ann = startLine > 0 && startLine <= annotations.Count ? annotations[startLine - 1] : null;
- if (ann != null)
+
+ GetAnnotationFromY (evnt.Y, out var annotation, out var startLine);
+
+ if (annotation != null)
TooltipText = GetCommitMessage (startLine - 1, true);
- highlightPositon = evnt.Y;
- if (highlightAnnotation != ann) {
- highlightAnnotation = ann;
- widget.QueueDraw ();
- }
-
+ SetHighlight (annotation, startLine, evnt.Y, highlightAnnotation != annotation);
+
return base.OnMotionNotifyEvent (evnt);
}
@@ -438,8 +447,8 @@ namespace MonoDevelop.VersionControl.Views
protected override bool OnButtonPressEvent (EventButton evnt)
{
if (evnt.TriggersContextMenu ()) {
- int startLine = widget.Editor.YToLine (widget.Editor.VAdjustment.Value + evnt.Y);
- menuAnnotation = startLine > 0 && startLine <= annotations.Count ? annotations[startLine - 1] : null;
+
+ GetAnnotationFromY (evnt.Y, out menuAnnotation, out var startLine);
CommandEntrySet opset = new CommandEntrySet ();
opset.AddItem (BlameCommands.ShowDiff);
@@ -556,6 +565,11 @@ namespace MonoDevelop.VersionControl.Views
document.Text = widget.VersionControlItem.Repository.GetTextAtRevision (widget.Document.FileName, widget.revision);
} else {
document.Text = widget.Document.Editor.Text;
+ if (widget.Document.Editor.TextView is MonoTextEditor exEditor) {
+ document.UpdateFoldSegments (exEditor.Document.FoldSegments.Select (f => new Mono.TextEditor.FoldSegment (f)));
+ widget.Editor.SetCaretTo (exEditor.Caret.Line, exEditor.Caret.Column);
+ widget.Editor.VAdjustment.Value = exEditor.VAdjustment.Value;
+ }
}
widget.editor.Caret.Location = location;
widget.editor.VAdjustment.Value = adj;
@@ -773,7 +787,26 @@ namespace MonoDevelop.VersionControl.Views
}
return true;
}
-
+
+ int YToStartLine (double y) => widget.Editor.YToLine (widget.Editor.VAdjustment.Value + y);
+
+ void GetAnnotationFromY (double y, out Annotation annotation, out int startLine)
+ {
+ startLine = YToStartLine (y);
+ annotation = GetAnnotationFromLine (startLine);
+ }
+
+ internal Annotation GetAnnotationFromLine (int startLine) =>
+ startLine > 0 && startLine <= annotations.Count ? annotations [startLine - 1] : null;
+
+ internal void SetHighlight (Annotation annotation, int line, double y, bool needsRedraw)
+ {
+ highlightPositon = y;
+ highlightAnnotation = annotation;
+ if (needsRedraw) {
+ widget.QueueDraw ();
+ }
+ }
}
}
}
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/DiffView.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/DiffView.cs
index d9d4321c50..c8de96214b 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/DiffView.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/DiffView.cs
@@ -30,6 +30,7 @@ using MonoDevelop.Components;
using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Content;
using MonoDevelop.Ide.Gui.Documents;
+using MonoDevelop.Ide;
namespace MonoDevelop.VersionControl.Views
{
@@ -46,7 +47,11 @@ namespace MonoDevelop.VersionControl.Views
if (widget == null) {
widget = new DiffWidget (info);
- ComparisonWidget.DiffEditor.Document.Text = info.Item.Repository.GetBaseText (info.Item.Path);
+ try {
+ ComparisonWidget.DiffEditor.Document.Text = info.Item.Repository.GetBaseText (info.Item.Path);
+ } catch (Exception ex) {
+ LoggingService.LogInternalError ("Error fetching text from repository ", ex);
+ }
ComparisonWidget.SetLocal (ComparisonWidget.OriginalEditor.GetTextEditorData ());
widget.ShowAll ();
}
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogView.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogView.cs
index 5033015745..c1fa66ad2e 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogView.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogView.cs
@@ -87,9 +87,10 @@ namespace MonoDevelop.VersionControl.Views
[CommandHandler (MonoDevelop.Ide.Commands.EditCommands.Copy)]
protected void OnCopy ()
{
- string data = widget.DiffText;
- if (data == null)
+ string data = widget.GetSelectedText ();
+ if (data == null) {
return;
+ }
var clipboard = Clipboard.Get (Gdk.Atom.Intern ("CLIPBOARD", false));
clipboard.Text = data;
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs
index ea3cbf2c67..c318989981 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs
@@ -36,6 +36,7 @@ using Mono.TextEditor;
using System.Linq;
using MonoDevelop.Ide.Editor;
using MonoDevelop.Ide.Fonts;
+using Humanizer;
namespace MonoDevelop.VersionControl.Views
{
@@ -368,7 +369,9 @@ namespace MonoDevelop.VersionControl.Views
doc?.GetContent<VersionControlDocumentController> ()?.ShowDiffView (SelectedRevision.GetPrevious (), SelectedRevision, line);
}
+ const int colFile = 3;
const int colOperation = 4;
+ const int colOperationText = 1;
const int colPath = 5;
const int colDiff = 6;
@@ -497,21 +500,15 @@ namespace MonoDevelop.VersionControl.Views
static void DateFunc (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
{
- CellRendererText renderer = (CellRendererText)cell;
- var rev = (Revision)model.GetValue (iter, 0);
- string day;
-
+ var renderer = (CellRendererText)cell;
+ var revision = (Revision)model.GetValue (iter, 0);
// Grab today's day and the start of tomorrow's day to make Today/Yesterday calculations.
var now = DateTime.Now;
- var age = new DateTime (now.Year, now.Month, now.Day).AddDays(1) - rev.Time;
- if (age.Days >= 0 && age.Days < 1) { // Check whether it's a commit that's less than a day away. Also discard future commits.
- day = GettextCatalog.GetString ("Today");
- } else if (age.Days < 2) { // Check whether it's a commit from yesterday.
- day = GettextCatalog.GetString ("Yesterday");
- } else {
- day = rev.Time.ToShortDateString ();
- }
- renderer.Text = string.Format ("{0} {1:HH:mm}", day, rev.Time);
+ var age = new DateTime (now.Year, now.Month, now.Day).AddDays (1) - revision.Time;
+
+ renderer.Text = age.Days >= 2 ?
+ revision.Time.ToShortDateString () :
+ revision.Time.Humanize (utcDate: false, dateToCompareAgainst: now);
}
static void GraphFunc (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
@@ -777,16 +774,60 @@ namespace MonoDevelop.VersionControl.Views
}
}
- internal string DiffText {
- get {
- TreeIter iter;
- if (treeviewFiles.Selection.GetSelected (out iter)) {
- string [] items = changedpathstore.GetValue (iter, colDiff) as string [];
- if (items != null)
- return String.Join (Environment.NewLine, items);
+ internal string GetSelectedText ()
+ {
+ if (treeviewFiles.HasFocus ) {
+ if (treeviewFiles.Selection.GetSelected (out var iter)) {
+ if (changedpathstore.GetValue (iter, colDiff) is string [] items) {
+ return string.Join (Environment.NewLine, items);
+ }
+ if (changedpathstore.GetValue (iter, colFile) is string file) {
+ var path = changedpathstore.GetValue (iter, colPath) as string;
+ var operation = changedpathstore.GetValue (iter, colOperationText) as string;
+ return string.Format ("{0}, {1}, {2}", file, operation, path);
+ }
+ }
+ }
+
+ if (treeviewLog.HasFocus) {
+ if (treeviewLog.Selection.GetSelected (out var iter)) {
+ if (logstore.GetValue (iter, 0) is Revision revision) {
+ return string.Format ("{0}, {1}, {2}", revision.ShortMessage, revision.Time, revision.Author);
+ }
+ }
+ }
+
+ if (textviewDetails.HasFocus) {
+ textviewDetails.Buffer.GetSelectionBounds (out var A, out var B);
+ var result = textviewDetails.Buffer.GetText (A, B, true);
+ if (!string.IsNullOrEmpty (result)) {
+ return result;
+ }
+ }
+
+ int start, end;
+ if (labelDate.HasFocus) {
+ labelDate.GetSelectionBounds (out start, out end);
+ if (start != end) {
+ return labelDate.Text.Substring (start, end - start);
}
- return null;
}
+
+ if (labelAuthor.HasFocus) {
+ labelAuthor.GetSelectionBounds (out start, out end);
+ if (start != end) {
+ return labelAuthor.Text.Substring (start, end - start);
+ }
+ }
+
+ if (labelRevision.HasFocus) {
+ labelRevision.GetSelectionBounds (out start, out end);
+ if (start != end) {
+ return labelRevision.Text.Substring (start, end - start);
+ }
+ }
+
+ return null;
}
}
}
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 762880365f..a662180f1e 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
@@ -83,7 +83,7 @@ namespace MonoDevelop.VersionControl.Views
bool updatingComment;
ChangeSet changeSet;
bool firstLoad = true;
- VersionControlItemList fileList;
+ volatile VersionControlItemList fileList;
const int ColIcon = 0;
const int ColStatus = 1;
@@ -428,6 +428,7 @@ namespace MonoDevelop.VersionControl.Views
SetupToolbar (view.GetToolbar ());
return widget;
}
+ object updateLock = new object ();
void StartUpdate ()
{
@@ -444,25 +445,27 @@ namespace MonoDevelop.VersionControl.Views
buttonCommit.Sensitive = false;
ThreadPool.QueueUserWorkItem (delegate {
- 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 ());
+ 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;
}
- changeSet.AddFiles (fileList.Where (v => !v.IsDirectory).Select (v => v.VersionInfo).ToArray ());
- fileList = null;
+ List<VersionInfo> newList = new List<VersionInfo> ();
+ newList.AddRange (vc.GetDirectoryVersionInfo (filepath, remoteStatus, true));
+ Runtime.RunInMainThread (delegate {
+ if (!disposed)
+ LoadStatus (newList);
+ });
}
- List<VersionInfo> newList = new List<VersionInfo> ();
- newList.AddRange (vc.GetDirectoryVersionInfo (filepath, remoteStatus, true));
- Runtime.RunInMainThread (delegate {
- if (!disposed)
- LoadStatus (newList);
- });
});
}
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj
index 1094a11cf4..e9317f97b6 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj
@@ -78,6 +78,10 @@
<HintPath>..\..\..\..\build\bin\Microsoft.VisualStudio.Text.UI.dll</HintPath>
<Private>False</Private>
</Reference>
+ <Reference Include="Humanizer">
+ <HintPath>..\..\..\..\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="icons\added-overlay-16.png" />
@@ -516,10 +520,14 @@
<Compile Include="Gui\MonoDevelop.VersionControl.Views.DiffWidget.cs" />
<Compile Include="Gui\MonoDevelop.VersionControl.Views.LogWidget.cs" />
<Compile Include="MonoDevelop.VersionControl\VersionControlDocumentController.cs" />
+ <Compile Include="MonoDevelop.VersionControl\Instrumentation.cs" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="MonoDevelop.VersionControl.Git.Tests" />
<InternalsVisibleTo Include="MonoDevelop.MacDev" />
</ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs
new file mode 100644
index 0000000000..402ec0183a
--- /dev/null
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs
@@ -0,0 +1,163 @@
+//
+// Counters.cs
+//
+// Author:
+// Vsevolod Kukol <sevoku@microsoft.com>
+//
+// Copyright (c) 2019 (c) Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using MonoDevelop.Core.Instrumentation;
+using System.Threading;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.VersionControl
+{
+ static class Instrumentation
+ {
+ const string Category = "Version Control";
+ const string Identifier = "VersionControl";
+
+ public static readonly Counter<RepositoryMetadata> Repositories = InstrumentationService.CreateCounter<RepositoryMetadata> ("VersionControl.RepositoryOpened", Category, id: $"{Identifier}.RepositoryOpened");
+ public static readonly TimerCounter<MultipathOperationMetadata> CheckoutCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Checkout", Category, id: $"{Identifier}.Checkout");
+ public static readonly TimerCounter<MultipathOperationMetadata> CommitCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Commit", Category, id: $"{Identifier}.Commit");
+ public static readonly TimerCounter<RevertMetadata> RevertCounter = InstrumentationService.CreateTimerCounter<RevertMetadata> ("Revert", Category, id: $"{Identifier}.Revert");
+ public static readonly TimerCounter<PublishMetadata> PublishCounter = InstrumentationService.CreateTimerCounter<PublishMetadata> ("Publish", Category, id: $"{Identifier}.Publish");
+ public static readonly TimerCounter<MultipathOperationMetadata> UpdateCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Update", Category, id: $"{Identifier}.Update");
+ public static readonly TimerCounter<MultipathOperationMetadata> AddCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Add", Category, id: $"{Identifier}.Add");
+ public static readonly TimerCounter<MoveMetadata> MoveCounter = InstrumentationService.CreateTimerCounter<MoveMetadata> ("Move", Category, id: $"{Identifier}.Move");
+ public static readonly TimerCounter<DeleteMetadata> DeleteCounter = InstrumentationService.CreateTimerCounter<DeleteMetadata> ("Delete", Category, id: $"{Identifier}.Delete");
+ public static readonly TimerCounter<MultipathOperationMetadata> LockCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Lock", Category, id: $"{Identifier}.Lock");
+ public static readonly TimerCounter<MultipathOperationMetadata> UnlockCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Unlock", Category, id: $"{Identifier}.Unlock");
+ public static readonly TimerCounter<MultipathOperationMetadata> IgnoreCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Ignore", Category, id: $"{Identifier}.Ignore");
+ public static readonly TimerCounter<MultipathOperationMetadata> UnignoreCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Unignore", Category, id: $"{Identifier}.Unignore");
+
+ public static readonly TimerCounter<RepositoryMetadata> GetRevisionChangesCounter = InstrumentationService.CreateTimerCounter<RepositoryMetadata> ("Get Revision Changes", Category, id: $"{Identifier}.GetRevisionChanges");
+ public static readonly TimerCounter<RepositoryMetadata> GetHistoryCounter = InstrumentationService.CreateTimerCounter<RepositoryMetadata> ("Get History", Category, id: $"{Identifier}.GetHistory");
+ }
+
+ class MultipathOperationMetadata : RepositoryMetadata
+ {
+ public MultipathOperationMetadata ()
+ {
+ }
+
+ public MultipathOperationMetadata (VersionControlSystem versionControl) : base (versionControl)
+ {
+ }
+
+ public int PathsCount {
+ get => GetProperty<int> ();
+ set => SetProperty (value);
+ }
+
+ public bool Recursive {
+ get => GetProperty<bool> ();
+ set => SetProperty (value);
+ }
+ }
+
+ class RevertMetadata : MultipathOperationMetadata
+ {
+ public enum RevertType
+ {
+ LocalChanges,
+ ToRevision,
+ SpecificRevision
+ };
+
+ public RevertMetadata ()
+ {
+ }
+
+ public RevertMetadata (VersionControlSystem versionControl) : base (versionControl)
+ {
+ }
+
+ public RevertType OperationType {
+ get => GetProperty<RevertType> ();
+ set => SetProperty (value);
+ }
+ }
+
+ class PublishMetadata : MultipathOperationMetadata
+ {
+ public PublishMetadata ()
+ {
+ }
+
+ public PublishMetadata (VersionControlSystem versionControl) : base (versionControl)
+ {
+ }
+
+ public bool SubFolder {
+ get => GetProperty<bool> ();
+ set => SetProperty (value);
+ }
+ }
+
+ class DeleteMetadata : MultipathOperationMetadata
+ {
+ public DeleteMetadata ()
+ {
+ }
+
+ public DeleteMetadata (VersionControlSystem versionControl) : base (versionControl)
+ {
+ }
+
+ public bool Force {
+ get => GetProperty<bool> ();
+ set => SetProperty (value);
+ }
+
+ public bool KeepLocal {
+ get => GetProperty<bool> ();
+ set => SetProperty (value);
+ }
+ }
+
+ class MoveMetadata : RepositoryMetadata
+ {
+ public enum MoveType
+ {
+ File,
+ Directory
+ }
+
+ public MoveMetadata ()
+ {
+ }
+
+ public MoveMetadata (VersionControlSystem versionControl) : base (versionControl)
+ {
+ }
+
+ public bool Force {
+ get => GetProperty<bool> ();
+ set => SetProperty (value);
+ }
+
+ public MoveType OperationType {
+ get => GetProperty<MoveType> ();
+ set => SetProperty (value);
+ }
+ }
+}
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 8359d9a992..8cea552dcb 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs
@@ -468,7 +468,14 @@ namespace MonoDevelop.VersionControl
/// </param>
public RevisionPath[] GetRevisionChanges (Revision revision)
{
- return OnGetRevisionChanges (revision);
+ using (var tracker = Instrumentation.GetRevisionChangesCounter.BeginTiming (new RepositoryMetadata (VersionControlSystem))) {
+ try {
+ return OnGetRevisionChanges (revision);
+ } catch {
+ tracker.Metadata.SetFailure ();
+ throw;
+ }
+ }
}
@@ -478,7 +485,14 @@ namespace MonoDevelop.VersionControl
// Returns the revision history of a file
public Revision[] GetHistory (FilePath localFile, Revision since)
{
- return OnGetHistory (localFile, since);
+ using (var tracker = Instrumentation.GetHistoryCounter.BeginTiming (new RepositoryMetadata (VersionControlSystem))) {
+ try {
+ return OnGetHistory (localFile, since);
+ } catch {
+ tracker.Metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract Revision[] OnGetHistory (FilePath localFile, Revision since);
@@ -505,9 +519,17 @@ namespace MonoDevelop.VersionControl
// repository directory (must use absolute local paths).
public Repository Publish (string serverPath, FilePath localPath, FilePath[] files, string message, ProgressMonitor monitor)
{
- var res = OnPublish (serverPath, localPath, files, message, monitor);
- ClearCachedVersionInfo (localPath);
- return res;
+ var metadata = new PublishMetadata (VersionControlSystem) { PathsCount = files.Length, SubFolder = localPath.IsChildPathOf (RootPath) };
+ using (var tracker = Instrumentation.PublishCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ var res = OnPublish (serverPath, localPath, files, message, monitor);
+ ClearCachedVersionInfo (localPath);
+ return res;
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract Repository OnPublish (string serverPath, FilePath localPath, FilePath[] files, string message, ProgressMonitor monitor);
@@ -521,8 +543,16 @@ namespace MonoDevelop.VersionControl
public void Update (FilePath[] localPaths, bool recurse, ProgressMonitor monitor)
{
- OnUpdate (localPaths, recurse, monitor);
- ClearCachedVersionInfo (localPaths);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Recursive = recurse };
+ using (var tracker = Instrumentation.UpdateCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ OnUpdate (localPaths, recurse, monitor);
+ ClearCachedVersionInfo (localPaths);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnUpdate (FilePath[] localPaths, bool recurse, ProgressMonitor monitor);
@@ -553,8 +583,16 @@ namespace MonoDevelop.VersionControl
// Commits changes in a set of files or directories into the repository
public void Commit (ChangeSet changeSet, ProgressMonitor monitor)
{
- ClearCachedVersionInfo (changeSet.BaseLocalPath);
- OnCommit (changeSet, monitor);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = changeSet.Count };
+ using (var tracker = Instrumentation.CommitCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (changeSet.BaseLocalPath);
+ OnCommit (changeSet, monitor);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnCommit (ChangeSet changeSet, ProgressMonitor monitor);
@@ -567,16 +605,37 @@ namespace MonoDevelop.VersionControl
public void Checkout (FilePath targetLocalPath, Revision rev, bool recurse, ProgressMonitor monitor)
{
- ClearCachedVersionInfo (targetLocalPath);
- OnCheckout (targetLocalPath, rev, recurse, monitor);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { Recursive = recurse };
+ using (var tracker = Instrumentation.CheckoutCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (targetLocalPath);
+ OnCheckout (targetLocalPath, rev, recurse, monitor);
+
+ if (!Directory.Exists (targetLocalPath))
+ metadata.SetFailure ();
+ else
+ metadata.SetSuccess ();
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnCheckout (FilePath targetLocalPath, Revision rev, bool recurse, ProgressMonitor monitor);
public void Revert (FilePath[] localPaths, bool recurse, ProgressMonitor monitor)
{
- ClearCachedVersionInfo (localPaths);
- OnRevert (localPaths, recurse, monitor);
+ var metadata = new RevertMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Recursive = recurse, OperationType = RevertMetadata.RevertType.LocalChanges };
+ using (var tracker = Instrumentation.RevertCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (localPaths);
+ OnRevert (localPaths, recurse, monitor);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
public void Revert (FilePath localPath, bool recurse, ProgressMonitor monitor)
@@ -588,16 +647,32 @@ namespace MonoDevelop.VersionControl
public void RevertRevision (FilePath localPath, Revision revision, ProgressMonitor monitor)
{
- ClearCachedVersionInfo (localPath);
- OnRevertRevision (localPath, revision, monitor);
+ var metadata = new RevertMetadata (VersionControlSystem) { PathsCount = 1, Recursive = true, OperationType = RevertMetadata.RevertType.SpecificRevision };
+ using (var tracker = Instrumentation.RevertCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (localPath);
+ OnRevertRevision (localPath, revision, monitor);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnRevertRevision (FilePath localPath, Revision revision, ProgressMonitor monitor);
public void RevertToRevision (FilePath localPath, Revision revision, ProgressMonitor monitor)
{
- ClearCachedVersionInfo (localPath);
- OnRevertToRevision (localPath, revision, monitor);
+ var metadata = new RevertMetadata (VersionControlSystem) { PathsCount = 1, Recursive = true, OperationType = RevertMetadata.RevertType.ToRevision };
+ using (var tracker = Instrumentation.RevertCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (localPath);
+ OnRevertToRevision (localPath, revision, monitor);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnRevertToRevision (FilePath localPath, Revision revision, ProgressMonitor monitor);
@@ -610,12 +685,17 @@ namespace MonoDevelop.VersionControl
public void Add (FilePath[] localPaths, bool recurse, ProgressMonitor monitor)
{
- try {
- OnAdd (localPaths, recurse, monitor);
- } catch (Exception e) {
- LoggingService.LogError ("Failed to add file", e);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Recursive = recurse };
+ using (var tracker = Instrumentation.AddCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ OnAdd (localPaths, recurse, monitor);
+ } catch (Exception e) {
+ LoggingService.LogError ("Failed to add file", e);
+ metadata.SetFailure ();
+ } finally {
+ ClearCachedVersionInfo (localPaths);
+ }
}
- ClearCachedVersionInfo (localPaths);
}
protected abstract void OnAdd (FilePath[] localPaths, bool recurse, ProgressMonitor monitor);
@@ -635,11 +715,15 @@ namespace MonoDevelop.VersionControl
public void MoveFile (FilePath localSrcPath, FilePath localDestPath, bool force, ProgressMonitor monitor)
{
ClearCachedVersionInfo (localSrcPath, localDestPath);
- try {
- OnMoveFile (localSrcPath, localDestPath, force, monitor);
- } catch (Exception e) {
- LoggingService.LogError ("Failed to move file", e);
- File.Move (localSrcPath, localDestPath);
+ var metadata = new MoveMetadata (VersionControlSystem) { Force = force, OperationType = MoveMetadata.MoveType.File };
+ using (var tracker = Instrumentation.MoveCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ OnMoveFile (localSrcPath, localDestPath, force, monitor);
+ } catch (Exception e) {
+ LoggingService.LogError ("Failed to move file", e);
+ metadata.SetFailure ();
+ File.Move (localSrcPath, localDestPath);
+ }
}
}
@@ -653,11 +737,15 @@ namespace MonoDevelop.VersionControl
public void MoveDirectory (FilePath localSrcPath, FilePath localDestPath, bool force, ProgressMonitor monitor)
{
ClearCachedVersionInfo (localSrcPath, localDestPath);
- try {
- OnMoveDirectory (localSrcPath, localDestPath, force, monitor);
- } catch (Exception e) {
- LoggingService.LogError ("Failed to move directory", e);
- FileService.SystemDirectoryRename (localSrcPath, localDestPath);
+ var metadata = new MoveMetadata (VersionControlSystem) { Force = force, OperationType = MoveMetadata.MoveType.Directory };
+ using (var tracker = Instrumentation.MoveCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ OnMoveDirectory (localSrcPath, localDestPath, force, monitor);
+ } catch (Exception e) {
+ LoggingService.LogError ("Failed to move directory", e);
+ metadata.SetFailure ();
+ FileService.SystemDirectoryRename (localSrcPath, localDestPath);
+ }
}
}
@@ -675,13 +763,17 @@ namespace MonoDevelop.VersionControl
public void DeleteFiles (FilePath[] localPaths, bool force, ProgressMonitor monitor, bool keepLocal = true)
{
- try {
- OnDeleteFiles (localPaths, force, monitor, keepLocal);
- } catch (Exception e) {
- LoggingService.LogError ("Failed to delete file", e);
- if (!keepLocal)
- foreach (var path in localPaths)
- File.Delete (path);
+ var metadata = new DeleteMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Force = force, KeepLocal = keepLocal };
+ using (var tracker = Instrumentation.DeleteCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ OnDeleteFiles (localPaths, force, monitor, keepLocal);
+ } catch (Exception e) {
+ LoggingService.LogError ("Failed to delete file", e);
+ metadata.SetFailure ();
+ if (!keepLocal)
+ foreach (var path in localPaths)
+ File.Delete (path);
+ }
}
ClearCachedVersionInfo (localPaths);
}
@@ -695,13 +787,17 @@ namespace MonoDevelop.VersionControl
public void DeleteDirectories (FilePath[] localPaths, bool force, ProgressMonitor monitor, bool keepLocal = true)
{
- try {
- OnDeleteDirectories (localPaths, force, monitor, keepLocal);
- } catch (Exception e) {
- LoggingService.LogError ("Failed to delete directory", e);
- if (!keepLocal)
- foreach (var path in localPaths)
- Directory.Delete (path, true);
+ var metadata = new DeleteMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Force = force, KeepLocal = keepLocal };
+ using (var tracker = Instrumentation.DeleteCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ OnDeleteDirectories (localPaths, force, monitor, keepLocal);
+ } catch (Exception e) {
+ LoggingService.LogError ("Failed to delete directory", e);
+ metadata.SetFailure ();
+ if (!keepLocal)
+ foreach (var path in localPaths)
+ Directory.Delete (path, true);
+ }
}
ClearCachedVersionInfo (localPaths);
}
@@ -733,8 +829,16 @@ namespace MonoDevelop.VersionControl
// Locks a file in the repository so no other users can change it
public void Lock (ProgressMonitor monitor, params FilePath[] localPaths)
{
- ClearCachedVersionInfo (localPaths);
- OnLock (monitor, localPaths);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length };
+ using (var tracker = Instrumentation.LockCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (localPaths);
+ OnLock (monitor, localPaths);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
// Locks a file in the repository so no other users can change it
@@ -746,8 +850,16 @@ namespace MonoDevelop.VersionControl
// Unlocks a file in the repository so other users can change it
public void Unlock (ProgressMonitor monitor, params FilePath[] localPaths)
{
- ClearCachedVersionInfo (localPaths);
- OnUnlock (monitor, localPaths);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length };
+ using (var tracker = Instrumentation.UnlockCounter.BeginTiming (metadata, monitor.CancellationToken)) {
+ try {
+ ClearCachedVersionInfo (localPaths);
+ OnUnlock (monitor, localPaths);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected virtual void OnUnlock (ProgressMonitor monitor, params FilePath[] localPaths)
@@ -882,8 +994,16 @@ namespace MonoDevelop.VersionControl
// Ignores a file for version control operations.
public void Ignore (FilePath[] localPath)
{
- ClearCachedVersionInfo (localPath);
- OnIgnore (localPath);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPath.Length };
+ using (var tracker = Instrumentation.IgnoreCounter.BeginTiming (metadata)) {
+ try {
+ ClearCachedVersionInfo (localPath);
+ OnIgnore (localPath);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnIgnore (FilePath[] localPath);
@@ -891,8 +1011,16 @@ namespace MonoDevelop.VersionControl
// Unignores a file for version control operations.
public void Unignore (FilePath[] localPath)
{
- ClearCachedVersionInfo (localPath);
- OnUnignore (localPath);
+ var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPath.Length };
+ using (var tracker = Instrumentation.UnignoreCounter.BeginTiming (metadata)) {
+ try {
+ ClearCachedVersionInfo (localPath);
+ OnUnignore (localPath);
+ } catch {
+ metadata.SetFailure ();
+ throw;
+ }
+ }
}
protected abstract void OnUnignore (FilePath[] localPath);
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/UrlBasedRepositoryEditor.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/UrlBasedRepositoryEditor.cs
index 45cefe96fa..33c295cfc0 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/UrlBasedRepositoryEditor.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/UrlBasedRepositoryEditor.cs
@@ -11,6 +11,7 @@ namespace MonoDevelop.VersionControl
{
UrlBasedRepository repo;
public event EventHandler<EventArgs> PathChanged;
+ public event EventHandler<EventArgs> UrlChanged;
bool updating;
List<string> protocols = new List<string> ();
@@ -53,6 +54,10 @@ namespace MonoDevelop.VersionControl
get { return repositoryPathEntry.Text; }
}
+ public string RepositoryServer {
+ get { return repositoryServerEntry.Text; }
+ }
+
bool ParseSSHUrl (string url)
{
if (!url.Contains (':'))
@@ -81,6 +86,7 @@ namespace MonoDevelop.VersionControl
comboProtocol.Active = protocols.IndexOf ("ssh");
comboProtocol.Sensitive = false;
PathChanged?.Invoke (this, EventArgs.Empty);
+ UrlChanged?.Invoke (this, EventArgs.Empty);
return true;
}
@@ -133,6 +139,7 @@ namespace MonoDevelop.VersionControl
repo.Name = repo.Uri.Host;
}
updating = false;
+ UrlChanged?.Invoke (this, EventArgs.Empty);
}
void UpdateControls ()
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 6f43ccae81..aa25a066f2 100644
--- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs
@@ -226,7 +226,6 @@ namespace MonoDevelop.VersionControl
return repo;
}
- readonly static Counter<RepositoryMetadata> Repositories = InstrumentationService.CreateCounter<RepositoryMetadata> ("VersionControl.RepositoryOpened", "Version Control", id:"VersionControl.RepositoryOpened");
internal static readonly Dictionary<FilePath,Repository> repositoryCache = new Dictionary<FilePath,Repository> ();
public static Repository GetRepositoryReference (string path, string id)
{
@@ -257,10 +256,7 @@ namespace MonoDevelop.VersionControl
var repo = detectedVCS?.GetRepositoryReference (bestMatch, id);
if (repo != null) {
repositoryCache.Add (bestMatch, repo);
- Repositories.Inc (new RepositoryMetadata {
- Type = detectedVCS.Name,
- Version = detectedVCS.Version,
- });
+ Instrumentation.Repositories.Inc (new RepositoryMetadata (detectedVCS));
}
return repo;
} catch (Exception e) {
@@ -841,10 +837,17 @@ namespace MonoDevelop.VersionControl
Other
}
- public class RepositoryMetadata : CounterMetadata
+ class RepositoryMetadata : CounterMetadata
{
public RepositoryMetadata ()
{
+ throw new InvalidOperationException ();
+ }
+
+ public RepositoryMetadata (VersionControlSystem versionControl)
+ {
+ Type = versionControl?.Name;
+ Version = versionControl?.Version;
}
public string Type {
diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config b/main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config
new file mode 100644
index 0000000000..f3aeebbbc7
--- /dev/null
+++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Humanizer.Core" version="2.2.0" targetFramework="net471" />
+</packages> \ No newline at end of file
diff --git a/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs b/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs
index 0ecc94b5a9..27562c546d 100644
--- a/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs
+++ b/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs
@@ -66,17 +66,6 @@ namespace MonoDevelop.Xml.Completion
}
/// <summary>
- /// Creates completion data from the schema passed in
- /// via the reader object.
- /// </summary>
- [Obsolete ("Please pass in a TextReader instead")]
- public XmlSchemaCompletionData(XmlTextReader reader)
- {
- reader.XmlResolver = new LocalOnlyXmlResolver ();
- ReadSchema(reader);
- }
-
- /// <summary>
/// Creates the completion data from the specified schema file.
/// </summary>
public XmlSchemaCompletionData (string fileName) : this (String.Empty, fileName)
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/ClipboardColoredText.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/ClipboardColoredText.cs
index 4ecfda0278..1115115d51 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/ClipboardColoredText.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/ClipboardColoredText.cs
@@ -34,6 +34,7 @@ using MonoDevelop.Core.Text;
using MonoDevelop.Ide.Editor.Highlighting;
using System.Collections.Immutable;
using System.Threading.Tasks;
+using System.Threading;
namespace Mono.TextEditor.Utils
{
@@ -48,7 +49,7 @@ namespace Mono.TextEditor.Utils
this.Text = doc.GetTextAt (chunk);
}
- public static async Task<List<List<ClipboardColoredText>>> GetChunks (TextEditorData data, ISegment selectedSegment)
+ public static async Task<List<List<ClipboardColoredText>>> GetChunks (TextEditorData data, ISegment selectedSegment, CancellationToken cancellationToken = default)
{
int startLineNumber = data.OffsetToLineNumber (selectedSegment.Offset);
int endLineNumber = data.OffsetToLineNumber (selectedSegment.EndOffset);
@@ -59,7 +60,8 @@ namespace Mono.TextEditor.Utils
var chunks = await data.GetChunks (
line,
offset,
- length
+ length,
+ cancellationToken
);
copiedColoredChunks.Add (
chunks
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs
index 6f121254c2..113f6992ac 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs
@@ -29,6 +29,7 @@ using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
using MonoDevelop.Ide.Editor;
+using MonoDevelop.Core;
namespace Mono.TextEditor
{
@@ -116,8 +117,14 @@ namespace Mono.TextEditor
insertionPoint = vsp;
if (args.CaretChangeReason == CaretChangeReason.Movement) {
oldCaretLocation = args.Location;
- var oldOffset = TextEditor.LocationToOffset (args.Location);
- var snapshotPoint = new SnapshotPoint (TextEditor.TextSnapshot, oldOffset);
+ var snapShot = args.Snapshot;
+ var snapshotLine = snapShot.GetLineFromLineNumber (args.Location.Line - 1);
+ if (snapshotLine == null) {
+ LoggingService.LogError ("PositionChanged_ITextCaret line number : " + args.Location.Line + " is out of range.");
+ return;
+ }
+ var oldOffset = snapshotLine.Start.Position + Math.Min (snapshotLine.Length, args.Location.Column - 1);
+ var snapshotPoint = new SnapshotPoint (snapShot, oldOffset);
var mappingPoint = TextEditor.BufferGraph.CreateMappingPoint (snapshotPoint, PointTrackingMode.Positive);
var oldCaretPosition = new CaretPosition (vsp, mappingPoint, _caretAffinity);
var eventArgs = new CaretPositionChangedEventArgs (TextEditor, oldCaretPosition, ((ITextCaret)this).Position);
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs
index 9822b5a151..7ad29ede3d 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs
@@ -39,7 +39,9 @@ namespace Mono.TextEditor
bool autoScrollToCaret = true;
CaretMode mode;
-
+
+ ITextSnapshot currentBuffer;
+
int line = DocumentLocation.MinLine;
public override int Line {
get {
@@ -54,7 +56,7 @@ namespace Mono.TextEditor
CheckLine ();
SetColumn ();
UpdateCaretOffset ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.Movement));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.Movement));
}
}
}
@@ -73,7 +75,7 @@ namespace Mono.TextEditor
CheckColumn ();
SetDesiredColumn ();
UpdateCaretOffset ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.Movement));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.Movement));
}
}
}
@@ -93,7 +95,7 @@ namespace Mono.TextEditor
CheckColumn ();
SetDesiredColumn ();
UpdateCaretOffset ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.Movement));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.Movement));
}
}
}
@@ -119,7 +121,7 @@ namespace Mono.TextEditor
CheckLine ();
CheckColumn ();
SetDesiredColumn ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.Movement));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.Movement));
}
}
@@ -270,7 +272,7 @@ namespace Mono.TextEditor
}
UpdateCaretOffset ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.Movement));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.Movement));
}
void SetDesiredColumn ()
@@ -301,7 +303,7 @@ namespace Mono.TextEditor
var old = Location;
DesiredColumn = desiredColumn;
SetColumn ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.Movement));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.Movement));
}
public override string ToString ()
@@ -330,6 +332,7 @@ namespace Mono.TextEditor
TextEditorData.Document.EnsureOffsetIsUnfolded (Offset);
base.OnPositionChanged (args);
PositionChanged_ITextCaret (args);
+ currentBuffer = TextEditorData.Document.TextBuffer.CurrentSnapshot;
}
protected virtual void OnModeChanged ()
@@ -363,8 +366,9 @@ namespace Mono.TextEditor
//}
var curVersion = TextEditorData.Version;
var newOffset = e.GetNewOffset (caretOffset);
- if (newOffset == caretOffset || !AutoUpdatePosition)
+ if (!AutoUpdatePosition)
return;
+
DocumentLocation old = Location;
var newLocation = TextEditorData.OffsetToLocation (newOffset);
int newColumn = newLocation.Column;
@@ -385,7 +389,7 @@ namespace Mono.TextEditor
SetDesiredColumn ();
UpdateCaretOffset ();
- OnPositionChanged (new CaretLocationEventArgs (old, CaretChangeReason.BufferChange));
+ OnPositionChanged (new CaretLocationEventArgs (old, currentBuffer ?? TextEditorData.Document.TextBuffer.CurrentSnapshot, CaretChangeReason.BufferChange));
}
public void SetDocument (TextDocument doc)
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs
index 2b4946c069..c801947809 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs
@@ -1,4 +1,4 @@
-//
+//
// TextDocument.cs
//
// Author:
@@ -282,7 +282,6 @@ namespace Mono.TextEditor
}
var textChange = new TextChangeEventArgs(changes);
- InterruptFoldWorker();
TextChanging?.Invoke(this, textChange);
// After TextChanging notification has been sent, we can update the cached snapshot
this.currentSnapshot = args.After;
@@ -1371,10 +1370,6 @@ namespace Mono.TextEditor
void RemoveFolding (FoldSegment folding)
{
folding.isAttached = false;
- if (folding.isFolded) {
- foldedSegments.Remove (folding);
- CommitUpdateAll ();
- }
foldSegmentTree.Remove (folding);
}
@@ -1386,7 +1381,7 @@ namespace Mono.TextEditor
{
var oldSegments = new List<FoldSegment> (FoldSegments);
int oldIndex = 0;
- bool foldedSegmentAdded = false;
+ bool foldedSegmentAdded = false, foldedFoldingRemoved = false;
var newSegments = segments.ToList ();
newSegments.Sort ();
var newFoldedSegments = new HashSet<FoldSegment> ();
@@ -1399,6 +1394,7 @@ namespace Mono.TextEditor
int offset = newFoldSegment.Offset;
while (oldIndex < oldSegments.Count && offset > oldSegments [oldIndex].Offset) {
RemoveFolding (oldSegments [oldIndex]);
+ foldedFoldingRemoved |= oldSegments [oldIndex].IsCollapsed;
oldIndex++;
}
if (oldIndex < oldSegments.Count && offset == oldSegments [oldIndex].Offset) {
@@ -1432,33 +1428,19 @@ namespace Mono.TextEditor
return null;
}
RemoveFolding (oldSegments [oldIndex]);
+ foldedFoldingRemoved |= oldSegments [oldIndex].IsCollapsed;
oldIndex++;
}
bool countChanged = foldedSegments.Count != newFoldedSegments.Count;
- update = foldedSegmentAdded || countChanged;
+ update = foldedSegmentAdded || countChanged || foldedFoldingRemoved;
return newFoldedSegments;
}
- public void WaitForFoldUpdateFinished ()
- {
- if (foldSegmentTask != null) {
- try {
- foldSegmentTask.Wait (5000);
- } catch (AggregateException e) {
- e.Flatten ().Handle (x => x is OperationCanceledException);
- } catch (OperationCanceledException) {
-
- }
- foldSegmentTask = null;
- }
- }
-
internal void InterruptFoldWorker ()
{
if (foldSegmentSrc == null)
return;
foldSegmentSrc.Cancel ();
- WaitForFoldUpdateFinished ();
foldSegmentSrc = null;
}
@@ -1742,6 +1724,7 @@ namespace Mono.TextEditor
OnHeightChanged (EventArgs.Empty);
}
}
+ marker.parent = null;
if (updateLine)
this.CommitLineUpdate (line);
}
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Styles.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Styles.cs
index 6fe1fc0ac6..7de9bbeea7 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Styles.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Styles.cs
@@ -55,7 +55,7 @@ namespace Mono.TextEditor.PopupWindow
public static void LoadStyles ()
{
- var bgColor = Platform.IsMac ? Color.FromName ("#5189ed") : Color.FromName ("#cce8ff");
+ var bgColor = Platform.IsMac ? Color.FromName ("#2862d9") : Color.FromName ("#cce8ff");
var fgColor = Platform.IsMac ? Color.FromName ("#ffffff") : Color.FromName ("#000000");
ModeHelpWindowTokenOutlineColor = fgColor;
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs
index e669554bee..2accdab270 100644
--- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs
+++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs
@@ -1,4 +1,4 @@
-// TextEditorData.cs
+// TextEditorData.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
@@ -413,6 +413,11 @@ namespace Mono.TextEditor
public string GetMarkup (int offset, int length, bool removeIndent, bool useColors = true, bool replaceTabs = true, bool fitIdeStyle = false)
{
+ return GetMarkupAsync (offset, length, removeIndent, useColors, replaceTabs, fitIdeStyle).WaitAndGetResult (default (CancellationToken));
+ }
+
+ public async Task<string> GetMarkupAsync (int offset, int length, bool removeIndent, bool useColors = true, bool replaceTabs = true, bool fitIdeStyle = false, CancellationToken cancellationToken = default)
+ {
var doc = Document;
var mode = doc.SyntaxMode;
var style = fitIdeStyle ? SyntaxHighlightingService.GetEditorTheme (Parent.GetIdeColorStyleName ()) : ColorStyle;
@@ -441,7 +446,7 @@ namespace Mono.TextEditor
}
}
- foreach (var chunk in GetChunks (line, curOffset, toOffset - curOffset).WaitAndGetResult (default (CancellationToken))) {
+ foreach (var chunk in await GetChunks (line, curOffset, toOffset - curOffset, cancellationToken)) {
if (chunk.Length == 0)
continue;
var chunkStyle = style.GetChunkStyle (chunk.ScopeStack);
@@ -486,14 +491,30 @@ namespace Mono.TextEditor
return StringBuilderCache.ReturnAndFree (result);
}
- internal async Task<IEnumerable<MonoDevelop.Ide.Editor.Highlighting.ColoredSegment>> GetChunks (DocumentLine line, int offset, int length)
+ internal async Task<IEnumerable<MonoDevelop.Ide.Editor.Highlighting.ColoredSegment>> GetChunks (DocumentLine line, int offset, int length, CancellationToken cancellationToken = default)
{
- var lineOffset = line.Offset;
- return TextViewMargin.TrimChunks (
- (await document.SyntaxMode.GetHighlightedLineAsync (line, CancellationToken.None).ConfigureAwait(false))
- .Segments
- .Select (c => c.WithOffset (c.Offset + lineOffset))
- .ToList (), offset, length);
+ if (line == null)
+ throw new ArgumentNullException (nameof (line));
+ if (document == null)
+ throw new InvalidOperationException ("TextEditorData was disposed.");
+ Runtime.AssertMainThread ();
+ try {
+ var lineOffset = line.Offset;
+ var syntaxMode = document.SyntaxMode;
+ var highlightedLine = await syntaxMode.GetHighlightedLineAsync (line, cancellationToken).ConfigureAwait (false);
+ var result = new List<MonoDevelop.Ide.Editor.Highlighting.ColoredSegment> (highlightedLine.Segments.Count);
+ foreach (var segment in highlightedLine.Segments) {
+ if (segment == null) {
+ LoggingService.LogInternalError (new InvalidOperationException ("Segment== null insede highlighed line " + highlightedLine));
+ continue;
+ }
+ result.Add (segment.WithOffset (segment.Offset + lineOffset));
+ }
+ return TextViewMargin.TrimChunks (result, offset, length);
+ } catch (Exception e) {
+ LoggingService.LogInternalError ("Error while getting chunks.", e);
+ return new [] { new MonoDevelop.Ide.Editor.Highlighting.ColoredSegment (offset, length, ScopeStack.Empty) };
+ }
}
public int Insert (int offset, string value)
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs
new file mode 100644
index 0000000000..bcb83e52a9
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs
@@ -0,0 +1,103 @@
+//
+// AssemblyUtilities.cs
+//
+// Author:
+// Mike Krüger <mikkrg@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft Corporation. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Threading;
+using System.IO;
+using System.Xml;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using MonoDevelop.Core;
+using MonoDevelop.Core.Execution;
+using MonoDevelop.Core.AddIns;
+using MonoDevelop.Core.Serialization;
+using Mono.Addins;
+using Mono.PkgConfig;
+using System.Reflection.PortableExecutable;
+using System.Reflection.Metadata;
+
+namespace MonoDevelop.Core.Assemblies
+{
+ public static class AssemblyUtilities
+ {
+ static bool Is64BitPE (Machine machine)
+ {
+ return machine == Machine.Amd64 || machine == Machine.Arm64 || machine == Machine.IA64 || machine == Machine.Alpha64;
+ }
+
+ static bool TryReadPEHeaders (string assemblyPath, out PortableExecutableKinds peKind, out Machine machine)
+ {
+ peKind = default;
+ machine = default;
+
+ try {
+ if (!File.Exists (assemblyPath))
+ return false;
+
+ using (var reader = new PEReader (File.OpenRead (assemblyPath))) {
+ var peHeaders = reader.PEHeaders;
+
+ var corFlags = peHeaders.CorHeader.Flags;
+ if ((corFlags & CorFlags.ILOnly) != 0)
+ peKind |= PortableExecutableKinds.ILOnly;
+
+ if ((corFlags & CorFlags.Prefers32Bit) != 0)
+ peKind |= PortableExecutableKinds.Preferred32Bit;
+ else if ((corFlags & CorFlags.Requires32Bit) != 0)
+ peKind |= PortableExecutableKinds.Required32Bit;
+
+ if (peHeaders.PEHeader.Magic == PEMagic.PE32Plus)
+ peKind |= PortableExecutableKinds.PE32Plus;
+
+ machine = peHeaders.CoffHeader.Machine;
+ }
+
+ return true;
+ } catch (Exception e) {
+ LoggingService.LogError ("Error while determining 64/32 bit assembly.", e);
+ return false;
+ }
+ }
+
+ public static ProcessExecutionArchitecture GetProcessExecutionArchitectureForAssembly (string assemblyPath)
+ {
+ if (string.IsNullOrEmpty (assemblyPath))
+ throw new ArgumentNullException (nameof (assemblyPath));
+
+ if (TryReadPEHeaders (assemblyPath, out var peKind, out var machine)) {
+ if ((peKind & (PortableExecutableKinds.Preferred32Bit | PortableExecutableKinds.Required32Bit)) != 0)
+ return ProcessExecutionArchitecture.X86;
+
+ if ((peKind & PortableExecutableKinds.PE32Plus) != 0 || Is64BitPE (machine))
+ return ProcessExecutionArchitecture.X64;
+ }
+
+ return ProcessExecutionArchitecture.Unspecified;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs
index 57c6ab4c57..67c332cc6d 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs
@@ -294,13 +294,6 @@ namespace MonoDevelop.Core.Assemblies
return new ExecutionEnvironment (EnvironmentVariables);
}
- static bool Is64BitPE (Mono.Cecil.TargetArchitecture machine)
- {
- return machine == Mono.Cecil.TargetArchitecture.AMD64 ||
- machine == Mono.Cecil.TargetArchitecture.IA64 ||
- machine == Mono.Cecil.TargetArchitecture.ARM64;
- }
-
/// <summary>
/// Get the Mono executable best matching the assembly architecture flags.
/// </summary>
@@ -309,35 +302,30 @@ namespace MonoDevelop.Core.Assemblies
/// <param name="assemblyPath">Assembly path.</param>
public string GetMonoExecutableForAssembly (string assemblyPath)
{
- Mono.Cecil.ModuleAttributes peKind;
- Mono.Cecil.TargetArchitecture machine;
-
- try {
- using (var adef = Mono.Cecil.AssemblyDefinition.ReadAssembly (assemblyPath)) {
- peKind = adef.MainModule.Attributes;
- machine = adef.MainModule.Architecture;
- }
- } catch {
- peKind = Mono.Cecil.ModuleAttributes.ILOnly;
- machine = Mono.Cecil.TargetArchitecture.I386;
- }
+ return GetMonoExecutable (AssemblyUtilities.GetProcessExecutionArchitectureForAssembly (assemblyPath));
+ }
+ internal string GetMonoExecutable (ProcessExecutionArchitecture use64Bit)
+ {
string monoPath;
-
- if ((peKind & (Mono.Cecil.ModuleAttributes.Required32Bit | Mono.Cecil.ModuleAttributes.Preferred32Bit)) != 0) {
- monoPath = Path.Combine (MonoRuntimeInfo.Prefix, "bin", "mono32");
+ switch (use64Bit) {
+ case ProcessExecutionArchitecture.X64:
+ monoPath = Path.Combine (MonoRuntimeInfo.Prefix, "bin", "mono64");
if (File.Exists (monoPath))
return monoPath;
- } else if (Is64BitPE (machine)) {
- monoPath = Path.Combine (MonoRuntimeInfo.Prefix, "bin", "mono64");
+ break;
+ case ProcessExecutionArchitecture.X86:
+ monoPath = Path.Combine (MonoRuntimeInfo.Prefix, "bin", "mono32");
if (File.Exists (monoPath))
return monoPath;
+ break;
}
return monoPath = Path.Combine (MonoRuntimeInfo.Prefix, "bin", "mono");
}
+
}
-
+
class PcFileCacheContext: Mono.PkgConfig.IPcFileCacheContext<LibraryPackageInfo>
{
public void ReportError (string message, System.Exception ex)
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs
index 4ec62572f9..3d42b374db 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs
@@ -30,12 +30,14 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
using System.Threading;
using Mono.Addins;
-using Mono.Cecil;
using MonoDevelop.Core.AddIns;
namespace MonoDevelop.Core.Assemblies
@@ -369,29 +371,54 @@ namespace MonoDevelop.Core.Assemblies
{
if (!File.Exists (file))
return TargetFrameworkMoniker.UNKNOWN;
- AssemblyDefinition assembly = null;
+
try {
- assembly = AssemblyDefinition.ReadAssembly (file);
- var att = assembly.CustomAttributes.FirstOrDefault (a =>
- a.AttributeType.FullName == "System.Runtime.Versioning.TargetFrameworkAttribute"
- );
- if (att != null) {
- if (att.ConstructorArguments.Count == 1) {
- var v = att.ConstructorArguments[0].Value as string;
- TargetFrameworkMoniker m;
- if (v != null && TargetFrameworkMoniker.TryParse (v, out m)) {
+ using (var reader = new PEReader (File.OpenRead (file))) {
+ var mr = reader.GetMetadataReader ();
+
+ foreach (var customAttributeHandle in mr.GetAssemblyDefinition ().GetCustomAttributes ()) {
+ var customAttribute = mr.GetCustomAttribute (customAttributeHandle);
+
+ var ctorHandle = customAttribute.Constructor;
+ if (ctorHandle.Kind != HandleKind.MemberReference)
+ continue;
+
+ var ctor = mr.GetMemberReference ((MemberReferenceHandle)ctorHandle);
+ var attrType = mr.GetTypeReference ((TypeReferenceHandle)ctor.Parent);
+
+ var ns = mr.GetString (attrType.Namespace);
+ if (ns != "System.Runtime.Versioning")
+ continue;
+
+ var typeName = mr.GetString (attrType.Name);
+ if (typeName != "TargetFrameworkAttribute")
+ continue;
+
+ var provider = new StringParameterValueTypeProvider (mr, customAttribute.Value);
+ var signature = ctor.DecodeMethodSignature (provider, null);
+ var parameterTypes = signature.ParameterTypes;
+ if (parameterTypes.Length != 1)
+ continue;
+
+ var value = parameterTypes [0];
+ if (value != null && TargetFrameworkMoniker.TryParse (value, out var m)) {
return m;
}
+ LoggingService.LogError ("Invalid TargetFrameworkAttribute in assembly {0} - {1}", file, value);
}
- LoggingService.LogError ("Invalid TargetFrameworkAttribute in assembly {0}", file);
- }
- if (tr != null) {
- foreach (var r in assembly.MainModule.AssemblyReferences) {
- if (r.Name == "mscorlib") {
+
+ if (tr != null) {
+ foreach (var assemblyReferenceHandle in mr.AssemblyReferences) {
+ var assemblyReference = mr.GetAssemblyReference (assemblyReferenceHandle);
+
+ var name = mr.GetString (assemblyReference.Name);
+ if (name != "mscorlib")
+ continue;
+
TargetFramework compatibleFramework = null;
// If there are several frameworks that can run the file, pick one that is installed
foreach (TargetFramework tf in GetKnownFrameworks ()) {
- if (tf.GetCorlibVersion () == r.Version.ToString ()) {
+ if (tf.GetCorlibVersion () == assemblyReference.Version.ToString ()) {
compatibleFramework = tf;
if (tr.IsInstalled (tf))
return tf.Id;
@@ -405,125 +432,66 @@ namespace MonoDevelop.Core.Assemblies
}
} catch (Exception ex) {
LoggingService.LogError ("Error determining target framework for assembly {0}: {1}", file, ex);
- return TargetFrameworkMoniker.UNKNOWN;
- } finally {
- assembly?.Dispose ();
}
- LoggingService.LogError ("Failed to determine target framework for assembly {0}", file);
return TargetFrameworkMoniker.UNKNOWN;
}
/// <summary>
/// Simply get all assembly reference names from an assembly given it's file name.
/// </summary>
- public static IEnumerable<string> GetAssemblyReferences (string fileName)
- {
- AssemblyDefinition assembly = null;
- try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- return Enumerable.Empty<string> ();
- }
- return assembly.MainModule.AssemblyReferences.Select (x => x.Name);
- } finally {
- assembly?.Dispose ();
- }
- }
+ public static ImmutableArray<string> GetAssemblyReferences (string fileName)
+ {
+ try {
+ using (var reader = new PEReader (File.OpenRead (fileName))) {
+ var mr = reader.GetMetadataReader ();
+ var assemblyReferences = mr.AssemblyReferences;
- static Dictionary<string, bool> referenceDict = new Dictionary<string, bool> ();
+ var builder = ImmutableArray.CreateBuilder<string> (assemblyReferences.Count);
- static bool ContainsReferenceToSystemRuntimeInternal (string fileName)
- {
- bool result;
- if (referenceDict.TryGetValue (fileName, out result))
- return result;
-
- //const int cacheLimit = 4096;
- //if (referenceDict.Count > cacheLimit)
- // referenceDict = ImmutableDictionary<string, bool>.Empty
-
- AssemblyDefinition assembly = null;
- try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- return false;
- }
- foreach (var r in assembly.MainModule.AssemblyReferences) {
- // Don't compare the version number since it may change depending on the version of .net standard
- if (r.Name.Equals ("System.Runtime")) {
- referenceDict [fileName] = true; ;
- return true;
- }
- }
- } finally {
- assembly?.Dispose ();
- }
- referenceDict [fileName] = false;
- return false;
+ foreach (var assemblyReferenceHandle in assemblyReferences) {
+ var assemblyReference = mr.GetAssemblyReference (assemblyReferenceHandle);
+ builder.Add (mr.GetString (assemblyReference.Name));
+ }
+
+ return builder.MoveToImmutable();
+ }
+ } catch {
+ return ImmutableArray<string>.Empty;
+ }
}
static Dictionary<string, bool> facadeReferenceDict = new Dictionary<string, bool> ();
static bool RequiresFacadeAssembliesInternal (string fileName)
{
- bool result;
- if (facadeReferenceDict.TryGetValue (fileName, out result))
+ if (facadeReferenceDict.TryGetValue (fileName, out var result))
return result;
- AssemblyDefinition assembly = null;
try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- return false;
- }
- foreach (var r in assembly.MainModule.AssemblyReferences) {
- // Don't compare the version number since it may change depending on the version of .net standard
- if (r.Name.Equals ("System.Runtime") || r.Name.Equals ("netstandard")) {
- facadeReferenceDict [fileName] = true; ;
- return true;
+ using (var reader = new PEReader (File.OpenRead (fileName))) {
+ var mr = reader.GetMetadataReader ();
+
+ foreach (var assemblyReferenceHandle in mr.AssemblyReferences) {
+ var assemblyReference = mr.GetAssemblyReference (assemblyReferenceHandle);
+ var name = mr.GetString (assemblyReference.Name);
+
+ // Don't compare the version number since it may change depending on the version of .net standard
+ if (name.Equals ("System.Runtime") || name.Equals ("netstandard")) {
+ facadeReferenceDict [fileName] = true;
+ return true;
+ }
}
}
- } finally {
- assembly?.Dispose ();
+ } catch {
+ return false;
}
+
facadeReferenceDict [fileName] = false;
return false;
}
- static object referenceLock = new object ();
-
- [Obsolete ("Use RequiresFacadeAssemblies (string fileName)")]
- public static bool ContainsReferenceToSystemRuntime (string fileName)
- {
- lock (referenceLock) {
- return ContainsReferenceToSystemRuntimeInternal (fileName);
- }
- }
-
- static SemaphoreSlim referenceLockAsync = new SemaphoreSlim (1, 1);
-
- [Obsolete ("Use RequiresFacadeAssembliesAsync (string fileName)")]
- public static async System.Threading.Tasks.Task<bool> ContainsReferenceToSystemRuntimeAsync (string filename)
- {
- try {
- await referenceLockAsync.WaitAsync ().ConfigureAwait (false);
- return ContainsReferenceToSystemRuntimeInternal (filename);
- } finally {
- referenceLockAsync.Release ();
- }
- }
-
- internal static bool RequiresFacadeAssemblies (string fileName)
- {
- lock (referenceLock) {
- return RequiresFacadeAssembliesInternal (fileName);
- }
- }
-
- internal static async System.Threading.Tasks.Task<bool> RequiresFacadeAssembliesAsync (string filename)
+ static readonly SemaphoreSlim referenceLockAsync = new SemaphoreSlim (1, 1);
+ public static async System.Threading.Tasks.Task<bool> RequiresFacadeAssembliesAsync (string filename)
{
try {
await referenceLockAsync.WaitAsync ().ConfigureAwait (false);
@@ -556,25 +524,51 @@ namespace MonoDevelop.Core.Assemblies
/// Simply get all assembly manifest resources from an assembly given it's file name.
/// </summary>
public static IEnumerable<ManifestResource> GetAssemblyManifestResources (string fileName)
- {
- AssemblyDefinition assembly = null;
- try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- yield break;
- }
- foreach (var r in assembly.MainModule.Resources) {
- if (r.ResourceType == ResourceType.Embedded) {
- var er = (EmbeddedResource)r;
-
- // Explicitly create a capture and query it here so the stream isn't queried after the module is disposed.
- var rs = er.GetResourceStream ();
- yield return new ManifestResource (er.Name, () => rs);
- }
- }
- } finally {
- assembly?.Dispose ();
+ {
+ using (var reader = new PEReader (File.OpenRead (fileName))) {
+ var mr = reader.GetMetadataReader ();
+
+ var headers = reader.PEHeaders;
+ var resources = headers.CorHeader.ResourcesDirectory;
+ var sectionData = reader.GetSectionData (resources.RelativeVirtualAddress);
+ if (sectionData.Length == 0)
+ return Array.Empty<ManifestResource> (); // RVA could not be found in any section
+
+ var sectionReader = sectionData.GetReader ();
+ var manifestResources = mr.ManifestResources;
+ var result = new List<ManifestResource> (manifestResources.Count);
+
+ foreach (var manifestResourceHandle in manifestResources) {
+ var manifestResource = mr.GetManifestResource (manifestResourceHandle);
+
+ // This means the type is Embedded.
+ var isEmbeddedResource = manifestResource.Implementation.IsNil;
+ if (!isEmbeddedResource)
+ continue;
+
+ int offset = (int)manifestResource.Offset;
+ sectionReader.Offset += offset;
+ try {
+ int length = sectionReader.ReadInt32 ();
+ if ((uint)length > sectionReader.RemainingBytes) {
+ LoggingService.LogError ("Resource stream invalid length {0}", length.ToString ());
+ continue;
+ }
+
+ var name = mr.GetString (manifestResource.Name);
+ unsafe {
+ using (var unmanagedStream = new UnmanagedMemoryStream (sectionReader.CurrentPointer, length, length, FileAccess.Read)) {
+ var memoryStream = new MemoryStream (length);
+ unmanagedStream.CopyTo (memoryStream);
+ memoryStream.Position = 0;
+ result.Add (new ManifestResource (name, () => memoryStream));
+ }
+ }
+ } finally {
+ sectionReader.Offset -= offset;
+ }
+ }
+ return result;
}
}
@@ -583,5 +577,37 @@ namespace MonoDevelop.Core.Assemblies
{
return Runtime.LoadAssemblyFrom (asmPath);
}
+
+ sealed class StringParameterValueTypeProvider : ISignatureTypeProvider<string, object>
+ {
+ readonly BlobReader valueReader;
+
+ public StringParameterValueTypeProvider (MetadataReader reader, BlobHandle value)
+ {
+ valueReader = reader.GetBlobReader (value);
+
+ var prolog = valueReader.ReadUInt16 ();
+ if (prolog != 1)
+ throw new BadImageFormatException ("Invalid custom attribute prolog.");
+ }
+
+ public string GetPrimitiveType (PrimitiveTypeCode typeCode) => typeCode != PrimitiveTypeCode.String ? "" : valueReader.ReadSerializedString ();
+ public string GetArrayType (string elementType, ArrayShape shape) => "";
+ public string GetByReferenceType (string elementType) => "";
+ public string GetFunctionPointerType (MethodSignature<string> signature) => "";
+ public string GetGenericInstance (string genericType, ImmutableArray<string> typestrings) => "";
+ public string GetGenericInstantiation (string genericType, ImmutableArray<string> typeArguments) { throw new NotImplementedException (); }
+ public string GetGenericMethodParameter (int index) => "";
+ public string GetGenericMethodParameter (object genericContext, int index) { throw new NotImplementedException (); }
+ public string GetGenericTypeParameter (int index) => "";
+ public string GetGenericTypeParameter (object genericContext, int index) { throw new NotImplementedException (); }
+ public string GetModifiedType (string modifier, string unmodifiedType, bool isRequired) => "";
+ public string GetPinnedType (string elementType) => "";
+ public string GetPointerType (string elementType) => "";
+ public string GetSZArrayType (string elementType) => "";
+ public string GetTypeFromDefinition (MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) => "";
+ public string GetTypeFromReference (MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) => "";
+ public string GetTypeFromSpecification (MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) => "";
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs
index 845ffd7727..6e832f7296 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/MonoPlatformExecutionHandler.cs
@@ -45,18 +45,24 @@ namespace MonoDevelop.Core.Execution
public override ProcessAsyncOperation Execute (ExecutionCommand command, OperationConsole console)
{
- var dotcmd = (DotNetExecutionCommand) command;
-
- string runtimeArgs = string.IsNullOrEmpty (dotcmd.RuntimeArguments) ? "--debug" : dotcmd.RuntimeArguments;
+ var dotcmd = (DotNetExecutionCommand)command;
- var monoRunner = runtime.GetMonoExecutableForAssembly (dotcmd.Command);
-
+ string runtimeArgs = string.IsNullOrEmpty (dotcmd.RuntimeArguments) ? "--debug" : dotcmd.RuntimeArguments;
+ var monoRunner = GetExecutionRunner (command, dotcmd);
string args = string.Format ("{2} \"{0}\" {1}", dotcmd.Command, dotcmd.Arguments, runtimeArgs);
NativeExecutionCommand cmd = new NativeExecutionCommand (monoRunner, args, dotcmd.WorkingDirectory, dotcmd.EnvironmentVariables);
-
+
return base.Execute (cmd, console);
}
-
+
+ private string GetExecutionRunner (ExecutionCommand command, DotNetExecutionCommand dotcmd)
+ {
+ if (command is ProcessExecutionCommand processExecutionCommand && processExecutionCommand.ProcessExecutionArchitecture != ProcessExecutionArchitecture.Unspecified) {
+ return runtime.GetMonoExecutable (processExecutionCommand.ProcessExecutionArchitecture);
+ }
+ return runtime.GetMonoExecutableForAssembly (dotcmd.Command);
+ }
+
public override bool CanExecute (ExecutionCommand command)
{
return command is DotNetExecutionCommand;
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessExecutionCommand.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessExecutionCommand.cs
index 76b3af1915..01af607c01 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessExecutionCommand.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessExecutionCommand.cs
@@ -29,6 +29,13 @@ using System.Collections.Generic;
namespace MonoDevelop.Core.Execution
{
+ public enum ProcessExecutionArchitecture
+ {
+ Unspecified,
+ X86,
+ X64
+ }
+
public class ProcessExecutionCommand: ExecutionCommand
{
IDictionary<string, string> environmentVariables;
@@ -69,7 +76,9 @@ namespace MonoDevelop.Core.Execution
}
public string WorkingDirectory { get; set; }
-
+
+ public ProcessExecutionArchitecture ProcessExecutionArchitecture { get; set; }
+
public IDictionary<string, string> EnvironmentVariables {
get {
if (environmentVariables == null)
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs
index 74c89622dd..2d804cf738 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs
@@ -156,15 +156,6 @@ namespace MonoDevelop.Core.Execution
}
}
- [Obsolete ("Use Disconnect()")]
- public void Disconnect (bool waitUntilDone)
- {
- if (waitUntilDone)
- Disconnect ().Wait (TimeSpan.FromSeconds (7));
- else
- Disconnect ().Ignore ();
- }
-
public async Task Disconnect ()
{
StopPinger ();
@@ -285,6 +276,8 @@ namespace MonoDevelop.Core.Execution
SetStatus (ConnectionStatus.ConnectionFailed, ex.Message, ex);
}
+ public ProcessExecutionArchitecture ProcessExecutionArchitecture { get; set; }
+
Task StartRemoteProcess ()
{
return Task.Run (() => {
@@ -296,7 +289,7 @@ namespace MonoDevelop.Core.Execution
// Explicitly propagate the PATH var to the process. It ensures that tools required
// to run XS are also in the PATH for remote processes.
cmd.EnvironmentVariables ["PATH"] = Environment.GetEnvironmentVariable ("PATH");
-
+ cmd.ProcessExecutionArchitecture = ProcessExecutionArchitecture;
process = executionHandler.Execute (cmd, console);
process.Task.ContinueWith (t => ProcessExited ());
});
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs
index ada6e0c8c6..f0fa0c4205 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs
@@ -250,6 +250,11 @@ namespace MonoDevelop.Core.Text
// clear cache
return result;
}
+
+ public override string ToString ()
+ {
+ return string.Format ("[BacktrackingStringMatcher: filterText={0}]", filterText);
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/AmbientAuthenticationState.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/AmbientAuthenticationState.cs
new file mode 100644
index 0000000000..43165df3e8
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/AmbientAuthenticationState.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// Represents source authentication status per active operation
+ /// </summary>
+ class AmbientAuthenticationState
+ {
+ internal const int MaxAuthRetries = 4;
+
+ public bool IsBlocked { get; private set; } = false;
+ public int AuthenticationRetriesCount { get; private set; } = 0;
+
+ public void Block ()
+ {
+ IsBlocked = true;
+ }
+
+ public void Increment ()
+ {
+ AuthenticationRetriesCount++;
+
+ if (AuthenticationRetriesCount >= MaxAuthRetries) {
+ // Block future attempts.
+ Block ();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialResponse.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialResponse.cs
new file mode 100644
index 0000000000..1ab0b0dfcf
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialResponse.cs
@@ -0,0 +1,48 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+using System.Net;
+using System;
+
+namespace MonoDevelop.Core.Web
+{
+ class CredentialResponse
+ {
+ /// <summary>
+ /// Creates a credential response object without giving credentials. This constructor is used only if the
+ /// credential provider was not able to get credentials. The <paramref name="status"/> indicates why the
+ /// provider was not able to get credentials.
+ /// </summary>
+ /// <param name="status">The status of why the credential provider was not able to get credentials.</param>
+ public CredentialResponse (CredentialStatus status)
+ : this (null, status)
+ {
+ }
+
+ /// <summary>
+ /// Creates a credential response object with a given set of credentials. This constuctor is used only if the
+ /// credential provider was able to get credentials.
+ /// </summary>
+ /// <param name="credentials">The credentials fetched by the credential provider.</param>
+ public CredentialResponse (ICredentials credentials)
+ : this (credentials, CredentialStatus.Success)
+ {
+ }
+
+ CredentialResponse (ICredentials credentials, CredentialStatus status)
+ {
+ if ((credentials != null && status != CredentialStatus.Success) ||
+ (credentials == null && status == CredentialStatus.Success)) {
+ throw new InvalidOperationException ("Could not create credential response object because the response was invalid.");
+ }
+
+ Credentials = credentials;
+ Status = status;
+ }
+
+ public ICredentials Credentials { get; }
+ public CredentialStatus Status { get; }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialService.cs
new file mode 100644
index 0000000000..2d073a3e5a
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialService.cs
@@ -0,0 +1,73 @@
+//
+// CredentialService.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// Task based wrapper around the synchronous ICredentialProvider
+ /// </summary>
+ class CredentialService : ICredentialService
+ {
+ /// <summary>
+ /// Provides credentials for http requests.
+ /// </summary>
+ /// <param name="uri">
+ /// The URI of a web resource for which credentials are needed.
+ /// </param>
+ /// <param name="proxy">
+ /// The currently configured proxy. It may be necessary for CredentialProviders
+ /// to use this proxy in order to acquire credentials from their authentication source.
+ /// </param>
+ /// <param name="type">
+ /// The type of credential request that is being made.
+ /// </param>
+ /// <param name="cancellationToken">A cancellation token.</param>
+ /// <returns>A credential object, or null if no credentials could be acquired.</returns>
+ public Task<ICredentials> GetCredentialsAsync (
+ Uri uri,
+ IWebProxy proxy,
+ CredentialType type,
+ bool isRetry,
+ CancellationToken cancellationToken)
+ {
+ if (uri == null)
+ throw new ArgumentNullException (nameof (uri));
+
+ var cp = WebRequestHelper.CredentialProvider;
+ if (cp == null)
+ return null;
+
+ return Task.Run (() => {
+ return cp.GetCredentials (uri, proxy, type, isRetry);
+ });
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialStatus.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialStatus.cs
new file mode 100644
index 0000000000..da88fd9e66
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/CredentialStatus.cs
@@ -0,0 +1,18 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// Result of an attempt to acquire credentials.
+ /// Keep in sync with NuGet.VisualStudio.VsCredentialStatus
+ /// </summary>
+ enum CredentialStatus
+ {
+ Success,
+ ProviderNotApplicable,
+ UserCanceled
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpClientHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpClientHandler.cs
new file mode 100644
index 0000000000..47ad8092b6
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpClientHandler.cs
@@ -0,0 +1,42 @@
+//
+// DefaultHttpClientHandler.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Net;
+using System.Net.Http;
+
+namespace MonoDevelop.Core.Web
+{
+ class DefaultHttpClientHandler : HttpClientHandler, IHttpCredentialsHandler
+ {
+ public DefaultHttpClientHandler (IWebProxy proxy, HttpClientSettings settings)
+ {
+ AllowAutoRedirect = settings.AllowAutoRedirect;
+ AutomaticDecompression = settings.AutomaticDecompression;
+ PreAuthenticate = settings.PreAuthenticate;
+ Proxy = proxy;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpMessageHandlerProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpMessageHandlerProvider.cs
new file mode 100644
index 0000000000..ecf8844f11
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/DefaultHttpMessageHandlerProvider.cs
@@ -0,0 +1,38 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// Based on: src/NuGet.Core/NuGet.Protocol/HttpSource/HttpHandlerResourceV3Provider.cs
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Net.Http;
+
+namespace MonoDevelop.Core.Web
+{
+ public class DefaultHttpMessageHandlerProvider : HttpMessageHandlerProvider
+ {
+ public override HttpMessageHandler CreateHttpMessageHandler (Uri uri, HttpClientSettings settings)
+ {
+ var proxy = WebRequestHelper.ProxyCache.GetProxy (uri);
+ var clientHandler = new DefaultHttpClientHandler (proxy, settings);
+
+ HttpMessageHandler messageHandler = clientHandler;
+
+ if (proxy != null) {
+ messageHandler = new ProxyAuthenticationHandler (clientHandler, HttpClientProvider.CredentialService, WebRequestHelper.ProxyCache) {
+ NonInteractive = settings.NonInteractive
+ };
+ }
+
+ if (settings.SourceAuthenticationRequired) {
+ var innerHandler = messageHandler;
+
+ messageHandler = new HttpSourceAuthenticationHandler (uri, clientHandler, HttpClientProvider.CredentialService) {
+ InnerHandler = innerHandler
+ };
+ }
+
+ return messageHandler;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientProvider.cs
new file mode 100644
index 0000000000..9b11bbb68e
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientProvider.cs
@@ -0,0 +1,81 @@
+//
+// HttpClientProvider.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Net.Http;
+using Mono.Addins;
+
+namespace MonoDevelop.Core.Web
+{
+ public static class HttpClientProvider
+ {
+ const string ProvidersPath = "/MonoDevelop/Core/HttpMessageHandlerProviders";
+
+ static HttpMessageHandlerProvider httpMessageHandlerProvider;
+ static CredentialService credentialService;
+ static readonly HttpClientSettings defaultHttpClientSettings = new HttpClientSettings ();
+
+ internal static void Initialize ()
+ {
+ httpMessageHandlerProvider = AddinManager.GetExtensionObjects<HttpMessageHandlerProvider> (ProvidersPath).FirstOrDefault ();
+
+ if (httpMessageHandlerProvider == null) {
+ httpMessageHandlerProvider = new DefaultHttpMessageHandlerProvider ();
+ }
+
+ credentialService = new CredentialService ();
+ }
+
+ public static HttpClient CreateHttpClient (string uri)
+ {
+ return CreateHttpClient (new Uri (uri));
+ }
+
+ public static HttpClient CreateHttpClient (Uri uri)
+ {
+ return CreateHttpClient (uri, defaultHttpClientSettings);
+ }
+
+ public static HttpClient CreateHttpClient (string uri, HttpClientSettings settings)
+ {
+ return CreateHttpClient (new Uri (uri), settings);
+ }
+
+ public static HttpClient CreateHttpClient (Uri uri, HttpClientSettings settings)
+ {
+ var handler = CreateHttpMessageHandler (uri, settings);
+ return new HttpClient (handler);
+ }
+
+ public static HttpMessageHandler CreateHttpMessageHandler (Uri uri, HttpClientSettings settings)
+ {
+ return httpMessageHandlerProvider.CreateHttpMessageHandler (uri, settings ?? defaultHttpClientSettings);
+ }
+
+ static internal ICredentialService CredentialService => credentialService;
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientSettings.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientSettings.cs
new file mode 100644
index 0000000000..2e59d2e09c
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpClientSettings.cs
@@ -0,0 +1,39 @@
+//
+// HttpClientSettings.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Net;
+
+namespace MonoDevelop.Core.Web
+{
+ public class HttpClientSettings
+ {
+ public bool AllowAutoRedirect { get; set; } = true;
+ public DecompressionMethods AutomaticDecompression { get; set; } = DecompressionMethods.Deflate | DecompressionMethods.GZip;
+ public bool PreAuthenticate { get; set; }
+ public bool SourceAuthenticationRequired { get; set; } = true;
+ public bool NonInteractive { get; set; }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpMessageHandlerProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpMessageHandlerProvider.cs
new file mode 100644
index 0000000000..2521854aa1
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpMessageHandlerProvider.cs
@@ -0,0 +1,36 @@
+//
+// HttpMessageHandlerProvider.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Net.Http;
+
+namespace MonoDevelop.Core.Web
+{
+ public abstract class HttpMessageHandlerProvider
+ {
+ public abstract HttpMessageHandler CreateHttpMessageHandler (Uri uri, HttpClientSettings settings);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceAuthenticationHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceAuthenticationHandler.cs
new file mode 100644
index 0000000000..4ff35e00e4
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceAuthenticationHandler.cs
@@ -0,0 +1,180 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MonoDevelop.Core.Web
+{
+ public class HttpSourceAuthenticationHandler : DelegatingHandler
+ {
+ public static readonly int MaxAuthRetries = AmbientAuthenticationState.MaxAuthRetries;
+
+ // Only one source may prompt at a time
+ readonly static SemaphoreSlim credentialPromptLock = new SemaphoreSlim (1, 1);
+
+ readonly Uri source;
+ readonly IHttpCredentialsHandler credentialsHandler;
+ readonly ICredentialService credentialService;
+
+ readonly SemaphoreSlim httpClientLock = new SemaphoreSlim (1, 1);
+ HttpSourceCredentials credentials;
+
+ internal HttpSourceAuthenticationHandler (
+ Uri source,
+ DefaultHttpClientHandler clientHandler,
+ ICredentialService credentialService)
+ : this (source, clientHandler, clientHandler, credentialService)
+ {
+ }
+
+ HttpSourceAuthenticationHandler (
+ Uri source,
+ IHttpCredentialsHandler credentialsHandler,
+ HttpMessageHandler innerHandler,
+ ICredentialService credentialService)
+ : base (innerHandler)
+ {
+ this.source = source ?? throw new ArgumentNullException (nameof (source));
+ credentialsHandler = credentialsHandler ?? throw new ArgumentNullException (nameof (credentialsHandler));
+
+ this.credentialService = credentialService;
+
+ // Create a new wrapper for ICredentials that can be modified
+ // This is used to match the value of HttpClientHandler.UseDefaultCredentials = true
+ credentials = new HttpSourceCredentials (CredentialCache.DefaultNetworkCredentials);
+
+ credentialsHandler.Credentials = credentials;
+ // Always take the credentials from the helper.
+ credentialsHandler.UseDefaultCredentials = false;
+ }
+
+ public HttpSourceAuthenticationHandler (Uri source, IHttpCredentialsHandler credentialsHandler, HttpMessageHandler innerHandler)
+ : this (source, credentialsHandler, innerHandler, HttpClientProvider.CredentialService)
+ {
+ }
+
+ protected override async Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ HttpResponseMessage response = null;
+ ICredentials promptCredentials = null;
+ var authState = new AmbientAuthenticationState ();
+
+ // Authorizing may take multiple attempts
+ while (true) {
+ // Clean up any previous responses
+ if (response != null) {
+ response.Dispose ();
+ }
+
+ // store the auth state before sending the request
+ var beforeLockVersion = credentials.Version;
+
+ response = await base.SendAsync (request, cancellationToken).ConfigureAwait (false);
+
+ if (credentialService == null) {
+ return response;
+ }
+
+ if (response.StatusCode == HttpStatusCode.Unauthorized ||
+ response.StatusCode == HttpStatusCode.Forbidden) {
+ promptCredentials = await AcquireCredentialsAsync (
+ authState,
+ beforeLockVersion,
+ cancellationToken);
+
+ if (promptCredentials == null) {
+ return response;
+ }
+
+ continue;
+ }
+
+ return response;
+ }
+ }
+
+ async Task<ICredentials> AcquireCredentialsAsync (AmbientAuthenticationState authState, Guid credentialsVersion, CancellationToken cancellationToken)
+ {
+ try {
+ // Only one request may prompt and attempt to auth at a time
+ await httpClientLock.WaitAsync ();
+
+ cancellationToken.ThrowIfCancellationRequested ();
+
+ // Auth may have happened on another thread, if so just continue
+ if (credentialsVersion != credentials.Version) {
+ return credentials.Credentials;
+ }
+
+ if (authState.IsBlocked) {
+ cancellationToken.ThrowIfCancellationRequested ();
+ return null;
+ }
+
+ var promptCredentials = await PromptForCredentialsAsync (
+ CredentialType.RequestCredentials,
+ authState,
+ cancellationToken);
+
+ if (promptCredentials == null) {
+ return null;
+ }
+
+ credentials.Credentials = promptCredentials;
+
+ return promptCredentials;
+ } finally {
+ httpClientLock.Release ();
+ }
+ }
+
+ async Task<ICredentials> PromptForCredentialsAsync (
+ CredentialType type,
+ AmbientAuthenticationState authState,
+ CancellationToken token)
+ {
+ ICredentials promptCredentials;
+
+ try {
+ // Only one prompt may display at a time.
+ await credentialPromptLock.WaitAsync ();
+
+ // Get the proxy for this URI so we can pass it to the credentialService methods
+ // this lets them use the proxy if they have to hit the network.
+ var proxyCache = WebRequestHelper.ProxyCache;
+ var proxy = proxyCache?.GetProxy (source);
+
+ bool isRetry = authState.AuthenticationRetriesCount > 0;
+ promptCredentials = await credentialService.GetCredentialsAsync (source, proxy, type, isRetry, token);
+
+ if (promptCredentials == null) {
+ // If this is the case, this means none of the credential providers were able to
+ // handle the credential request or no credentials were available for the
+ // endpoint.
+ authState.Block ();
+ } else {
+ authState.Increment ();
+ }
+ } catch (OperationCanceledException) {
+ // This indicates a non-human cancellation.
+ throw;
+ } catch (Exception) {
+ // If this is the case, this means there was a fatal exception when interacting
+ // with the credential service (or its underlying credential providers). Either way,
+ // block asking for credentials for the live of this operation.
+ promptCredentials = null;
+ authState.Block ();
+ } finally {
+ credentialPromptLock.Release ();
+ }
+
+ return promptCredentials;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceCredentials.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceCredentials.cs
new file mode 100644
index 0000000000..cffcbccf22
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/HttpSourceCredentials.cs
@@ -0,0 +1,88 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Net;
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// A mutable CredentialCache wrapper. This allows the underlying ICredentials to
+ /// be changed to work around HttpClientHandler not allowing Credentials to change.
+ /// This class intentionally inherits from CredentialCache to support authentication on redirects.
+ /// According to System.Net implementation any other ICredentials implementation is dropped for security reasons.
+ /// </summary>
+ class HttpSourceCredentials : CredentialCache, ICredentials
+ {
+ public HttpSourceCredentials ()
+ {
+ Credentials = null;
+ }
+
+ /// <summary>
+ /// Credentials can be changed by other threads, for this reason volatile
+ /// is added below so that the value is not cached anywhere.
+ /// </summary>
+ volatile VersionedCredentials credentials;
+
+ /// <summary>
+ /// The latest credentials to be used.
+ /// </summary>
+ public ICredentials Credentials {
+ get {
+ return credentials.Credentials;
+ }
+
+ set {
+ // We must update the credentials and it's associated version GUID atomically. This
+ // can be achieved with a reference assignment. It is important that credentials and
+ // version always match. In other words, if the credentials are updated, it should
+ // at no instant be possible to get old version GUID and the new credentials.
+ credentials = new VersionedCredentials (value);
+ }
+ }
+
+ /// <summary>
+ /// The latest version ID of the <see cref="Credentials"/>.
+ /// </summary>
+ public Guid Version {
+ get {
+ return credentials.Version;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpSourceCredentials"/> class
+ /// </summary>
+ /// <param name="credentials">
+ /// Optional initial credentials. May be null.
+ /// </param>
+ public HttpSourceCredentials (ICredentials credentials = null)
+ {
+ Credentials = credentials;
+ }
+
+ /// <summary>
+ /// Used by the HttpClientHandler to retrieve the current credentials.
+ /// </summary>
+ NetworkCredential ICredentials.GetCredential (Uri uri, string authType)
+ {
+ // Get credentials from the current credential store, if any
+ return Credentials?.GetCredential (uri, authType);
+ }
+
+ class VersionedCredentials
+ {
+ public VersionedCredentials (ICredentials credentials)
+ {
+ Version = Guid.NewGuid ();
+ Credentials = credentials;
+ }
+
+ public ICredentials Credentials { get; }
+ public Guid Version { get; }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IAsyncCredentialProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IAsyncCredentialProvider.cs
new file mode 100644
index 0000000000..bc6ef8d2d2
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IAsyncCredentialProvider.cs
@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// Based on ICredentialProvider
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MonoDevelop.Core.Web
+{
+ interface IAsyncCredentialProvider
+ {
+ string Id { get; }
+
+ Task<CredentialResponse> GetAsync(
+ Uri uri,
+ IWebProxy proxy,
+ CredentialType type,
+ bool isRetry,
+ CancellationToken cancellationToken);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ICredentialService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ICredentialService.cs
new file mode 100644
index 0000000000..fd0750ffaa
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ICredentialService.cs
@@ -0,0 +1,35 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MonoDevelop.Core.Web
+{
+ interface ICredentialService
+ {
+ /// <summary>
+ /// Asynchronously gets credentials.
+ /// </summary>
+ /// <param name="uri">The URI for which credentials should be retrieved.</param>
+ /// <param name="proxy">A web proxy.</param>
+ /// <param name="type">The credential request type.</param>
+ /// <param name="isRetry">The request is being retried.</param>
+ /// <param name="cancellationToken">A cancellation token.</param>
+ /// <returns>A task that represents the asynchronous operation.
+ /// The task result (<see cref="Task{TResult}.Result" />) returns a <see cref="ICredentials" />.</returns>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="uri" /> is <c>null</c>.</exception>
+ /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
+ /// is cancelled.</exception>
+ Task<ICredentials> GetCredentialsAsync (
+ Uri uri,
+ IWebProxy proxy,
+ CredentialType type,
+ bool isRetry,
+ CancellationToken cancellationToken);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IHttpCredentialsHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IHttpCredentialsHandler.cs
new file mode 100644
index 0000000000..5ce9fe5e70
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IHttpCredentialsHandler.cs
@@ -0,0 +1,39 @@
+//
+// IHttpCredentialsHandler.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System.Net;
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// Must be implemented by one HttpMessageHandler in order for credentials to be configured for requests.
+ /// </summary>
+ public interface IHttpCredentialsHandler
+ {
+ ICredentials Credentials { get; set; }
+ bool UseDefaultCredentials { get; set; }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IProxyCredentialCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IProxyCredentialCache.cs
new file mode 100644
index 0000000000..78bfc4c970
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/IProxyCredentialCache.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Net;
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// <see cref="CredentialCache"/>-like interface with Update credential semantics rather than Add/Remove
+ /// </summary>
+ interface IProxyCredentialCache : ICredentials
+ {
+ /// <summary>
+ /// Tracks the cache version. Changes every time proxy credential is updated.
+ /// </summary>
+ Guid Version { get; }
+
+ /// <summary>
+ /// Add or update proxy credential
+ /// </summary>
+ /// <param name="proxyAddress">Proxy network address</param>
+ /// <param name="credentials">New credential object</param>
+ void UpdateCredential (Uri proxyAddress, NetworkCredential credentials);
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyAuthenticationHandler.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyAuthenticationHandler.cs
new file mode 100644
index 0000000000..7c20165326
--- /dev/null
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyAuthenticationHandler.cs
@@ -0,0 +1,172 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+//
+// From: https://github.com/NuGet/NuGet.Client
+
+using System;
+using System.Globalization;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MonoDevelop.Core.Web
+{
+ /// <summary>
+ /// A message handler responsible for retrying request for authenticated proxies
+ /// with missing credentials.
+ /// </summary>
+ class ProxyAuthenticationHandler : DelegatingHandler
+ {
+ public static readonly int MaxAuthRetries = 3;
+ const string BasicAuthenticationType = "Basic";
+
+ // Only one source may prompt at a time
+ static readonly SemaphoreSlim credentialPromptLock = new SemaphoreSlim (1, 1);
+
+ readonly HttpClientHandler clientHandler;
+ readonly ICredentialService credentialService;
+ readonly IProxyCredentialCache credentialCache;
+
+ int authRetries;
+
+ public ProxyAuthenticationHandler (
+ HttpClientHandler clientHandler,
+ ICredentialService credentialService,
+ IProxyCredentialCache credentialCache)
+ : base (clientHandler)
+ {
+ if (clientHandler == null) {
+ throw new ArgumentNullException (nameof (clientHandler));
+ }
+
+ this.clientHandler = clientHandler;
+
+ // credential service is optional
+ this.credentialService = credentialService;
+
+ if (credentialCache == null) {
+ throw new ArgumentNullException (nameof (credentialCache));
+ }
+
+ this.credentialCache = credentialCache;
+ }
+
+ public bool NonInteractive { get; set; }
+
+ protected override async Task<HttpResponseMessage> SendAsync (
+ HttpRequestMessage request,
+ CancellationToken cancellationToken)
+ {
+ while (true) {
+ // Store the auth start before sending the request
+ var cacheVersion = credentialCache.Version;
+
+ try {
+ var response = await base.SendAsync (request, cancellationToken).ConfigureAwait (false);
+
+ if (response.StatusCode != HttpStatusCode.ProxyAuthenticationRequired) {
+ return response;
+ }
+
+ if (clientHandler.Proxy == null) {
+ return response;
+ }
+
+ if (credentialService == null) {
+ return response;
+ }
+
+ if (!await AcquireCredentialsAsync (request.RequestUri, cacheVersion, cancellationToken)) {
+ return response;
+ }
+ } catch (Exception ex)
+ when (ProxyAuthenticationRequired (ex) && clientHandler.Proxy != null && credentialService != null) {
+ if (!await AcquireCredentialsAsync (request.RequestUri, cacheVersion, cancellationToken)) {
+ throw;
+ }
+ }
+ }
+ }
+
+ // Returns true if the cause of the exception is proxy authentication failure
+ static bool ProxyAuthenticationRequired (Exception ex)
+ {
+ var response = ExtractResponse (ex);
+ return response?.StatusCode == HttpStatusCode.ProxyAuthenticationRequired;
+ }
+
+ static HttpWebResponse ExtractResponse (Exception ex)
+ {
+ var webException = ex.InnerException as WebException;
+ var response = webException?.Response as HttpWebResponse;
+ return response;
+ }
+
+ async Task<bool> AcquireCredentialsAsync (Uri requestUri, Guid cacheVersion, CancellationToken cancellationToken)
+ {
+ if (NonInteractive)
+ return false;
+
+ try {
+ await credentialPromptLock.WaitAsync ();
+
+ cancellationToken.ThrowIfCancellationRequested ();
+
+ // Check if the credentials have already changed
+ if (cacheVersion != credentialCache.Version) {
+ // retry the request with updated credentials
+ return true;
+ }
+
+ // Limit the number of retries
+ authRetries++;
+ if (authRetries >= MaxAuthRetries) {
+ // user prompting no more
+ return false;
+ }
+
+ var proxyAddress = clientHandler.Proxy.GetProxy (requestUri);
+
+ // prompt user for proxy credentials.
+ var credentials = await PromptForProxyCredentialsAsync (proxyAddress, clientHandler.Proxy, cancellationToken);
+
+ cancellationToken.ThrowIfCancellationRequested ();
+
+ if (credentials == null) {
+ // user cancelled or error occured
+ return false;
+ }
+
+ credentialCache.UpdateCredential (proxyAddress, credentials);
+
+ // use the user provided credential to send the request again.
+ return true;
+ } finally {
+ credentialPromptLock.Release ();
+ }
+ }
+
+ async Task<NetworkCredential> PromptForProxyCredentialsAsync (Uri proxyAddress, IWebProxy proxy, CancellationToken cancellationToken)
+ {
+ ICredentials promptCredentials;
+
+ try {
+ promptCredentials = await credentialService.GetCredentialsAsync (
+ proxyAddress,
+ proxy,
+ type: CredentialType.ProxyCredentials,
+ authRetries > 1,
+ cancellationToken: cancellationToken);
+ } catch (OperationCanceledException) {
+ throw; // pass-thru
+ } catch (Exception ex) {
+ // Fatal credential service failure
+ LoggingService.LogError ("PromptForProxyCredentialsAsync error", ex);
+ promptCredentials = null;
+ }
+
+ return promptCredentials?.GetCredential (proxyAddress, BasicAuthenticationType);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyCache.cs
index ddba33042a..2b4c6e1f00 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyCache.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Web/ProxyCache.cs
@@ -21,7 +21,7 @@ using System.Net;
namespace MonoDevelop.Core.Web
{
- class ProxyCache : IProxyCache
+ class ProxyCache : IProxyCache, IProxyCredentialCache
{
/// <summary>
/// Capture the default System Proxy so that it can be re-used by the IProxyFinder
@@ -30,25 +30,70 @@ namespace MonoDevelop.Core.Web
/// </summary>
static readonly IWebProxy originalSystemProxy = WebRequest.GetSystemWebProxy ();
- readonly ConcurrentDictionary<Uri, WebProxy> cache = new ConcurrentDictionary<Uri, WebProxy> ();
+ readonly ConcurrentDictionary<Uri, ICredentials> cache = new ConcurrentDictionary<Uri, ICredentials> ();
+
+ public Guid Version { get; private set; } = Guid.NewGuid ();
public IWebProxy GetProxy (Uri uri)
{
if (!IsSystemProxySet (uri))
return null;
- WebProxy systemProxy = GetSystemProxy (uri), effectiveProxy;
+ var systemProxy = GetSystemProxy (uri);
+ TryAddProxyCredentialsToCache (systemProxy);
+ systemProxy.Credentials = this;
+ return systemProxy;
+ }
+
+ // Adds new proxy credentials to cache if there's not any in there yet
+ bool TryAddProxyCredentialsToCache (WebProxy configuredProxy)
+ {
+ // If a proxy was cached, it means the stored credentials are incorrect. Use the cached one in this case.
+ var proxyCredentials = configuredProxy.Credentials ?? CredentialCache.DefaultCredentials;
+ return cache.TryAdd (configuredProxy.Address, proxyCredentials);
+ }
+
+ public void UpdateCredential (Uri proxyAddress, NetworkCredential credentials)
+ {
+ if (credentials == null)
+ throw new ArgumentNullException (nameof (credentials));
+
+ Version = Guid.NewGuid ();
+ cache [proxyAddress] = credentials;
+ }
+
+ public NetworkCredential GetCredential (Uri proxyAddress, string authType)
+ {
+ var credential = GetCredentialInternal (proxyAddress, authType);
+ if (credential != null)
+ return credential;
+
+ // Add workaround for Mono using the request url not the proxy address for non-secure http requests
+ // when getting the proxy credentials.
+ // https://github.com/mono/mono/issues/10622
+ var correctedProxyAddress = originalSystemProxy.GetProxy (proxyAddress);
+ if (!string.Equals (correctedProxyAddress.AbsoluteUri, proxyAddress.AbsoluteUri))
+ return GetCredentialInternal (correctedProxyAddress, authType);
- // See if we have a proxy instance cached for this proxy address
- return cache.TryGetValue (systemProxy.Address, out effectiveProxy) ? effectiveProxy : systemProxy;
+ return null;
+ }
+
+ NetworkCredential GetCredentialInternal (Uri proxyAddress, string authType)
+ {
+ ICredentials cachedCredentials;
+ if (cache.TryGetValue (proxyAddress, out cachedCredentials)) {
+ return cachedCredentials.GetCredential (proxyAddress, authType);
+ }
+ return null;
}
+ [Obsolete ("Retained for backcompat only. Use UpdateCredential instead")]
public void Add (IWebProxy proxy)
{
var webProxy = proxy as WebProxy;
if (webProxy != null)
- cache.TryAdd (webProxy.Address, webProxy);
+ cache.TryAdd (webProxy.Address, webProxy.Credentials);
}
static WebProxy GetSystemProxy (Uri uri)
@@ -81,7 +126,6 @@ namespace MonoDevelop.Core.Web
return false;
if (proxy.IsBypassed (uri))
return false;
- proxy = new WebProxy (proxyAddress);
}
return proxy != null;
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml
index 0797a0af98..2448b77e25 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.addin.xml
@@ -43,6 +43,11 @@
<Description>Platform-specific services used for obtaining proxy credentials.</Description>
<ExtensionNode name="Class" objectType="MonoDevelop.Core.Web.ICredentialProvider" />
</ExtensionPoint>
+
+ <ExtensionPoint path = "/MonoDevelop/Core/HttpMessageHandlerProviders" name = "HttpMessageHandler providers">
+ <Description>Platform-specific services used to create HttpMessageHandlers used by the HttpClient.</Description>
+ <ExtensionNode name="Class" objectType="MonoDevelop.Core.Web.HttpMessageHandlerProvider" />
+ </ExtensionPoint>
<ExtensionPoint path = "/MonoDevelop/Core/FileSystemExtensions" name = "File system extensions">
<Description>File system extensions which can provide specific behavior when handling files. Specified classes must implement MonoDevelop.Core.FileSystem.FileSystemExtension.</Description>
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj
index 64c64568bf..187756f0e5 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj
@@ -1,4 +1,4 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="..\..\..\packages\Microsoft.Build.Locator.1.1.2\build\Microsoft.Build.Locator.props" Condition="Exists('..\..\..\packages\Microsoft.Build.Locator.1.1.2\build\Microsoft.Build.Locator.props')" />
<Import Project="..\..\..\MonoDevelop.props" />
<Import Project="$(ReferencesGtk)" />
@@ -75,6 +75,7 @@
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
+ <Reference Include="System.Net.Http" />
<Reference Include="monodoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
<Reference Include="System.Runtime.InteropServices.RuntimeInformation" />
<Reference Include="System.Xml.Linq" />
@@ -706,26 +707,6 @@
<Compile Include="MonoDevelop.Projects.MSBuild\FileUtilities.cs" />
<Compile Include="MonoDevelop.Projects\ProgressEvent.cs" />
<Compile Include="MonoDevelop.Core.Setup\UpdateChannel.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\FileSystemWatcher.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\FileSystemWatcher.OSX.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.CoreFoundation.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.Error.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.EventStream.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.IOError.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.Libraries.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.PathConf.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.RealPath.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.RunLoop.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\Interop.Sync.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\PathInternal.CaseSensitivity.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\PathInternal.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\PathInternal.Unix.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\PatternMatcher.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\SafeCreateHandle.OSX.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\SafeEventStreamHandle.OSX.cs" />
- <Compile Include="MonoDevelop.FSW\OSX\SR.cs" />
- <Compile Include="MonoDevelop.FSW\FileSystemWatcher.cs" />
- <Compile Include="MonoDevelop.FSW\Mono\FileSystemWatcher.cs" />
<Compile Include="MonoDevelop.Core\StringBuilderCache.cs" />
<Compile Include="MonoDevelop.Core\ObjectPool.cs" />
<Compile Include="MonoDevelop.Core\SharedPools.cs" />
@@ -742,6 +723,22 @@
<Compile Include="MonoDevelop.Projects.MSBuild\SdkResolverManifest.cs" />
<Compile Include="MonoDevelop.Core.FeatureConfiguration\FeatureSwitchService.cs" />
<Compile Include="MonoDevelop.Core.FeatureConfiguration\FeatureSwitchCondition.cs" />
+ <Compile Include="MonoDevelop.Core.Web\HttpMessageHandlerProvider.cs" />
+ <Compile Include="MonoDevelop.Core.Web\HttpClientProvider.cs" />
+ <Compile Include="MonoDevelop.Core.Web\DefaultHttpMessageHandlerProvider.cs" />
+ <Compile Include="MonoDevelop.Core.Web\ProxyAuthenticationHandler.cs" />
+ <Compile Include="MonoDevelop.Core.Web\IProxyCredentialCache.cs" />
+ <Compile Include="MonoDevelop.Core.Web\ICredentialService.cs" />
+ <Compile Include="MonoDevelop.Core.Web\CredentialService.cs" />
+ <Compile Include="MonoDevelop.Core.Web\IAsyncCredentialProvider.cs" />
+ <Compile Include="MonoDevelop.Core.Web\CredentialResponse.cs" />
+ <Compile Include="MonoDevelop.Core.Web\CredentialStatus.cs" />
+ <Compile Include="MonoDevelop.Core.Web\HttpSourceAuthenticationHandler.cs" />
+ <Compile Include="MonoDevelop.Core.Web\AmbientAuthenticationState.cs" />
+ <Compile Include="MonoDevelop.Core.Web\HttpSourceCredentials.cs" />
+ <Compile Include="MonoDevelop.Core.Web\HttpClientSettings.cs" />
+ <Compile Include="MonoDevelop.Core.Web\IHttpCredentialsHandler.cs" />
+ <Compile Include="MonoDevelop.Core.Web\DefaultHttpClientHandler.cs" />
<Compile Include="MonoDevelop.Projects.MSBuild\MSBuildProcessService.cs" />
<Compile Include="MonoDevelop.Core.FeatureConfiguration\IFeatureSwitchController.cs" />
<Compile Include="MonoDevelop.Core\Service.cs" />
@@ -750,6 +747,7 @@
<Compile Include="MonoDevelop.Core\BasicServiceProvider.cs" />
<Compile Include="MonoDevelop.Core.AddIns\PlatformConditionAttribute.cs" />
<Compile Include="MonoDevelop.Core\CoreServices.cs" />
+ <Compile Include="MonoDevelop.Core.Assemblies\AssemblyUtilities.cs" />
</ItemGroup>
<ItemGroup>
<None Include="BuildVariables.cs.in" />
@@ -786,6 +784,7 @@
<ItemGroup>
<InternalsVisibleTo Include="MonoDevelop.Core.Tests" />
<InternalsVisibleTo Include="MonoDevelop.Ide.Tests" />
+ <InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<Folder Include="MonoDevelop.Utilities\" />
@@ -804,7 +803,7 @@
<Exec Command="&quot;$(Git)&quot; rev-parse HEAD &gt; $(VcRevision)" WorkingDirectory="$(MSBuildProjectDirectory)" IgnoreExitCode="True" />
<RemoveDir Directories="$(FullBuildInfo)" />
</Target>
- <Import Project="..\..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.6.46\build\Microsoft.VisualStudio.Threading.Analyzers.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.6.46\build\Microsoft.VisualStudio.Threading.Analyzers.targets')" />
+ <Import Project="..\..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.8.209\build\Microsoft.VisualStudio.Threading.Analyzers.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.8.209\build\Microsoft.VisualStudio.Threading.Analyzers.targets')" />
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs
index c4f105defb..48d285fd69 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs
@@ -35,11 +35,13 @@ using System.Text;
using Mono.Addins;
using Mono.Unix.Native;
using MonoDevelop.Core.FileSystem;
+using MonoDevelop.Core.Web;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
+using System.Net.Http;
namespace MonoDevelop.Core
{
@@ -808,24 +810,27 @@ namespace MonoDevelop.Core
{
bool deleteTempFile = true;
var tempFile = cacheFile + ".temp";
+ HttpClient client = null;
try {
- var response = await WebRequestHelper.GetResponseAsync (
- () => (HttpWebRequest)WebRequest.Create (url),
- r => {
- //check to see if the online file has been modified since it was last downloaded
- var localNewsXml = new FileInfo (cacheFile);
- if (localNewsXml.Exists)
- r.IfModifiedSince = localNewsXml.LastWriteTime;
- },
- ct
- ).ConfigureAwait (false);
+ client = HttpClientProvider.CreateHttpClient (url);
+ //check to see if the online file has been modified since it was last downloaded
+ var localNewsXml = new FileInfo (cacheFile);
+ if (localNewsXml.Exists)
+ client.DefaultRequestHeaders.IfModifiedSince = localNewsXml.LastWriteTime;
+ using (var response = await client.GetAsync (url, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait (false)) {
- ct.ThrowIfCancellationRequested ();
+ ct.ThrowIfCancellationRequested ();
- //TODO: limit this size in case open wifi hotspots provide junk data
- if (response.StatusCode == HttpStatusCode.OK) {
- using (var fs = File.Create (tempFile))
- response.GetResponseStream ().CopyTo (fs, 2048);
+ //TODO: limit this size in case open wifi hotspots provide junk data
+ if (response.StatusCode == HttpStatusCode.OK) {
+ using (var fs = File.Create (tempFile))
+ await response.Content.CopyToAsync (fs);
+ } else if (response.StatusCode == HttpStatusCode.NotModified) {
+ return false;
+ } else {
+ LoggingService.LogWarning ("FileService.UpdateDownloadedCacheFile. Unexpected status code {0}", response.StatusCode);
+ return false;
+ }
}
//check the document is valid, might get bad ones from wifi hotspots etc
@@ -850,13 +855,8 @@ namespace MonoDevelop.Core
SystemRename (tempFile, cacheFile);
deleteTempFile = false;
return true;
- } catch (Exception ex) {
- if (ex.FlattenAggregate ().InnerException is WebException wex) {
- if (wex.Response is HttpWebResponse resp && resp.StatusCode == HttpStatusCode.NotModified)
- return false;
- }
- throw;
- } finally {
+ } finally {
+ client?.Dispose ();
if (deleteTempFile) {
try {
File.Delete (tempFile);
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs
index 8887c89a41..ad91bdca38 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs
@@ -44,6 +44,7 @@ using MonoDevelop.Core.Assemblies;
using MonoDevelop.Core.Execution;
using MonoDevelop.Core.Instrumentation;
using MonoDevelop.Core.Setup;
+using MonoDevelop.Core.Web;
namespace MonoDevelop.Core
{
@@ -128,8 +129,9 @@ namespace MonoDevelop.Core
PropertyService.Initialize ();
WebRequestHelper.Initialize ();
- Mono.Addins.Setup.WebRequestHelper.SetRequestHandler (WebRequestHelper.GetResponse);
-
+ Web.HttpClientProvider.Initialize ();
+ Mono.Addins.Setup.HttpClientProvider.SetHttpClientFactory (Web.HttpClientProvider.CreateHttpClient);
+
//have to do this after the addin service and property service have initialized
if (UserDataMigrationService.HasSource) {
Counters.RuntimeInitialization.Trace ("Migrating User Data from MD " + UserDataMigrationService.SourceVersion);
@@ -217,7 +219,7 @@ namespace MonoDevelop.Core
static void OnLoad (object s, AddinEventArgs args)
{
- Counters.AddinsLoaded.Inc ("Add-in loaded: " + args.AddinId, new Dictionary<string, string> {
+ Counters.AddinsLoaded.Inc (1, "Add-in loaded: " + args.AddinId, new Dictionary<string, object> {
{ "AddinId", args.AddinId },
{ "LoadTrace", Environment.StackTrace },
});
@@ -251,7 +253,6 @@ namespace MonoDevelop.Core
ShuttingDown (null, EventArgs.Empty);
PropertyService.SaveProperties ();
- FSW.OSX.FileSystemWatcher.DisposeAll ();
if (processService != null) {
processService.Dispose ();
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/WebRequestHelper.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/WebRequestHelper.cs
index 504b07cd64..b6cbdbbe88 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/WebRequestHelper.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/WebRequestHelper.cs
@@ -61,6 +61,8 @@ namespace MonoDevelop.Core
[Obsolete]
public static IProxyAuthenticationHandler ProxyAuthenticationHandler { get; internal set; }
+ static internal ProxyCache ProxyCache => proxyCache;
+
/// <summary>
/// Gets the web response, using the <see cref="ProxyAuthenticationHandler"/> to handle proxy authentication
/// if necessary.
@@ -73,6 +75,7 @@ namespace MonoDevelop.Core
/// Keeps sending requests until a response code that doesn't require authentication happens or if the request
/// requires authentication and the user has stopped trying to enter them (i.e. they hit cancel when they are prompted).
/// </remarks>
+ [Obsolete ("Use HttpClientProvider.CreateHttpClient")]
public static async Task<HttpWebResponse> GetResponseAsync (
Func<HttpWebRequest> createRequest,
Action<HttpWebRequest> prepareRequest = null,
@@ -109,6 +112,7 @@ namespace MonoDevelop.Core
/// Keeps sending requests until a response code that doesn't require authentication happens or if the request
/// requires authentication and the user has stopped trying to enter them (i.e. they hit cancel when they are prompted).
/// </remarks>
+ [Obsolete ("Use HttpClientProvider.CreateHttpClient")]
public static HttpWebResponse GetResponse (
Func<HttpWebRequest> createRequest,
Action<HttpWebRequest> prepareRequest = null,
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/FileSystemWatcher.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/FileSystemWatcher.cs
deleted file mode 100644
index c10ca2df28..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/FileSystemWatcher.cs
+++ /dev/null
@@ -1,580 +0,0 @@
-//
-// FileSystemWatcher.cs
-//
-// Author:
-// ludovic <ludovic.henry@xamarin.com>
-//
-// Copyright (c) 2017 ludovic
-//
-// 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.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW
-{
- internal class FileSystemWatcher : System.ComponentModel.Component, System.ComponentModel.ISupportInitialize
- {
- static Platform _platform;
-
- enum Platform
- {
- OSX,
- Mono,
- }
-
- [DllImport (OSX.Interop.Libraries.SystemNative, EntryPoint = "SystemNative_HasOSXSupport")]
- private static extern bool HasOSXSupport ();
-
- static FileSystemWatcher ()
- {
- try {
- if (Core.Platform.IsMac && HasOSXSupport ()) {
- _platform = Platform.OSX;
- return;
- }
- } catch (EntryPointNotFoundException) {
- } catch (DllNotFoundException) {
- }
-
- _platform = Platform.Mono;
- }
-
- OSX.FileSystemWatcher _osxFsw;
- Mono.FileSystemWatcher _monoFsw;
-
- public FileSystemWatcher ()
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw = new Mono.FileSystemWatcher ();
- break;
- case Platform.OSX:
- _osxFsw = new OSX.FileSystemWatcher ();
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- public FileSystemWatcher (string path)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw = new Mono.FileSystemWatcher (path);
- break;
- case Platform.OSX:
- _osxFsw = new OSX.FileSystemWatcher (path);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- public FileSystemWatcher (string path, string filter)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw = new Mono.FileSystemWatcher (path, filter);
- break;
- case Platform.OSX:
- _osxFsw = new OSX.FileSystemWatcher (path, filter);
- break;
- default: throw new NotImplementedException ();
- }
- }
-
- /// <summary>
- /// Used by unit tests to verify the native file watcher is being used.
- /// </summary>
- internal static bool IsMac {
- get { return _platform == Platform.OSX; }
- }
-
- public bool EnableRaisingEvents {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.EnableRaisingEvents;
- case Platform.OSX:
- return _osxFsw.EnableRaisingEvents;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.EnableRaisingEvents = value;
- break;
- case Platform.OSX:
- _osxFsw.EnableRaisingEvents = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
- public string Filter {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.Filter;
- case Platform.OSX:
- return _osxFsw.Filter;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Filter = value;
- break;
- case Platform.OSX:
- _osxFsw.Filter = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public bool IncludeSubdirectories {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.IncludeSubdirectories;
- case Platform.OSX:
- return _osxFsw.IncludeSubdirectories;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.IncludeSubdirectories = value;
- break;
- case Platform.OSX:
- _osxFsw.IncludeSubdirectories = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public int InternalBufferSize {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.InternalBufferSize;
- case Platform.OSX:
- return _osxFsw.InternalBufferSize;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.InternalBufferSize = value;
- break;
- case Platform.OSX:
- _osxFsw.InternalBufferSize = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public System.IO.NotifyFilters NotifyFilter {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.NotifyFilter;
- case Platform.OSX:
- return _osxFsw.NotifyFilter;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.NotifyFilter = value;
- break;
- case Platform.OSX:
- _osxFsw.NotifyFilter = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public string Path {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.Path;
- case Platform.OSX:
- return _osxFsw.Path;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Path = value;
- break;
- case Platform.OSX:
- _osxFsw.Path = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public event System.IO.FileSystemEventHandler Changed {
- add {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Changed += value;
- break;
- case Platform.OSX:
- _osxFsw.Changed += value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- remove {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Changed -= value;
- break;
- case Platform.OSX:
- _osxFsw.Changed -= value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public event System.IO.FileSystemEventHandler Created {
- add {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Created += value;
- break;
- case Platform.OSX:
- _osxFsw.Created += value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- remove {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Created -= value;
- break;
- case Platform.OSX:
- _osxFsw.Created -= value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public event System.IO.FileSystemEventHandler Deleted {
- add {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Deleted += value;
- break;
- case Platform.OSX:
- _osxFsw.Deleted += value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- remove {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Deleted -= value;
- break;
- case Platform.OSX:
- _osxFsw.Deleted -= value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public event System.IO.ErrorEventHandler Error {
- add {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Error += value;
- break;
- case Platform.OSX:
- _osxFsw.Error += value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- remove {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Error -= value;
- break;
- case Platform.OSX:
- _osxFsw.Error -= value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public event System.IO.RenamedEventHandler Renamed {
- add {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Renamed += value;
- break;
- case Platform.OSX:
- _osxFsw.Renamed += value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- remove {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Renamed -= value;
- break;
- case Platform.OSX:
- _osxFsw.Renamed -= value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- protected internal void OnChanged (System.IO.FileSystemEventArgs e)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.OnChanged (e);
- break;
- case Platform.OSX:
- _osxFsw.OnChanged (e);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- protected internal void OnCreated (System.IO.FileSystemEventArgs e)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.OnChanged (e);
- break;
- case Platform.OSX:
- _osxFsw.OnChanged (e);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- protected internal void OnDeleted (System.IO.FileSystemEventArgs e)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.OnDeleted (e);
- break;
- case Platform.OSX:
- _osxFsw.OnDeleted (e);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- protected internal void OnError (System.IO.ErrorEventArgs e)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.OnError (e);
- break;
- case Platform.OSX:
- _osxFsw.OnError (e);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- protected internal void OnRenamed (System.IO.RenamedEventArgs e)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.OnRenamed (e);
- break;
- case Platform.OSX:
- _osxFsw.OnRenamed (e);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- public System.IO.WaitForChangedResult WaitForChanged (System.IO.WatcherChangeTypes changeType)
- {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.WaitForChanged (changeType);
- case Platform.OSX:
- return _osxFsw.WaitForChanged (changeType);
- default:
- throw new NotImplementedException ();
- }
- }
-
- public System.IO.WaitForChangedResult WaitForChanged (System.IO.WatcherChangeTypes changeType, int timeout)
- {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.WaitForChanged (changeType, timeout);
- case Platform.OSX:
- return _osxFsw.WaitForChanged (changeType, timeout);
- default:
- throw new NotImplementedException ();
- }
- }
-
- public override System.ComponentModel.ISite Site {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.Site;
- case Platform.OSX:
- return _osxFsw.Site;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Site = value;
- break;
- case Platform.OSX:
- _osxFsw.Site = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public System.ComponentModel.ISynchronizeInvoke SynchronizingObject {
- get {
- switch (_platform) {
- case Platform.Mono:
- return _monoFsw.SynchronizingObject;
- case Platform.OSX:
- return _osxFsw.SynchronizingObject;
- default:
- throw new NotImplementedException ();
- }
- }
- set {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.SynchronizingObject = value;
- break;
- case Platform.OSX:
- _osxFsw.SynchronizingObject = value;
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-
- public void BeginInit ()
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.BeginInit ();
- break;
- case Platform.OSX:
- _osxFsw.BeginInit ();
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- protected override void Dispose (bool disposing)
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.Dispose (disposing);
- break;
- case Platform.OSX:
- _osxFsw.Dispose (disposing);
- break;
- default:
- throw new NotImplementedException ();
- }
- }
-
- public void EndInit ()
- {
- switch (_platform) {
- case Platform.Mono:
- _monoFsw.EndInit ();
- break;
- case Platform.OSX:
- _osxFsw.EndInit ();
- break;
- default:
- throw new NotImplementedException ();
- }
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.OSX.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.OSX.cs
deleted file mode 100644
index cab4ca7a64..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.OSX.cs
+++ /dev/null
@@ -1,484 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.IO;
-
-using CFStringRef = System.IntPtr;
-using FSEventStreamRef = System.IntPtr;
-using size_t = System.IntPtr;
-using FSEventStreamEventId = System.UInt64;
-using CFRunLoopRef = System.IntPtr;
-using Microsoft.Win32.SafeHandles;
-using System;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal partial class FileSystemWatcher
- {
- /// <summary>Called when FileSystemWatcher is finalized.</summary>
- private void FinalizeDispose ()
- {
- // Make sure we cleanup
- StopRaisingEvents ();
- }
-
- private void StartRaisingEvents ()
- {
- // If we're called when "Initializing" is true, set enabled to true
- if (IsSuspended ()) {
- _enabled = true;
- return;
- }
-
- // Don't start another instance if one is already runnings
- if (_cancellation != null) {
- return;
- }
-
- try {
- CancellationTokenSource cancellation = new CancellationTokenSource ();
- RunningInstance instance = new RunningInstance (this, _directory, _includeSubdirectories, TranslateFlags (_notifyFilters), cancellation.Token);
- _enabled = true;
- _cancellation = cancellation;
- instance.Start ();
-
- lock (fileSystemWatchers)
- fileSystemWatchers.Add (this);
- } catch {
- _enabled = false;
- _cancellation = null;
- throw;
- }
- }
-
- private void StopRaisingEvents ()
- {
- _enabled = false;
-
- if (IsSuspended ())
- return;
-
- lock (fileSystemWatchers)
- fileSystemWatchers.Remove (this);
-
- CancellationTokenSource token = _cancellation;
- if (token != null) {
- _cancellation = null;
- token.Cancel ();
- }
- }
-
- // -----------------------------
- // ---- PAL layer ends here ----
- // -----------------------------
-
- private CancellationTokenSource _cancellation;
-
- private static Interop.EventStream.FSEventStreamEventFlags TranslateFlags (NotifyFilters flagsToTranslate)
- {
- Interop.EventStream.FSEventStreamEventFlags flags = 0;
-
- // Always re-create the filter flags when start is called since they could have changed
- if ((flagsToTranslate & (NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Size)) != 0) {
- flags = Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemInodeMetaMod |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemFinderInfoMod |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemModified |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemChangeOwner;
- }
- if ((flagsToTranslate & NotifyFilters.Security) != 0) {
- flags |= Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemChangeOwner |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemXattrMod;
- }
- if ((flagsToTranslate & NotifyFilters.DirectoryName) != 0) {
- flags |= Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsDir |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsSymlink |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemCreated |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRemoved |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRenamed;
- }
- if ((flagsToTranslate & NotifyFilters.FileName) != 0) {
- flags |= Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsFile |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsSymlink |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemCreated |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRemoved |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRenamed;
- }
-
- return flags;
- }
-
- private sealed class RunningInstance
- {
- // Flags used to create the event stream
- private const Interop.EventStream.FSEventStreamCreateFlags EventStreamFlags = (Interop.EventStream.FSEventStreamCreateFlags.kFSEventStreamCreateFlagFileEvents |
- Interop.EventStream.FSEventStreamCreateFlags.kFSEventStreamCreateFlagNoDefer |
- Interop.EventStream.FSEventStreamCreateFlags.kFSEventStreamCreateFlagWatchRoot);
-
- // Weak reference to the associated watcher. A weak reference is used so that the FileSystemWatcher may be collected and finalized,
- // causing an active operation to be torn down.
- private readonly WeakReference<FileSystemWatcher> _weakWatcher;
-
- // The user can input relative paths, which can muck with our path comparisons. Save off the
- // actual full path so we can use it for comparing
- private string _fullDirectory;
-
- // Boolean if we allow events from nested folders
- private bool _includeChildren;
-
- // The bitmask of events that we want to send to the user
- private Interop.EventStream.FSEventStreamEventFlags _filterFlags;
-
- // The EventStream to listen for events on
- private SafeEventStreamHandle _eventStream;
-
- // A reference to the RunLoop that we can use to start or stop a Watcher
- private CFRunLoopRef _watcherRunLoop;
-
- // Callback delegate for the EventStream events
- private Interop.EventStream.FSEventStreamCallback _callback;
-
- // Token to monitor for cancellation requests, upon which processing is stopped and all
- // state is cleaned up.
- private readonly CancellationToken _cancellationToken;
-
- // Calling RunLoopStop multiple times SegFaults so protect the call to it
- private bool _stopping;
- private object StopLock => this;
-
- internal RunningInstance (
- FileSystemWatcher watcher,
- string directory,
- bool includeChildren,
- Interop.EventStream.FSEventStreamEventFlags filter,
- CancellationToken cancelToken)
- {
- Debug.Assert (string.IsNullOrEmpty (directory) == false);
- Debug.Assert (!cancelToken.IsCancellationRequested);
-
- _weakWatcher = new WeakReference<FileSystemWatcher> (watcher);
- _watcherRunLoop = IntPtr.Zero;
- _fullDirectory = System.IO.Path.GetFullPath (directory);
- _includeChildren = includeChildren;
- _filterFlags = filter;
- _cancellationToken = cancelToken;
- _cancellationToken.Register (obj => ((RunningInstance)obj).CancellationCallback (), this);
- _stopping = false;
- }
-
- private void CancellationCallback ()
- {
- lock (StopLock) {
- if (!_stopping && _watcherRunLoop != IntPtr.Zero) {
- _stopping = true;
-
- // Stop the FS event message pump
- Interop.RunLoop.CFRunLoopStop (_watcherRunLoop);
- }
- }
- }
-
- internal void Start ()
- {
- // Make sure _fullPath doesn't contain a link or alias
- // since the OS will give back the actual, non link'd or alias'd paths
- _fullDirectory = Interop.Sys.RealPath (_fullDirectory);
- if (_fullDirectory == null) {
- throw Interop.GetExceptionForIoErrno (Interop.Sys.GetLastErrorInfo (), _fullDirectory, true);
- }
-
- Debug.Assert (string.IsNullOrEmpty (_fullDirectory) == false, "Watch directory is null or empty");
-
- // Normalize the _fullDirectory path to have a trailing slash
- if (_fullDirectory [_fullDirectory.Length - 1] != '/') {
- _fullDirectory += "/";
- }
-
- // Get the path to watch and verify we created the CFStringRef
- SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString (_fullDirectory);
- if (path.IsInvalid) {
- throw Interop.GetExceptionForIoErrno (Interop.Sys.GetLastErrorInfo (), _fullDirectory, true);
- }
-
- // Take the CFStringRef and put it into an array to pass to the EventStream
- SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate (new CFStringRef [1] { path.DangerousGetHandle () }, (UIntPtr)1);
- if (arrPaths.IsInvalid) {
- path.Dispose ();
- throw Interop.GetExceptionForIoErrno (Interop.Sys.GetLastErrorInfo (), _fullDirectory, true);
- }
-
- // Create the callback for the EventStream if it wasn't previously created for this instance.
- if (_callback == null) {
- _callback = new Interop.EventStream.FSEventStreamCallback (FileSystemEventCallback);
- }
-
- // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O
- Interop.Sys.Sync ();
-
- // Create the event stream for the path and tell the stream to watch for file system events.
- _eventStream = Interop.EventStream.FSEventStreamCreate (
- _callback,
- arrPaths,
- Interop.EventStream.kFSEventStreamEventIdSinceNow,
- 0.0f,
- EventStreamFlags);
- if (_eventStream.IsInvalid) {
- arrPaths.Dispose ();
- path.Dispose ();
- throw Interop.GetExceptionForIoErrno (Interop.Sys.GetLastErrorInfo (), _fullDirectory, true);
- }
-
- // Create and start our watcher thread then wait for the thread to initialize and start
- // the RunLoop. We wait for that to prevent this function from returning before the RunLoop
- // has a chance to start so that any callers won't race with the background thread's initialization
- // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet.
- var runLoopStarted = new ManualResetEventSlim ();
- new Thread (WatchForFileSystemEventsThreadStart) { IsBackground = true }.Start (runLoopStarted);
- runLoopStarted.Wait ();
- }
-
- private void WatchForFileSystemEventsThreadStart (object arg)
- {
- var runLoopStarted = (ManualResetEventSlim)arg;
-
- // Get this thread's RunLoop
- _watcherRunLoop = Interop.RunLoop.CFRunLoopGetCurrent ();
- Debug.Assert (_watcherRunLoop != IntPtr.Zero);
-
- // Retain the RunLoop so that it doesn't get moved or cleaned up before we're done with it.
- IntPtr retainResult = Interop.CoreFoundation.CFRetain (_watcherRunLoop);
- Debug.Assert (retainResult == _watcherRunLoop, "CFRetain is supposed to return the input value");
-
- // Schedule the EventStream to run on the thread's RunLoop
- Interop.EventStream.FSEventStreamScheduleWithRunLoop (_eventStream, _watcherRunLoop, Interop.RunLoop.kCFRunLoopDefaultMode);
-
- try {
- bool started = Interop.EventStream.FSEventStreamStart (_eventStream);
-
- // Notify the StartRaisingEvents call that we are initialized and about to start
- // so that it can return and avoid a race-condition around multiple threads calling Stop and Start
- runLoopStarted.Set ();
-
- if (started) {
- // Start the OS X RunLoop (a blocking call) that will pump file system changes into the callback function
- Interop.RunLoop.CFRunLoopRun ();
-
- // When we get here, we've requested to stop so cleanup the EventStream and unschedule from the RunLoop
- Interop.EventStream.FSEventStreamStop (_eventStream);
- } else {
- // Try to get the Watcher to raise the error event; if we can't do that, just silently exist since the watcher is gone anyway
- FileSystemWatcher watcher;
- if (_weakWatcher.TryGetTarget (out watcher)) {
- // An error occurred while trying to start the run loop so fail out
- watcher.OnError (new ErrorEventArgs (new IOException (SR.EventStream_FailedToStart, Marshal.GetLastWin32Error ())));
- }
- }
- } finally {
- // Always unschedule the RunLoop before cleaning up
- Interop.EventStream.FSEventStreamUnscheduleFromRunLoop (_eventStream, _watcherRunLoop, Interop.RunLoop.kCFRunLoopDefaultMode);
-
- // Release the WatcherLoop Core Foundation object.
- lock (StopLock) {
- Interop.CoreFoundation.CFRelease (_watcherRunLoop);
- _watcherRunLoop = IntPtr.Zero;
- }
- }
- }
-
- private void FileSystemEventCallback (
- FSEventStreamRef streamRef,
- IntPtr clientCallBackInfo,
- size_t numEvents,
- String [] eventPaths,
- [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
- Interop.EventStream.FSEventStreamEventFlags[] eventFlags,
- [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
- FSEventStreamEventId[] eventIds)
- {
- Debug.Assert ((numEvents.ToInt32 () == eventPaths.Length) && (numEvents.ToInt32 () == eventFlags.Length) && (numEvents.ToInt32 () == eventIds.Length));
-
- // Try to get the actual watcher from our weak reference. We maintain a weak reference most of the time
- // so as to avoid a rooted cycle that would prevent our processing loop from ever ending
- // if the watcher is dropped by the user without being disposed. If we can't get the watcher,
- // there's nothing more to do (we can't raise events), so bail.
- FileSystemWatcher watcher;
- if (!_weakWatcher.TryGetTarget (out watcher)) {
- CancellationCallback ();
- return;
- }
-
- // Since renames come in pairs, when we find the first we need to search for the next one. Once we find it, we'll add it to this
- // list so when the for-loop comes across it, we'll skip it since it's already been processed as part of the original of the pair.
- List<FSEventStreamEventId> handledRenameEvents = null;
-
- for (long i = 0; i < numEvents.ToInt32 (); i++) {
- Debug.Assert (eventPaths [i].Length > 0, "Empty events are not supported");
- Debug.Assert (eventPaths [i] [eventPaths [i].Length - 1] != '/', "Trailing slashes on events is not supported");
-
- // Match Windows and don't notify us about changes to the Root folder
- string path = eventPaths [i];
- if (string.Compare (path, 0, _fullDirectory, 0, path.Length, StringComparison.OrdinalIgnoreCase) == 0) {
- continue;
- }
-
- WatcherChangeTypes eventType = 0;
- // First, we should check if this event should kick off a re-scan since we can't really rely on anything after this point if that is true
- if (ShouldRescanOccur (eventFlags [i])) {
- watcher.OnError (new ErrorEventArgs (new IOException (SR.FSW_BufferOverflow, (int)eventFlags [i])));
- break;
- } else if ((handledRenameEvents != null) && (handledRenameEvents.Contains (eventIds [i]))) {
- // If this event is the second in a rename pair then skip it
- continue;
- } else if (CheckIfPathIsNested (path) && ((eventType = FilterEvents (eventFlags [i], path)) != 0)) {
- // The base FileSystemWatcher does a match check against the relative path before combining with
- // the root dir; however, null is special cased to signify the root dir, so check if we should use that.
- string relativePath = null;
- if (path.Equals (_fullDirectory, StringComparison.OrdinalIgnoreCase) == false) {
- // Remove the root directory to get the relative path
- relativePath = path.Remove (0, _fullDirectory.Length);
- }
-
- // Raise a notification for the event
- if (((eventType & WatcherChangeTypes.Changed) > 0)) {
- watcher.NotifyFileSystemEventArgs (WatcherChangeTypes.Changed, relativePath);
- }
- if (((eventType & WatcherChangeTypes.Created) > 0)) {
- watcher.NotifyFileSystemEventArgs (WatcherChangeTypes.Created, relativePath);
- }
- if (((eventType & WatcherChangeTypes.Deleted) > 0)) {
- watcher.NotifyFileSystemEventArgs (WatcherChangeTypes.Deleted, relativePath);
- }
- if (((eventType & WatcherChangeTypes.Renamed) > 0)) {
- // Find the rename that is paired to this rename, which should be the next rename in the list
- long pairedId = FindRenameChangePairedChange (i, eventPaths, eventFlags, eventIds);
- if (pairedId == long.MinValue) {
- // Getting here means we have a rename without a pair, meaning it should be a create for the
- // move from unwatched folder to watcher folder scenario or a move from the watcher folder out.
- // Check if the item exists on disk to check which it is
- // Don't send a new notification if we already sent one for this event.
- if (DoesItemExist (path, IsFlagSet (eventFlags [i], Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsFile))) {
- if ((eventType & WatcherChangeTypes.Created) == 0) {
- watcher.NotifyFileSystemEventArgs (WatcherChangeTypes.Created, relativePath);
- }
- } else if ((eventType & WatcherChangeTypes.Deleted) == 0) {
- watcher.NotifyFileSystemEventArgs (WatcherChangeTypes.Deleted, relativePath);
- }
- } else {
- // Remove the base directory prefix and add the paired event to the list of
- // events to skip and notify the user of the rename
- string newPathRelativeName = eventPaths [pairedId].Remove (0, _fullDirectory.Length);
- watcher.NotifyRenameEventArgs (WatcherChangeTypes.Renamed, newPathRelativeName, relativePath);
-
- // Create a new list, if necessary, and add the event
- if (handledRenameEvents == null) {
- handledRenameEvents = new List<FSEventStreamEventId> ();
- }
- handledRenameEvents.Add (eventIds [pairedId]);
- }
- }
- }
- }
- }
-
- /// <summary>
- /// Compares the given event flags to the filter flags and returns which event (if any) corresponds
- /// to those flags.
- /// </summary>
- private WatcherChangeTypes FilterEvents (Interop.EventStream.FSEventStreamEventFlags eventFlags, string fullPath)
- {
- const Interop.EventStream.FSEventStreamEventFlags changedFlags = Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemInodeMetaMod |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemFinderInfoMod |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemModified |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemChangeOwner |
- Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemXattrMod;
- WatcherChangeTypes eventType = 0;
- // If any of the Changed flags are set in both Filter and Event then a Changed event has occurred.
- if (((_filterFlags & changedFlags) & (eventFlags & changedFlags)) > 0) {
- eventType |= WatcherChangeTypes.Changed;
- }
-
- // Notify created/deleted/renamed events if they pass through the filters
- bool allowDirs = (_filterFlags & Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsDir) > 0;
- bool allowFiles = (_filterFlags & Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsFile) > 0;
- bool isDir = (eventFlags & Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsDir) > 0;
- bool isFile = (eventFlags & Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsFile) > 0;
- bool eventIsCorrectType = (isDir && allowDirs) || (isFile && allowFiles);
- bool eventIsLink = (eventFlags & (Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsHardlink | Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsSymlink | Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemIsLastHardlink)) > 0;
-
- if (eventIsCorrectType || ((allowDirs || allowFiles) && (eventIsLink))) {
- // Notify Created/Deleted/Renamed events.
- if (IsFlagSet (eventFlags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRenamed)) {
- eventType |= WatcherChangeTypes.Renamed;
- }
- if (IsFlagSet (eventFlags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemCreated)) {
- eventType |= WatcherChangeTypes.Created;
- }
- if (IsFlagSet (eventFlags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRemoved)) {
- eventType |= WatcherChangeTypes.Deleted;
- }
- }
- return eventType;
- }
-
- private bool ShouldRescanOccur (Interop.EventStream.FSEventStreamEventFlags flags)
- {
- // Check if any bit is set that signals that the caller should rescan
- return (IsFlagSet (flags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagMustScanSubDirs) ||
- IsFlagSet (flags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagUserDropped) ||
- IsFlagSet (flags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagKernelDropped) ||
- IsFlagSet (flags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagRootChanged) ||
- IsFlagSet (flags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagMount) ||
- IsFlagSet (flags, Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagUnmount));
- }
-
- private bool CheckIfPathIsNested (string eventPath)
- {
- bool doesPathPass = true;
-
- // If we shouldn't include subdirectories, check if this path's parent is the watch directory
- if (_includeChildren == false) {
- // Check if the parent is the root. If so, then we'll continue processing based on the name.
- // If it isn't, then this will be set to false and we'll skip the name processing since it's irrelevant.
- string parent = System.IO.Path.GetDirectoryName (eventPath);
- doesPathPass = (string.Compare (parent, 0, _fullDirectory, 0, parent.Length, StringComparison.OrdinalIgnoreCase) == 0);
- }
-
- return doesPathPass;
- }
-
- private long FindRenameChangePairedChange (
- long currentIndex,
- String [] eventPaths,
- Interop.EventStream.FSEventStreamEventFlags [] eventFlags,
- FSEventStreamEventId [] eventIds)
- {
- // Start at one past the current index and try to find the next Rename item, which should be the old path.
- for (long i = currentIndex + 1; i < eventPaths.Length; i++) {
- if (IsFlagSet (eventFlags [i], Interop.EventStream.FSEventStreamEventFlags.kFSEventStreamEventFlagItemRenamed)) {
- // We found match, stop looking
- return i;
- }
- }
-
- return long.MinValue;
- }
-
- private static bool IsFlagSet (Interop.EventStream.FSEventStreamEventFlags flags, Interop.EventStream.FSEventStreamEventFlags value)
- {
- return (value & flags) == value;
- }
-
- private static bool DoesItemExist (string path, bool isFile)
- {
- if (isFile)
- return File.Exists (path);
- else
- return Directory.Exists (path);
- }
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.cs
deleted file mode 100644
index f34689ae9e..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/FileSystemWatcher.cs
+++ /dev/null
@@ -1,603 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Threading;
-using System.Threading.Tasks;
-using System.IO;
-using System;
-using System.Collections.Generic;
-
-namespace MonoDevelop.FSW.OSX
-{
- /// <devdoc>
- /// Listens to the system directory change notifications and
- /// raises events when a directory or file within a directory changes.
- /// </devdoc>
-
- internal partial class FileSystemWatcher : Component, ISupportInitialize
- {
- /// <devdoc>
- /// Private instance variables
- /// </devdoc>
- // Directory being monitored
- private string _directory;
-
- // Filter for name matching
- private string _filter;
-
- // The watch filter for the API call.
- private const NotifyFilters c_defaultNotifyFilters = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
- private NotifyFilters _notifyFilters = c_defaultNotifyFilters;
-
- // Flag to watch subtree of this directory
- private bool _includeSubdirectories = false;
-
- // Flag to note whether we are attached to the thread pool and responding to changes
- private bool _enabled = false;
-
- // Are we in init?
- private bool _initializing = false;
-
- // Buffer size
- private int _internalBufferSize = 8192;
-
- // Used for synchronization
- private bool _disposed;
- private ISynchronizeInvoke _synchronizingObject;
-
- // Event handlers
- private FileSystemEventHandler _onChangedHandler = null;
- private FileSystemEventHandler _onCreatedHandler = null;
- private FileSystemEventHandler _onDeletedHandler = null;
- private RenamedEventHandler _onRenamedHandler = null;
- private ErrorEventHandler _onErrorHandler = null;
-
- // To validate the input for "path"
- private static readonly char [] s_wildcards = new char [] { '?', '*' };
-
- private const int c_notifyFiltersValidMask = (int)(NotifyFilters.Attributes |
- NotifyFilters.CreationTime |
- NotifyFilters.DirectoryName |
- NotifyFilters.FileName |
- NotifyFilters.LastAccess |
- NotifyFilters.LastWrite |
- NotifyFilters.Security |
- NotifyFilters.Size);
-
-#if DEBUG
- static FileSystemWatcher ()
- {
- int s_notifyFiltersValidMask = 0;
- foreach (int enumValue in Enum.GetValues (typeof (NotifyFilters)))
- s_notifyFiltersValidMask |= enumValue;
- Debug.Assert (c_notifyFiltersValidMask == s_notifyFiltersValidMask, "The NotifyFilters enum has changed. The c_notifyFiltersValidMask must be updated to reflect the values of the NotifyFilters enum.");
- }
-#endif
- static HashSet<FileSystemWatcher> fileSystemWatchers = new HashSet<FileSystemWatcher> ();
- public static void DisposeAll()
- {
- lock (fileSystemWatchers) {
- foreach (var fsw in fileSystemWatchers) {
- fsw.Dispose ();
- }
- fileSystemWatchers.Clear ();
- }
- }
-
- /// <devdoc>
- /// Initializes a new instance of the <see cref='System.IO.FileSystemWatcher'/> class.
- /// </devdoc>
- public FileSystemWatcher ()
- {
- _directory = string.Empty;
- _filter = "*.*";
- }
-
- /// <devdoc>
- /// Initializes a new instance of the <see cref='System.IO.FileSystemWatcher'/> class,
- /// given the specified directory to monitor.
- /// </devdoc>
- public FileSystemWatcher (string path) : this (path, "*.*")
- {
- }
-
- /// <devdoc>
- /// Initializes a new instance of the <see cref='System.IO.FileSystemWatcher'/> class,
- /// given the specified directory and type of files to monitor.
- /// </devdoc>
- public FileSystemWatcher (string path, string filter)
- {
- if (path == null)
- throw new ArgumentNullException (nameof (path));
-
- if (filter == null)
- throw new ArgumentNullException (nameof (filter));
-
- // Early check for directory parameter so that an exception can be thrown as early as possible.
- if (path.Length == 0 || !Directory.Exists (path))
- throw new ArgumentException (SR.Format (SR.InvalidDirName, path), nameof (path));
-
- _directory = path;
- _filter = filter;
- }
-
- /// <devdoc>
- /// Gets or sets the type of changes to watch for.
- /// </devdoc>
- public NotifyFilters NotifyFilter {
- get {
- return _notifyFilters;
- }
- set {
- if (((int)value & ~c_notifyFiltersValidMask) != 0)
- throw new ArgumentException (SR.Format (SR.InvalidEnumArgument, nameof (value), (int)value, nameof (NotifyFilters)));
-
- if (_notifyFilters != value) {
- _notifyFilters = value;
-
- Restart ();
- }
- }
- }
-
- /// <devdoc>
- /// Gets or sets a value indicating whether the component is enabled.
- /// </devdoc>
- public bool EnableRaisingEvents {
- get {
- return _enabled;
- }
- set {
- if (_enabled == value) {
- return;
- }
-
- if (IsSuspended ()) {
- _enabled = value; // Alert the Component to start watching for events when EndInit is called.
- } else {
- if (value) {
- StartRaisingEventsIfNotDisposed (); // will set _enabled to true once successfully started
- } else {
- StopRaisingEvents (); // will set _enabled to false
- }
- }
- }
- }
-
- /// <devdoc>
- /// Gets or sets the filter string, used to determine what files are monitored in a directory.
- /// </devdoc>
- public string Filter {
- get {
- return _filter;
- }
- set {
- if (string.IsNullOrEmpty (value)) {
- // Skip the string compare for "*.*" since it has no case-insensitive representation that differs from
- // the case-sensitive representation.
- _filter = "*.*";
- } else if (!string.Equals (_filter, value, PathInternal.StringComparison)) {
- _filter = value;
- }
- }
- }
-
- /// <devdoc>
- /// Gets or sets a value indicating whether subdirectories within the specified path should be monitored.
- /// </devdoc>
- public bool IncludeSubdirectories {
- get {
- return _includeSubdirectories;
- }
- set {
- if (_includeSubdirectories != value) {
- _includeSubdirectories = value;
-
- Restart ();
- }
- }
- }
-
- /// <devdoc>
- /// Gets or sets the size of the internal buffer.
- /// </devdoc>
- public int InternalBufferSize {
- get {
- return _internalBufferSize;
- }
- set {
- if (_internalBufferSize != value) {
- if (value < 4096) {
- _internalBufferSize = 4096;
- } else {
- _internalBufferSize = value;
- }
-
- Restart ();
- }
- }
- }
-
- /// <summary>Allocates a buffer of the requested internal buffer size.</summary>
- /// <returns>The allocated buffer.</returns>
- private byte [] AllocateBuffer ()
- {
- try {
- return new byte [_internalBufferSize];
- } catch (OutOfMemoryException) {
- throw new OutOfMemoryException (SR.Format (SR.BufferSizeTooLarge, _internalBufferSize));
- }
- }
-
- /// <devdoc>
- /// Gets or sets the path of the directory to watch.
- /// </devdoc>
- public string Path {
- get {
- return _directory;
- }
- set {
- value = (value == null) ? string.Empty : value;
- if (!string.Equals (_directory, value, PathInternal.StringComparison)) {
- if (!Directory.Exists (value)) {
- throw new ArgumentException (SR.Format (SR.InvalidDirName, value));
- }
-
- _directory = value;
- Restart ();
- }
- }
- }
-
- /// <devdoc>
- /// Occurs when a file or directory in the specified <see cref='System.IO.FileSystemWatcher.Path'/> is changed.
- /// </devdoc>
- public event FileSystemEventHandler Changed {
- add {
- _onChangedHandler += value;
- }
- remove {
- _onChangedHandler -= value;
- }
- }
-
- /// <devdoc>
- /// Occurs when a file or directory in the specified <see cref='System.IO.FileSystemWatcher.Path'/> is created.
- /// </devdoc>
- public event FileSystemEventHandler Created {
- add {
- _onCreatedHandler += value;
- }
- remove {
- _onCreatedHandler -= value;
- }
- }
-
- /// <devdoc>
- /// Occurs when a file or directory in the specified <see cref='System.IO.FileSystemWatcher.Path'/> is deleted.
- /// </devdoc>
- public event FileSystemEventHandler Deleted {
- add {
- _onDeletedHandler += value;
- }
- remove {
- _onDeletedHandler -= value;
- }
- }
-
- /// <devdoc>
- /// Occurs when the internal buffer overflows.
- /// </devdoc>
- public event ErrorEventHandler Error {
- add {
- _onErrorHandler += value;
- }
- remove {
- _onErrorHandler -= value;
- }
- }
-
- /// <devdoc>
- /// Occurs when a file or directory in the specified <see cref='System.IO.FileSystemWatcher.Path'/>
- /// is renamed.
- /// </devdoc>
- public event RenamedEventHandler Renamed {
- add {
- _onRenamedHandler += value;
- }
- remove {
- _onRenamedHandler -= value;
- }
- }
-
- /// <devdoc>
- /// </devdoc>
- protected internal new void Dispose (bool disposing)
- {
- try {
- if (disposing) {
- //Stop raising events cleans up managed and
- //unmanaged resources.
- StopRaisingEvents ();
-
- // Clean up managed resources
- _onChangedHandler = null;
- _onCreatedHandler = null;
- _onDeletedHandler = null;
- _onRenamedHandler = null;
- _onErrorHandler = null;
- } else {
- FinalizeDispose ();
- }
- } finally {
- _disposed = true;
- base.Dispose (disposing);
- }
- }
-
- /// <devdoc>
- /// Sees if the name given matches the name filter we have.
- /// </devdoc>
- /// <internalonly/>
- private bool MatchPattern (string relativePath)
- {
- string name = System.IO.Path.GetFileName (relativePath);
- return name != null ?
- PatternMatcher.StrictMatchPattern (_filter, name) :
- false;
- }
-
- /// <devdoc>
- /// Raises the event to each handler in the list.
- /// </devdoc>
- /// <internalonly/>
- private void NotifyInternalBufferOverflowEvent ()
- {
- _onErrorHandler?.Invoke (this, new ErrorEventArgs (
- new InternalBufferOverflowException (SR.Format (SR.FSW_BufferOverflow, _directory))));
- }
-
- /// <devdoc>
- /// Raises the event to each handler in the list.
- /// </devdoc>
- /// <internalonly/>
- private void NotifyRenameEventArgs (WatcherChangeTypes action, string name, string oldName)
- {
- // filter if there's no handler or neither new name or old name match a specified pattern
- RenamedEventHandler handler = _onRenamedHandler;
- if (handler != null &&
- (MatchPattern (name) || MatchPattern (oldName))) {
- handler (this, new RenamedEventArgs (action, _directory, name, oldName));
- }
- }
-
- /// <devdoc>
- /// Raises the event to each handler in the list.
- /// </devdoc>
- /// <internalonly/>
- private void NotifyFileSystemEventArgs (WatcherChangeTypes changeType, string name)
- {
- FileSystemEventHandler handler = null;
- switch (changeType) {
- case WatcherChangeTypes.Created:
- handler = _onCreatedHandler;
- break;
- case WatcherChangeTypes.Deleted:
- handler = _onDeletedHandler;
- break;
- case WatcherChangeTypes.Changed:
- handler = _onChangedHandler;
- break;
- default:
- Debug.Fail ("Unknown FileSystemEvent change type! Value: " + changeType);
- break;
- }
-
- if (handler != null && MatchPattern (string.IsNullOrEmpty (name) ? _directory : name)) {
- handler (this, new FileSystemEventArgs (changeType, _directory, name));
- }
- }
-
- /// <devdoc>
- /// Raises the <see cref='System.IO.FileSystemWatcher.Changed'/> event.
- /// </devdoc>
- [SuppressMessage ("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")]
- protected internal void OnChanged (FileSystemEventArgs e)
- {
- InvokeOn (e, _onChangedHandler);
- }
-
- /// <devdoc>
- /// Raises the <see cref='System.IO.FileSystemWatcher.Created'/> event.
- /// </devdoc>
- [SuppressMessage ("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")]
- protected internal void OnCreated (FileSystemEventArgs e)
- {
- InvokeOn (e, _onCreatedHandler);
- }
-
- /// <devdoc>
- /// Raises the <see cref='System.IO.FileSystemWatcher.Deleted'/> event.
- /// </devdoc>
- [SuppressMessage ("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")]
- protected internal void OnDeleted (FileSystemEventArgs e)
- {
- InvokeOn (e, _onDeletedHandler);
- }
-
- private void InvokeOn (FileSystemEventArgs e, FileSystemEventHandler handler)
- {
- if (handler != null) {
- ISynchronizeInvoke syncObj = SynchronizingObject;
- if (syncObj != null && syncObj.InvokeRequired)
- syncObj.BeginInvoke (handler, new object [] { this, e });
- else
- handler (this, e);
- }
- }
-
- /// <devdoc>
- /// Raises the <see cref='System.IO.FileSystemWatcher.Error'/> event.
- /// </devdoc>
- [SuppressMessage ("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")]
- protected internal void OnError (ErrorEventArgs e)
- {
- ErrorEventHandler handler = _onErrorHandler;
- if (handler != null) {
- ISynchronizeInvoke syncObj = SynchronizingObject;
- if (syncObj != null && syncObj.InvokeRequired)
- syncObj.BeginInvoke (handler, new object [] { this, e });
- else
- handler (this, e);
- }
- }
-
- /// <devdoc>
- /// Raises the <see cref='System.IO.FileSystemWatcher.Renamed'/> event.
- /// </devdoc>
- [SuppressMessage ("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#", Justification = "Changing from protected to private would be a breaking change")]
- protected internal void OnRenamed (RenamedEventArgs e)
- {
- RenamedEventHandler handler = _onRenamedHandler;
- if (handler != null) {
- ISynchronizeInvoke syncObj = SynchronizingObject;
- if (syncObj != null && syncObj.InvokeRequired)
- syncObj.BeginInvoke (handler, new object [] { this, e });
- else
- handler (this, e);
- }
- }
-
- public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType) =>
- WaitForChanged (changeType, Timeout.Infinite);
-
- public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType, int timeout)
- {
- // The full framework implementation doesn't do any argument validation, so
- // none is done here, either.
-
- var tcs = new TaskCompletionSource<WaitForChangedResult> ();
- FileSystemEventHandler fseh = null;
- RenamedEventHandler reh = null;
-
- // Register the event handlers based on what events are desired. The full framework
- // doesn't register for the Error event, so this doesn't either.
- if ((changeType & (WatcherChangeTypes.Created | WatcherChangeTypes.Deleted | WatcherChangeTypes.Changed)) != 0) {
- fseh = (s, e) => {
- if ((e.ChangeType & changeType) != 0) {
- tcs.TrySetResult (new WaitForChangedResult { ChangeType = e.ChangeType, Name = e.Name, OldName = null, TimedOut = false });
- }
- };
- if ((changeType & WatcherChangeTypes.Created) != 0) Created += fseh;
- if ((changeType & WatcherChangeTypes.Deleted) != 0) Deleted += fseh;
- if ((changeType & WatcherChangeTypes.Changed) != 0) Changed += fseh;
- }
- if ((changeType & WatcherChangeTypes.Renamed) != 0) {
- reh = (s, e) => {
- if ((e.ChangeType & changeType) != 0) {
- tcs.TrySetResult (new WaitForChangedResult { ChangeType = e.ChangeType, Name = e.Name, OldName = e.OldName, TimedOut = false });
- }
- };
- Renamed += reh;
- }
- try {
- // Enable the FSW if it wasn't already.
- bool wasEnabled = EnableRaisingEvents;
- if (!wasEnabled) {
- EnableRaisingEvents = true;
- }
-
- // Block until an appropriate event arrives or until we timeout.
- Debug.Assert (EnableRaisingEvents, "Expected EnableRaisingEvents to be true");
- tcs.Task.Wait (timeout);
-
- // Reset the enabled state to what it was.
- EnableRaisingEvents = wasEnabled;
- } finally {
- // Unregister the event handlers.
- if (reh != null) {
- Renamed -= reh;
- }
- if (fseh != null) {
- if ((changeType & WatcherChangeTypes.Changed) != 0) Changed -= fseh;
- if ((changeType & WatcherChangeTypes.Deleted) != 0) Deleted -= fseh;
- if ((changeType & WatcherChangeTypes.Created) != 0) Created -= fseh;
- }
- }
-
- // Return the results.
- return tcs.Task.Status == TaskStatus.RanToCompletion ?
- tcs.Task.Result :
- new WaitForChangedResult { ChangeType = 0, Name = null, OldName = null, TimedOut = true };
- }
-
- /// <devdoc>
- /// Stops and starts this object.
- /// </devdoc>
- /// <internalonly/>
- private void Restart ()
- {
- if ((!IsSuspended ()) && _enabled) {
- StopRaisingEvents ();
- StartRaisingEventsIfNotDisposed ();
- }
- }
-
- private void StartRaisingEventsIfNotDisposed ()
- {
- //Cannot allocate the directoryHandle and the readBuffer if the object has been disposed; finalization has been suppressed.
- if (_disposed)
- throw new ObjectDisposedException (GetType ().Name);
- StartRaisingEvents ();
- }
-
- public override ISite Site {
- get {
- return base.Site;
- }
- set {
- base.Site = value;
-
- // set EnableRaisingEvents to true at design time so the user
- // doesn't have to manually.
- if (Site != null && Site.DesignMode)
- EnableRaisingEvents = true;
- }
- }
-
- public ISynchronizeInvoke SynchronizingObject {
- get {
- return _synchronizingObject;
- }
-
- set {
- _synchronizingObject = value;
- }
- }
-
- public void BeginInit ()
- {
- bool oldEnabled = _enabled;
- StopRaisingEvents ();
- _enabled = oldEnabled;
- _initializing = true;
- }
-
- public void EndInit ()
- {
- _initializing = false;
- // Start listening to events if _enabled was set to true at some point.
- if (_directory.Length != 0 && _enabled)
- StartRaisingEvents ();
- }
-
- private bool IsSuspended ()
- {
- return _initializing || DesignMode;
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.CoreFoundation.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.CoreFoundation.cs
deleted file mode 100644
index 292a69ba91..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.CoreFoundation.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-using Microsoft.Win32.SafeHandles;
-
-using CFStringRef = System.IntPtr;
-using CFArrayRef = System.IntPtr;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- internal static partial class CoreFoundation
- {
- /// <summary>
- /// Tells the OS what encoding the passed in String is in. These come from the CFString.h header file in the CoreFoundation framework.
- /// </summary>
- private enum CFStringBuiltInEncodings : uint
- {
- kCFStringEncodingMacRoman = 0,
- kCFStringEncodingWindowsLatin1 = 0x0500,
- kCFStringEncodingISOLatin1 = 0x0201,
- kCFStringEncodingNextStepLatin = 0x0B01,
- kCFStringEncodingASCII = 0x0600,
- kCFStringEncodingUnicode = 0x0100,
- kCFStringEncodingUTF8 = 0x08000100,
- kCFStringEncodingNonLossyASCII = 0x0BFF,
-
- kCFStringEncodingUTF16 = 0x0100,
- kCFStringEncodingUTF16BE = 0x10000100,
- kCFStringEncodingUTF16LE = 0x14000100,
- kCFStringEncodingUTF32 = 0x0c000100,
- kCFStringEncodingUTF32BE = 0x18000100,
- kCFStringEncodingUTF32LE = 0x1c000100
- }
-
- /// <summary>
- /// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
- /// </summary>
- /// <param name="allocator">Should be IntPtr.Zero</param>
- /// <param name="str">The string to get a CFStringRef for</param>
- /// <param name="encoding">The encoding of the str variable. This should be UTF 8 for OS X</param>
- /// <returns>Returns a pointer to a CFString on success; otherwise, returns IntPtr.Zero</returns>
- /// <remarks>For *nix systems, the CLR maps ANSI to UTF-8, so be explicit about that</remarks>
- [DllImport (Interop.Libraries.CoreFoundationLibrary, CharSet = CharSet.Ansi)]
- private static extern SafeCreateHandle CFStringCreateWithCString (
- IntPtr allocator,
- string str,
- CFStringBuiltInEncodings encoding);
-
- /// <summary>
- /// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
- /// </summary>
- /// <param name="str">The string to get a CFStringRef for</param>
- /// <returns>Returns a valid SafeCreateHandle to a CFString on success; otherwise, returns an invalid SafeCreateHandle</returns>
- internal static SafeCreateHandle CFStringCreateWithCString (string str)
- {
- return CFStringCreateWithCString (IntPtr.Zero, str, CFStringBuiltInEncodings.kCFStringEncodingUTF8);
- }
-
- /// <summary>
- /// Creates a pointer to an unmanaged CFArray containing the input values. Follows the "Create Rule" where if you create it, you delete it.
- /// </summary>
- /// <param name="allocator">Should be IntPtr.Zero</param>
- /// <param name="values">The values to put in the array</param>
- /// <param name="numValues">The number of values in the array</param>
- /// <param name="callbacks">Should be IntPtr.Zero</param>
- /// <returns>Returns a pointer to a CFArray on success; otherwise, returns IntPtr.Zero</returns>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- private static extern SafeCreateHandle CFArrayCreate (
- IntPtr allocator,
- [MarshalAs(UnmanagedType.LPArray)]
- IntPtr[] values,
- UIntPtr numValues,
- IntPtr callbacks);
-
- /// <summary>
- /// Creates a pointer to an unmanaged CFArray containing the input values. Follows the "Create Rule" where if you create it, you delete it.
- /// </summary>
- /// <param name="values">The values to put in the array</param>
- /// <param name="numValues">The number of values in the array</param>
- /// <returns>Returns a valid SafeCreateHandle to a CFArray on success; otherwise, returns an invalid SafeCreateHandle</returns>
- internal static SafeCreateHandle CFArrayCreate (IntPtr [] values, UIntPtr numValues)
- {
- return CFArrayCreate (IntPtr.Zero, values, numValues, IntPtr.Zero);
- }
-
- /// <summary>
- /// You should retain a Core Foundation object when you receive it from elsewhere
- /// (that is, you did not create or copy it) and you want it to persist. If you
- /// retain a Core Foundation object you are responsible for releasing it
- /// </summary>
- /// <param name="ptr">The CFType object to retain. This value must not be NULL</param>
- /// <returns>The input value</returns>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal extern static IntPtr CFRetain (IntPtr ptr);
-
- /// <summary>
- /// Decrements the reference count on the specified object and, if the ref count hits 0, cleans up the object.
- /// </summary>
- /// <param name="ptr">The pointer on which to decrement the reference count.</param>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal extern static void CFRelease (IntPtr ptr);
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Error.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Error.cs
deleted file mode 100644
index 7356bf5ace..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Error.cs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- /// <summary>Common Unix errno error codes.</summary>
- internal enum Error
- {
- // These values were defined in src/Native/System.Native/fxerrno.h
- //
- // They compare against values obtained via Interop.Sys.GetLastError() not Marshal.GetLastWin32Error()
- // which obtains the raw errno that varies between unixes. The strong typing as an enum is meant to
- // prevent confusing the two. Casting to or from int is suspect. Use GetLastErrorInfo() if you need to
- // correlate these to the underlying platform values or obtain the corresponding error message.
- //
-
- SUCCESS = 0,
-
- E2BIG = 0x10001, // Argument list too long.
- EACCES = 0x10002, // Permission denied.
- EADDRINUSE = 0x10003, // Address in use.
- EADDRNOTAVAIL = 0x10004, // Address not available.
- EAFNOSUPPORT = 0x10005, // Address family not supported.
- EAGAIN = 0x10006, // Resource unavailable, try again (same value as EWOULDBLOCK),
- EALREADY = 0x10007, // Connection already in progress.
- EBADF = 0x10008, // Bad file descriptor.
- EBADMSG = 0x10009, // Bad message.
- EBUSY = 0x1000A, // Device or resource busy.
- ECANCELED = 0x1000B, // Operation canceled.
- ECHILD = 0x1000C, // No child processes.
- ECONNABORTED = 0x1000D, // Connection aborted.
- ECONNREFUSED = 0x1000E, // Connection refused.
- ECONNRESET = 0x1000F, // Connection reset.
- EDEADLK = 0x10010, // Resource deadlock would occur.
- EDESTADDRREQ = 0x10011, // Destination address required.
- EDOM = 0x10012, // Mathematics argument out of domain of function.
- EDQUOT = 0x10013, // Reserved.
- EEXIST = 0x10014, // File exists.
- EFAULT = 0x10015, // Bad address.
- EFBIG = 0x10016, // File too large.
- EHOSTUNREACH = 0x10017, // Host is unreachable.
- EIDRM = 0x10018, // Identifier removed.
- EILSEQ = 0x10019, // Illegal byte sequence.
- EINPROGRESS = 0x1001A, // Operation in progress.
- EINTR = 0x1001B, // Interrupted function.
- EINVAL = 0x1001C, // Invalid argument.
- EIO = 0x1001D, // I/O error.
- EISCONN = 0x1001E, // Socket is connected.
- EISDIR = 0x1001F, // Is a directory.
- ELOOP = 0x10020, // Too many levels of symbolic links.
- EMFILE = 0x10021, // File descriptor value too large.
- EMLINK = 0x10022, // Too many links.
- EMSGSIZE = 0x10023, // Message too large.
- EMULTIHOP = 0x10024, // Reserved.
- ENAMETOOLONG = 0x10025, // Filename too long.
- ENETDOWN = 0x10026, // Network is down.
- ENETRESET = 0x10027, // Connection aborted by network.
- ENETUNREACH = 0x10028, // Network unreachable.
- ENFILE = 0x10029, // Too many files open in system.
- ENOBUFS = 0x1002A, // No buffer space available.
- ENODEV = 0x1002C, // No such device.
- ENOENT = 0x1002D, // No such file or directory.
- ENOEXEC = 0x1002E, // Executable file format error.
- ENOLCK = 0x1002F, // No locks available.
- ENOLINK = 0x10030, // Reserved.
- ENOMEM = 0x10031, // Not enough space.
- ENOMSG = 0x10032, // No message of the desired type.
- ENOPROTOOPT = 0x10033, // Protocol not available.
- ENOSPC = 0x10034, // No space left on device.
- ENOSYS = 0x10037, // Function not supported.
- ENOTCONN = 0x10038, // The socket is not connected.
- ENOTDIR = 0x10039, // Not a directory or a symbolic link to a directory.
- ENOTEMPTY = 0x1003A, // Directory not empty.
- ENOTRECOVERABLE = 0x1003B, // State not recoverable.
- ENOTSOCK = 0x1003C, // Not a socket.
- ENOTSUP = 0x1003D, // Not supported (same value as EOPNOTSUP).
- ENOTTY = 0x1003E, // Inappropriate I/O control operation.
- ENXIO = 0x1003F, // No such device or address.
- EOVERFLOW = 0x10040, // Value too large to be stored in data type.
- EOWNERDEAD = 0x10041, // Previous owner died.
- EPERM = 0x10042, // Operation not permitted.
- EPIPE = 0x10043, // Broken pipe.
- EPROTO = 0x10044, // Protocol error.
- EPROTONOSUPPORT = 0x10045, // Protocol not supported.
- EPROTOTYPE = 0x10046, // Protocol wrong type for socket.
- ERANGE = 0x10047, // Result too large.
- EROFS = 0x10048, // Read-only file system.
- ESPIPE = 0x10049, // Invalid seek.
- ESRCH = 0x1004A, // No such process.
- ESTALE = 0x1004B, // Reserved.
- ETIMEDOUT = 0x1004D, // Connection timed out.
- ETXTBSY = 0x1004E, // Text file busy.
- EXDEV = 0x1004F, // Cross-device link.
- ESOCKTNOSUPPORT = 0x1005E, // Socket type not supported.
- EPFNOSUPPORT = 0x10060, // Protocol family not supported.
- ESHUTDOWN = 0x1006C, // Socket shutdown.
- EHOSTDOWN = 0x10070, // Host is down.
- ENODATA = 0x10071, // No data available.
-
- // POSIX permits these to have the same value and we make them always equal so
- // that CoreFX cannot introduce a dependency on distinguishing between them that
- // would not work on all platforms.
- EOPNOTSUPP = ENOTSUP, // Operation not supported on socket.
- EWOULDBLOCK = EAGAIN, // Operation would block.
- }
-
-
- // Represents a platform-agnostic Error and underlying platform-specific errno
- internal struct ErrorInfo
- {
- private Error _error;
- private int _rawErrno;
-
- internal ErrorInfo (int errno)
- {
- _error = Interop.Sys.ConvertErrorPlatformToPal (errno);
- _rawErrno = errno;
- }
-
- internal ErrorInfo (Error error)
- {
- _error = error;
- _rawErrno = -1;
- }
-
- internal Error Error {
- get { return _error; }
- }
-
- internal int RawErrno {
- get { return _rawErrno == -1 ? (_rawErrno = Interop.Sys.ConvertErrorPalToPlatform (_error)) : _rawErrno; }
- }
-
- internal string GetErrorMessage ()
- {
- return Interop.Sys.StrError (RawErrno);
- }
-
- public override string ToString ()
- {
- return string.Format (
- "RawErrno: {0} Error: {1} GetErrorMessage: {2}", // No localization required; text is member names used for debugging purposes
- RawErrno, Error, GetErrorMessage ());
- }
- }
-
- internal partial class Sys
- {
- internal static Error GetLastError ()
- {
- return ConvertErrorPlatformToPal (Marshal.GetLastWin32Error ());
- }
-
- internal static ErrorInfo GetLastErrorInfo ()
- {
- return new ErrorInfo (Marshal.GetLastWin32Error ());
- }
-
- internal static unsafe string StrError (int platformErrno)
- {
- int maxBufferLength = 1024; // should be long enough for most any UNIX error
- byte* buffer = stackalloc byte [maxBufferLength];
- byte* message = StrErrorR (platformErrno, buffer, maxBufferLength);
-
- if (message == null) {
- // This means the buffer was not large enough, but still contains
- // as much of the error message as possible and is guaranteed to
- // be null-terminated. We're not currently resizing/retrying because
- // maxBufferLength is large enough in practice, but we could do
- // so here in the future if necessary.
- message = buffer;
- }
-
- return Marshal.PtrToStringAnsi ((IntPtr)message);
- }
-
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_ConvertErrorPlatformToPal")]
- internal static extern Error ConvertErrorPlatformToPal (int platformErrno);
-
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_ConvertErrorPalToPlatform")]
- internal static extern int ConvertErrorPalToPlatform (Error error);
-
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_StrErrorR")]
- private static extern unsafe byte* StrErrorR (int platformErrno, byte* buffer, int bufferSize);
- }
- }
-
- // NOTE: extension method can't be nested inside Interop class.
- internal static class InteropErrorExtensions
- {
- // Intended usage is e.g. Interop.Error.EFAIL.Info() for brevity
- // vs. new Interop.ErrorInfo(Interop.Error.EFAIL) for synthesizing
- // errors. Errors originated from the system should be obtained
- // via GetLastErrorInfo(), not GetLastError().Info() as that will
- // convert twice, which is not only inefficient but also lossy if
- // we ever encounter a raw errno that no equivalent in the Error
- // enum.
- public static Interop.ErrorInfo Info (this Interop.Error error)
- {
- return new Interop.ErrorInfo (error);
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.EventStream.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.EventStream.cs
deleted file mode 100644
index 6adf8c19a5..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.EventStream.cs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-using Microsoft.Win32.SafeHandles;
-
-using CFStringRef = System.IntPtr;
-using CFArrayRef = System.IntPtr;
-using FSEventStreamRef = System.IntPtr;
-using size_t = System.IntPtr;
-using FSEventStreamEventId = System.UInt64;
-using CFTimeInterval = System.Double;
-using CFRunLoopRef = System.IntPtr;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- internal static partial class EventStream
- {
- /// <summary>
- /// This constant specifies that we don't want historical file system events, only new ones
- /// </summary>
- internal const ulong kFSEventStreamEventIdSinceNow = 0xFFFFFFFFFFFFFFFF;
-
- /// <summary>
- /// Flags that describe what happened in the event that was received. These come from the FSEvents.h header file in the CoreServices framework.
- /// </summary>
- [Flags]
- internal enum FSEventStreamEventFlags : uint
- {
- /* flags when creating the stream. */
- kFSEventStreamEventFlagNone = 0x00000000,
- kFSEventStreamEventFlagMustScanSubDirs = 0x00000001,
- kFSEventStreamEventFlagUserDropped = 0x00000002,
- kFSEventStreamEventFlagKernelDropped = 0x00000004,
- kFSEventStreamEventFlagEventIdsWrapped = 0x00000008,
- kFSEventStreamEventFlagHistoryDone = 0x00000010,
- kFSEventStreamEventFlagRootChanged = 0x00000020,
- kFSEventStreamEventFlagMount = 0x00000040,
- kFSEventStreamEventFlagUnmount = 0x00000080,
- /* These flags are only set if you specified the FileEvents */
- kFSEventStreamEventFlagItemCreated = 0x00000100,
- kFSEventStreamEventFlagItemRemoved = 0x00000200,
- kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400,
- kFSEventStreamEventFlagItemRenamed = 0x00000800,
- kFSEventStreamEventFlagItemModified = 0x00001000,
- kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000,
- kFSEventStreamEventFlagItemChangeOwner = 0x00004000,
- kFSEventStreamEventFlagItemXattrMod = 0x00008000,
- kFSEventStreamEventFlagItemIsFile = 0x00010000,
- kFSEventStreamEventFlagItemIsDir = 0x00020000,
- kFSEventStreamEventFlagItemIsSymlink = 0x00040000,
- kFSEventStreamEventFlagOwnEvent = 0x00080000,
- kFSEventStreamEventFlagItemIsHardlink = 0x00100000,
- kFSEventStreamEventFlagItemIsLastHardlink = 0x00200000,
- }
-
- /// <summary>
- /// Flags that describe what kind of event stream should be created (and therefore what events should be
- /// piped into this stream). These come from the FSEvents.h header file in the CoreServices framework.
- /// </summary>
- [Flags]
- internal enum FSEventStreamCreateFlags : uint
- {
- kFSEventStreamCreateFlagNone = 0x00000000,
- kFSEventStreamCreateFlagUseCFTypes = 0x00000001,
- kFSEventStreamCreateFlagNoDefer = 0x00000002,
- kFSEventStreamCreateFlagWatchRoot = 0x00000004,
- kFSEventStreamCreateFlagIgnoreSelf = 0x00000008,
- kFSEventStreamCreateFlagFileEvents = 0x00000010
- }
-
- /// <summary>
- /// The EventStream callback that will be called for every event batch.
- /// </summary>
- /// <param name="streamReference">The stream that was created for this callback.</param>
- /// <param name="clientCallBackInfo">A pointer to optional context info; otherwise, IntPtr.Zero.</param>
- /// <param name="numEvents">The number of paths, events, and IDs. Path[2] corresponds to Event[2] and ID[2], etc.</param>
- /// <param name="eventPaths">The paths that have changed somehow, according to their corresponding event.</param>
- /// <param name="eventFlags">The events for the corresponding path.</param>
- /// <param name="eventIds">The machine-and-disk-drive-unique Event ID for the specific event.</param>
- [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
- internal delegate void FSEventStreamCallback (
- FSEventStreamRef streamReference,
- IntPtr clientCallBackInfo,
- size_t numEvents,
- [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
- String[] eventPaths,
- [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
- FSEventStreamEventFlags[] eventFlags,
- [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
- FSEventStreamEventId[] eventIds);
-
- /// <summary>
- /// Internal wrapper to create a new EventStream to listen to events from the core OS (such as File System events).
- /// </summary>
- /// <param name="allocator">Should be IntPtr.Zero</param>
- /// <param name="cb">A callback instance that will be called for every event batch.</param>
- /// <param name="context">Should be IntPtr.Zero</param>
- /// <param name="pathsToWatch">A CFArray of the path(s) to watch for events.</param>
- /// <param name="sinceWhen">
- /// The start point to receive events from. This can be to retrieve historical events or only new events.
- /// To get historical events, pass in the corresponding ID of the event you want to start from.
- /// To get only new events, pass in kFSEventStreamEventIdSinceNow.
- /// </param>
- /// <param name="latency">Coalescing period to wait before sending events.</param>
- /// <param name="flags">Flags to say what kind of events should be sent through this stream.</param>
- /// <returns>On success, returns a pointer to an FSEventStream object; otherwise, returns IntPtr.Zero</returns>
- /// <remarks>For *nix systems, the CLR maps ANSI to UTF-8, so be explicit about that</remarks>
- [DllImport (Interop.Libraries.CoreServicesLibrary, CharSet = CharSet.Ansi)]
- private static extern SafeEventStreamHandle FSEventStreamCreate (
- IntPtr allocator,
- FSEventStreamCallback cb,
- IntPtr context,
- SafeCreateHandle pathsToWatch,
- FSEventStreamEventId sinceWhen,
- CFTimeInterval latency,
- FSEventStreamCreateFlags flags);
-
- /// <summary>
- /// Creates a new EventStream to listen to events from the core OS (such as File System events).
- /// </summary>
- /// <param name="cb">A callback instance that will be called for every event batch.</param>
- /// <param name="pathsToWatch">A CFArray of the path(s) to watch for events.</param>
- /// <param name="sinceWhen">
- /// The start point to receive events from. This can be to retrieve historical events or only new events.
- /// To get historical events, pass in the corresponding ID of the event you want to start from.
- /// To get only new events, pass in kFSEventStreamEventIdSinceNow.
- /// </param>
- /// <param name="latency">Coalescing period to wait before sending events.</param>
- /// <param name="flags">Flags to say what kind of events should be sent through this stream.</param>
- /// <returns>On success, returns a valid SafeCreateHandle to an FSEventStream object; otherwise, returns an invalid SafeCreateHandle</returns>
- internal static SafeEventStreamHandle FSEventStreamCreate (
- FSEventStreamCallback cb,
- SafeCreateHandle pathsToWatch,
- FSEventStreamEventId sinceWhen,
- CFTimeInterval latency,
- FSEventStreamCreateFlags flags)
- {
- return FSEventStreamCreate (IntPtr.Zero, cb, IntPtr.Zero, pathsToWatch, sinceWhen, latency, flags);
- }
-
- /// <summary>
- /// Attaches an EventStream to a RunLoop so events can be received. This should usually be the current thread's RunLoop.
- /// </summary>
- /// <param name="streamRef">The stream to attach to the RunLoop</param>
- /// <param name="runLoop">The RunLoop to attach the stream to</param>
- /// <param name="runLoopMode">The mode of the RunLoop; this should usually be kCFRunLoopDefaultMode. See the documentation for RunLoops for more info.</param>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern void FSEventStreamScheduleWithRunLoop (
- SafeEventStreamHandle streamRef,
- CFRunLoopRef runLoop,
- SafeCreateHandle runLoopMode);
-
- /// <summary>
- /// Starts receiving events on the specified stream.
- /// </summary>
- /// <param name="streamRef">The stream to receive events on.</param>
- /// <returns>Returns true if the stream was started; otherwise, returns false and no events will be received.</returns>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern bool FSEventStreamStart (SafeEventStreamHandle streamRef);
-
- /// <summary>
- /// Stops receiving events on the specified stream. The stream can be restarted and not miss any events.
- /// </summary>
- /// <param name="streamRef">The stream to stop receiving events on.</param>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern void FSEventStreamStop (SafeEventStreamHandle streamRef);
-
- /// <summary>
- /// Stops receiving events on the specified stream. The stream can be restarted and not miss any events.
- /// </summary>
- /// <param name="streamRef">The stream to stop receiving events on.</param>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern void FSEventStreamStop (IntPtr streamRef);
-
- /// <summary>
- /// Invalidates an EventStream and removes it from any RunLoops.
- /// </summary>
- /// <param name="streamRef">The FSEventStream to invalidate</param>
- /// <remarks>This can only be called after FSEventStreamScheduleWithRunLoop has be called</remarks>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern void FSEventStreamInvalidate (IntPtr streamRef);
-
- /// <summary>
- /// Removes the event stream from the RunLoop.
- /// </summary>
- /// <param name="streamRef">The stream to remove from the RunLoop</param>
- /// <param name="runLoop">The RunLoop to remove the stream from.</param>
- /// <param name="runLoopMode">The mode of the RunLoop; this should usually be kCFRunLoopDefaultMode. See the documentation for RunLoops for more info.</param>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern void FSEventStreamUnscheduleFromRunLoop (
- SafeEventStreamHandle streamRef,
- CFRunLoopRef runLoop,
- SafeCreateHandle runLoopMode);
-
- /// <summary>
- /// Releases a reference count on the specified EventStream and, if necessary, cleans the stream up.
- /// </summary>
- /// <param name="streamRef">The stream on which to decrement the reference count.</param>
- [DllImport (Interop.Libraries.CoreServicesLibrary)]
- internal static extern void FSEventStreamRelease (IntPtr streamRef);
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.IOError.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.IOError.cs
deleted file mode 100644
index 2ea4514e6c..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.IOError.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Runtime.InteropServices;
-using Microsoft.Win32.SafeHandles;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- private static void ThrowExceptionForIoErrno (ErrorInfo errorInfo, string path, bool isDirectory, Func<ErrorInfo, ErrorInfo> errorRewriter)
- {
- Debug.Assert (errorInfo.Error != Error.SUCCESS);
- Debug.Assert (errorInfo.Error != Error.EINTR, "EINTR errors should be handled by the native shim and never bubble up to managed code");
-
- if (errorRewriter != null) {
- errorInfo = errorRewriter (errorInfo);
- }
-
- throw Interop.GetExceptionForIoErrno (errorInfo, path, isDirectory);
- }
-
- internal static void CheckIo (Error error, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
- {
- if (error != Interop.Error.SUCCESS) {
- ThrowExceptionForIoErrno (error.Info (), path, isDirectory, errorRewriter);
- }
- }
-
- /// <summary>
- /// Validates the result of system call that returns greater than or equal to 0 on success
- /// and less than 0 on failure, with errno set to the error code.
- /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded.
- /// </summary>
- /// <param name="result">The result of the system call.</param>
- /// <param name="path">The path with which this error is associated. This may be null.</param>
- /// <param name="isDirectory">true if the <paramref name="path"/> is known to be a directory; otherwise, false.</param>
- /// <param name="errorRewriter">Optional function to change an error code prior to processing it.</param>
- /// <returns>
- /// On success, returns the non-negative result long that was validated.
- /// </returns>
- internal static long CheckIo (long result, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
- {
- if (result < 0) {
- ThrowExceptionForIoErrno (Sys.GetLastErrorInfo (), path, isDirectory, errorRewriter);
- }
-
- return result;
- }
-
- /// <summary>
- /// Validates the result of system call that returns greater than or equal to 0 on success
- /// and less than 0 on failure, with errno set to the error code.
- /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded.
- /// </summary>
- /// <returns>
- /// On success, returns the non-negative result int that was validated.
- /// </returns>
- internal static int CheckIo (int result, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
- {
- CheckIo ((long)result, path, isDirectory, errorRewriter);
-
- return result;
- }
-
- /// <summary>
- /// Validates the result of system call that returns greater than or equal to 0 on success
- /// and less than 0 on failure, with errno set to the error code.
- /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded.
- /// </summary>
- /// <returns>
- /// On success, returns the non-negative result IntPtr that was validated.
- /// </returns>
- internal static IntPtr CheckIo (IntPtr result, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
- {
- CheckIo ((long)result, path, isDirectory, errorRewriter);
-
- return result;
- }
-
- /// <summary>
- /// Validates the result of system call that returns greater than or equal to 0 on success
- /// and less than 0 on failure, with errno set to the error code.
- /// If the system call failed for any reason, an exception is thrown. Otherwise, the system call succeeded.
- /// </summary>
- /// <returns>
- /// On success, returns the valid SafeFileHandle that was validated.
- /// </returns>
- internal static TSafeHandle CheckIo<TSafeHandle> (TSafeHandle handle, string path = null, bool isDirectory = false, Func<ErrorInfo, ErrorInfo> errorRewriter = null)
- where TSafeHandle : SafeHandle
- {
- if (handle.IsInvalid) {
- ThrowExceptionForIoErrno (Sys.GetLastErrorInfo (), path, isDirectory, errorRewriter);
- }
-
- return handle;
- }
-
- /// <summary>
- /// Gets an Exception to represent the supplied error info.
- /// </summary>
- /// <param name="errorInfo">The error info</param>
- /// <param name="path">The path with which this error is associated. This may be null.</param>
- /// <param name="isDirectory">true if the <paramref name="path"/> is known to be a directory; otherwise, false.</param>
- /// <returns></returns>
- internal static Exception GetExceptionForIoErrno (ErrorInfo errorInfo, string path = null, bool isDirectory = false)
- {
- // Translate the errno into a known set of exception types. For cases where multiple errnos map
- // to the same exception type, include an inner exception with the details.
- switch (errorInfo.Error) {
- case Error.ENOENT:
- if (isDirectory) {
- return !string.IsNullOrEmpty (path) ?
- new DirectoryNotFoundException (SR.Format (SR.IO_PathNotFound_Path, path)) :
- new DirectoryNotFoundException (SR.IO_PathNotFound_NoPathName);
- } else {
- return !string.IsNullOrEmpty (path) ?
- new FileNotFoundException (SR.Format (SR.IO_FileNotFound_FileName, path), path) :
- new FileNotFoundException (SR.IO_FileNotFound);
- }
-
- case Error.EACCES:
- case Error.EBADF:
- case Error.EPERM:
- Exception inner = GetIOException (errorInfo);
- return !string.IsNullOrEmpty (path) ?
- new UnauthorizedAccessException (SR.Format (SR.UnauthorizedAccess_IODenied_Path, path), inner) :
- new UnauthorizedAccessException (SR.UnauthorizedAccess_IODenied_NoPathName, inner);
-
- case Error.ENAMETOOLONG:
- return new PathTooLongException (SR.IO_PathTooLong);
-
- case Error.EWOULDBLOCK:
- return !string.IsNullOrEmpty (path) ?
- new IOException (SR.Format (SR.IO_SharingViolation_File, path), errorInfo.RawErrno) :
- new IOException (SR.IO_SharingViolation_NoFileName, errorInfo.RawErrno);
-
- case Error.ECANCELED:
- return new OperationCanceledException ();
-
- case Error.EFBIG:
- return new ArgumentOutOfRangeException ("value", SR.ArgumentOutOfRange_FileLengthTooBig);
-
- case Error.EEXIST:
- if (!string.IsNullOrEmpty (path)) {
- return new IOException (SR.Format (SR.IO_FileExists_Name, path), errorInfo.RawErrno);
- }
- goto default;
-
- default:
- return GetIOException (errorInfo);
- }
- }
-
- internal static Exception GetIOException (Interop.ErrorInfo errorInfo)
- {
- return new IOException (errorInfo.GetErrorMessage (), errorInfo.RawErrno);
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Libraries.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Libraries.cs
deleted file mode 100644
index 68d88a1739..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Libraries.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace MonoDevelop.FSW.OSX
-{
- // OSX
- internal static partial class Interop
- {
- internal static partial class Libraries
- {
- internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
- internal const string CoreServicesLibrary = "/System/Library/Frameworks/CoreServices.framework/CoreServices";
-#if false
- internal const string libproc = "libproc";
- internal const string LibSystemCommonCrypto = "/usr/lib/system/libcommonCrypto";
- internal const string LibSystemKernel = "/usr/lib/system/libsystem_kernel";
- internal const string SystemConfigurationLibrary = "/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration";
- internal const string AppleCryptoNative = "System.Security.Cryptography.Native.Apple";
-#endif
- }
- }
-
- // Unix
- internal static partial class Interop
- {
- internal static partial class Libraries
- {
- // Shims
- internal const string SystemNative = "libsystemnative.dylib";
-#if false
- internal const string HttpNative = "System.Net.Http.Native";
- internal const string NetSecurityNative = "System.Net.Security.Native";
- internal const string CryptoNative = "System.Security.Cryptography.Native.OpenSsl";
- internal const string GlobalizationNative = "System.Globalization.Native";
- internal const string CompressionNative = "System.IO.Compression.Native";
-#endif
- }
- }
-
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.PathConf.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.PathConf.cs
deleted file mode 100644
index 994859f832..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.PathConf.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- internal static partial class Sys
- {
- internal static int DEFAULT_PC_NAME_MAX = 255;
-
- internal enum PathConfName : int
- {
- PC_LINK_MAX = 1,
- PC_MAX_CANON = 2,
- PC_MAX_INPUT = 3,
- PC_NAME_MAX = 4,
- PC_PATH_MAX = 5,
- PC_PIPE_BUF = 6,
- PC_CHOWN_RESTRICTED = 7,
- PC_NO_TRUNC = 8,
- PC_VDISABLE = 9,
- }
-
- /// <summary>The maximum path length for the system. -1 if it hasn't yet been initialized.</summary>
- private static int s_maxPath = -1;
-
- /// <summary>The maximum name length for the system. -1 if it hasn't yet been initialized.</summary>
- private static int s_maxName = -1;
-
- internal static int MaxPath {
- get {
- // Benign race condition on cached value
- if (s_maxPath < 0) {
- // GetMaximumPath returns a long from PathConf
- // but our callers expect an int so we need to convert.
- long temp = GetMaximumPath ();
- if (temp > int.MaxValue)
- s_maxPath = int.MaxValue;
- else
- s_maxPath = Convert.ToInt32 (temp);
- }
- return s_maxPath;
- }
- }
-
- internal static int MaxName {
- get {
- // Benign race condition on cached value
- if (s_maxName < 0) {
- int result = PathConf ("/", PathConfName.PC_NAME_MAX);
- s_maxName = result >= 0 ? result : DEFAULT_PC_NAME_MAX;
- }
-
- return s_maxName;
- }
- }
-
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_PathConf", SetLastError = true)]
- private static extern int PathConf (string path, PathConfName name);
-
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_GetMaximumPath")]
- private static extern long GetMaximumPath ();
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RealPath.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RealPath.cs
deleted file mode 100644
index 6f1b0bfdde..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RealPath.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- internal static partial class Sys
- {
- /// <summary>
- /// Takes a path containing relative subpaths or links and returns the absolute path.
- /// This function works on both files and folders and returns a null-terminated string.
- /// </summary>
- /// <param name="path">The path to the file system object</param>
- /// <returns>Returns the result string on success and null on failure</returns>
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_RealPath", SetLastError = true)]
- internal static extern string RealPath (string path);
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RunLoop.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RunLoop.cs
deleted file mode 100644
index e13f12ddfb..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.RunLoop.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-using Microsoft.Win32.SafeHandles;
-
-using CFRunLoopRef = System.IntPtr;
-using CFRunLoopSourceRef = System.IntPtr;
-using CFStringRef = System.IntPtr;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- internal static partial class RunLoop
- {
- /// <summary>
- /// This constant specifies that we want to use the default Run mode for the thread's Run loop.
- /// </summary>
- /// <remarks>
- /// For more information, see the Apple documentation: https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html
- /// </remarks>
- internal static SafeCreateHandle kCFRunLoopDefaultMode = Interop.CoreFoundation.CFStringCreateWithCString ("kCFRunLoopDefaultMode");
-
- /// <summary>
- /// Starts the current thread's RunLoop. If the RunLoop is already running, creates a new, nested, RunLoop in the same stack.
- /// </summary>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal extern static void CFRunLoopRun ();
-
- /// <summary>
- /// Notifies a RunLoop to stop and return control to the execution context that called CFRunLoopRun
- /// </summary>
- /// <param name="rl">The RunLoop to notify to stop</param>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal extern static void CFRunLoopStop (CFRunLoopRef rl);
-
- /// <summary>
- /// Retrieves the RunLoop associated with the current thread; all threads automatically have a RunLoop.
- /// Follows the "Get Rule" where you do not own the object unless you CFRetain it; in which case, you must also CFRelease it as well.
- /// </summary>
- /// <returns>Returns a pointer to a CFRunLoop on success; otherwise, returns IntPtr.Zero</returns>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal static extern CFRunLoopRef CFRunLoopGetCurrent ();
-
- /// <summary>
- /// Adds a CFRunLoopSource object to a run loop mode.
- /// </summary>
- /// <param name="rl">The run loop to modify.</param>
- /// <param name="source">The run loop source to add. The source is retained by the run loop.</param>
- /// <param name="mode">The run loop mode to which to add source.</param>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal static extern void CFRunLoopAddSource (CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
-
- /// <summary>
- /// Removes a CFRunLoopSource object from a run loop mode.
- /// </summary>
- /// <param name="rl">The run loop to modify.</param>
- /// <param name="source">The run loop source to remove.</param>
- /// <param name="mode">The run loop mode of rl from which to remove source.</param>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal static extern void CFRunLoopRemoveSource (CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
-
- /// <summary>
- /// Returns a bool that indicates whether the run loop is waiting for an event.
- /// </summary>
- /// <param name="rl">The run loop to examine.</param>
- /// <returns>true if rl has no events to process and is blocking,
- /// waiting for a source or timer to become ready to fire;
- /// false if rl either is not running or is currently processing
- /// a source, timer, or observer.</returns>
- [DllImport (Interop.Libraries.CoreFoundationLibrary)]
- internal static extern bool CFRunLoopIsWaiting (CFRunLoopRef rl);
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Sync.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Sync.cs
deleted file mode 100644
index dd8bfb35f9..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/Interop.Sync.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static partial class Interop
- {
- internal static partial class Sys
- {
- /// <summary>
- /// Forces a write of all modified I/O buffers to their storage mediums.
- /// </summary>
- [DllImport (Libraries.SystemNative, EntryPoint = "SystemNative_Sync")]
- internal static extern void Sync ();
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.CaseSensitivity.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.CaseSensitivity.cs
deleted file mode 100644
index 631050677f..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.CaseSensitivity.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.IO;
-using System.Diagnostics;
-
-namespace MonoDevelop.FSW.OSX
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- private static readonly bool s_isCaseSensitive = GetIsCaseSensitive ();
-
- /// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
- internal static StringComparison StringComparison {
- get {
- return s_isCaseSensitive ?
- StringComparison.Ordinal :
- StringComparison.OrdinalIgnoreCase;
- }
- }
-
- /// <summary>Gets whether the system is case-sensitive.</summary>
- internal static bool IsCaseSensitive { get { return s_isCaseSensitive; } }
-
- /// <summary>
- /// Determines whether the file system is case sensitive.
- /// </summary>
- /// <remarks>
- /// Ideally we'd use something like pathconf with _PC_CASE_SENSITIVE, but that is non-portable,
- /// not supported on Windows or Linux, etc. For now, this function creates a tmp file with capital letters
- /// and then tests for its existence with lower-case letters. This could return invalid results in corner
- /// cases where, for example, different file systems are mounted with differing sensitivities.
- /// </remarks>
- private static bool GetIsCaseSensitive ()
- {
- try {
- string pathWithUpperCase = Path.Combine (Path.GetTempPath (), "CASESENSITIVETEST" + Guid.NewGuid ().ToString ("N"));
- using (new FileStream (pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) {
- string lowerCased = pathWithUpperCase.ToLowerInvariant ();
- return !File.Exists (lowerCased);
- }
- } catch (Exception exc) {
- // In case something goes terribly wrong, we don't want to fail just because
- // of a casing test, so we assume case-insensitive-but-preserving.
- Debug.Fail ("Casing test failed: " + exc);
- return false;
- }
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.Unix.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.Unix.cs
deleted file mode 100644
index 229cf1c3b8..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.Unix.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.IO;
-using System.Text;
-
-namespace MonoDevelop.FSW.OSX
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- // There is only one invalid path character in Unix
- private const char InvalidPathChar = '\0';
- internal static char [] GetInvalidPathChars () => new char [] { InvalidPathChar };
-
- internal static readonly int MaxComponentLength = Interop.Sys.MaxName;
-
- internal const string ParentDirectoryPrefix = @"../";
-
- /// <summary>Returns a value indicating if the given path contains invalid characters.</summary>
- internal static bool HasIllegalCharacters (string path)
- {
- Debug.Assert (path != null);
- return path.IndexOf (InvalidPathChar) >= 0;
- }
-
- internal static int GetRootLength (string path)
- {
- return path.Length > 0 && IsDirectorySeparator (path [0]) ? 1 : 0;
- }
-
- internal static bool IsDirectorySeparator (char c)
- {
- // The alternate directory separator char is the same as the directory separator,
- // so we only need to check one.
- Debug.Assert (Path.DirectorySeparatorChar == Path.AltDirectorySeparatorChar);
- return c == Path.DirectorySeparatorChar;
- }
-
- /// <summary>
- /// Returns true if the path is too long
- /// </summary>
- internal static bool IsPathTooLong (string fullPath)
- {
- return fullPath.Length >= Interop.Sys.MaxPath;
- }
-
- /// <summary>
- /// Returns true if the directory is too long
- /// </summary>
- internal static bool IsDirectoryTooLong (string fullPath)
- {
- return fullPath.Length >= Interop.Sys.MaxPath;
- }
-
- /// <summary>
- /// Normalize separators in the given path. Compresses forward slash runs.
- /// </summary>
- internal static string NormalizeDirectorySeparators (string path)
- {
- if (string.IsNullOrEmpty (path)) return path;
-
- // Make a pass to see if we need to normalize so we can potentially skip allocating
- bool normalized = true;
-
- for (int i = 0; i < path.Length; i++) {
- if (IsDirectorySeparator (path [i])
- && (i + 1 < path.Length && IsDirectorySeparator (path [i + 1]))) {
- normalized = false;
- break;
- }
- }
-
- if (normalized) return path;
-
- StringBuilder builder = new StringBuilder (path.Length);
-
- for (int i = 0; i < path.Length; i++) {
- char current = path [i];
-
- // Skip if we have another separator following
- if (IsDirectorySeparator (current)
- && (i + 1 < path.Length && IsDirectorySeparator (path [i + 1])))
- continue;
-
- builder.Append (current);
- }
-
- return builder.ToString ();
- }
-
- /// <summary>
- /// Returns true if the character is a directory or volume separator.
- /// </summary>
- /// <param name="ch">The character to test.</param>
- internal static bool IsDirectoryOrVolumeSeparator (char ch)
- {
- // The directory separator, volume separator, and the alternate directory
- // separator should be the same on Unix, so we only need to check one.
- Debug.Assert (Path.DirectorySeparatorChar == Path.AltDirectorySeparatorChar);
- Debug.Assert (Path.DirectorySeparatorChar == Path.VolumeSeparatorChar);
- return ch == Path.DirectorySeparatorChar;
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.cs
deleted file mode 100644
index 60a71e057a..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PathInternal.cs
+++ /dev/null
@@ -1,187 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.IO;
-using System.Diagnostics;
-using System.Text;
-
-namespace MonoDevelop.FSW.OSX
-{
- /// <summary>Contains internal path helpers that are shared between many projects.</summary>
- internal static partial class PathInternal
- {
- /// <summary>
- /// Checks for invalid path characters in the given path.
- /// </summary>
- /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
- /// <exception cref="System.ArgumentException">Thrown if the path has invalid characters.</exception>
- /// <param name="path">The path to check for invalid characters.</param>
- internal static void CheckInvalidPathChars (string path)
- {
- if (path == null)
- throw new ArgumentNullException (nameof (path));
-
- if (HasIllegalCharacters (path))
- throw new ArgumentException (SR.Argument_InvalidPathChars, nameof (path));
- }
-
-
- /// <summary>
- /// Returns true if the given StringBuilder starts with the given value.
- /// </summary>
- /// <param name="value">The string to compare against the start of the StringBuilder.</param>
- internal static bool StartsWithOrdinal (this StringBuilder builder, string value)
- {
- if (value == null || builder.Length < value.Length)
- return false;
-
- for (int i = 0; i < value.Length; i++) {
- if (builder [i] != value [i]) return false;
- }
- return true;
- }
-
- /// <summary>
- /// Returns true if the given string starts with the given value.
- /// </summary>
- /// <param name="value">The string to compare against the start of the source string.</param>
- internal static bool StartsWithOrdinal (this string source, string value)
- {
- if (value == null || source.Length < value.Length)
- return false;
-
- return source.StartsWith (value, StringComparison.Ordinal);
- }
-
- /// <summary>
- /// Trims the specified characters from the end of the StringBuilder.
- /// </summary>
- internal static StringBuilder TrimEnd (this StringBuilder builder, params char [] trimChars)
- {
- if (trimChars == null || trimChars.Length == 0)
- return builder;
-
- int end = builder.Length - 1;
-
- for (; end >= 0; end--) {
- int i = 0;
- char ch = builder [end];
- for (; i < trimChars.Length; i++) {
- if (trimChars [i] == ch) break;
- }
- if (i == trimChars.Length) {
- // Not a trim char
- break;
- }
- }
-
- builder.Length = end + 1;
- return builder;
- }
-
- /// <summary>
- /// Returns the start index of the filename
- /// in the given path, or 0 if no directory
- /// or volume separator is found.
- /// </summary>
- /// <param name="path">The path in which to find the index of the filename.</param>
- /// <remarks>
- /// This method returns path.Length for
- /// inputs like "/usr/foo/" on Unix. As such,
- /// it is not safe for being used to index
- /// the string without additional verification.
- /// </remarks>
- internal static int FindFileNameIndex (string path)
- {
- Debug.Assert (path != null);
- CheckInvalidPathChars (path);
-
- for (int i = path.Length - 1; i >= 0; i--) {
- char ch = path [i];
- if (IsDirectoryOrVolumeSeparator (ch))
- return i + 1;
- }
-
- return 0; // the whole path is the filename
- }
-
- /// <summary>
- /// Returns true if the path ends in a directory separator.
- /// </summary>
- internal static bool EndsInDirectorySeparator (string path) =>
- !string.IsNullOrEmpty (path) && IsDirectorySeparator (path [path.Length - 1]);
-
- /// <summary>
- /// Get the common path length from the start of the string.
- /// </summary>
- internal static int GetCommonPathLength (string first, string second, bool ignoreCase)
- {
- int commonChars = EqualStartingCharacterCount (first, second, ignoreCase: ignoreCase);
-
- // If nothing matches
- if (commonChars == 0)
- return commonChars;
-
- // Or we're a full string and equal length or match to a separator
- if (commonChars == first.Length
- && (commonChars == second.Length || IsDirectorySeparator (second [commonChars])))
- return commonChars;
-
- if (commonChars == second.Length && IsDirectorySeparator (first [commonChars]))
- return commonChars;
-
- // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar.
- while (commonChars > 0 && !IsDirectorySeparator (first [commonChars - 1]))
- commonChars--;
-
- return commonChars;
- }
-
- /// <summary>
- /// Gets the count of common characters from the left optionally ignoring case
- /// </summary>
- internal static unsafe int EqualStartingCharacterCount (string first, string second, bool ignoreCase)
- {
- if (string.IsNullOrEmpty (first) || string.IsNullOrEmpty (second)) return 0;
-
- int commonChars = 0;
-
- fixed (char* f = first)
- fixed (char* s = second) {
- char* l = f;
- char* r = s;
- char* leftEnd = l + first.Length;
- char* rightEnd = r + second.Length;
-
- while (l != leftEnd && r != rightEnd
- && (*l == *r || (ignoreCase && char.ToUpperInvariant ((*l)) == char.ToUpperInvariant ((*r))))) {
- commonChars++;
- l++;
- r++;
- }
- }
-
- return commonChars;
- }
-
- /// <summary>
- /// Returns true if the two paths have the same root
- /// </summary>
- internal static bool AreRootsEqual (string first, string second, StringComparison comparisonType)
- {
- int firstRootLength = GetRootLength (first);
- int secondRootLength = GetRootLength (second);
-
- return firstRootLength == secondRootLength
- && string.Compare (
- strA: first,
- indexA: 0,
- strB: second,
- indexB: 0,
- length: firstRootLength,
- comparisonType: comparisonType) == 0;
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PatternMatcher.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PatternMatcher.cs
deleted file mode 100644
index 32ce8e3024..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/PatternMatcher.cs
+++ /dev/null
@@ -1,473 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal static class PatternMatcher
- {
- /// <devdoc>
- /// Private constants (directly from C header files)
- /// </devdoc>
- private const int MATCHES_ARRAY_SIZE = 16;
- private const char ANSI_DOS_STAR = '>';
- private const char ANSI_DOS_QM = '<';
- private const char DOS_DOT = '"';
-
- /// <devdoc>
- /// Tells whether a given name matches the expression given with a strict (i.e. UNIX like)
- /// semantics. This code is a port of unmanaged code. Original code comment follows:
- ///
- /// Routine Description:
- ///
- /// This routine compares a Dbcs name and an expression and tells the caller
- /// if the name is in the language defined by the expression. The input name
- /// cannot contain wildcards, while the expression may contain wildcards.
- ///
- /// Expression wild cards are evaluated as shown in the nondeterministic
- /// finite automatons below. Note that ~* and ~? are DOS_STAR and DOS_QM.
- ///
- ///
- /// ~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT
- ///
- ///
- /// S
- /// &lt;-----&lt;
- /// X | | e Y
- /// X * Y == (0)-----&gt;-(1)-&gt;-----(2)-----(3)
- ///
- ///
- /// S-.
- /// &lt;-----&lt;
- /// X | | e Y
- /// X ~* Y == (0)-----&gt;-(1)-&gt;-----(2)-----(3)
- ///
- ///
- ///
- /// X S S Y
- /// X ?? Y == (0)---(1)---(2)---(3)---(4)
- ///
- ///
- ///
- /// X . . Y
- /// X ~.~. Y == (0)---(1)----(2)------(3)---(4)
- /// | |________|
- /// | ^ |
- /// |_______________|
- /// ^EOF or .^
- ///
- ///
- /// X S-. S-. Y
- /// X ~?~? Y == (0)---(1)-----(2)-----(3)---(4)
- /// | |________|
- /// | ^ |
- /// |_______________|
- /// ^EOF or .^
- ///
- ///
- ///
- /// where S is any single character
- ///
- /// S-. is any single character except the final .
- ///
- /// e is a null character transition
- ///
- /// EOF is the end of the name string
- ///
- /// In words:
- ///
- /// * matches 0 or more characters.
- ///
- /// ? matches exactly 1 character.
- ///
- /// DOS_STAR matches 0 or more characters until encountering and matching
- /// the final . in the name.
- ///
- /// DOS_QM matches any single character, or upon encountering a period or
- /// end of name string, advances the expression to the end of the
- /// set of contiguous DOS_QMs.
- ///
- /// DOS_DOT matches either a . or zero characters beyond name string.
- ///
- /// Arguments:
- ///
- /// Expression - Supplies the input expression to check against
- ///
- /// Name - Supplies the input name to check for.
- ///
- /// Return Value:
- ///
- /// BOOLEAN - TRUE if Name is an element in the set of strings denoted
- /// by the input Expression and FALSE otherwise.
- ///
- /// </devdoc>
- public static bool StrictMatchPattern (string expression, string name)
- {
- //
- // The idea behind the algorithm is pretty simple. We keep track of
- // all possible locations in the regular expression that are matching
- // the name. If when the name has been exhausted one of the locations
- // in the expression is also just exhausted, the name is in the language
- // defined by the regular expression.
- //
-
- if (name == null || name.Length == 0 || expression == null || expression.Length == 0) {
- return false;
- }
-
- //
- // Special case by far the most common wild card search of * or *.*
- //
-
- if (expression.Equals ("*") || expression.Equals ("*.*")) {
- return true;
- }
-
- // If this class is ever exposed for generic use,
- // we need to make sure that name doesn't contain wildcards. Currently
- // the only component that calls this method is FileSystemWatcher and
- // it will never pass a name that contains a wildcard.
-
-
- //
- // Also special case expressions of the form *X. With this and the prior
- // case we have covered virtually all normal queries.
- //
- if (expression [0] == '*' && expression.IndexOf ('*', 1) == -1) {
- int rightLength = expression.Length - 1;
- // if name is shorter that the stuff to the right of * in expression, we don't
- // need to do the string compare, otherwise we compare rightlength characters
- // and the end of both strings.
- if (name.Length >= rightLength &&
- string.Compare (expression, 1, name, name.Length - rightLength, rightLength, PathInternal.StringComparison) == 0) {
- return true;
- }
- }
-
- //
- // Walk through the name string, picking off characters. We go one
- // character beyond the end because some wild cards are able to match
- // zero characters beyond the end of the string.
- //
- // With each new name character we determine a new set of states that
- // match the name so far. We use two arrays that we swap back and forth
- // for this purpose. One array lists the possible expression states for
- // all name characters up to but not including the current one, and other
- // array is used to build up the list of states considering the current
- // name character as well. The arrays are then switched and the process
- // repeated.
- //
- // There is not a one-to-one correspondence between state number and
- // offset into the expression. This is evident from the NFAs in the
- // initial comment to this function. State numbering is not continuous.
- // This allows a simple conversion between state number and expression
- // offset. Each character in the expression can represent one or two
- // states. * and DOS_STAR generate two states: ExprOffset*2 and
- // ExprOffset*2 + 1. All other expression characters can produce only
- // a single state. Thus ExprOffset = State/2.
- //
- //
- // Here is a short description of the variables involved:
- //
- // NameOffset - The offset of the current name char being processed.
- //
- // ExprOffset - The offset of the current expression char being processed.
- //
- // SrcCount - Prior match being investigated with current name char
- //
- // DestCount - Next location to put a matching assuming current name char
- //
- // NameFinished - Allows one more iteration through the Matches array
- // after the name is exhausted (to come *s for example)
- //
- // PreviousDestCount - This is used to prevent entry duplication, see comment
- //
- // PreviousMatches - Holds the previous set of matches (the Src array)
- //
- // CurrentMatches - Holds the current set of matches (the Dest array)
- //
- // AuxBuffer, LocalBuffer - the storage for the Matches arrays
- //
-
- //
- // Set up the initial variables
- //
- int nameOffset;
- int exprOffset;
- int length;
-
- int srcCount;
- int destCount;
- int previousDestCount;
- int matchesCount;
-
- char nameChar = '\0';
- char exprChar = '\0';
-
- int [] previousMatches = new int [MATCHES_ARRAY_SIZE];
- int [] currentMatches = new int [MATCHES_ARRAY_SIZE];
-
- int maxState;
- int currentState;
-
- bool nameFinished = false;
-
- previousMatches [0] = 0;
- matchesCount = 1;
-
- nameOffset = 0;
- maxState = expression.Length * 2;
-
- while (!nameFinished) {
- if (nameOffset < name.Length) {
- nameChar = name [nameOffset];
- nameOffset++;
- } else {
- nameFinished = true;
-
- //
- // if we have already exhausted the expression, C#. Don't
- // continue.
- //
- if (previousMatches [matchesCount - 1] == maxState) {
- break;
- }
- }
-
- //
- // Now, for each of the previous stored expression matches, see what
- // we can do with this name character.
- //
- srcCount = 0;
- destCount = 0;
- previousDestCount = 0;
-
- while (srcCount < matchesCount) {
- //
- // We have to carry on our expression analysis as far as possible
- // for each character of name, so we loop here until the
- // expression stops matching. A clue here is that expression
- // cases that can match zero or more characters end with a
- // continue, while those that can accept only a single character
- // end with a break.
- //
- exprOffset = ((previousMatches [srcCount++] + 1) / 2);
- length = 0;
-
- while (true) {
- if (exprOffset == expression.Length) {
- break;
- }
-
- //
- // The first time through the loop we don't want
- // to increment ExprOffset.
- //
-
- exprOffset += length;
-
- currentState = exprOffset * 2;
-
- if (exprOffset == expression.Length) {
- currentMatches [destCount++] = maxState;
- break;
- }
-
- exprChar = expression [exprOffset];
- length = 1;
-
- //
- // We may be about to exhaust the local
- // space for ExpressionMatches[][], so we have to allocate
- // some pool if this is the case.
- //
-
- if (destCount >= MATCHES_ARRAY_SIZE - 2) {
- int newSize = currentMatches.Length * 2;
- int [] tmp = new int [newSize];
- Array.Copy (currentMatches, 0, tmp, 0, currentMatches.Length);
- currentMatches = tmp;
-
- tmp = new int [newSize];
- Array.Copy (previousMatches, 0, tmp, 0, previousMatches.Length);
- previousMatches = tmp;
- }
-
- //
- // * matches any character zero or more times.
- //
-
- if (exprChar == '*') {
- currentMatches [destCount++] = currentState;
- currentMatches [destCount++] = (currentState + 1);
- continue;
- }
-
- //
- // DOS_STAR matches any character except . zero or more times.
- //
-
- if (exprChar == ANSI_DOS_STAR) {
- bool iCanEatADot = false;
-
- //
- // If we are at a period, determine if we are allowed to
- // consume it, i.e. make sure it is not the last one.
- //
- if (!nameFinished && (nameChar == '.')) {
- char tmpChar;
- int offset;
-
- int nameLength = name.Length;
- for (offset = nameOffset; offset < nameLength; offset++) {
- tmpChar = name [offset];
- length = 1;
-
- if (tmpChar == '.') {
- iCanEatADot = true;
- break;
- }
- }
- }
-
- if (nameFinished || (nameChar != '.') || iCanEatADot) {
- currentMatches [destCount++] = currentState;
- currentMatches [destCount++] = (currentState + 1);
- continue;
- } else {
- //
- // We are at a period. We can only match zero
- // characters (i.e. the epsilon transition).
- //
- currentMatches [destCount++] = (currentState + 1);
- continue;
- }
- }
-
- //
- // The following expression characters all match by consuming
- // a character, thus force the expression, and thus state
- // forward.
- //
- currentState += length * 2;
-
- //
- // DOS_QM is the most complicated. If the name is finished,
- // we can match zero characters. If this name is a '.', we
- // don't match, but look at the next expression. Otherwise
- // we match a single character.
- //
- if (exprChar == ANSI_DOS_QM) {
- if (nameFinished || (nameChar == '.')) {
- continue;
- }
-
- currentMatches [destCount++] = currentState;
- break;
- }
-
- //
- // A DOS_DOT can match either a period, or zero characters
- // beyond the end of name.
- //
- if (exprChar == DOS_DOT) {
- if (nameFinished) {
- continue;
- }
-
- if (nameChar == '.') {
- currentMatches [destCount++] = currentState;
- break;
- }
- }
-
- //
- // From this point on a name character is required to even
- // continue, let alone make a match.
- //
- if (nameFinished) {
- break;
- }
-
- //
- // If this expression was a '?' we can match it once.
- //
- if (exprChar == '?') {
- currentMatches [destCount++] = currentState;
- break;
- }
-
- //
- // Finally, check if the expression char matches the name char
- //
-
- if (PathInternal.IsCaseSensitive ?
- (exprChar == nameChar) :
- (char.ToUpperInvariant (exprChar) == char.ToUpperInvariant (nameChar))) {
- currentMatches [destCount++] = currentState;
- break;
- }
-
- //
- // The expression didn't match so go look at the next
- // previous match.
- //
-
- break;
- }
-
-
- //
- // Prevent duplication in the destination array.
- //
- // Each of the arrays is monotonically increasing and non-
- // duplicating, thus we skip over any source element in the src
- // array if we just added the same element to the destination
- // array. This guarantees non-duplication in the dest. array.
- //
-
- if ((srcCount < matchesCount) && (previousDestCount < destCount)) {
- while (previousDestCount < destCount) {
- int previousLength = previousMatches.Length;
- while ((srcCount < previousLength) && (previousMatches [srcCount] < currentMatches [previousDestCount])) {
- srcCount += 1;
- }
- previousDestCount += 1;
- }
- }
- }
-
- //
- // If we found no matches in the just finished iteration, it's time
- // to bail.
- //
-
- if (destCount == 0) {
- return false;
- }
-
- //
- // Swap the meaning the two arrays
- //
-
- {
- int [] tmp;
-
- tmp = previousMatches;
-
- previousMatches = currentMatches;
-
- currentMatches = tmp;
- }
-
- matchesCount = destCount;
- }
-
- currentState = previousMatches [matchesCount - 1];
-
- return currentState == maxState;
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SR.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SR.cs
deleted file mode 100644
index 2f8b13d9eb..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SR.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Resources;
-using System.Runtime.CompilerServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- internal partial class SR
- {
-#if false
- private static ResourceManager s_resourceManager;
-
- private static ResourceManager ResourceManager
- => s_resourceManager ?? (s_resourceManager = new ResourceManager (ResourceType));
-#endif
-
- // This method is used to decide if we need to append the exception message parameters to the message when calling SR.Format.
- // by default it returns false.
- [MethodImpl (MethodImplOptions.NoInlining)]
- private static bool UsingResourceKeys ()
- {
- return false;
- }
-
-#if false
- internal static string GetResourceString (string resourceKey, string defaultString)
- {
- string resourceString = null;
- try { resourceString = ResourceManager.GetString (resourceKey); } catch (MissingManifestResourceException) { }
-
- if (defaultString != null && resourceKey.Equals (resourceString, StringComparison.Ordinal)) {
- return defaultString;
- }
-
- return resourceString;
- }
-#endif
-
- internal static string Format (string resourceFormat, params object [] args)
- {
- if (args != null) {
- if (UsingResourceKeys ()) {
- return resourceFormat + string.Join (", ", args);
- }
-
- return string.Format (resourceFormat, args);
- }
-
- return resourceFormat;
- }
-
- internal static string Format (string resourceFormat, object p1)
- {
- if (UsingResourceKeys ()) {
- return string.Join (", ", resourceFormat, p1);
- }
-
- return string.Format (resourceFormat, p1);
- }
-
- internal static string Format (string resourceFormat, object p1, object p2)
- {
- if (UsingResourceKeys ()) {
- return string.Join (", ", resourceFormat, p1, p2);
- }
-
- return string.Format (resourceFormat, p1, p2);
- }
-
- internal static string Format (string resourceFormat, object p1, object p2, object p3)
- {
- if (UsingResourceKeys ()) {
- return string.Join (", ", resourceFormat, p1, p2, p3);
- }
-
- return string.Format (resourceFormat, p1, p2, p3);
- }
-
- internal static readonly string Argument_InvalidPathChars = "Illegal characters in path.";
- internal static readonly string ArgumentOutOfRange_FileLengthTooBig = "Specified file length was too large for the file system.";
- internal static readonly string BufferSizeTooLarge = "The specified buffer size is too large. FileSystemWatcher cannot allocate {0} bytes for the internal buffer.";
- internal static readonly string EventStream_FailedToStart = "Failed to start the EventStream";
- internal static readonly string FSW_BufferOverflow = "Too many changes at once in directory:{0}.";
- internal static readonly string InvalidDirName = "The directory name {0} is invalid.";
- internal static readonly string InvalidEnumArgument = "The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.";
- internal static readonly string IO_FileExists_Name = "The file '{0}' already exists.";
- internal static readonly string IO_FileNotFound = "Unable to find the specified file.";
- internal static readonly string IO_FileNotFound_FileName = "Could not find file '{0}'.";
- internal static readonly string IO_PathNotFound_NoPathName = "Could not find a part of the path.";
- internal static readonly string IO_PathNotFound_Path = "Could not find a part of the path '{0}'.";
- internal static readonly string IO_PathTooLong = "The specified file name or path is too long, or a component of the specified path is too long.";
- internal static readonly string IO_SharingViolation_File = "The process cannot access the file '{0}' because it is being used by another process.";
- internal static readonly string IO_SharingViolation_NoFileName = "The process cannot access the file because it is being used by another process.";
- internal static readonly string UnauthorizedAccess_IODenied_NoPathName = "Access to the path is denied.";
- internal static readonly string UnauthorizedAccess_IODenied_Path = "Access to the path '{0}' is denied.";
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeCreateHandle.OSX.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeCreateHandle.OSX.cs
deleted file mode 100644
index fc6c4fa90c..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeCreateHandle.OSX.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- /// <summary>
- /// This class is a wrapper around the Create pattern in OS X where
- /// if a Create* function is called, the caller must also CFRelease
- /// on the same pointer in order to correctly free the memory.
- /// </summary>
- [System.Security.SecurityCritical]
- internal sealed partial class SafeCreateHandle : SafeHandle
- {
- internal SafeCreateHandle () : base (IntPtr.Zero, true) { }
-
- internal SafeCreateHandle (IntPtr ptr) : base (IntPtr.Zero, true)
- {
- this.SetHandle (ptr);
- }
-
- [System.Security.SecurityCritical]
- protected override bool ReleaseHandle ()
- {
- Interop.CoreFoundation.CFRelease (handle);
-
- return true;
- }
-
- public override bool IsInvalid {
- [System.Security.SecurityCritical]
- get {
- return handle == IntPtr.Zero;
- }
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeEventStreamHandle.OSX.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeEventStreamHandle.OSX.cs
deleted file mode 100644
index f8e13691cd..0000000000
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.FSW/OSX/SafeEventStreamHandle.OSX.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace MonoDevelop.FSW.OSX
-{
- /// <summary>
- /// This class is a wrapper around the EventStream and Create pattern.
- /// Usually, the Create pattern has the caller call Create* to allocate
- /// and CFRelease to free; however, FSEventStream has it's own release
- /// function, so we need to extend the pattern to account for that.
- /// </summary>
- [System.Security.SecurityCritical]
- internal sealed partial class SafeEventStreamHandle : SafeHandle
- {
- internal SafeEventStreamHandle () : base (IntPtr.Zero, true) { }
-
- internal SafeEventStreamHandle (IntPtr ptr) : base (IntPtr.Zero, true)
- {
- this.SetHandle (ptr);
- }
-
- [System.Security.SecurityCritical]
- protected override bool ReleaseHandle ()
- {
- Interop.EventStream.FSEventStreamStop (handle);
- Interop.EventStream.FSEventStreamInvalidate (handle);
- Interop.EventStream.FSEventStreamRelease (handle);
-
- return true;
- }
-
- public override bool IsInvalid {
- [System.Security.SecurityCritical]
- get {
- return handle == IntPtr.Zero;
- }
- }
- }
-}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs
index e35886fdfe..57f4a1f42b 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs
@@ -37,7 +37,7 @@ using System.Linq;
namespace MonoDevelop.Projects.MSBuild
{
- public abstract class MSBuildFileFormat
+ public abstract class MSBuildFileFormat : IComparable<MSBuildFileFormat>, IEquatable<MSBuildFileFormat>
{
readonly SlnFileFormat slnFileFormat;
@@ -51,9 +51,6 @@ namespace MonoDevelop.Projects.MSBuild
public static readonly MSBuildFileFormat VS2010 = new MSBuildFileFormatVS10 ();
public static readonly MSBuildFileFormat VS2012 = new MSBuildFileFormatVS12 ();
- [Obsolete("This is the same as VS2012")]
- public static readonly MSBuildFileFormat VS2017 = VS2012;
-
public static IEnumerable<MSBuildFileFormat> GetSupportedFormats ()
{
yield return VS2012;
@@ -68,12 +65,6 @@ namespace MonoDevelop.Projects.MSBuild
}
public static MSBuildFileFormat DefaultFormat => VS2012;
-
- [Obsolete ("Use ProductDescription or ID")]
- public string Name => "MSBuild";
-
- [Obsolete]
- public abstract Version Version { get; }
internal SlnFileFormat SlnFileFormat {
get { return slnFileFormat; }
@@ -229,17 +220,41 @@ namespace MonoDevelop.Projects.MSBuild
}
return string.Empty;
}
-
+
public abstract string Id { get; }
+
+ #region IComparable<MSBuildFileFormat> implementation and overloads
+
+ public override bool Equals (object obj) => obj is MSBuildFileFormat other && Equals (other);
+ public bool Equals (MSBuildFileFormat other) => other != null && Id == other.Id;
+ public override int GetHashCode () => Id.GetHashCode ();
+
+ public int CompareTo (MSBuildFileFormat other) => Version.Parse (SlnVersion).CompareTo (Version.Parse (other.SlnVersion));
+
+ public static bool operator == (MSBuildFileFormat a, MSBuildFileFormat b)
+ {
+ if (ReferenceEquals (a, b))
+ return true;
+
+ if (a is null)
+ return b is null;
+
+ return a.Equals (b);
+ }
+
+ public static bool operator != (MSBuildFileFormat a, MSBuildFileFormat b) => !(a == b);
+ public static bool operator < (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) < 0;
+ public static bool operator > (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) > 0;
+ public static bool operator <= (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) <= 0;
+ public static bool operator >= (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) >= 0;
+
+ #endregion
}
class MSBuildFileFormatVS05 : MSBuildFileFormat
{
public override string Id => "MSBuild05";
- [Obsolete("Unused")]
- public override Version Version => new Version ("2005");
-
public override string DefaultProductVersion => "8.0.50727";
public override string DefaultToolsVersion => "2.0";
public override string DefaultSchemaVersion => "2.0";
@@ -255,9 +270,6 @@ namespace MonoDevelop.Projects.MSBuild
{
public override string Id => "MSBuild08";
- [Obsolete ("Unused")]
- public override Version Version => new Version ("2008");
-
public override string DefaultProductVersion => "9.0.21022";
public override string DefaultToolsVersion => "3.5";
public override string DefaultSchemaVersion => "2.0";
@@ -279,9 +291,6 @@ namespace MonoDevelop.Projects.MSBuild
{
public override string Id => "MSBuild10";
- [Obsolete ("Unused")]
- public override Version Version => new Version ("2010");
-
public override string DefaultProductVersion => "8.0.30703";
public override string DefaultSchemaVersion => "2.0";
public override string DefaultToolsVersion => "4.0";
@@ -294,9 +303,6 @@ namespace MonoDevelop.Projects.MSBuild
{
public override string Id => "MSBuild12";
- [Obsolete ("Unused")]
- public override Version Version => new Version ("2012");
-
// This is mostly irrelevant, the builder always uses the latest
// tools version. It's only used for new projects created with
// the old project template engine.
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs
index 2062fae1a8..40cf37edf2 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs
@@ -63,9 +63,19 @@ namespace MonoDevelop.Projects.MSBuild
return MSBuildProjectService.GetMSBuildBinPath (Runtime.SystemAssemblyService.CurrentRuntime);
}
+ static FilePath GetMSBuildBinDirectory (TargetRuntime runtime)
+ {
+ return MSBuildProjectService.GetMSBuildBinPath (runtime);
+ }
+
static FilePath GetMSBuildBinPath ()
{
- FilePath binDirectory = GetMSBuildBinDirectory ();
+ return GetMSBuildBinPath (Runtime.SystemAssemblyService.CurrentRuntime);
+ }
+
+ internal static FilePath GetMSBuildBinPath (TargetRuntime runtime)
+ {
+ FilePath binDirectory = GetMSBuildBinDirectory (runtime);
FilePath binPath = binDirectory.Combine ("MSBuild.dll");
if (File.Exists (binPath)) {
return binPath;
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs
index ec0a17aa31..d9d2e29b79 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs
@@ -266,10 +266,8 @@ namespace MonoDevelop.Projects.MSBuild
builders.Add (builderKey, builder);
builder.ReferenceCount = 0;
builder.BuildSessionId = buildSessionId;
- builder.Disconnected += async delegate {
- using (await buildersLock.EnterAsync ().ConfigureAwait (false))
- builders.Remove (builder);
- };
+
+ builder.Disconnected += OnBuilderDisconnected;
if (setBusy)
builder.SetBusy ();
if (buildSessionId != null) {
@@ -280,6 +278,13 @@ namespace MonoDevelop.Projects.MSBuild
});
}
+ // PERF: Avoid making this an instance method, as it will cause delegates to be retained.
+ static async Task OnBuilderDisconnected (object sender, EventArgs args)
+ {
+ var disconnectedBuilder = (RemoteBuildEngine)sender;
+ using (await buildersLock.EnterAsync ().ConfigureAwait (false))
+ builders.Remove (disconnectedBuilder);
+ }
/// <summary>
/// Unloads a project from all engines
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs
index 90092361ef..e4808bd77c 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs
@@ -3,13 +3,13 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Microsoft.Build.Framework;
using MonoDevelop.Core.Assemblies;
-using MonoDevelop.Core;
namespace MonoDevelop.Projects.MSBuild
{
@@ -24,6 +24,7 @@ namespace MonoDevelop.Projects.MSBuild
readonly object _lockObject = new object ();
IList<SdkResolver> _resolvers;
TargetRuntime runtime;
+ Version msbuildVersion;
internal SdkResolution (TargetRuntime runtime)
{
@@ -58,7 +59,7 @@ namespace MonoDevelop.Projects.MSBuild
try {
var buildEngineLogger = new SdkLoggerImpl (logger, buildEventContext);
foreach (var sdkResolver in _resolvers) {
- var context = new SdkResolverContextImpl (buildEngineLogger, projectFile, solutionPath);
+ var context = new SdkResolverContextImpl (buildEngineLogger, projectFile, solutionPath, msbuildVersion);
var resultFactory = new SdkResultFactoryImpl (sdk);
try {
var result = (SdkResultImpl)sdkResolver.Resolve (sdk, context, resultFactory);
@@ -96,10 +97,21 @@ namespace MonoDevelop.Projects.MSBuild
{
lock (_lockObject) {
if (_resolvers != null) return;
+ msbuildVersion = GetMSBuildVersion ();
_resolvers = LoadResolvers (logger);
}
}
+ Version GetMSBuildVersion ()
+ {
+ var msbuildFileName = MSBuildProcessService.GetMSBuildBinPath (runtime);
+ if (!File.Exists (msbuildFileName))
+ return null;
+
+ var versionInfo = FileVersionInfo.GetVersionInfo (msbuildFileName);
+ return new Version (versionInfo.FileMajorPart, versionInfo.FileMinorPart, versionInfo.FileBuildPart, versionInfo.FilePrivatePart);
+ }
+
IList<SdkResolver> LoadResolvers (ILoggingService logger)
{
// Add the MonoDevelop resolver, which resolves SDKs registered by add-ins.
@@ -282,16 +294,12 @@ namespace MonoDevelop.Projects.MSBuild
public SdkReference Sdk { get; }
- public string Path { get; }
-
- public string Version { get; }
-
public IEnumerable<string> Errors { get; }
public IEnumerable<string> Warnings { get; }
}
- class SdkResultFactoryImpl : SdkResultFactory
+ internal class SdkResultFactoryImpl : SdkResultFactory
{
readonly SdkReference _sdkReference;
@@ -313,11 +321,12 @@ namespace MonoDevelop.Projects.MSBuild
sealed class SdkResolverContextImpl : SdkResolverContext
{
- public SdkResolverContextImpl (SdkLogger logger, string projectFilePath, string solutionPath)
+ public SdkResolverContextImpl (SdkLogger logger, string projectFilePath, string solutionPath, Version msbuildVersion)
{
Logger = logger;
ProjectFilePath = projectFilePath;
SolutionFilePath = solutionPath;
+ MSBuildVersion = msbuildVersion;
}
}
}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs
index 7631c0cb30..aeae42b1d2 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs
@@ -1915,8 +1915,14 @@ namespace MonoDevelop.Projects
{
bool externalConsole = false, pauseConsole = false;
- var dotNetExecutionCommand = executionCommand as DotNetExecutionCommand;
- if (dotNetExecutionCommand != null) {
+ if (executionCommand is ProcessExecutionCommand processExecutionCommand) {
+ if (!string.IsNullOrEmpty (processExecutionCommand.WorkingDirectory) && !Directory.Exists (processExecutionCommand.WorkingDirectory)) {
+ monitor.ReportError (GettextCatalog.GetString ("Can not execute. The run configuration working directory doesn't exist at {0}", processExecutionCommand.WorkingDirectory), null);
+ return;
+ }
+ }
+
+ if (executionCommand is DotNetExecutionCommand dotNetExecutionCommand) {
dotNetExecutionCommand.UserAssemblyPaths = GetUserAssemblyPaths (configuration);
externalConsole = dotNetExecutionCommand.ExternalConsole;
pauseConsole = dotNetExecutionCommand.PauseConsoleOutput;
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/FileWatcherService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/FileWatcherService.cs
index 6d3bc6eae2..d61fc51705 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/FileWatcherService.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/FileWatcherService.cs
@@ -1,322 +1,319 @@
-//
-// FileWatcherService.cs
-//
-// Author:
-// Matt Ward <matt.ward@microsoft.com>
-//
-// Copyright (c) 2018 Microsoft
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
+//
+// FileWatcherService.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2018 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MonoDevelop.Core;
-using MonoDevelop.FSW;
-
-namespace MonoDevelop.Projects
-{
- public static class FileWatcherService
- {
- // We don't want more than 8 threads for FileSystemWatchers.
- const int maxWatchers = 8;
-
- static readonly PathTree tree = new PathTree ();
- static readonly Dictionary<FilePath, FileWatcherWrapper> watchers = new Dictionary<FilePath, FileWatcherWrapper> ();
- static readonly Dictionary<object, HashSet<FilePath>> monitoredDirectories = new Dictionary<object, HashSet<FilePath>> ();
- static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ();
-
- public static Task Add (WorkspaceItem item)
- {
- lock (watchers) {
- item.RootDirectoriesChanged += OnRootDirectoriesChanged;
- return WatchDirectories (item, item.GetRootDirectories ());
- }
- }
-
- public static Task Remove (WorkspaceItem item)
- {
- lock (watchers) {
- item.RootDirectoriesChanged -= OnRootDirectoriesChanged;
- return WatchDirectories (item, null);
- }
- }
-
- static void OnRootDirectoriesChanged (object sender, EventArgs args)
- {
- lock (watchers) {
- var item = (WorkspaceItem)sender;
- WatchDirectories (item, item.GetRootDirectories ()).Ignore ();
- }
- }
-
- static Task UpdateWatchersAsync ()
- {
- cancellationTokenSource.Cancel ();
- cancellationTokenSource = new CancellationTokenSource ();
- CancellationToken token = cancellationTokenSource.Token;
-
- return Task.Run (() => UpdateWatchers (token));
- }
-
- static void UpdateWatchers (CancellationToken token)
- {
- if (token.IsCancellationRequested)
- return;
-
- lock (watchers) {
- if (token.IsCancellationRequested)
- return;
-
- var newPathsToWatch = tree.Normalize (maxWatchers).Select (node => (FilePath)node.GetPath ().ToString ());
- var newWatchers = new HashSet<FilePath> (newPathsToWatch.Where (dir => Directory.Exists (dir)));
- if (newWatchers.Count == 0 && watchers.Count == 0) {
- // Unchanged.
- return;
- }
-
- List<FilePath> toRemove;
- if (newWatchers.Count == 0)
- toRemove = watchers.Keys.ToList ();
- else {
- toRemove = new List<FilePath> ();
- foreach (var kvp in watchers) {
- var directory = kvp.Key;
- if (!newWatchers.Contains (directory))
- toRemove.Add (directory);
- }
- }
-
- // After this point, the watcher update is real and a destructive operation, so do not use the token.
- if (token.IsCancellationRequested)
- return;
-
- // First remove the watchers, so we don't spin too many threads.
- foreach (var directory in toRemove) {
- RemoveWatcher_NoLock (directory);
- }
-
- // Add the new ones.
- if (newWatchers.Count == 0)
- return;
-
- foreach (var path in newWatchers) {
- // Don't modify a watcher that already exists.
- if (watchers.ContainsKey (path)) {
- continue;
- }
- var watcher = new FileWatcherWrapper (path);
- watchers.Add (path, watcher);
- try {
- watcher.EnableRaisingEvents = true;
- } catch (UnauthorizedAccessException e) {
- LoggingService.LogWarning ("Access to " + path + " denied. Stopping file watcher.", e);
- watcher.Dispose ();
- watchers.Remove (path);
- }
- }
-
- }
- }
-
- static void RemoveWatcher_NoLock (FilePath directory)
- {
- Debug.Assert (Monitor.IsEntered (watchers));
-
- if (watchers.TryGetValue (directory, out FileWatcherWrapper watcher)) {
- watcher.EnableRaisingEvents = false;
- watcher.Dispose ();
- watchers.Remove (directory);
- }
- }
-
- public static Task WatchDirectories (object id, IEnumerable<FilePath> directories)
- {
- lock (watchers) {
- HashSet<FilePath> set = null;
- if (directories != null)
- set = new HashSet<FilePath> (directories.Where (x => !x.IsNullOrEmpty));
-
- if (RegisterDirectoriesInTree_NoLock (id, set))
- return UpdateWatchersAsync ();
- return Task.CompletedTask;
- }
- }
-
- static bool RegisterDirectoriesInTree_NoLock (object id, HashSet<FilePath> set)
- {
- Debug.Assert (Monitor.IsEntered (watchers));
-
- // Remove paths subscribed for this id.
-
- bool modified = false;
-
- if (monitoredDirectories.TryGetValue (id, out var oldDirectories)) {
- HashSet<FilePath> toRemove = null;
- if (set != null) {
- toRemove = new HashSet<FilePath> (oldDirectories);
- // Remove the old ones which are not in the new set.
- toRemove.ExceptWith (set);
- } else
- toRemove = oldDirectories;
-
- foreach (var dir in toRemove) {
- var node = tree.RemoveNode (dir, id);
-
- bool wasRemoved = node != null && !node.IsLive;
- modified |= wasRemoved;
- }
- }
-
- // Remove the current registered directories
- monitoredDirectories.Remove (id);
- if (set == null)
- return modified;
-
- HashSet<FilePath> toAdd = null;
- if (oldDirectories != null) {
- toAdd = new HashSet<FilePath> (set);
- toAdd.ExceptWith (oldDirectories);
- } else
- toAdd = set;
-
- // Apply new ones if we have any
- if (set.Count > 0) {
- monitoredDirectories [id] = set;
- foreach (var path in toAdd) {
- tree.AddNode (path, id, out bool isNew);
-
- // We have only modified the tree if there is any new pathtree node item added
- modified |= isNew;
- }
- }
- return modified;
- }
-
- /// <summary>
- /// Used by unit tests to ensure the file watcher is up to date.
- /// </summary>
- internal static Task Update ()
- {
- lock (watchers) {
- return UpdateWatchersAsync ();
- }
- }
- }
-
- class FileWatcherWrapper : IDisposable
- {
- FSW.FileSystemWatcher watcher;
-
- public FileWatcherWrapper (FilePath path)
- {
- Path = path;
- watcher = new FSW.FileSystemWatcher (path) {
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MonoDevelop.Core;
+using MonoDevelop.FSW;
+
+namespace MonoDevelop.Projects
+{
+ public static class FileWatcherService
+ {
+ // We don't want more than 8 threads for FileSystemWatchers.
+ const int maxWatchers = 8;
+
+ static readonly PathTree tree = new PathTree ();
+ static readonly Dictionary<FilePath, FileWatcherWrapper> watchers = new Dictionary<FilePath, FileWatcherWrapper> ();
+ static readonly Dictionary<object, HashSet<FilePath>> monitoredDirectories = new Dictionary<object, HashSet<FilePath>> ();
+ static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ();
+
+ public static Task Add (WorkspaceItem item)
+ {
+ lock (watchers) {
+ item.RootDirectoriesChanged += OnRootDirectoriesChanged;
+ return WatchDirectories (item, item.GetRootDirectories ());
+ }
+ }
+
+ public static Task Remove (WorkspaceItem item)
+ {
+ lock (watchers) {
+ item.RootDirectoriesChanged -= OnRootDirectoriesChanged;
+ return WatchDirectories (item, null);
+ }
+ }
+
+ static void OnRootDirectoriesChanged (object sender, EventArgs args)
+ {
+ lock (watchers) {
+ var item = (WorkspaceItem)sender;
+ WatchDirectories (item, item.GetRootDirectories ()).Ignore ();
+ }
+ }
+
+ static Task UpdateWatchersAsync ()
+ {
+ cancellationTokenSource.Cancel ();
+ cancellationTokenSource = new CancellationTokenSource ();
+ CancellationToken token = cancellationTokenSource.Token;
+
+ return Task.Run (() => UpdateWatchers (token));
+ }
+ static HashSet<FilePath> newWatchers = new HashSet<FilePath>();
+ static List<FilePath> toRemove = new List<FilePath> ();
+
+ static void UpdateWatchers (CancellationToken token)
+ {
+ if (token.IsCancellationRequested)
+ return;
+ lock (watchers) {
+ if (token.IsCancellationRequested)
+ return;
+ newWatchers.Clear ();
+ foreach (var node in tree.Normalize (maxWatchers)) {
+ if (token.IsCancellationRequested)
+ return;
+ var dir = node.GetPath ().ToString ();
+ if (Directory.Exists (dir))
+ newWatchers.Add (dir);
+ }
+ if (newWatchers.Count == 0 && watchers.Count == 0) {
+ // Unchanged.
+ return;
+ }
+ toRemove.Clear ();
+ foreach (var kvp in watchers) {
+ var directory = kvp.Key;
+ if (!newWatchers.Contains (directory))
+ toRemove.Add (directory);
+ }
+
+ // After this point, the watcher update is real and a destructive operation, so do not use the token.
+ if (token.IsCancellationRequested)
+ return;
+
+ // First remove the watchers, so we don't spin too many threads.
+ foreach (var directory in toRemove) {
+ RemoveWatcher_NoLock (directory);
+ }
+
+ // Add the new ones.
+ foreach (var path in newWatchers) {
+ // Don't modify a watcher that already exists.
+ if (watchers.ContainsKey (path)) {
+ continue;
+ }
+ var watcher = new FileWatcherWrapper (path);
+ watchers.Add (path, watcher);
+ try {
+ watcher.EnableRaisingEvents = true;
+ } catch (UnauthorizedAccessException e) {
+ LoggingService.LogWarning ("Access to " + path + " denied. Stopping file watcher.", e);
+ watcher.Dispose ();
+ watchers.Remove (path);
+ }
+ }
+
+ }
+ }
+
+ static void RemoveWatcher_NoLock (FilePath directory)
+ {
+ Debug.Assert (Monitor.IsEntered (watchers));
+
+ if (watchers.TryGetValue (directory, out FileWatcherWrapper watcher)) {
+ watcher.EnableRaisingEvents = false;
+ watcher.Dispose ();
+ watchers.Remove (directory);
+ }
+ }
+
+ public static Task WatchDirectories (object id, IEnumerable<FilePath> directories)
+ {
+ lock (watchers) {
+ HashSet<FilePath> set = null;
+ if (directories != null)
+ set = new HashSet<FilePath> (directories.Where (x => !x.IsNullOrEmpty));
+
+ if (RegisterDirectoriesInTree_NoLock (id, set))
+ return UpdateWatchersAsync ();
+ return Task.CompletedTask;
+ }
+ }
+
+ static bool RegisterDirectoriesInTree_NoLock (object id, HashSet<FilePath> set)
+ {
+ Debug.Assert (Monitor.IsEntered (watchers));
+
+ // Remove paths subscribed for this id.
+
+ bool modified = false;
+
+ if (monitoredDirectories.TryGetValue (id, out var oldDirectories)) {
+ HashSet<FilePath> toRemove = null;
+ if (set != null) {
+ toRemove = new HashSet<FilePath> (oldDirectories);
+ // Remove the old ones which are not in the new set.
+ toRemove.ExceptWith (set);
+ } else
+ toRemove = oldDirectories;
+
+ foreach (var dir in toRemove) {
+ var node = tree.RemoveNode (dir, id);
+
+ bool wasRemoved = node != null && !node.IsLive;
+ modified |= wasRemoved;
+ }
+ }
+
+ // Remove the current registered directories
+ monitoredDirectories.Remove (id);
+ if (set == null)
+ return modified;
+
+ HashSet<FilePath> toAdd = null;
+ if (oldDirectories != null) {
+ toAdd = new HashSet<FilePath> (set);
+ toAdd.ExceptWith (oldDirectories);
+ } else
+ toAdd = set;
+
+ // Apply new ones if we have any
+ if (set.Count > 0) {
+ monitoredDirectories [id] = set;
+ foreach (var path in toAdd) {
+ tree.AddNode (path, id, out bool isNew);
+
+ // We have only modified the tree if there is any new pathtree node item added
+ modified |= isNew;
+ }
+ }
+ return modified;
+ }
+
+ /// <summary>
+ /// Used by unit tests to ensure the file watcher is up to date.
+ /// </summary>
+ internal static Task Update ()
+ {
+ lock (watchers) {
+ return UpdateWatchersAsync ();
+ }
+ }
+ }
+
+ class FileWatcherWrapper : IDisposable
+ {
+ FileSystemWatcher watcher;
+
+ public FileWatcherWrapper (FilePath path)
+ {
+ Path = path;
+ watcher = new FileSystemWatcher (path) {
// Need LastWrite otherwise no file change events are generated by the native file watcher.
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName,
IncludeSubdirectories = true,
InternalBufferSize = 32768
- };
-
- watcher.Changed += OnFileChanged;
- watcher.Created += OnFileCreated;
- watcher.Deleted += OnFileDeleted;
- watcher.Renamed += OnFileRenamed;
- watcher.Error += OnFileWatcherError;
- }
-
- public FilePath Path { get; }
-
- public bool EnableRaisingEvents {
- get { return watcher.EnableRaisingEvents; }
- set { watcher.EnableRaisingEvents = value; }
- }
-
- public void Dispose ()
- {
- watcher.Changed -= OnFileChanged;
- watcher.Created -= OnFileCreated;
- watcher.Deleted -= OnFileDeleted;
- watcher.Renamed -= OnFileRenamed;
- watcher.Error -= OnFileWatcherError;
- watcher.Dispose ();
- }
-
- void OnFileChanged (object sender, FileSystemEventArgs e)
- {
- FileService.NotifyFileChanged (e.FullPath);
- }
-
- void OnFileCreated (object sender, FileSystemEventArgs e)
- {
- FileService.NotifyFileCreated (e.FullPath);
-
- // The native file watcher sometimes generates a single Created event for a file when it is renamed
- // from a non-monitored directory to a monitored directory. So this is turned into a Changed
- // event so the file will be reloaded.
- FileService.NotifyFileChanged (e.FullPath);
- }
-
- void OnFileDeleted (object sender, FileSystemEventArgs e)
- {
- // The native file watcher sometimes generates a Changed, Created and Deleted event in
- // that order from a single native file event. So check the file has been deleted before raising
- // a FileRemoved event.
- if (!File.Exists (e.FullPath) && !Directory.Exists (e.FullPath))
- FileService.NotifyFileRemoved (e.FullPath);
- }
-
- /// <summary>
- /// File rename events have various problems.
- /// 1. They are sometimes raised out of order.
- /// 2. Sometimes the rename information is incorrect with the wrong file names being used.
- /// 3. Some applications use a rename to update the original file so these are turned into
- /// a change event and a remove event.
- /// </summary>
- void OnFileRenamed (object sender, RenamedEventArgs e)
- {
- FileService.NotifyFileRenamedExternally (e.OldFullPath, e.FullPath);
- // Some applications, such as TextEdit.app, will create a backup file
- // and then rename that to the original file. This results in no file
- // change event being generated by the file watcher. To handle this
- // a rename is treated as a file change for the destination file.
- FileService.NotifyFileChanged (e.FullPath);
-
- // Deleting a file with Finder will move the file to the ~/.Trashes
- // folder. To handle this a remove event is fired for the source
- // file being renamed. Also handle file events being received out of
- // order on saving a file in TextEdit.app - with a rename event of
- // the original file to the temp file being the last event even though
- // the original file still exists.
- if (File.Exists (e.OldFullPath))
- FileService.NotifyFileChanged (e.OldFullPath);
- else
- FileService.NotifyFileRemoved (e.OldFullPath);
- }
-
- void OnFileWatcherError (object sender, ErrorEventArgs e)
- {
- LoggingService.LogError ("FileService.FileWatcher error", e.GetException ());
- }
- }
-}
+ };
+
+ watcher.Changed += OnFileChanged;
+ watcher.Created += OnFileCreated;
+ watcher.Deleted += OnFileDeleted;
+ watcher.Renamed += OnFileRenamed;
+ watcher.Error += OnFileWatcherError;
+ }
+
+ public FilePath Path { get; }
+
+ public bool EnableRaisingEvents {
+ get { return watcher.EnableRaisingEvents; }
+ set { watcher.EnableRaisingEvents = value; }
+ }
+
+ public void Dispose ()
+ {
+ watcher.Changed -= OnFileChanged;
+ watcher.Created -= OnFileCreated;
+ watcher.Deleted -= OnFileDeleted;
+ watcher.Renamed -= OnFileRenamed;
+ watcher.Error -= OnFileWatcherError;
+ watcher.Dispose ();
+ }
+
+ void OnFileChanged (object sender, FileSystemEventArgs e)
+ {
+ FileService.NotifyFileChanged (e.FullPath);
+ }
+
+ void OnFileCreated (object sender, FileSystemEventArgs e)
+ {
+ FileService.NotifyFileCreated (e.FullPath);
+
+ // The native file watcher sometimes generates a single Created event for a file when it is renamed
+ // from a non-monitored directory to a monitored directory. So this is turned into a Changed
+ // event so the file will be reloaded.
+ FileService.NotifyFileChanged (e.FullPath);
+ }
+
+ void OnFileDeleted (object sender, FileSystemEventArgs e)
+ {
+ // The native file watcher sometimes generates a Changed, Created and Deleted event in
+ // that order from a single native file event. So check the file has been deleted before raising
+ // a FileRemoved event.
+ if (!File.Exists (e.FullPath) && !Directory.Exists (e.FullPath))
+ FileService.NotifyFileRemoved (e.FullPath);
+ }
+
+ /// <summary>
+ /// File rename events have various problems.
+ /// 1. They are sometimes raised out of order.
+ /// 2. Sometimes the rename information is incorrect with the wrong file names being used.
+ /// 3. Some applications use a rename to update the original file so these are turned into
+ /// a change event and a remove event.
+ /// </summary>
+ void OnFileRenamed (object sender, RenamedEventArgs e)
+ {
+ FileService.NotifyFileRenamedExternally (e.OldFullPath, e.FullPath);
+ // Some applications, such as TextEdit.app, will create a backup file
+ // and then rename that to the original file. This results in no file
+ // change event being generated by the file watcher. To handle this
+ // a rename is treated as a file change for the destination file.
+ FileService.NotifyFileChanged (e.FullPath);
+
+ // Deleting a file with Finder will move the file to the ~/.Trashes
+ // folder. To handle this a remove event is fired for the source
+ // file being renamed. Also handle file events being received out of
+ // order on saving a file in TextEdit.app - with a rename event of
+ // the original file to the temp file being the last event even though
+ // the original file still exists.
+ if (File.Exists (e.OldFullPath))
+ FileService.NotifyFileChanged (e.OldFullPath);
+ else
+ FileService.NotifyFileRemoved (e.OldFullPath);
+ }
+
+ void OnFileWatcherError (object sender, ErrorEventArgs e)
+ {
+ LoggingService.LogError ("FileService.FileWatcher error", e.GetException ());
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs
index c08e57f1de..22afe401ea 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs
@@ -4216,6 +4216,11 @@ namespace MonoDevelop.Projects
try {
IsReevaluating = true;
+ // Re-evaluating may change MSBuild items and cause the custom tool generator to run. If a
+ // custom MSBuild target is run it may run before the project builder is refreshed so the
+ // target may not available. To avoid this shutdown the project builder before re-evaluating.
+ ShutdownProjectBuilder ();
+
// Reevaluate the msbuild project
monitorItemsModifiedDuringReevaluation = true;
await sourceProject.EvaluateAsync ();
@@ -4528,6 +4533,13 @@ namespace MonoDevelop.Projects
void OnFileCreatedExternally (FilePath fileName)
{
+ if (sourceProject == null) {
+ // sometimes this method is called after disposing this class.
+ // (i.e. when quitting MD or creating a new project.)
+ LoggingService.LogWarning ("File created externally not processed. {0}", fileName);
+ return;
+ }
+
// Check file is inside the project directory. The file globs would exclude the file anyway
// if the relative path starts with "..\" but checking here avoids checking the file globs.
if (!fileName.IsChildPathOf (BaseDirectory))
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs
index c37c7ed678..62fcb98017 100644
--- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs
+++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectItem.cs
@@ -49,7 +49,8 @@ namespace MonoDevelop.Projects
get {
return project;
}
- internal set {
+ // HACK: internal
+ set {
project = value;
OnProjectSet ();
}
diff --git a/main/src/core/MonoDevelop.Core/packages.config b/main/src/core/MonoDevelop.Core/packages.config
index 22e96f2c44..7c1fa9d24f 100644
--- a/main/src/core/MonoDevelop.Core/packages.config
+++ b/main/src/core/MonoDevelop.Core/packages.config
@@ -35,8 +35,8 @@
<package id="Microsoft.VisualStudio.Text.Logic" version="15.8.519" targetFramework="net45" />
<package id="Microsoft.VisualStudio.Text.UI" version="15.8.519" targetFramework="net45" />
<package id="Microsoft.VisualStudio.Text.UI.Wpf" version="15.8.519" targetFramework="net45" />
- <package id="Microsoft.VisualStudio.Threading" version="15.6.46" targetFramework="net461" />
- <package id="Microsoft.VisualStudio.Threading.Analyzers" version="15.6.46" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Threading" version="15.8.209" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Threading.Analyzers" version="15.8.209" targetFramework="net461" />
<package id="Microsoft.VisualStudio.Validation" version="15.3.32" targetFramework="net461" />
<package id="Microsoft.Win32.Primitives" version="4.0.1" targetFramework="net461" />
<package id="Mono.Cecil" version="0.10.0" targetFramework="net471" />
diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml
index 9bb9cb676f..333f953015 100644
--- a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml
@@ -221,6 +221,10 @@
defaultHandler = "MonoDevelop.Ide.Commands.SolutionItemOptionsHandler"
_label = "_Options"
_description = "Show options window" />
+ <Command id = "MonoDevelop.Ide.Commands.ProjectCommands.SetStartupProjects"
+ defaultHandler = "MonoDevelop.Ide.Commands.SetStartupProjectsHandler"
+ _label = "Se_t Startup Projects..."
+ _description = "Set multiple startup projects" />
<Command id = "MonoDevelop.Ide.Commands.ProjectCommands.SolutionOptions"
defaultHandler = "MonoDevelop.Ide.Commands.SolutionOptionsHandler"
icon = "gtk-preferences"
@@ -252,7 +256,7 @@
_description = "Adds and existing folder and its contents"
_label = "_Add Existing Folder..." />
<Command id = "MonoDevelop.Ide.Commands.ProjectCommands.NewFolder"
- _label = "New _Folder"
+ _label = "New _Folder…"
_description = "Create a new folder"
icon = "md-new-folder" />
<Command id = "MonoDevelop.Ide.Commands.ProjectCommands.IncludeToProject"
diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml
index 007c4a671a..aeec88b1a6 100644
--- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml
@@ -205,6 +205,7 @@
<CommandItem id = "MonoDevelop.Ide.Commands.ProjectCommands.ExportPolicy" />
<SeparatorItem id = "PolicyToolsEnd" />
<SeparatorItem id = "OptionsSeparator" />
+ <CommandItem id = "MonoDevelop.Ide.Commands.ProjectCommands.SetStartupProjects" />
<CommandItem id = "MonoDevelop.Ide.Commands.ProjectCommands.SolutionOptions" />
<CommandItem id = "MonoDevelop.Ide.Commands.ProjectCommands.ProjectOptions" />
<SeparatorItem id = "ProjectToolsSeparator" />
diff --git a/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Gui.OptionPanels.LoadSavePanelWidget.cs b/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Gui.OptionPanels.LoadSavePanelWidget.cs
index 4a8327ae06..6622f7e893 100644
--- a/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Gui.OptionPanels.LoadSavePanelWidget.cs
+++ b/main/src/core/MonoDevelop.Ide/Gui/MonoDevelop.Ide.Gui.OptionPanels.LoadSavePanelWidget.cs
@@ -10,15 +10,15 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
private global::Gtk.VBox vbox18;
private global::Gtk.Label loadLabel;
private global::Gtk.HBox hbox14;
- private global::Gtk.Label label25;
private global::Gtk.HBox hbox10;
private global::Gtk.VBox vbox65;
private global::Gtk.CheckButton loadUserDataCheckButton;
- private global::Gtk.CheckButton loadPrevProjectCheckButton;
+ private global::Gtk.RadioButton openStartWindowRadioButton;
+ private global::Gtk.RadioButton loadPrevProjectRadioButton;
+ private global::Gtk.RadioButton emptyEnvironmentRadioButton;
private global::Gtk.VBox vbox19;
private global::Gtk.Label saveLabel;
private global::Gtk.HBox hbox11;
- private global::Gtk.Label label21;
private global::Gtk.VBox vbox20;
private global::Gtk.CheckButton createBackupCopyCheckButton;
@@ -43,24 +43,14 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
this.locationLabel.Yalign = 0F;
this.locationLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("Default _Solution location");
this.locationLabel.UseUnderline = true;
- this.vbox26.Add (this.locationLabel);
- global::Gtk.Box.BoxChild w1 = ((global::Gtk.Box.BoxChild)(this.vbox26 [this.locationLabel]));
- w1.Position = 0;
- w1.Expand = false;
- w1.Fill = false;
+ this.vbox26.PackStart (this.locationLabel, false, false, 0);
+
// Container child vbox26.Gtk.Box+BoxChild
this.folderEntry = new global::MonoDevelop.Components.FolderEntry ();
this.folderEntry.Name = "folderEntry";
this.folderEntry.DisplayAsRelativePath = false;
- this.vbox26.Add (this.folderEntry);
- global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox26 [this.folderEntry]));
- w2.Position = 1;
- w2.Expand = false;
- w2.Fill = false;
- this.vbox17.Add (this.vbox26);
- global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox17 [this.vbox26]));
- w3.Position = 0;
- w3.Expand = false;
+ this.vbox26.PackStart (this.folderEntry, false, false, 0);
+ this.vbox17.PackStart (this.vbox26, false, false, 0);
// Container child vbox17.Gtk.Box+BoxChild
this.vbox18 = new global::Gtk.VBox ();
this.vbox18.Name = "vbox18";
@@ -72,27 +62,12 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
this.loadLabel.Yalign = 0F;
this.loadLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("<b>Load</b>");
this.loadLabel.UseMarkup = true;
- this.vbox18.Add (this.loadLabel);
- global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox18 [this.loadLabel]));
- w4.Position = 0;
- w4.Expand = false;
- w4.Fill = false;
+ this.vbox18.PackStart (this.loadLabel, false, false, 0);
// Container child vbox18.Gtk.Box+BoxChild
this.hbox14 = new global::Gtk.HBox ();
this.hbox14.Name = "hbox14";
this.hbox14.Spacing = 6;
// Container child hbox14.Gtk.Box+BoxChild
- this.label25 = new global::Gtk.Label ();
- this.label25.Name = "label25";
- this.label25.Xalign = 0F;
- this.label25.Yalign = 0F;
- this.label25.LabelProp = " ";
- this.hbox14.Add (this.label25);
- global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.hbox14 [this.label25]));
- w5.Position = 0;
- w5.Expand = false;
- w5.Fill = false;
- // Container child hbox14.Gtk.Box+BoxChild
this.hbox10 = new global::Gtk.HBox ();
this.hbox10.Name = "hbox10";
this.hbox10.Spacing = 6;
@@ -106,38 +81,60 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
this.loadUserDataCheckButton.Label = global::Mono.Unix.Catalog.GetString ("Load user-specific settings with the document");
this.loadUserDataCheckButton.DrawIndicator = true;
this.loadUserDataCheckButton.UseUnderline = true;
- this.vbox65.Add (this.loadUserDataCheckButton);
- global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox65 [this.loadUserDataCheckButton]));
- w6.Position = 0;
- w6.Expand = false;
- w6.Fill = false;
- // Container child vbox65.Gtk.Box+BoxChild
- this.loadPrevProjectCheckButton = new global::Gtk.CheckButton ();
- this.loadPrevProjectCheckButton.Name = "loadPrevProjectCheckButton";
- this.loadPrevProjectCheckButton.Label = global::Mono.Unix.Catalog.GetString ("_Load previous solution on startup");
- this.loadPrevProjectCheckButton.DrawIndicator = true;
- this.loadPrevProjectCheckButton.UseUnderline = true;
- this.vbox65.Add (this.loadPrevProjectCheckButton);
- global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox65 [this.loadPrevProjectCheckButton]));
- w7.Position = 1;
- w7.Expand = false;
- w7.Fill = false;
- this.hbox10.Add (this.vbox65);
- global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.hbox10 [this.vbox65]));
- w8.Position = 0;
- w8.Expand = false;
- w8.Fill = false;
- this.hbox14.Add (this.hbox10);
- global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.hbox14 [this.hbox10]));
- w9.Position = 1;
- this.vbox18.Add (this.hbox14);
- global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox18 [this.hbox14]));
- w10.Position = 1;
- this.vbox17.Add (this.vbox18);
- global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.vbox17 [this.vbox18]));
- w11.Position = 1;
- w11.Expand = false;
- // Container child vbox17.Gtk.Box+BoxChild
+ this.vbox65.PackStart (this.loadUserDataCheckButton, false, false, 0);
+
+ // Startup options
+ var startSectionVbox = new global::Gtk.VBox ();
+ startSectionVbox.Name = "startVbox";
+ startSectionVbox.Spacing = 6;
+
+ var startSectionLabel = new global::Gtk.Label ();
+ startSectionLabel.Name = "startSectionLabel";
+ startSectionLabel.Xalign = 0F;
+ startSectionLabel.Yalign = 0F;
+ startSectionLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("<b>Start</b>");
+ startSectionLabel.UseMarkup = true;
+ startSectionVbox.PackStart (startSectionLabel, false, false, 0);
+
+ var startContentHbox = new global::Gtk.HBox ();
+ startSectionVbox.PackStart (startContentHbox, false, false, 0);
+
+ var startContentVbox = new global::Gtk.VBox ();
+ startContentVbox.Name = "startContentVbox";
+ startContentVbox.Spacing = 6;
+ startContentHbox.PackStart (startContentVbox, false, false, 24);
+
+ this.openStartWindowRadioButton = new global::Gtk.RadioButton ((global::Gtk.RadioButton) null) {
+ Name = "openStartWindowCheckButton",
+ Label = global::Mono.Unix.Catalog.GetString ("_Always show me the Start Window"),
+ DrawIndicator = true,
+ UseUnderline = true
+ };
+ startContentVbox.PackStart (this.openStartWindowRadioButton, false, false, 0);
+
+ // Container child vbox65.Gtk.Box+BoxChild
+ this.loadPrevProjectRadioButton = new global::Gtk.RadioButton (this.openStartWindowRadioButton);
+ this.loadPrevProjectRadioButton.Name = "loadPrevProjectCheckButton";
+ this.loadPrevProjectRadioButton.Label = global::Mono.Unix.Catalog.GetString ("_Load previous solution on startup");
+ this.loadPrevProjectRadioButton.DrawIndicator = true;
+ this.loadPrevProjectRadioButton.UseUnderline = true;
+ startContentVbox.PackStart (this.loadPrevProjectRadioButton, false, false, 0);
+
+ this.emptyEnvironmentRadioButton = new global::Gtk.RadioButton (this.openStartWindowRadioButton) {
+ Name = "emptyEnvironmentCheckButton",
+ Label = global::Mono.Unix.Catalog.GetString ("_Show empty environment"),
+ DrawIndicator = true,
+ UseUnderline = true
+ };
+ startContentVbox.PackStart (this.emptyEnvironmentRadioButton, false, false, 0);
+
+ this.hbox10.PackStart (this.vbox65, false, false, 0);
+ this.hbox14.PackStart (this.hbox10, false, false, 24);
+ this.vbox18.PackStart (this.hbox14, false, false, 0);
+ this.vbox17.PackStart (startSectionVbox, false, false, 0);
+ this.vbox17.PackStart (this.vbox18, false, false, 0);
+
+ // Container child vbox17.Gtk.Box+BoxChild
this.vbox19 = new global::Gtk.VBox ();
this.vbox19.Name = "vbox19";
this.vbox19.Spacing = 6;
@@ -148,27 +145,12 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
this.saveLabel.Yalign = 0F;
this.saveLabel.LabelProp = global::Mono.Unix.Catalog.GetString ("<b>Save</b>");
this.saveLabel.UseMarkup = true;
- this.vbox19.Add (this.saveLabel);
- global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.vbox19 [this.saveLabel]));
- w12.Position = 0;
- w12.Expand = false;
- w12.Fill = false;
+ this.vbox19.PackStart (this.saveLabel, false, false, 0);
// Container child vbox19.Gtk.Box+BoxChild
this.hbox11 = new global::Gtk.HBox ();
this.hbox11.Name = "hbox11";
this.hbox11.Spacing = 6;
// Container child hbox11.Gtk.Box+BoxChild
- this.label21 = new global::Gtk.Label ();
- this.label21.Name = "label21";
- this.label21.Xalign = 0F;
- this.label21.Yalign = 0F;
- this.label21.LabelProp = " ";
- this.hbox11.Add (this.label21);
- global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox11 [this.label21]));
- w13.Position = 0;
- w13.Expand = false;
- w13.Fill = false;
- // Container child hbox11.Gtk.Box+BoxChild
this.vbox20 = new global::Gtk.VBox ();
this.vbox20.Name = "vbox20";
this.vbox20.Spacing = 6;
@@ -178,20 +160,12 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
this.createBackupCopyCheckButton.Label = global::Mono.Unix.Catalog.GetString ("Always create backup copy");
this.createBackupCopyCheckButton.DrawIndicator = true;
this.createBackupCopyCheckButton.UseUnderline = true;
- this.vbox20.Add (this.createBackupCopyCheckButton);
- global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vbox20 [this.createBackupCopyCheckButton]));
- w14.Position = 0;
- w14.Expand = false;
- w14.Fill = false;
- this.hbox11.Add (this.vbox20);
- global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox11 [this.vbox20]));
- w15.Position = 1;
- this.vbox19.Add (this.hbox11);
- global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.vbox19 [this.hbox11]));
- w16.Position = 1;
- this.vbox17.Add (this.vbox19);
+ this.vbox20.PackStart (this.createBackupCopyCheckButton, false, false, 0);
+ this.hbox11.PackStart (this.vbox20, false, false, 24);
+ this.vbox19.PackStart (this.hbox11, false, false, 0);
+ this.vbox17.PackStart (this.vbox19, false, false, 0);
global::Gtk.Box.BoxChild w17 = ((global::Gtk.Box.BoxChild)(this.vbox17 [this.vbox19]));
- w17.Position = 2;
+ w17.Position = 3;
this.Add (this.vbox17);
if ((this.Child != null)) {
this.Child.ShowAll ();
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs
index 5650cb953e..60314a14b0 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs
@@ -37,6 +37,7 @@ using Foundation;
using Gdk;
using ObjCRuntime;
using MetalPerformanceShaders;
+using MonoDevelop.Components.Mac;
namespace MonoDevelop.Components.AtkCocoaHelper
{
@@ -291,29 +292,38 @@ namespace MonoDevelop.Components.AtkCocoaHelper
nsa.AccessibilityOrientation = orientation == Gtk.Orientation.Vertical ? NSAccessibilityOrientation.Vertical : NSAccessibilityOrientation.Horizontal;
}
- public static void SetTitleFor (this Atk.Object o, params Atk.Object [] objects)
+ static NSArray ToAccessibilityArray (this Atk.Object[] objects)
{
- var nsa = GetNSAccessibilityElement (o);
- if (nsa == null) {
- return;
- }
+ if (objects == null)
+ return null;
- NSObject [] titleElements = new NSObject [objects.Length];
- int idx = 0;
+ var array = new NSMutableArray ((nuint)objects.Length);
foreach (var obj in objects) {
var nsao = GetNSAccessibilityElement (obj);
if (nsao == null) {
- return;
+ continue;
}
- titleElements [idx] = (NSObject)nsao;
- idx++;
+ array.Add ((NSObject)nsao);
}
+ return array;
+ }
- nsa.AccessibilityServesAsTitleForUIElements = titleElements;
+ static readonly IntPtr selSetAccessibilityServesAsTitleForUIElements_Handle = Selector.GetHandle ("setAccessibilityServesAsTitleForUIElements:");
+ public static void SetTitleFor (this Atk.Object o, params Atk.Object [] objects)
+ {
+ var nsa = GetNSAccessibilityElement (o);
+ if (nsa == null) {
+ return;
+ }
+
+ using (var titleElements = objects.ToAccessibilityArray ()) {
+ nsa.SendSelector (selSetAccessibilityServesAsTitleForUIElements_Handle, titleElements);
+ }
}
+ static readonly IntPtr selSetAccessibilityTabs_Handle = Selector.GetHandle ("setAccessibilityTabs:");
public static void SetTabs (this Atk.Object o, params AccessibilityElementProxy [] tabs)
{
var nsa = GetNSAccessibilityElement (o);
@@ -321,7 +331,9 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- nsa.AccessibilityTabs = ConvertToRealProxyArray (tabs);
+ using (var realTabs = tabs.ConvertToRealProxyArray ()) {
+ nsa.SendSelector (selSetAccessibilityTabs_Handle, realTabs);
+ }
}
public static void SetTabs (this Atk.Object o, params Atk.Object [] tabs)
@@ -331,16 +343,12 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- NSObject [] realTabs = new NSObject [tabs.Length];
- int i = 0;
- foreach (var tab in tabs) {
- realTabs [i] = (NSObject)GetNSAccessibilityElement (tab);
- i++;
+ using (var realTabs = tabs.ToAccessibilityArray ()) {
+ nsa.SendSelector (selSetAccessibilityTabs_Handle, realTabs);
}
-
- nsa.AccessibilityTabs = realTabs;
}
+ static readonly IntPtr selAccessibilityServesAsTitleForUIElements_Handle = Selector.GetHandle ("setAccessibilityServesAsTitleForUIElements:");
public static void AddElementToTitle (this Atk.Object title, Atk.Object o)
{
var titleNsa = GetNSAccessibilityElement (title);
@@ -350,20 +358,12 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- NSObject [] oldElements = titleNsa.AccessibilityServesAsTitleForUIElements;
- int length = oldElements != null ? oldElements.Length : 0;
-
- if (oldElements != null && oldElements.IndexOf ((NSObject)nsa) != -1) {
- return;
+ IntPtr ptr = Messaging.IntPtr_objc_msgSend (titleNsa.Handle, selAccessibilityServesAsTitleForUIElements_Handle);
+ using (var array = Runtime.GetNSObject<NSArray> (ptr))
+ using (var copy = array != null ? (NSMutableArray)array.MutableCopy () : new NSMutableArray (1)) {
+ copy.Add ((NSObject)nsa);
+ nsa.SendSelector (selSetAccessibilityServesAsTitleForUIElements_Handle, copy);
}
-
- NSObject [] titleElements = new NSObject [length + 1];
- if (oldElements != null) {
- oldElements.CopyTo (titleElements, 0);
- }
- titleElements [length] = (NSObject)nsa;
-
- titleNsa.AccessibilityServesAsTitleForUIElements = titleElements;
}
public static void RemoveElementFromTitle (this Atk.Object title, Atk.Object o)
@@ -375,48 +375,56 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- if (titleNsa.AccessibilityServesAsTitleForUIElements == null) {
- return;
- }
+ IntPtr ptr = Messaging.IntPtr_objc_msgSend (titleNsa.Handle, selAccessibilityServesAsTitleForUIElements_Handle);
+ using (var array = Runtime.GetNSObject<NSArray> (ptr)) {
+ if (array == null)
+ return;
+
+ var nso = (NSObject)nsa;
+ var index = (nint)array.IndexOf (nso);
+ if (index == NSRange.NotFound)
+ return;
- List<NSObject> oldElements = new List<NSObject> (titleNsa.AccessibilityServesAsTitleForUIElements);
- oldElements.Remove ((NSObject)nsa);
+ using (var copy = (NSMutableArray)array.MutableCopy ())
+ using (var set = NSIndexSet.FromIndex (index)) {
+ copy.RemoveObjectsAtIndexes (set);
- titleNsa.AccessibilityServesAsTitleForUIElements = oldElements.ToArray ();
+ nsa.SendSelector (selSetAccessibilityServesAsTitleForUIElements_Handle, copy);
+ }
+ }
}
- static RealAccessibilityElementProxy [] ConvertToRealProxyArray (AccessibilityElementProxy [] proxies)
+ static NSArray ConvertToRealProxyArray (this AccessibilityElementProxy [] proxies)
{
if (proxies == null) {
return null;
}
- var realProxies = new RealAccessibilityElementProxy [proxies.Length];
- int idx = 0;
+ var array = new NSMutableArray ((nuint)proxies.Length);
foreach (var p in proxies) {
- var rp = p.Proxy as RealAccessibilityElementProxy;
- if (rp == null) {
+ if (!(p.Proxy is RealAccessibilityElementProxy rp)) {
throw new Exception ($"Invalid type {p.GetType ()} in accessibleChildren");
}
- realProxies [idx] = rp;
- idx++;
+ array.Add (rp);
}
- return realProxies;
+ return array;
}
- public static void ReplaceAccessibilityElements (this Atk.Object parent, AccessibilityElementProxy [] children)
+ static void SendSelector (this INSAccessibility nsa, IntPtr selectorHandle, NSArray array)
{
- var nsa = GetNSAccessibilityElement (parent);
-
- if (nsa == null) {
- return;
- }
+ Messaging.void_objc_msgSend_IntPtr (nsa.Handle, selectorHandle, array != null ? array.Handle : IntPtr.Zero);
+ }
- nsa.AccessibilityChildren = ConvertToRealProxyArray (children);
+ static readonly IntPtr selAccessibilityChildren_Handle = Selector.GetHandle ("accessibilityChildren");
+ static readonly IntPtr selSetAccessibilityChildren_Handle = Selector.GetHandle ("setAccessibilityChildren:");
+ public static void ReplaceAccessibilityElements (this Atk.Object parent, AccessibilityElementProxy [] children)
+ {
+ parent.SetAccessibleChildren (children);
}
+ static readonly IntPtr selSetAccessibilityColumns_Handle = Selector.GetHandle ("setAccessibilityColumns:");
public static void SetColumns (this Atk.Object parent, AccessibilityElementProxy [] columns)
{
var nsa = GetNSAccessibilityElement (parent);
@@ -425,9 +433,12 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- nsa.AccessibilityColumns = ConvertToRealProxyArray (columns);
+ using (var array = columns.ConvertToRealProxyArray ()) {
+ nsa.SendSelector (selSetAccessibilityColumns_Handle, array);
+ }
}
+ static readonly IntPtr selSetAccessibilityRows_Handle = Selector.GetHandle ("setAccessibilityRows:");
public static void SetRows (this Atk.Object parent, AccessibilityElementProxy [] rows)
{
var nsa = GetNSAccessibilityElement (parent);
@@ -436,7 +447,9 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- nsa.AccessibilityRows = ConvertToRealProxyArray (rows);
+ using (var array = rows.ConvertToRealProxyArray ()) {
+ nsa.SendSelector (selSetAccessibilityRows_Handle, array);
+ }
}
public static void AddAccessibleElement (this Atk.Object o, AccessibilityElementProxy child)
@@ -460,34 +473,27 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- var children = nsa.AccessibilityChildren;
-
- if (children == null || children.Length == 0) {
- return;
- }
-
var p = child.Proxy as RealAccessibilityElementProxy;
if (p == null) {
throw new Exception ($"Invalid proxy child type {p.GetType ()}");
}
- var idx = children.IndexOf (p);
- if (idx == -1) {
- return;
- }
+ var childrenHandle = Messaging.IntPtr_objc_msgSend (nsa.Handle, selAccessibilityChildren_Handle);
+ using (var array = Runtime.GetNSObject<NSArray> (childrenHandle)) {
+ if (array == null)
+ return;
- var newChildren = new NSObject [children.Length - 1];
+ var nso = (NSObject)nsa;
+ var index = (nint)array.IndexOf (nso);
+ if (index == NSRange.NotFound)
+ return;
- for (int i = 0, j = 0; i < children.Length; i++) {
- if (i == idx) {
- continue;
+ using (var copy = (NSMutableArray)array.MutableCopy ())
+ using (var set = NSIndexSet.FromIndex (index)) {
+ copy.RemoveObjectsAtIndexes (set);
+ nsa.SendSelector (selSetAccessibilityChildren_Handle, array);
}
-
- newChildren [j] = children [i];
- j++;
}
-
- nsa.AccessibilityChildren = newChildren;
}
public static void TransferAccessibleChild (this Atk.Object from, Atk.Object to, Atk.Object child)
@@ -500,21 +506,28 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- var fromChildren = fromNsa.AccessibilityChildren;
+ var fromChildren = Messaging.IntPtr_objc_msgSend (fromNsa.Handle, selAccessibilityChildren_Handle);
+ using (var fromArray = Runtime.GetNSObject<NSArray> (fromChildren)) {
+ if (fromArray == null)
+ return;
- if (fromChildren == null || fromChildren.Length == 0) {
- return;
+ var nso = (NSObject)childNsa;
+ var index = (nint)fromArray.IndexOf (nso);
+ if (index != NSRange.NotFound) {
+ using (var copy = (NSMutableArray)fromArray.MutableCopy ())
+ using (var set = NSIndexSet.FromIndex (index)) {
+ copy.RemoveObjectsAtIndexes (set);
+ fromNsa.SendSelector (selSetAccessibilityChildren_Handle, copy);
+ }
+ }
}
- var fromList = fromChildren.ToList ();
- fromList.Remove ((NSObject) childNsa);
- fromNsa.AccessibilityChildren = fromList.ToArray ();
-
- var toChildren = toNsa.AccessibilityChildren;
- List<NSObject> toList = toChildren == null ? new List<NSObject> () : toChildren.ToList ();
-
- toList.Add ((NSObject)childNsa);
- toNsa.AccessibilityChildren = toList.ToArray ();
+ var toChildren = Messaging.IntPtr_objc_msgSend (fromNsa.Handle, selAccessibilityChildren_Handle);
+ using (var toArray = Runtime.GetNSObject<NSArray> (toChildren))
+ using (var copy = toArray != null ? (NSMutableArray)toArray.MutableCopy () : new NSMutableArray (1)) {
+ copy.Add ((NSObject)childNsa);
+ toNsa.SendSelector (selSetAccessibilityChildren_Handle, copy);
+ }
}
public static void SetAccessibleChildren (this Atk.Object o, AccessibilityElementProxy [] children)
@@ -524,9 +537,13 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- nsa.AccessibilityChildren = ConvertToRealProxyArray (children);
+ using (var array = children.ConvertToRealProxyArray ()) {
+ nsa.SendSelector (selSetAccessibilityChildren_Handle, array);
+ }
}
+ static readonly IntPtr selAccessibilityLinkedUIElements_Handle = Selector.GetHandle ("accessibilityLinkedUIElements");
+ static readonly IntPtr selSetAccessibilityLinkedUIElements_Handle = Selector.GetHandle ("setAccessibilityLinkedUIElements:");
public static void AddLinkedUIElement (this Atk.Object o, Atk.Object linked)
{
var nsa = GetNSAccessibilityElement (o);
@@ -535,18 +552,12 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- var current = nsa.AccessibilityLinkedUIElements;
- NSObject [] newLinkedElements;
- if (current != null) {
- int length = nsa.AccessibilityLinkedUIElements.Length;
- newLinkedElements = new NSObject [length + 1];
- Array.Copy (nsa.AccessibilityLinkedUIElements, newLinkedElements, length);
- newLinkedElements [length] = (NSObject)linkedNSA;
- } else {
- newLinkedElements = new NSObject [] { (NSObject)linkedNSA };
+ var current = Messaging.IntPtr_objc_msgSend (nsa.Handle, selAccessibilityLinkedUIElements_Handle);
+ using (var array = Runtime.GetNSObject<NSArray> (current))
+ using (var copy = array != null ? (NSMutableArray)array.MutableCopy () : new NSMutableArray (1)) {
+ copy.Add ((NSObject)linkedNSA);
+ nsa.SendSelector (selSetAccessibilityLinkedUIElements_Handle, copy);
}
-
- nsa.AccessibilityLinkedUIElements = newLinkedElements;
}
public static void AddLinkedUIElement (this Atk.Object o, params Atk.Object [] linked)
@@ -556,23 +567,15 @@ namespace MonoDevelop.Components.AtkCocoaHelper
return;
}
- var current = nsa.AccessibilityLinkedUIElements;
-
- int length = current != null ? current.Length : 0;
- var newLinkedElements = new NSObject [length + linked.Length];
-
- if (current != null) {
- Array.Copy (current, newLinkedElements, length);
- }
-
- int idx = length;
- foreach (var e in linked) {
- var nsaLinked = GetNSAccessibilityElement (e);
- newLinkedElements [idx] = (NSObject)nsaLinked;
- idx++;
+ var current = Messaging.IntPtr_objc_msgSend (nsa.Handle, selAccessibilityLinkedUIElements_Handle);
+ using (var array = Runtime.GetNSObject<NSArray> (current))
+ using (var copy = array != null ? (NSMutableArray)array.MutableCopy () : new NSMutableArray ((nuint)linked.Length)) {
+ foreach (var e in linked) {
+ var nsaLinked = GetNSAccessibilityElement (e);
+ copy.Add ((NSObject)nsaLinked);
+ }
+ nsa.SendSelector (selSetAccessibilityLinkedUIElements_Handle, copy);
}
-
- nsa.AccessibilityLinkedUIElements = newLinkedElements;
}
public static void MakeAccessibilityAnnouncement (this Atk.Object o, string message)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkNotebookResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkNotebookResult.cs
index c3d799acd3..89f59380bd 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkNotebookResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkNotebookResult.cs
@@ -73,6 +73,12 @@ namespace MonoDevelop.Components.AutoTest.Results
}
return false;
}
+
+ protected override void Dispose (bool disposing)
+ {
+ noteBook = null;
+ base.Dispose (disposing);
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkTreeModelResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkTreeModelResult.cs
index 7e3dbbccbd..6aaee7e68c 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkTreeModelResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkTreeModelResult.cs
@@ -282,6 +282,13 @@ namespace MonoDevelop.Components.AutoTest.Results
base.SetProperty (modelValue, propertyName, value);
}
}
+
+ protected override void Dispose (bool disposing)
+ {
+ ParentWidget = null;
+ TModel = null;
+ base.Dispose (disposing);
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkWidgetResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkWidgetResult.cs
index 6beaa805eb..b530748d1d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkWidgetResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/GtkWidgetResult.cs
@@ -530,6 +530,12 @@ namespace MonoDevelop.Components.AutoTest.Results
{
base.SetProperty (resultWidget, propertyName, value);
}
+
+ protected override void Dispose (bool disposing)
+ {
+ resultWidget = null;
+ base.Dispose (disposing);
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/NSObjectResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/NSObjectResult.cs
index c597a1d5cf..5fbc66abf0 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/NSObjectResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/NSObjectResult.cs
@@ -366,7 +366,13 @@ namespace MonoDevelop.Components.AutoTest.Results
pinfo.SetValue (ResultObject, runtime);
return true;
}
-#endregion
+ #endregion
+
+ protected override void Dispose (bool disposing)
+ {
+ ResultObject = null;
+ base.Dispose (disposing);
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/ObjectResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/ObjectResult.cs
index b96c6978e0..bdb2f5a912 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/ObjectResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest.Results/ObjectResult.cs
@@ -132,5 +132,11 @@ namespace MonoDevelop.Components.AutoTest.Results
{
SetProperty (value, propertyName, newValue);
}
+
+ protected override void Dispose (bool disposing)
+ {
+ value = null;
+ base.Dispose (disposing);
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppQuery.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppQuery.cs
index 3eeb5623a7..629e369713 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppQuery.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppQuery.cs
@@ -39,7 +39,7 @@ using AppKit;
namespace MonoDevelop.Components.AutoTest
{
- public class AppQuery : MarshalByRefObject
+ public class AppQuery : MarshalByRefObject, IDisposable
{
AppResult rootNode;
List<Operation> operations = new List<Operation> ();
@@ -368,7 +368,13 @@ namespace MonoDevelop.Components.AutoTest
{
var operationChain = string.Join (".", operations.Select (x => x.ToString ()));
return string.Format ("c => c.{0};", operationChain);
- }
+ }
+
+ public void Dispose ()
+ {
+ rootNode?.Dispose ();
+ rootNode = null;
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs
index 4bce0b11d5..a1c7373613 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs
@@ -35,7 +35,7 @@ using MonoDevelop.Core;
namespace MonoDevelop.Components.AutoTest
{
- public abstract class AppResult : MarshalByRefObject
+ public abstract class AppResult : MarshalByRefObject, IDisposable
{
//public Gtk.Widget ResultWidget { get; private set; }
@@ -229,5 +229,18 @@ namespace MonoDevelop.Components.AutoTest
return haystack != null && (haystack.IndexOf (needle, StringComparison.Ordinal) > -1);
}
}
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ FirstChild?.Dispose ();
+ NextSibling?.Dispose ();
+
+ FirstChild = NextSibling = ParentNode = PreviousSibling = null;
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestClientSession.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestClientSession.cs
index f699cc3b44..01d3baa316 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestClientSession.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestClientSession.cs
@@ -122,6 +122,8 @@ namespace MonoDevelop.Components.AutoTest
return null;
}
+ public void DisconnectQueries () => session.DisconnectQueries ();
+
public void Stop ()
{
if (service != null)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestService.cs
index 0ecdb78e83..8041c26115 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestService.cs
@@ -163,9 +163,11 @@ namespace MonoDevelop.Components.AutoTest
public void DetachClient (IAutoTestClient client)
{
- if (client == this.client)
+ if (client == this.client) {
this.client = null;
- else
+ currentSession?.Dispose ();
+ currentSession = null;
+ } else
throw new InvalidOperationException ("Not connected");
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestSession.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestSession.cs
index b7fad4b533..16d385b73b 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestSession.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AutoTestSession.cs
@@ -40,6 +40,7 @@ using MonoDevelop.Components.Commands;
using MonoDevelop.Core;
using System.Xml;
+using System.Runtime.Remoting;
namespace MonoDevelop.Components.AutoTest
{
@@ -58,6 +59,8 @@ namespace MonoDevelop.Components.AutoTest
set { SessionDebug.DebugObject = value; }
}
+ readonly List<AppQuery> queries = new List<AppQuery> ();
+
public AutoTestSession ()
{
}
@@ -67,6 +70,40 @@ namespace MonoDevelop.Components.AutoTest
return null;
}
+ ~AutoTestSession()
+ {
+ Dispose (false);
+ }
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize (this);
+ Dispose (true);
+ }
+
+ public void DisconnectQueries()
+ {
+ lock (queries) {
+ foreach (var query in queries) {
+ RemotingServices.Disconnect (query);
+ query.Dispose ();
+ }
+ queries.Clear ();
+ }
+ }
+
+ bool disposed;
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ disposed = true;
+ RemotingServices.Disconnect (this);
+
+ DisconnectQueries ();
+ }
+
[Serializable]
public struct MemoryStats {
public long PrivateMemory;
@@ -334,6 +371,8 @@ namespace MonoDevelop.Components.AutoTest
AppQuery query = new AppQuery ();
query.SessionDebug = SessionDebug;
+ lock (queries)
+ queries.Add (query);
return query;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs
index 2c079b852a..bb0c33fefc 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs
@@ -2405,7 +2405,7 @@ namespace MonoDevelop.Components.Commands
// It then queries widgets, which resurrects widget wrappers, which breaks on managed widgets
if (this.disposed)
return;
-
+
var activeWidget = GetActiveWidget (rootWidget);
foreach (ICommandBar toolbar in toolbars) {
toolbar.Update (activeWidget);
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 16b90c9854..9b63190523 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs
@@ -1,5 +1,4 @@
//
-// DockContainer.cs
//
// Author:
// Lluis Sanchez Gual
@@ -36,6 +35,7 @@ using Gtk;
using Gdk;
using System.Linq;
using MonoDevelop.Components.AtkCocoaHelper;
+using MonoDevelop.Core;
using MonoDevelop.Ide.Gui;
namespace MonoDevelop.Components.Docking
@@ -52,8 +52,8 @@ namespace MonoDevelop.Components.Docking
bool needsRelayout = true;
- PlaceholderWindow placeholderWindow;
- PadTitleWindow padTitleWindow;
+ volatile PlaceholderWindow placeholderWindow;
+ volatile PadTitleWindow padTitleWindow;
public DockContainer (DockFrame frame)
{
@@ -403,7 +403,15 @@ namespace MonoDevelop.Components.Docking
internal bool UpdatePlaceholder (DockItem item, Gdk.Size size, bool allowDocking)
{
- if (placeholderWindow == null)
+ if (!Runtime.IsMainThread) {
+ var msg = "UpdatePlaceholder called from background thread.";
+ LoggingService.LogInternalError ($"{msg}\n{Environment.StackTrace}", new InvalidOperationException (msg));
+ }
+
+ var placeholderWindow = this.placeholderWindow;
+ var padTitleWindow = this.padTitleWindow;
+
+ if (placeholderWindow == null || padTitleWindow == null)
return false;
int px, py;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs
index a07c47b6ca..3cbdd0fdd4 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid.Editors/EventEditor.cs
@@ -45,14 +45,20 @@ namespace MonoDevelop.Components.PropertyGrid.PropertyEditors
IEventBindingService evtBind;
protected override void Initialize ()
- {
- IComponent comp = Instance as IComponent;
- evtBind = (IEventBindingService) comp.Site.GetService (typeof (IEventBindingService));
- base.Initialize ();
+ {
+ IComponent comp = Instance as IComponent;
+ if (comp != null) {
+ evtBind = (IEventBindingService)comp.Site.GetService (typeof (IEventBindingService));
+ base.Initialize ();
+ }
}
protected override IPropertyEditor CreateEditor (Gdk.Rectangle cell_area, Gtk.StateType state)
- {
+ {
+ if (evtBind == null) {
+ return null;
+ }
+
//get existing method names
ICollection IColl = evtBind.GetCompatibleMethods (evtBind.GetEvent (Property)) ;
string[] methods = new string [IColl.Count + 1];
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/BaseFileEntry.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/BaseFileEntry.cs
index 89b90e9b4d..afe01c968e 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/BaseFileEntry.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/BaseFileEntry.cs
@@ -154,6 +154,12 @@ namespace MonoDevelop.Components
pathEntry.SetCommonAccessibilityAttributes (name, label, help);
}
+
+ public void SetEntryAccessibleTitleUIElement (Atk.Object accessible)
+ {
+ pathEntry.Accessible.SetTitleUIElement (accessible);
+ }
+
public Atk.Object EntryAccessible {
get {
return pathEntry.Accessible;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs
index 98cd671d2c..a6c2af0429 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs
@@ -1538,7 +1538,11 @@ namespace MonoDevelop.Components
[DllImport ("libgdk-win32-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void gdk_event_free (IntPtr raw);
- public static void FreeEvent (IntPtr raw) => gdk_event_free (raw);
+ public static void FreeEvent (IntPtr raw)
+ {
+ if (raw != IntPtr.Zero)
+ gdk_event_free (raw);
+ }
}
public readonly struct KeyboardShortcut : IEquatable<KeyboardShortcut>
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageLoader.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageLoader.cs
index dccc878c76..8c26c12d1e 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageLoader.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageLoader.cs
@@ -28,7 +28,9 @@
using System;
using System.IO;
using MonoDevelop.Core;
+using MonoDevelop.Core.Web;
using System.Net;
+using System.Net.Http;
namespace MonoDevelop.Components
{
@@ -62,43 +64,30 @@ namespace MonoDevelop.Components
var finfo = new FileInfo (cachePath);
- WebRequestHelper.GetResponseAsync (
- () => (HttpWebRequest)WebRequest.Create (url),
- r => {
- if (finfo.Exists)
- r.IfModifiedSince = finfo.LastWriteTime;
- }
- ).ContinueWith (t => {
+ var client = HttpClientProvider.CreateHttpClient (url);
+ if (finfo.Exists)
+ client.DefaultRequestHeaders.IfModifiedSince = finfo.LastWriteTime;
+ client.GetAsync (url, HttpCompletionOption.ResponseHeadersRead).ContinueWith (async t => {
try {
- if (t.IsFaulted) {
- var wex = t.Exception.Flatten ().InnerException as WebException;
- if (wex != null) {
- var resp = wex.Response as HttpWebResponse;
- if (resp != null) {
- // If the errorcode is NotModified the file we cached on disk is still the latest one.
- if (resp.StatusCode == HttpStatusCode.NotModified) {
- Cleanup ();
- return;
- }
- //if 404, there is no gravatar for the user
- if (resp.StatusCode == HttpStatusCode.NotFound) {
- image = null;
- Cleanup ();
- return;
- }
- }
+ using (var response = t.Result) {
+ // If the errorcode is NotModified the file we cached on disk is still the latest one.
+ if (t.Result.StatusCode == HttpStatusCode.NotModified) {
+ Cleanup ();
+ return;
+ }
+ //if 404, there is no gravatar for the user
+ if (t.Result.StatusCode == HttpStatusCode.NotFound) {
+ image = null;
+ Cleanup ();
+ return;
}
- }
- using (var response = t.Result) {
finfo.Directory.Create ();
// Copy out the new file and reload it
if (response.StatusCode == HttpStatusCode.OK) {
using (var tempFile = File.Create (tempPath)) {
- using (var stream = response.GetResponseStream ()) {
- stream.CopyTo (tempFile);
- }
+ await response.Content.CopyToAsync (tempFile);
}
FileService.SystemRename (tempPath, cachePath);
}
@@ -108,7 +97,7 @@ namespace MonoDevelop.Components
var aex = ex as AggregateException;
if (aex != null)
ex = aex.Flatten ().InnerException;
- var wex = ex as WebException;
+ var wex = ex?.InnerException as WebException;
if (wex != null && wex.Status.IsCannotReachInternetError ())
LoggingService.LogWarning ("Gravatar service could not be reached.");
else
@@ -116,6 +105,7 @@ namespace MonoDevelop.Components
Cleanup ();
} finally {
try {
+ client.Dispose ();
if (File.Exists (tempPath))
File.Delete (tempPath);
} catch (Exception ex) {
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/InformationPopoverWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/InformationPopoverWidget.cs
index 707aba213a..446faa8a23 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/InformationPopoverWidget.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/InformationPopoverWidget.cs
@@ -108,11 +108,24 @@ namespace MonoDevelop.Components
ShowPopover ();
}
+ bool WorkaroundNestedDialogFlickering ()
+ {
+ // There seems to be a problem with Gdk.Window focus events when the parent
+ // window is transient for another modal window (i.e. dialogs on top of the Ide preferences window).
+ // A native tooltip seems to confuse Gdk in this case and it rapidly fires LeaveNotify/EnterNotify
+ // events leading to fast flickering of the tooltip.
+ if (ParentWindow != null && Surface.ToolkitEngine.GetNativeWindow (ParentWindow) is Gtk.Window gtkWindow) {
+ if (gtkWindow.TransientFor?.TransientFor != null)
+ return true;
+ }
+ return false;
+ }
+
void ShowPopover ()
{
if (popover != null)
popover.Destroy ();
- popover = TooltipPopoverWindow.Create ();
+ popover = TooltipPopoverWindow.Create (!WorkaroundNestedDialogFlickering ());
popover.ShowArrow = true;
popover.Text = message;
popover.Severity = severity;
@@ -137,6 +150,13 @@ namespace MonoDevelop.Components
DestroyPopover ();
}
+ protected override void OnPreferredSizeChanged ()
+ {
+ base.OnPreferredSizeChanged ();
+ if (!Visible)
+ DestroyPopover ();
+ }
+
void DestroyPopover ()
{
if (popover != null) {
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/Messaging.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/Messaging.cs
index e50cb83ce3..fecd358c0f 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/Messaging.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/Messaging.cs
@@ -42,6 +42,9 @@ namespace MonoDevelop.Components.Mac
public static extern void void_objc_msgSend (IntPtr handle, IntPtr sel);
[DllImport (LIBOBJC_DYLIB, EntryPoint="objc_msgSend")]
+ public static extern void void_objc_msgSend_IntPtr (IntPtr receiver, IntPtr selector, IntPtr arg1);
+
+ [DllImport (LIBOBJC_DYLIB, EntryPoint="objc_msgSend")]
public static extern bool bool_objc_msgSend_IntPtr_IntPtr (IntPtr handle, IntPtr sel, IntPtr a1, IntPtr a2);
[DllImport (LIBOBJC_DYLIB, EntryPoint="objc_msgSendSuper")]
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs
index 25d1017485..ecbc055321 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs
@@ -684,6 +684,13 @@ namespace MonoDevelop.Components
}
+ public void SetEntryAccessibilityAttributes (string name, string label, string help)
+ {
+ entry.SetCommonAccessibilityAttributes (name, label, help);
+ }
+
+ public Atk.Object EntryAccessible => entry.Accessible;
+
private class FramelessEntry : Entry
{
private SearchEntry parent;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs
index 335cc4bbeb..84bcfd207e 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs
@@ -32,6 +32,7 @@ using Cairo;
using Gtk;
using System.Linq;
using MonoDevelop.Components.AtkCocoaHelper;
+using MonoDevelop.Core;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Ide.Fonts;
using MonoDevelop.Ide;
@@ -258,7 +259,11 @@ namespace MonoDevelop.Components
protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
{
if (hoverTab != null) {
- ActiveTab = tabs.IndexOf (hoverTab);
+ try {
+ ActiveTab = tabs.IndexOf (hoverTab);
+ } catch (Exception ex) {
+ LoggingService.LogInternalError (ex);
+ }
}
return base.OnButtonPressEvent (evnt);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs
index 9cde49a350..a1d8ba495d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionDataList.cs
@@ -192,7 +192,7 @@ namespace MonoDevelop.Ide.CodeCompletion
{
// default - word with highest match rating in the list.
int idx = -1;
- if (DefaultCompletionString != null && DefaultCompletionString.StartsWith (partialWord, StringComparison.OrdinalIgnoreCase)) {
+ if (DefaultCompletionString != null && string.IsNullOrEmpty(partialWord)) {
partialWord = DefaultCompletionString;
}
CompletionDataMatcher matcher = null;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs
index 9e7fef13ea..a954a6d4b2 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs
@@ -263,12 +263,6 @@ namespace MonoDevelop.Ide.CodeCompletion
controller.HideWindow ();
}
- [Obsolete("Use CompletionWindowManager.ToggleCategoryMode")]
- public void ToggleCategoryMode ()
- {
- controller.ToggleCategoryMode ();
- }
-
/// <summary>
/// Gets or sets a value indicating that shift was pressed during enter.
/// </summary>
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs
index 5565aca800..310e017785 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs
@@ -245,6 +245,8 @@ namespace MonoDevelop.Ide.CodeCompletion
}
}
+ public static bool RequestInsertText { get; internal set; }
+
public override void InsertCompletionText (CompletionListWindow window, ref KeyActions ka, KeyDescriptor descriptor)
{
var document = IdeApp.Workbench.ActiveDocument;
@@ -258,7 +260,13 @@ namespace MonoDevelop.Ide.CodeCompletion
internal void InsertCompletionText (TextEditor editor, DocumentContext context, ref KeyActions ka, KeyDescriptor descriptor)
{
- var completionChange = Provider.GetChangeAsync (doc, CompletionItem, null, default (CancellationToken)).WaitAndGetResult (default (CancellationToken));
+ CompletionChange completionChange;
+ try {
+ RequestInsertText = true;
+ completionChange = Provider.GetChangeAsync (doc, CompletionItem, null, default (CancellationToken)).WaitAndGetResult (default (CancellationToken));
+ } finally {
+ RequestInsertText = false;
+ }
var currentBuffer = editor.GetPlatformTextBuffer ();
var textChange = completionChange.TextChange;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs
index b2e4f6e945..6e579b74d2 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs
@@ -320,11 +320,15 @@ namespace MonoDevelop.Ide.CodeTemplates
s = variableDecarations [name].Default;
}
if (s != null) {
- link.AddLink (new TextSegment (sb.Length, s.Length));
- if (isNew) {
- link.GetStringFunc = delegate (Func<string, string> callback) {
- return expansion.RunFunction (context, callback, variableDecarations [name].Function);
- };
+ if (!link.IsEditable) {
+ result.TextLinks.Remove (link);
+ } else {
+ link.AddLink (new TextSegment (sb.Length, s.Length));
+ if (isNew) {
+ link.GetStringFunc = delegate (Func<string, string> callback) {
+ return expansion.RunFunction (context, callback, variableDecarations [name].Function);
+ };
+ }
}
sb.Append (s);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs
index 191409be2b..034be04164 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplatePanel.cs
@@ -32,6 +32,7 @@ using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Dialogs;
using MonoDevelop.Components;
using MonoDevelop.Ide.Editor;
+using MonoDevelop.Ide.Gui;
namespace MonoDevelop.Ide.CodeTemplates
{
@@ -177,7 +178,7 @@ namespace MonoDevelop.Ide.CodeTemplates
GLib.Markup.EscapeText (GettextCatalog.GetString (template.Description)) + ")";
} else {
crt.Markup = GLib.Markup.EscapeText (template.Shortcut) + " <span foreground=\"" +
- GetColorString (Style.Text (StateType.Insensitive)) + "\">("
+ Styles.SecondaryTextColorHexString + "\">("
+ GLib.Markup.EscapeText (GettextCatalog.GetString (template.Description)) + ")</span>";
}
}
@@ -210,11 +211,6 @@ namespace MonoDevelop.Ide.CodeTemplates
return templateStore.AppendValues (null, groupName, "<b>" + groupName + "</b>");
}
- internal static string GetColorString (Gdk.Color color)
- {
- return string.Format ("#{0:X02}{1:X02}{2:X02}", color.Red / 256, color.Green / 256, color.Blue / 256);
- }
-
TreeIter InsertTemplate (CodeTemplate template)
{
TreeIter iter = GetGroup (template.Group);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs
index 5da1f66ff8..7c27b795bd 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs
@@ -37,6 +37,13 @@ namespace MonoDevelop.Ide.Codons
public string ScanPath {
get {
+ // If the path starts with '${' then the path contains a placeholder
+ // that the StringParserService will replace. The path is returned
+ // without calling Addin.GetFilePath to prevent the addin directory
+ // being prefixed to the path.
+ if (path != null && path.StartsWith ("${", StringComparison.Ordinal)) {
+ return path;
+ }
return Addin.GetFilePath (path);
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs
index 414a354ff8..48d067d554 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs
@@ -82,7 +82,7 @@ namespace MonoDevelop.Ide.Commands
protected override void Run ()
{
var dlg = new OpenFileDialog (GettextCatalog.GetString ("File to Open"), MonoDevelop.Components.FileChooserAction.Open) {
- TransientFor = IdeApp.Workbench.RootWindow,
+ TransientFor = IdeServices.DesktopService.GetFocusedTopLevelWindow (),
ShowEncodingSelector = true,
ShowViewerSelector = true,
};
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs
index 7d9a07dc49..f8464dec79 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs
@@ -87,7 +87,8 @@ namespace MonoDevelop.Ide.Commands
SelectActiveConfiguration,
SelectActiveRuntime,
EditSolutionItem,
- Unload
+ Unload,
+ SetStartupProjects
}
internal class SolutionOptionsHandler : CommandHandler
@@ -131,6 +132,40 @@ namespace MonoDevelop.Ide.Commands
}
}
+ internal class SetStartupProjectsHandler : CommandHandler
+ {
+ protected override void Update (CommandInfo info)
+ {
+ info.Enabled = IdeApp.ProjectOperations.CurrentSelectedSolution?.GetAllProjects ()?.Skip (1)?.Any () ?? false;
+ }
+
+ protected override void Run ()
+ {
+ var sol = IdeApp.ProjectOperations.CurrentSelectedSolution;
+ if (sol != null) {
+ MultiItemSolutionRunConfiguration config = null;
+ if (!sol.MultiStartupRunConfigurations.Any ()) {
+ Xwt.Toolkit.NativeEngine.Invoke (() => {
+ using (var dlg = new NewSolutionRunConfigurationDialog ()) {
+ if (dlg.Run ().Id == "create") {
+ config = new MultiItemSolutionRunConfiguration (dlg.RunConfigurationName, dlg.RunConfigurationName);
+ sol.MultiStartupRunConfigurations.Add (config);
+ sol.StartupConfiguration = config;
+ }
+ }
+ });
+ } else {
+ config = sol.MultiStartupRunConfigurations.FirstOrDefault ();
+ }
+
+ // Show run configurations dialog
+ if (config != null) {
+ IdeApp.ProjectOperations.ShowRunConfiguration (sol, config);
+ }
+ }
+ }
+ }
+
internal class EditReferencesHandler : CommandHandler
{
protected override void Update (CommandInfo info)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs
index 9735c4473b..06976176a6 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ViewCommands.cs
@@ -77,6 +77,8 @@ namespace MonoDevelop.Ide.Commands
ci.Icon = pad.Icon;
ci.UseMarkup = true;
ci.Description = string.Format (descFormat, pad.Title);
+ // We only want these commands enabled if the main window is visible
+ ci.Enabled = IdeApp.Workbench.RootWindow.Visible;
ActionCommand cmd = IdeApp.CommandService.GetActionCommand ("Pad|" + pad.Id);
if (cmd != null) ci.AccelKey = cmd.AccelKey;
@@ -130,7 +132,7 @@ namespace MonoDevelop.Ide.Commands
pad.Visible = true;
pad.BringToFront (true);
- Counters.PadShown.Inc (new Dictionary<string,string> {{ "Pad", pad.Id }});
+ Counters.PadShown.Inc (1, null, new Dictionary<string,object> {{ "Pad", pad.Id }});
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs
index 3b02af4b92..9e71fd04a2 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs
@@ -27,7 +27,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Reflection;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Composition;
using Mono.Addins;
@@ -52,18 +54,20 @@ namespace MonoDevelop.Ide.Composition
/// </summary>
internal class Caching
{
- internal static bool writeCache;
- readonly ICachingFaultInjector cachingFaultInjector;
+ readonly ICachingFaultInjector cachingFaultInjector;
Task saveTask;
- public HashSet<Assembly> Assemblies { get; }
+ readonly HashSet<Assembly> loadedAssemblies;
+ public HashSet<Assembly> MefAssemblies { get; }
internal string MefCacheFile { get; }
internal string MefCacheControlFile { get; }
- public Caching (HashSet<Assembly> assemblies, Func<string, string> getCacheFilePath = null, ICachingFaultInjector cachingFaultInjector = null)
+ public Caching (HashSet<Assembly> mefAssemblies, Func<string, string> getCacheFilePath = null, ICachingFaultInjector cachingFaultInjector = null)
{
getCacheFilePath = getCacheFilePath ?? (file => Path.Combine (AddinManager.CurrentAddin.PrivateDataPath, file));
- Assemblies = assemblies;
+ loadedAssemblies = new HashSet<Assembly> (AppDomain.CurrentDomain.GetAssemblies ());
+
+ MefAssemblies = mefAssemblies;
MefCacheFile = getCacheFilePath ("mef-cache");
MefCacheControlFile = getCacheFilePath ("mef-cache-control");
this.cachingFaultInjector = cachingFaultInjector;
@@ -99,59 +103,78 @@ namespace MonoDevelop.Ide.Composition
// If we don't have a control file, bail early
if (!File.Exists (MefCacheControlFile) || !File.Exists (MefCacheFile))
return false;
+
+ // Read the cache from disk
+ var serializer = new JsonSerializer ();
+ MefControlCache controlCache;
- using (var timer = Counters.CompositionCacheControl.BeginTiming ()) {
- // Read the cache from disk
- var serializer = new JsonSerializer ();
- MefControlCache controlCache;
-
- try {
- using (var sr = File.OpenText (MefCacheControlFile)) {
- controlCache = (MefControlCache)serializer.Deserialize (sr, typeof(MefControlCache));
- }
- } catch (Exception ex) {
- LoggingService.LogError ("Could not deserialize MEF cache control", ex);
- DeleteFiles ();
- return false;
- }
-
- //this can return null (if the cache format changed?). clean up and start over.
- if (controlCache == null) {
- LoggingService.LogError ("MEF cache control deserialized as null");
- DeleteFiles ();
- return false;
+ try {
+ using (var sr = File.OpenText (MefCacheControlFile)) {
+ controlCache = (MefControlCache)serializer.Deserialize (sr, typeof (MefControlCache));
}
+ } catch (Exception ex) {
+ LoggingService.LogError ("Could not deserialize MEF cache control", ex);
+ DeleteFiles ();
+ return false;
+ }
- var currentAssemblies = new HashSet<string> (Assemblies.Select (asm => asm.Location));
+ //this can return null (if the cache format changed?). clean up and start over.
+ if (controlCache == null) {
+ LoggingService.LogError ("MEF cache control deserialized as null");
+ DeleteFiles ();
+ return false;
+ }
- // Short-circuit on number of assemblies change
- if (controlCache.AssemblyInfos.Length != currentAssemblies.Count)
+ try {
+ // Short-circuit on number of assemblies change
+ if (controlCache.MefAssemblyInfos.Count != MefAssemblies.Count)
+ return false;
+
+ if (!ValidateAssemblyCacheListIntegrity (MefAssemblies, controlCache.MefAssemblyInfos, cachingFaultInjector))
+ return false;
+
+ if (!ValidateAssemblyCacheListIntegrity (loadedAssemblies, controlCache.AdditionalInputAssemblyInfos, cachingFaultInjector))
return false;
+ } catch (Exception e) {
+ LoggingService.LogError ("MEF cache validation failed", e);
+ return false;
+ }
- // Validate that the assemblies match and we have the same time stamps on them.
- foreach (var assemblyInfo in controlCache.AssemblyInfos) {
- cachingFaultInjector?.FaultAssemblyInfo (assemblyInfo);
- if (!currentAssemblies.Contains (assemblyInfo.Location))
- return false;
+ return true;
+ }
+
+ static bool ValidateAssemblyCacheListIntegrity (HashSet<Assembly> assemblies, List<MefControlCacheAssemblyInfo> cachedAssemblyInfos, ICachingFaultInjector cachingFaultInjector)
+ {
+ var currentAssemblies = new Dictionary<string, Guid> (assemblies.Count);
+ foreach (var asm in assemblies) {
+ if (asm.IsDynamic)
+ continue;
- if (File.GetLastWriteTimeUtc (assemblyInfo.Location) != assemblyInfo.LastWriteTimeUtc)
- return false;
- }
- }
+ currentAssemblies.Add (asm.Location, asm.ManifestModule.ModuleVersionId);
+ }
+
+ foreach (var assemblyInfo in cachedAssemblyInfos) {
+ cachingFaultInjector?.FaultAssemblyInfo (assemblyInfo);
+ if (!currentAssemblies.TryGetValue (assemblyInfo.Location, out var mvid))
+ return false;
+
+ if (mvid != assemblyInfo.ModuleVersionId)
+ return false;
+ }
return true;
}
- internal Task Write (RuntimeComposition runtimeComposition, CachedComposition cacheManager)
+ internal Task Write (RuntimeComposition runtimeComposition, ComposableCatalog catalog, CachedComposition cacheManager)
{
return Runtime.RunInMainThread (async () => {
IdeApp.Exiting += IdeApp_Exiting;
saveTask = Task.Run (async () => {
try {
- await WriteMefCache (runtimeComposition, cacheManager);
+ await WriteMefCache (runtimeComposition, catalog, cacheManager);
} catch (Exception ex) {
- LoggingService.LogError ("Failed to write MEF cache", ex);
+ LoggingService.LogInternalError ("Failed to write MEF cache", ex);
}
});
await saveTask;
@@ -161,10 +184,10 @@ namespace MonoDevelop.Ide.Composition
});
}
- async Task WriteMefCache (RuntimeComposition runtimeComposition, CachedComposition cacheManager)
+ async Task WriteMefCache (RuntimeComposition runtimeComposition, ComposableCatalog catalog, CachedComposition cacheManager)
{
using (var timer = Counters.CompositionSave.BeginTiming ()) {
- WriteMefCacheControl (timer);
+ WriteMefCacheControl (catalog, timer);
// Serialize the MEF cache.
using (var stream = File.Open (MefCacheFile, FileMode.Create)) {
@@ -173,14 +196,41 @@ namespace MonoDevelop.Ide.Composition
}
}
- void WriteMefCacheControl (ITimeTracker timer)
- {
+ void WriteMefCacheControl (ComposableCatalog catalog, ITimeTracker timer)
+ {
+ var mefAssemblyNames = new HashSet<string> ();
+ var mefAssemblyInfos = new List<MefControlCacheAssemblyInfo> ();
+
+ foreach (var assembly in MefAssemblies) {
+ mefAssemblyNames.Add (assembly.GetName ().ToString ());
+
+ mefAssemblyInfos.Add (new MefControlCacheAssemblyInfo {
+ Location = assembly.Location,
+ ModuleVersionId = assembly.ManifestModule.ModuleVersionId,
+ });
+ }
+
+ var additionalInputAssemblies = new List<MefControlCacheAssemblyInfo> ();
+ var loadedMap = loadedAssemblies.ToDictionary (x => x.GetName ().ToString (), x => x);
+
+ foreach (var asm in catalog.GetInputAssemblies ()) {
+ var assemblyName = asm.ToString ();
+ if (mefAssemblyNames.Contains (assemblyName))
+ continue;
+
+ bool found = loadedMap.TryGetValue (assemblyName, out var assembly);
+ System.Diagnostics.Debug.Assert (found);
+
+ additionalInputAssemblies.Add (new MefControlCacheAssemblyInfo {
+ Location = assembly.Location,
+ ModuleVersionId = assembly.ManifestModule.ModuleVersionId,
+ });
+ }
+
// Create cache control data.
var controlCache = new MefControlCache {
- AssemblyInfos = Assemblies.Select (asm => new MefControlCacheAssemblyInfo {
- Location = asm.Location,
- LastWriteTimeUtc = File.GetLastWriteTimeUtc (asm.Location),
- }).ToArray (),
+ MefAssemblyInfos = mefAssemblyInfos,
+ AdditionalInputAssemblyInfos = additionalInputAssemblies,
};
var serializer = new JsonSerializer ();
@@ -197,7 +247,10 @@ namespace MonoDevelop.Ide.Composition
class MefControlCache
{
[JsonRequired]
- public MefControlCacheAssemblyInfo [] AssemblyInfos;
+ public List<MefControlCacheAssemblyInfo> MefAssemblyInfos;
+
+ [JsonRequired]
+ public List<MefControlCacheAssemblyInfo> AdditionalInputAssemblyInfos;
}
[Serializable]
@@ -207,7 +260,7 @@ namespace MonoDevelop.Ide.Composition
public string Location;
[JsonRequired]
- public DateTime LastWriteTimeUtc;
+ public Guid ModuleVersionId;
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs
index 39e3eaaf25..299fbac057 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs
@@ -107,34 +107,56 @@ namespace MonoDevelop.Ide.Composition
async Task InitializeInstanceAsync ()
{
- var assemblies = ReadAssembliesFromAddins ();
- var caching = new Caching (assemblies);
+ var timings = new Dictionary<string, long> ();
+ var metadata = new CompositionLoadMetadata (timings);
- // Try to use cached MEF data
- if (caching.CanUse ()) {
- RuntimeComposition = await TryCreateRuntimeCompositionFromCache (caching);
- }
+ using (var timer = Counters.CompositionLoad.BeginTiming (metadata)) {
+ var fullTimer = System.Diagnostics.Stopwatch.StartNew ();
+ var stepTimer = System.Diagnostics.Stopwatch.StartNew ();
+
+ var mefAssemblies = ReadAssembliesFromAddins (timer);
+ timings ["ReadFromAddins"] = stepTimer.ElapsedMilliseconds;
+ stepTimer.Restart ();
+
+ var caching = new Caching (mefAssemblies);
+
+ // Try to use cached MEF data
+
+ var canUse = metadata.ValidCache = caching.CanUse ();
+ if (canUse) {
+ LoggingService.LogInfo ("Creating MEF composition from cache");
+ RuntimeComposition = await TryCreateRuntimeCompositionFromCache (caching);
+ }
+ timings ["LoadFromCache"] = stepTimer.ElapsedMilliseconds;
+ stepTimer.Restart ();
+
+ // Otherwise fallback to runtime discovery.
+ if (RuntimeComposition == null) {
+ LoggingService.LogInfo ("Creating MEF composition from runtime");
+ var (runtimeComposition, catalog) = await CreateRuntimeCompositionFromDiscovery (caching, timer);
+ RuntimeComposition = runtimeComposition;
- // Otherwise fallback to runtime discovery.
- if (RuntimeComposition == null) {
- RuntimeComposition = await CreateRuntimeCompositionFromDiscovery (caching);
+ CachedComposition cacheManager = new CachedComposition ();
+ caching.Write (RuntimeComposition, catalog, cacheManager).Ignore ();
+ }
+ timings ["LoadRuntimeComposition"] = stepTimer.ElapsedMilliseconds;
+ stepTimer.Restart ();
+
+ ExportProviderFactory = RuntimeComposition.CreateExportProviderFactory ();
+ ExportProvider = ExportProviderFactory.CreateExportProvider ();
+ HostServices = MefV1HostServices.Create (ExportProvider.AsExportProvider ());
+ ExportProviderV1 = NetFxAdapters.AsExportProvider (ExportProvider);
- CachedComposition cacheManager = new CachedComposition ();
- caching.Write (RuntimeComposition, cacheManager).Ignore ();
+ timings ["CreateServices"] = stepTimer.ElapsedMilliseconds;
+ metadata.Duration = fullTimer.ElapsedMilliseconds;
}
-
- ExportProviderFactory = RuntimeComposition.CreateExportProviderFactory ();
- ExportProvider = ExportProviderFactory.CreateExportProvider ();
- HostServices = MefV1HostServices.Create (ExportProvider.AsExportProvider ());
- ExportProviderV1 = NetFxAdapters.AsExportProvider (ExportProvider);
}
internal static async Task<RuntimeComposition> TryCreateRuntimeCompositionFromCache (Caching caching)
{
- CachedComposition cacheManager = new CachedComposition ();
+ var cacheManager = new CachedComposition ();
try {
- using (Counters.CompositionCache.BeginTiming ())
using (var cacheStream = caching.OpenCacheStream ()) {
return await cacheManager.LoadRuntimeCompositionAsync (cacheStream, StandardResolver);
}
@@ -145,73 +167,72 @@ namespace MonoDevelop.Ide.Composition
return null;
}
- internal static async Task<RuntimeComposition> CreateRuntimeCompositionFromDiscovery (Caching caching)
+ internal static async Task<(RuntimeComposition, ComposableCatalog)> CreateRuntimeCompositionFromDiscovery (Caching caching, ITimeTracker timer = null)
{
- using (var timer = Counters.CompositionDiscovery.BeginTiming ()) {
- var parts = await Discovery.CreatePartsAsync (caching.Assemblies);
- timer.Trace ("Composition parts discovered");
-
- ComposableCatalog catalog = ComposableCatalog.Create (StandardResolver)
- .WithCompositionService ()
- .AddParts (parts);
-
- var discoveryErrors = catalog.DiscoveredParts.DiscoveryErrors;
- if (!discoveryErrors.IsEmpty) {
- foreach (var error in discoveryErrors) {
- LoggingService.LogInfo ("MEF discovery error", error);
- }
+ var parts = await Discovery.CreatePartsAsync (caching.MefAssemblies);
+ timer?.Trace ("Composition parts discovered");
+
+ ComposableCatalog catalog = ComposableCatalog.Create (StandardResolver)
+ .WithCompositionService ()
+ .AddParts (parts);
- // throw new ApplicationException ("MEF discovery errors");
+ var discoveryErrors = catalog.DiscoveredParts.DiscoveryErrors;
+ if (!discoveryErrors.IsEmpty) {
+ foreach (var error in discoveryErrors) {
+ LoggingService.LogInfo ("MEF discovery error", error);
}
- CompositionConfiguration configuration = CompositionConfiguration.Create (catalog);
+ // throw new ApplicationException ("MEF discovery errors");
+ }
- if (!configuration.CompositionErrors.IsEmpty) {
- // capture the errors in an array for easier debugging
- var errors = configuration.CompositionErrors.SelectMany (e => e).ToArray ();
- foreach (var error in errors) {
- LoggingService.LogInfo ("MEF composition error: " + error.Message);
- }
+ CompositionConfiguration configuration = CompositionConfiguration.Create (catalog);
- // For now while we're still transitioning to VSMEF it's useful to work
- // even if the composition has some errors. TODO: re-enable this.
- //configuration.ThrowOnErrors ();
+ if (!configuration.CompositionErrors.IsEmpty) {
+ // capture the errors in an array for easier debugging
+ var errors = configuration.CompositionErrors.SelectMany (e => e).ToArray ();
+ foreach (var error in errors) {
+ LoggingService.LogInfo ("MEF composition error: " + error.Message);
}
- timer.Trace ("Composition configured");
-
- var runtimeComposition = RuntimeComposition.CreateRuntimeComposition (configuration);
- return runtimeComposition;
+ // For now while we're still transitioning to VSMEF it's useful to work
+ // even if the composition has some errors. TODO: re-enable this.
+ //configuration.ThrowOnErrors ();
}
+ timer?.Trace ("Composition configured");
+
+ var runtimeComposition = RuntimeComposition.CreateRuntimeComposition (configuration);
+ timer?.Trace ("Composition created");
+
+ return (runtimeComposition, catalog);
}
- internal static HashSet<Assembly> ReadAssembliesFromAddins ()
+ internal static HashSet<Assembly> ReadAssembliesFromAddins (ITimeTracker<CompositionLoadMetadata> timer = null)
{
- using (var timer = Counters.CompositionAddinLoad.BeginTiming ()) {
- var assemblies = new HashSet<Assembly> ();
- ReadAssemblies (assemblies, "/MonoDevelop/Ide/TypeService/PlatformMefHostServices", timer);
- ReadAssemblies (assemblies, "/MonoDevelop/Ide/TypeService/MefHostServices", timer);
- ReadAssemblies (assemblies, "/MonoDevelop/Ide/Composition", timer);
- return assemblies;
- }
+ var readAssemblies = new HashSet<Assembly> ();
+
+ timer?.Trace ("Start: reading assemblies");
+ ReadAssemblies (readAssemblies, "/MonoDevelop/Ide/TypeService/PlatformMefHostServices");
+ ReadAssemblies (readAssemblies, "/MonoDevelop/Ide/TypeService/MefHostServices");
+ ReadAssemblies (readAssemblies, "/MonoDevelop/Ide/Composition");
+ timer?.Trace ("Start: end reading assemblies");
+
+ return readAssemblies;
- void ReadAssemblies (HashSet<Assembly> assemblies, string extensionPath, ITimeTracker timer)
+ void ReadAssemblies (HashSet<Assembly> assemblies, string extensionPath)
{
foreach (var node in AddinManager.GetExtensionNodes (extensionPath)) {
if (node is AssemblyExtensionNode assemblyNode) {
try {
string id = assemblyNode.Addin.Id;
string assemblyName = assemblyNode.FileName;
- timer.Trace ("Start: " + assemblyName);
// Make sure the add-in that registered the assembly is loaded, since it can bring other
// other assemblies required to load this one
+
AddinManager.LoadAddin (null, id);
var assemblyFilePath = assemblyNode.Addin.GetFilePath (assemblyNode.FileName);
var assembly = Runtime.LoadAssemblyFrom (assemblyFilePath);
assemblies.Add (assembly);
-
- timer.Trace ("Loaded: " + assemblyName);
} catch (Exception e) {
LoggingService.LogError ("Composition can't load assembly: " + assemblyNode.FileName, e);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs
index 5f84a7b3b8..f337143d22 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs
@@ -59,13 +59,6 @@ namespace MonoDevelop.Ide.Desktop
public abstract string Name { get; }
- [Obsolete]
- public virtual string DefaultControlLeftRightBehavior {
- get {
- return "MonoDevelop";
- }
- }
-
public virtual void Initialize ()
{
}
@@ -505,6 +498,25 @@ namespace MonoDevelop.Ide.Desktop
{
}
+ public virtual Window GetParentForModalWindow ()
+ {
+ foreach (var w in Gtk.Window.ListToplevels ())
+ if (w.Visible && w.HasToplevelFocus && w.Modal)
+ return w;
+
+ return GetFocusedTopLevelWindow ();
+ }
+
+ public virtual Window GetFocusedTopLevelWindow ()
+ {
+ // use the first "normal" toplevel window (skipping docks, popups, etc.) or the main IDE window
+ Window gtkToplevel = Gtk.Window.ListToplevels ().FirstOrDefault (w => w.Visible && w.HasToplevelFocus &&
+ (w.TypeHint == Gdk.WindowTypeHint.Dialog ||
+ w.TypeHint == Gdk.WindowTypeHint.Normal ||
+ w.TypeHint == Gdk.WindowTypeHint.Utility));
+ return gtkToplevel ?? IdeApp.Workbench.RootWindow;
+ }
+
public virtual void FocusWindow (Window window)
{
window.GrabFocus ();
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/EditorFormattingServiceTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/EditorFormattingServiceTextEditorExtension.cs
index a0f99acc8c..becedf94c4 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/EditorFormattingServiceTextEditorExtension.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/EditorFormattingServiceTextEditorExtension.cs
@@ -32,6 +32,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Text;
+using System.Linq;
namespace MonoDevelop.Ide.Editor.Extension
{
@@ -66,7 +67,7 @@ namespace MonoDevelop.Ide.Editor.Extension
bool TryFormat (IEditorFormattingService formattingService, char typedChar, int position, bool formatOnReturn, CancellationToken cancellationToken)
{
var document = DocumentContext.AnalysisDocument;
- IList<TextChange> changes;
+ IEnumerable<TextChange> changes;
if (formatOnReturn) {
if (!formattingService.SupportsFormatOnReturn)
return false;
@@ -75,12 +76,15 @@ namespace MonoDevelop.Ide.Editor.Extension
if (!formattingService.SupportsFormattingOnTypedCharacter (document, typedChar))
return false;
changes = formattingService.GetFormattingChangesAsync (document, typedChar, position, cancellationToken).WaitAndGetResult (cancellationToken);
+ var line = Editor.GetLineByOffset (position);
+ if (typedChar == '#') {
+ changes = changes.Where (c => c.Span.Start >= line.Offset);
+ }
}
- if (changes == null || changes.Count == 0) {
+ if (changes == null) {
return false;
}
-
Editor.ApplyTextChanges (changes);
return true;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs
index e3c42db2d7..d11ed56097 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/KeyDescriptor.cs
@@ -59,6 +59,74 @@ namespace MonoDevelop.Ide.Editor.Extension
return string.Format ("[KeyDescriptor: SpecialKey={0}, KeyChar={1}, ModifierKeys={2}]", SpecialKey, KeyChar, ModifierKeys);
}
+ #region XWT
+ public static KeyDescriptor FromXwt (Xwt.Key key, char c, Xwt.ModifierKeys modifiers)
+ {
+ return new KeyDescriptor (ConvertKey (key), c, ConvertModifiers (modifiers), Tuple.Create (key, modifiers));
+ }
+
+ static SpecialKey ConvertKey (Xwt.Key key)
+ {
+ switch (key) {
+ case Xwt.Key.BackSpace:
+ return SpecialKey.BackSpace;
+ case Xwt.Key.Tab:
+ case Xwt.Key.NumPadTab:
+ return SpecialKey.Tab;
+ case Xwt.Key.Return:
+ case Xwt.Key.NumPadEnter:
+ return SpecialKey.Return;
+ case Xwt.Key.Escape:
+ return SpecialKey.Escape;
+ case Xwt.Key.Space:
+ case Xwt.Key.NumPadSpace:
+ return SpecialKey.Space;
+ case Xwt.Key.PageUp:
+ return SpecialKey.PageUp;
+ case Xwt.Key.PageDown:
+ return SpecialKey.PageDown;
+ case Xwt.Key.End:
+ case Xwt.Key.NumPadEnd:
+ return SpecialKey.End;
+ case Xwt.Key.Home:
+ case Xwt.Key.NumPadHome:
+ return SpecialKey.Home;
+ case Xwt.Key.Left:
+ case Xwt.Key.NumPadLeft:
+ return SpecialKey.Left;
+ case Xwt.Key.Up:
+ case Xwt.Key.NumPadUp:
+ return SpecialKey.Up;
+ case Xwt.Key.Right:
+ case Xwt.Key.NumPadRight:
+ return SpecialKey.Right;
+ case Xwt.Key.Down:
+ case Xwt.Key.NumPadDown:
+ return SpecialKey.Down;
+ case Xwt.Key.Delete:
+ case Xwt.Key.NumPadDelete:
+ return SpecialKey.Delete;
+ }
+ return SpecialKey.None;
+ }
+
+ static ModifierKeys ConvertModifiers (Xwt.ModifierKeys s)
+ {
+ ModifierKeys m = ModifierKeys.None;
+ if ((s & Xwt.ModifierKeys.Shift) != 0)
+ m |= ModifierKeys.Shift;
+ if ((s & Xwt.ModifierKeys.Control) != 0)
+ m |= ModifierKeys.Control;
+ if ((s & Xwt.ModifierKeys.Alt) != 0)
+ m |= ModifierKeys.Alt;
+ if ((s & Xwt.ModifierKeys.Command) != 0)
+ m |= ModifierKeys.Command;
+ if ((s & Xwt.ModifierKeys.Command) != 0)
+ m |= ModifierKeys.Command;
+ return m;
+ }
+ #endregion
+
#region GTK
public static KeyDescriptor FromGtk (Gdk.Key key, char ch, Gdk.ModifierType state)
{
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorTheme.cs
index d35c26d936..8b06eff4a5 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorTheme.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorTheme.cs
@@ -332,5 +332,10 @@ namespace MonoDevelop.Ide.Editor.Highlighting
}
EditorTheme IEditorThemeProvider.GetEditorTheme () => this;
+
+ public override string ToString ()
+ {
+ return string.Format ("[EditorTheme: Name={0}]", Name);
+ }
}
} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs
index 6ff8b67139..74b3652f72 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs
@@ -61,6 +61,11 @@ namespace MonoDevelop.Ide.Editor.Highlighting
TextSegment = textSegment;
Segments = segments;
}
+
+ public override string ToString ()
+ {
+ return string.Format ("[HighlightedLine: TextSegment={0}, #Segments={1}, IsContinuedBeyondLineEnd={2}]", TextSegment, Segments?.Count, IsContinuedBeyondLineEnd);
+ }
}
/// <summary>
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs
index 63f28b5746..d4c5ab9f4b 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs
@@ -711,7 +711,10 @@ namespace MonoDevelop.Ide.Editor.Highlighting
{
HslColor result;
if (!style.TryGetColor (key, out result)) {
- DefaultColorStyle.TryGetColor (key, out result);
+ if (!DefaultColorStyle.TryGetColor (key, out result)) {
+ LoggingService.LogError ("SyntaxHighlightingService.GetColor color " + key + " not found in theme " + style);
+ return default;
+ }
}
return result;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Caret.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Caret.cs
index db7e5009c5..acd90387d3 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Caret.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Caret.cs
@@ -73,10 +73,12 @@ namespace MonoDevelop.Ide.Editor
public class CaretLocationEventArgs : DocumentLocationEventArgs
{
+ public Microsoft.VisualStudio.Text.ITextSnapshot Snapshot { get; }
public CaretChangeReason CaretChangeReason { get; }
- public CaretLocationEventArgs (DocumentLocation location, CaretChangeReason reason) : base (location)
+ public CaretLocationEventArgs (DocumentLocation location, Microsoft.VisualStudio.Text.ITextSnapshot textSnapshot, CaretChangeReason reason) : base (location)
{
+ Snapshot = textSnapshot;
CaretChangeReason = reason;
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
index 0d963f174e..c80ef8eb79 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs
@@ -199,7 +199,9 @@ namespace MonoDevelop.Ide.Editor
string GetMarkup (int offset, int length, MarkupOptions options);
- IndentationTracker IndentationTracker { get; set; }
+ Task<string> GetMarkupAsync (int offset, int length, MarkupOptions options, CancellationToken cancellationToken);
+
+ IndentationTracker IndentationTracker { get; set; }
void SetSelectionSurroundingProvider (SelectionSurroundingProvider surroundingProvider);
void SetTextPasteHandler (TextPasteHandler textPasteHandler);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/OSXEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/OSXEditor.cs
index 5e97624536..fc35dba10c 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/OSXEditor.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/OSXEditor.cs
@@ -66,9 +66,12 @@ namespace MonoDevelop.Ide.Editor
{
var editorFont = Xwt.Drawing.Font.FromName(fontName);
- using (var nsFont = NSFont.FromFontName(editorFont.Family, (nfloat)editorFont.Size))
- using (var lm = new NSLayoutManager())
- return lm.DefaultLineHeightForFont(nsFont);
+ using (var nsFont = NSFont.FromFontName (editorFont.Family, (nfloat)editorFont.Size)) {
+ if (nsFont == null)
+ return -1;
+ using (var lm = new NSLayoutManager ())
+ return lm.DefaultLineHeightForFont (nsFont);
+ }
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
index 868e8c603b..900ef6baf4 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
@@ -1226,10 +1226,13 @@ namespace MonoDevelop.Ide.Editor
return GetContents<T> ().FirstOrDefault ();
}
- public IEnumerable<T> GetContents<T>() where T : class
+ public IEnumerable<T> GetContents<T> () where T : class
{
- T result = textEditorImpl as T;
- if (result != null)
+ if (isDisposed) {
+ LoggingService.LogError ($"Error retrieving TextEditor.GetContents<{typeof(T)}>\n {Environment.StackTrace}");
+ yield break;
+ }
+ if (textEditorImpl is T result)
yield return result;
var ext = textEditorImpl.EditorExtension;
while (ext != null) {
@@ -1243,6 +1246,10 @@ namespace MonoDevelop.Ide.Editor
public IEnumerable<object> GetContents (Type type)
{
var res = Enumerable.Empty<object> ();
+ if (isDisposed) {
+ LoggingService.LogError ($"Error retrieving TextEditor.GetContents({type})\n {Environment.StackTrace}");
+ return res;
+ }
if (type.IsInstanceOfType (textEditorImpl))
res = res.Concat (textEditorImpl);
@@ -1262,7 +1269,7 @@ namespace MonoDevelop.Ide.Editor
return GetMarkup (offset, length, new MarkupOptions (MarkupFormat.Pango, fitIdeStyle));
}
- [Obsolete ("Use GetMarkup")]
+ [Obsolete ("Use GetMarkupAsync")]
public string GetPangoMarkup (ISegment segment, bool fitIdeStyle = false)
{
if (segment == null)
@@ -1286,6 +1293,22 @@ namespace MonoDevelop.Ide.Editor
return textEditorImpl.GetMarkup (segment.Offset, segment.Length, options);
}
+ public Task<string> GetMarkupAsync (int offset, int length, MarkupOptions options, CancellationToken cancellationToken = default)
+ {
+ if (options == null)
+ throw new ArgumentNullException (nameof (options));
+ return textEditorImpl.GetMarkupAsync (offset, length, options, cancellationToken);
+ }
+
+ public Task<string> GetMarkupAsync (ISegment segment, MarkupOptions options, CancellationToken cancellationToken = default)
+ {
+ if (options == null)
+ throw new ArgumentNullException (nameof (options));
+ if (segment == null)
+ throw new ArgumentNullException (nameof (segment));
+ return textEditorImpl.GetMarkupAsync (segment.Offset, segment.Length, options, cancellationToken);
+ }
+
public static implicit operator Microsoft.CodeAnalysis.Text.SourceText (TextEditor editor)
{
return new MonoDevelopSourceText (editor);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs
index b47ec23c96..9df4b77a04 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs
@@ -1,4 +1,4 @@
-//
+//
// TextEditorViewContent.cs
//
// Author:
@@ -115,7 +115,7 @@ namespace MonoDevelop.Ide.Editor
}
}
// Calling base class after initializing the editor extension chain
- // so that it picks additional content from the extensionasdfas dfasdf asdf asdf asdf asdf sadfsaf
+ // so that it picks additional content from the extension
base.OnContentChanged ();
}
@@ -125,7 +125,7 @@ namespace MonoDevelop.Ide.Editor
if (textEditor != null) {
if (FilePath != textEditorImpl.ContentName && !string.IsNullOrEmpty (textEditorImpl.ContentName))
AutoSave.RemoveAutoSaveFile (textEditorImpl.ContentName);
- textEditor.FileName = FilePath;
+ textEditor.FileName = FilePath; // TOTEST: VSTS #770920
if (documentContext != null)
textEditor.InitializeExtensionChain (documentContext);
UpdateTextEditorOptions (null, null);
@@ -372,8 +372,10 @@ namespace MonoDevelop.Ide.Editor
}
#endregion
-
-
+ protected override void OnGrabFocus ()
+ {
+ textEditor.GrabFocus ();
+ }
}
} \ No newline at end of file
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ExternalTools/ExternalToolPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ExternalTools/ExternalToolPanel.cs
index 7e984d5b06..803b6f3985 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ExternalTools/ExternalToolPanel.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ExternalTools/ExternalToolPanel.cs
@@ -153,7 +153,7 @@ namespace MonoDevelop.Ide.ExternalTools
GettextCatalog.GetString ("Enter the title for this command"));
browseButton.Accessible.SetCommonAttributes ("ExternalTools.Command", null,
GettextCatalog.GetString ("Enter or select the path for the external command"));
- browseButton.Accessible.SetTitleUIElement (commandLabel.Accessible);
+ browseButton.SetEntryAccessibleTitleUIElement (commandLabel.Accessible);
argumentTextBox.SetCommonAccessibilityAttributes ("ExternalTools.Arguments", "",
GettextCatalog.GetString ("Enter the arguments for the external command"));
argumentTextBox.SetAccessibilityLabelRelationship (argumentLabel);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs
index 58f710ad14..b33d81cc41 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs
@@ -242,33 +242,37 @@ namespace MonoDevelop.Ide.FindInFiles
void SetupAccessibility ()
{
comboboxentryFind.SetCommonAccessibilityAttributes ("FindInFilesDialog.comboboxentryFind",
- "Find",
+ labelFind,
GettextCatalog.GetString ("Enter string to find"));
- comboboxentryFind.SetAccessibilityLabelRelationship (labelFind);
+
+ comboboxScope.SetCommonAccessibilityAttributes ("FindInFilesDialog.comboboxScope",
+ labelScope,
+ GettextCatalog.GetString ("Select where to search"));
}
void SetupAccessibilityForReplace ()
{
comboboxentryReplace.SetCommonAccessibilityAttributes ("FindInFilesDialog.comboboxentryReplace",
- "Replace",
+ labelReplace,
GettextCatalog.GetString ("Enter string to replace"));
- comboboxentryReplace.SetAccessibilityLabelRelationship (labelReplace);
}
void SetupAccessibilityForPath ()
{
comboboxentryPath.SetCommonAccessibilityAttributes ("FindInFilesDialog.comboboxentryPath",
- "Path",
+ labelPath,
GettextCatalog.GetString ("Enter the Path"));
- comboboxentryPath.SetAccessibilityLabelRelationship (labelPath);
+
+ buttonBrowsePaths.SetCommonAccessibilityAttributes ("FindInFilesDialog.buttonBrowsePaths",
+ GettextCatalog.GetString ("Browse Path"),
+ GettextCatalog.GetString ("Select a folder"));
}
void SetupAccessibilityForSearch ()
{
- searchentryFileMask.SetCommonAccessibilityAttributes ("FindInFilesDialog.searchentryFileMask",
- "File Mask",
+ searchentryFileMask.SetEntryAccessibilityAttributes ("FindInFilesDialog.searchentryFileMask",
+ labelFileMask.Text,
GettextCatalog.GetString ("Enter the file mask"));
- searchentryFileMask.SetAccessibilityLabelRelationship (labelFileMask);
}
static void TableAddRow (Table table, uint row, Widget column1, Widget column2)
@@ -432,9 +436,7 @@ namespace MonoDevelop.Ide.FindInFiles
labelPath.MnemonicWidget = comboboxentryPath;
- SetupAccessibilityForPath ();
-
- buttonBrowsePaths = new Button { Label = "..." };
+ buttonBrowsePaths = new Button { Label = "…" };
buttonBrowsePaths.Clicked += ButtonBrowsePathsClicked;
buttonBrowsePaths.Show ();
hboxPath.PackStart (buttonBrowsePaths, false, false, 0);
@@ -454,6 +456,8 @@ namespace MonoDevelop.Ide.FindInFiles
checkbuttonRecursively.Show ();
TableAddRow (tableFindAndReplace, row, null, checkbuttonRecursively);
+
+ SetupAccessibilityForPath ();
}
void HideDirectoryPathUI ()
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs
index 6e7a803edd..965089b4e2 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/SearchResult.cs
@@ -121,7 +121,7 @@ namespace MonoDevelop.Ide.FindInFiles
const int maximumMarkupLength = 78;
- void CreateMarkup (SearchResultWidget widget, TextEditor doc, Editor.IDocumentLine line)
+ async void CreateMarkup (SearchResultWidget widget, TextEditor doc, Editor.IDocumentLine line)
{
int startIndex = 0, endIndex = 0;
@@ -175,35 +175,31 @@ namespace MonoDevelop.Ide.FindInFiles
markup = FormatMarkup (PangoHelper.ColorMarkupBackground (selectedMarkup, (int)startIndex, (int)endIndex, searchColor), trimStart, trimEnd, tabSize);
selectedMarkup = FormatMarkup (PangoHelper.ColorMarkupBackground (selectedMarkup, (int)startIndex, (int)endIndex, selectedSearchColor), trimStart, trimEnd, tabSize);
- Task.Run (delegate {
- var newMarkup = doc.GetMarkup (line.Offset + markupStartOffset + indent, length, new MarkupOptions (MarkupFormat.Pango));
- Runtime.RunInMainThread (delegate {
- newMarkup = widget.AdjustColors (newMarkup);
+ var newMarkup = await doc.GetMarkupAsync (line.Offset + markupStartOffset + indent, length, new MarkupOptions (MarkupFormat.Pango));
+ newMarkup = widget.AdjustColors (newMarkup);
- try {
- double delta = Math.Abs (b1 - b2);
- if (delta < 0.1) {
- var color1 = SyntaxHighlightingService.GetColor (widget.HighlightStyle, EditorThemeColors.FindHighlight);
- if (color1.L + 0.5 > 1.0) {
- color1.L -= 0.5;
- } else {
- color1.L += 0.5;
- }
- searchColor = color1;
- }
- if (startIndex != endIndex) {
- newMarkup = PangoHelper.ColorMarkupBackground (newMarkup, (int)startIndex, (int)endIndex, searchColor);
- }
- } catch (Exception e) {
- LoggingService.LogError ("Error while setting the text renderer markup to: " + newMarkup, e);
+ try {
+ double delta = Math.Abs (b1 - b2);
+ if (delta < 0.1) {
+ var color1 = SyntaxHighlightingService.GetColor (widget.HighlightStyle, EditorThemeColors.FindHighlight);
+ if (color1.L + 0.5 > 1.0) {
+ color1.L -= 0.5;
+ } else {
+ color1.L += 0.5;
}
+ searchColor = color1;
+ }
+ if (startIndex != endIndex) {
+ newMarkup = PangoHelper.ColorMarkupBackground (newMarkup, (int)startIndex, (int)endIndex, searchColor);
+ }
+ } catch (Exception e) {
+ LoggingService.LogError ("Error while setting the text renderer markup to: " + newMarkup, e);
+ }
- newMarkup = FormatMarkup (newMarkup, trimStart, trimEnd, tabSize);
+ newMarkup = FormatMarkup (newMarkup, trimStart, trimEnd, tabSize);
- this.markup = newMarkup;
- widget.QueueDraw ();
- });
- });
+ this.markup = newMarkup;
+ widget.QueueDraw ();
}
static string FormatMarkup (string str, bool trimeStart, bool trimEnd, int tabSize)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/CommonAboutDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/CommonAboutDialog.cs
index 33b444c068..754b69e3f6 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/CommonAboutDialog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/CommonAboutDialog.cs
@@ -59,7 +59,6 @@ namespace MonoDevelop.Ide.Gui.Dialogs
{
Name = "wizard_dialog";
Title = string.Format (GettextCatalog.GetString ("About {0}"), BrandingService.ApplicationLongName);
- TransientFor = IdeApp.Workbench.RootWindow;
AllowGrow = false;
HasSeparator = false;
BorderWidth = 0;
@@ -118,21 +117,22 @@ namespace MonoDevelop.Ide.Gui.Dialogs
ChangeColor (cw);
}
}
-
+
static CommonAboutDialog instance;
-
+
public static void ShowAboutDialog ()
{
if (Platform.IsMac) {
if (instance == null) {
instance = new CommonAboutDialog ();
- MessageService.PlaceDialog (instance, IdeApp.Workbench.RootWindow);
+ MessageService.PlaceDialog (instance, WelcomePage.WelcomePageService.WelcomeWindow ?? IdeApp.Workbench.RootWindow);
instance.Response += delegate {
instance.Destroy ();
instance.Dispose ();
instance = null;
};
}
+
instance.Present ();
return;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs
new file mode 100644
index 0000000000..03785e23a7
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs
@@ -0,0 +1,221 @@
+//
+// NewFolderDialog.cs
+//
+// Author:
+// Matt Ward <matt.ward@microsoft.com>
+//
+// Copyright (c) 2019 Microsoft
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using MonoDevelop.Components;
+using MonoDevelop.Components.AtkCocoaHelper;
+using MonoDevelop.Core;
+using MonoDevelop.Ide.Gui.Pads.ProjectPad;
+using MonoDevelop.Ide.Tasks;
+using Xwt;
+
+namespace MonoDevelop.Ide.Gui.Dialogs
+{
+ class NewFolderDialog : Xwt.Dialog
+ {
+ readonly FilePath parentFolder;
+ TextEntry folderNameTextEntry;
+ DialogButton addButton;
+ InformationPopoverWidget warningPopover;
+
+ public NewFolderDialog (FilePath parentFolder)
+ {
+ this.parentFolder = parentFolder;
+
+ Build ();
+
+ folderNameTextEntry.Text = GetDefaultFolderName ();
+ folderNameTextEntry.SetFocus ();
+ folderNameTextEntry.Changed += FolderNameTextEntryChanged;
+ addButton.Clicked += AddButtonClicked;
+ }
+
+ public FilePath NewFolderCreated { get; private set; }
+
+ void Build ()
+ {
+ Padding = 0;
+ Resizable = false;
+ Width = 320;
+ Title = GettextCatalog.GetString ("New Folder");
+
+ var mainVBox = new VBox ();
+
+ var folderNameHBox = new HBox ();
+ folderNameHBox.Margin = 20;
+ var folderNameLabel = new Label ();
+ folderNameLabel.Text = GettextCatalog.GetString ("Folder Name:");
+ folderNameHBox.PackStart (folderNameLabel);
+
+ folderNameTextEntry = new TextEntry ();
+ folderNameHBox.PackStart (folderNameTextEntry, true, true);
+ folderNameTextEntry.SetCommonAccessibilityAttributes (
+ "NewFolderDialog.FolderNameTextEntry",
+ folderNameLabel.Text,
+ GettextCatalog.GetString ("Enter the name for the new folder"));
+
+ warningPopover = new InformationPopoverWidget ();
+ warningPopover.Visible = false;
+ folderNameHBox.PackStart (warningPopover);
+
+ mainVBox.PackStart (folderNameHBox);
+
+ var cancelButton = new DialogButton (Command.Cancel);
+ Buttons.Add (cancelButton);
+
+ addButton = new DialogButton (Command.Add);
+ Buttons.Add (addButton);
+
+ DefaultCommand = addButton.Command;
+
+ Content = mainVBox;
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ base.Dispose (disposing);
+ if (disposing) {
+ folderNameTextEntry.Changed -= FolderNameTextEntryChanged;
+ addButton.Clicked -= AddButtonClicked;
+ }
+ }
+
+ internal static Task<FilePath> Open (FilePath parentFolder)
+ {
+ var result = new TaskCompletionSource<FilePath> ();
+ Toolkit.NativeEngine.Invoke (delegate {
+ using (var dialog = new NewFolderDialog (parentFolder)) {
+ dialog.Run (MessageDialog.RootWindow);
+ result.SetResult (dialog.NewFolderCreated);
+ }
+ });
+ return result.Task;
+ }
+
+ string GetDefaultFolderName ()
+ {
+ string childFolderName = GettextCatalog.GetString ("New Folder");
+ string directoryName = Path.Combine (parentFolder, childFolderName);
+ int index = -1;
+
+ if (Directory.Exists (directoryName)) {
+ while (Directory.Exists (directoryName + (++index + 1))) {
+ }
+ }
+
+ if (index >= 0) {
+ return childFolderName += index + 1;
+ }
+ return childFolderName;
+ }
+
+ void FolderNameTextEntryChanged (object sender, EventArgs e)
+ {
+ FilePath directoryPath = GetNewFolderPath ();
+
+ if (folderNameTextEntry.Text.Length == 0) {
+ addButton.Sensitive = false;
+ HidePopoverMessage ();
+ } else if (!IsValidFolderName (directoryPath, folderNameTextEntry.Text)) {
+ ShowWarning (GettextCatalog.GetString ("The name you have chosen contains illegal characters. Please choose a different name."));
+ addButton.Sensitive = false;
+ } else if (Directory.Exists (directoryPath)) {
+ ShowWarning (GettextCatalog.GetString ("Folder name is already in use. Please choose a different name."));
+ addButton.Sensitive = false;
+ } else {
+ addButton.Sensitive = true;
+ HidePopoverMessage ();
+ }
+ }
+
+ void AddButtonClicked (object sender, EventArgs e)
+ {
+ try {
+ bool canClose = AddNewFolder ();
+ if (canClose) {
+ Close ();
+ }
+ } catch (Exception ex) {
+ LoggingService.LogError ("Could not create folder", ex);
+ ShowError (GettextCatalog.GetString ("An error occurred creating the folder. {0}", ex.Message));
+ }
+ }
+
+ FilePath GetNewFolderPath ()
+ {
+ return parentFolder.Combine (folderNameTextEntry.Text);
+ }
+
+ bool AddNewFolder ()
+ {
+ FilePath directoryPath = GetNewFolderPath ();
+
+ Directory.CreateDirectory (directoryPath);
+ NewFolderCreated = directoryPath;
+
+ return true;
+ }
+
+ bool IsValidFolderName (FilePath folderPath, string folderName)
+ {
+ return FileService.IsValidPath (folderPath) &&
+ !ProjectFolderCommandHandler.ContainsDirectorySeparator (folderName);
+ }
+
+ protected override void OnCommandActivated (Command cmd)
+ {
+ if (cmd == Command.Add) {
+ // Prevent dialog closing after Add button is activated since an alert message dialog may have been shown.
+ return;
+ }
+ base.OnCommandActivated (cmd);
+ }
+
+ void ShowWarning (string message)
+ {
+ ShowPopoverMessage (message, TaskSeverity.Warning);
+ }
+
+ void ShowError (string message)
+ {
+ ShowPopoverMessage (message, TaskSeverity.Error);
+ }
+
+ void ShowPopoverMessage (string message, TaskSeverity severity)
+ {
+ warningPopover.Message = message;
+ warningPopover.Severity = severity;
+ warningPopover.Show ();
+ }
+
+ void HidePopoverMessage ()
+ {
+ warningPopover.Hide ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/PolicyOptionsPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/PolicyOptionsPanel.cs
index e7e33028f1..f394643d00 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/PolicyOptionsPanel.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/PolicyOptionsPanel.cs
@@ -122,8 +122,9 @@ namespace MonoDevelop.Ide.Gui.Dialogs
}
policyCombo.Changed += HandlePolicyComboChanged;
-
- return vbox;
+
+ var widget = new PolicyOptionsWidgetContainer (vbox);
+ return widget;
}
void LoadPolicy (T policy)
@@ -340,5 +341,21 @@ namespace MonoDevelop.Ide.Gui.Dialogs
loading = false;
}
}
+
+ /// <summary>
+ /// Container for the VBox used to display the options panel information.
+ /// This is needed since using just a VBox causes Voice Over on the Mac to
+ /// read out the UI widgets multiple times with using the arrow keys.
+ /// </summary>
+ class PolicyOptionsWidgetContainer : Bin
+ {
+ public PolicyOptionsWidgetContainer (VBox child)
+ {
+ BinContainer.Attach (this);
+
+ Add (child);
+ ShowAll ();
+ }
+ }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.OptionPanels/LoadSavePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.OptionPanels/LoadSavePanel.cs
index ce367f5dd7..5412251028 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.OptionPanels/LoadSavePanel.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.OptionPanels/LoadSavePanel.cs
@@ -81,7 +81,9 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
loadUserDataCheckButton.Active = IdeApp.Preferences.LoadDocumentUserProperties;
createBackupCopyCheckButton.Active = IdeApp.Preferences.CreateFileBackupCopies;
- loadPrevProjectCheckButton.Active = IdeApp.Preferences.LoadPrevSolutionOnStartup.Value;
+ openStartWindowRadioButton.Active = IdeApp.Preferences.StartupBehaviour.Value == OnStartupBehaviour.ShowStartWindow;
+ loadPrevProjectRadioButton.Active = IdeApp.Preferences.StartupBehaviour.Value == OnStartupBehaviour.LoadPreviousSolution;
+ emptyEnvironmentRadioButton.Active = IdeApp.Preferences.StartupBehaviour.Value == OnStartupBehaviour.EmptyEnvironment;
SetupAccessibility ();
}
@@ -95,8 +97,12 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
loadUserDataCheckButton.SetCommonAccessibilityAttributes ("LoadSavePanel.loadUserData", "",
GettextCatalog.GetString ("Check to load the user specific settings with the solution"));
- loadPrevProjectCheckButton.SetCommonAccessibilityAttributes ("LoadSavePanel.loadPrevious", "",
+ openStartWindowRadioButton.SetCommonAccessibilityAttributes ("LoadSavePanel.openStartWindow", "",
+ GettextCatalog.GetString ("Check to load the Start Window when starting the application"));
+ loadPrevProjectRadioButton.SetCommonAccessibilityAttributes ("LoadSavePanel.loadPrevious", "",
GettextCatalog.GetString ("Check to load the previous solution when starting the application"));
+ emptyEnvironmentRadioButton.SetCommonAccessibilityAttributes ("LoadSavePanel.emptyEnvironment", "",
+ GettextCatalog.GetString ("Check to load an empty environment when starting the application"));
createBackupCopyCheckButton.SetCommonAccessibilityAttributes ("LoadSavePanel.createBackup", "",
GettextCatalog.GetString ("Check to always create a backup copy"));
}
@@ -116,7 +122,14 @@ namespace MonoDevelop.Ide.Gui.OptionPanels
public void Store ()
{
- IdeApp.Preferences.LoadPrevSolutionOnStartup.Value = loadPrevProjectCheckButton.Active;
+ if (openStartWindowRadioButton.Active) {
+ IdeApp.Preferences.StartupBehaviour.Value = OnStartupBehaviour.ShowStartWindow;
+ } else if (loadPrevProjectRadioButton.Active) {
+ IdeApp.Preferences.StartupBehaviour.Value = OnStartupBehaviour.LoadPreviousSolution;
+ } else if (emptyEnvironmentRadioButton.Active) {
+ IdeApp.Preferences.StartupBehaviour.Value = OnStartupBehaviour.EmptyEnvironment;
+ }
+
IdeApp.Preferences.LoadDocumentUserProperties.Value = loadUserDataCheckButton.Active;
IdeApp.Preferences.CreateFileBackupCopies.Value = createBackupCopyCheckButton.Active;
IdeApp.Preferences.ProjectsDefaultPath.Value = folderEntry.Path;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs
index b81a7f6ebe..dcf9e34ad0 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs
@@ -438,7 +438,6 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad
void OnFileInserted (ITreeNavigator nav)
{
nav.Selected = true;
- Tree.StartLabelEdit ();
}
///<summary>Imports files and folders from a target folder into the current folder</summary>
@@ -549,27 +548,20 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad
// project node is collapsed and Refresh was used the project node would not expand and the new folder
// node would not be selected.
CurrentNode.Expanded = true;
- Project project = CurrentNode.GetParentDataItem (typeof(Project), true) as Project;
-
+
+ var project = CurrentNode.GetParentDataItem (typeof (Project), true) as Project;
string baseFolderPath = GetFolderPath (CurrentNode.DataItem);
- string directoryName = Path.Combine (baseFolderPath, GettextCatalog.GetString("New Folder"));
- int index = -1;
- if (Directory.Exists(directoryName)) {
- while (Directory.Exists(directoryName + (++index + 1))) ;
- }
-
- if (index >= 0) {
- directoryName += index + 1;
- }
-
- Directory.CreateDirectory (directoryName);
-
- ProjectFile newFolder = new ProjectFile (directoryName);
+ FilePath folder = await NewFolderDialog.Open (baseFolderPath);
+
+ if (folder.IsNull)
+ return;
+
+ var newFolder = new ProjectFile (folder);
newFolder.Subtype = Subtype.Directory;
project.Files.Add (newFolder);
- Tree.AddNodeInsertCallback (new ProjectFolder (directoryName, project), new TreeNodeCallback (OnFileInserted));
+ Tree.AddNodeInsertCallback (new ProjectFolder (folder, project), new TreeNodeCallback (OnFileInserted));
await IdeApp.ProjectOperations.SaveAsync (project);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs
index 7aae5b5548..d313c4f2e1 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs
@@ -451,7 +451,7 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad
project.ParentSolution.StartupItem = project;
await project.ParentSolution.SaveUserProperties ();
}
-
+
public override void DeleteItem ()
{
Project prj = CurrentNode.DataItem as Project;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectPadContextMenu.addin.xml b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectPadContextMenu.addin.xml
index 864d1822a8..c711f6e07d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectPadContextMenu.addin.xml
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectPadContextMenu.addin.xml
@@ -52,6 +52,9 @@
<Condition id="ItemType" value="Project">
<CommandItem id = "MonoDevelop.Ide.Commands.ProjectCommands.SetAsStartupProject" />
</Condition>
+ <Condition id="ItemType" value="Solution">
+ <CommandItem id="MonoDevelop.Ide.Commands.ProjectCommands.SetStartupProjects" />
+ </Condition>
<SeparatorItem id = "RunSectionEnd" />
<!-- Publish section -->
@@ -151,7 +154,7 @@
</Condition>
<Condition id="ItemType" value="IBuildTarget">
<CommandItem id = "MonoDevelop.Ide.Commands.ProjectCommands.Options" />
- </Condition>
+ </Condition>
<Condition id="ItemType" value="ProjectItem">
<CommandItem id = "MonoDevelop.Ide.Commands.FileCommands.ShowProperties" />
</Condition>
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs
index 2410849231..6e2a93e553 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs
@@ -1,4 +1,4 @@
-// DefaultWorkbench.cs
+// DefaultWorkbench.cs
//
// Author:
// Mike Krüger
@@ -80,6 +80,7 @@ namespace MonoDevelop.Ide.Gui.Shell
IWorkbenchWindow lastActive;
bool closeAll;
+ bool? fullScreenState = null;
Rectangle normalBounds = new Rectangle(0, 0, MinimumWidth, MinimumHeight);
@@ -148,13 +149,20 @@ namespace MonoDevelop.Ide.Gui.Shell
public DockFrame DockFrame {
get { return dock; }
}
-
+
public bool FullScreen {
get {
return IdeServices.DesktopService.GetIsFullscreen (this);
}
set {
- IdeServices.DesktopService.SetIsFullscreen (this, value);
+ // If this window is not visible, don't set full screen mode
+ // until it is, as that would conflict with other windows we
+ // might be opening before (Start Window, for instance)
+ if (Visible) {
+ IdeServices.DesktopService.SetIsFullscreen (this, value);
+ } else {
+ fullScreenState = value;
+ }
}
}
@@ -623,6 +631,16 @@ namespace MonoDevelop.Ide.Gui.Shell
}
}
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ if (fullScreenState != null && fullScreenState != IdeServices.DesktopService.GetIsFullscreen (this)) {
+ IdeServices.DesktopService.SetIsFullscreen (this, (bool)fullScreenState);
+ fullScreenState = null;
+ }
+ }
+
+ bool closing;
void OnClosing (object o, Gtk.DeleteEventArgs e)
{
// don't allow Gtk to close the workspace, in case Close() leaves the synchronization context
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs
index c566cf06e8..bb3392ae93 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Styles.cs
@@ -473,7 +473,7 @@ namespace MonoDevelop.Ide.Gui
PrimaryBackgroundColor = BaseBackgroundColor;
SecondaryBackgroundDarkerColor = Color.FromName ("#e7eaee");
SecondaryBackgroundLighterColor = Color.FromName ("#f9f9fb");
- SecondaryTextColorHexString = "#888888";
+ SecondaryTextColorHexString = "#767676";
SecondaryTextColor = Color.FromName (SecondaryTextColorHexString);
SecondarySelectionTextColor = Color.FromName ("#ffffff");
PadBackground = Color.FromName ("#fafafa");
@@ -597,7 +597,7 @@ namespace MonoDevelop.Ide.Gui
PrimaryBackgroundColor = BaseBackgroundColor;
SecondaryBackgroundDarkerColor = Color.FromName ("#484848");
SecondaryBackgroundLighterColor = SeparatorColor;
- SecondaryTextColorHexString = "#777777";
+ SecondaryTextColorHexString = "#ababab";
SecondaryTextColor = Color.FromName (SecondaryTextColorHexString);
SecondarySelectionTextColor = Color.FromName ("#ffffff");
PadBackground = Color.FromName ("#525252");
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 21c09974fe..fc40d29999 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs
@@ -1,4 +1,4 @@
-//
+//
// Workbench.cs
//
// Author:
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs
index 742d104691..a76e6a3594 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.ProgressMonitoring/MultiTaskDialogProgressMonitor.cs
@@ -68,7 +68,7 @@ namespace MonoDevelop.Ide.ProgressMonitoring
public MultiTaskDialogProgressMonitor (bool showProgress, bool allowCancel, bool showDetails, IDictionary<string, string> taskLabelAliases): base (Runtime.MainSynchronizationContext)
{
if (showProgress) {
- var parent = MessageService.GetDefaultModalParent ();
+ var parent = IdeServices.DesktopService.GetParentForModalWindow ();
dialog = new MultiTaskProgressDialog (allowCancel, showDetails, taskLabelAliases) {
DestroyWithParent = true,
Modal = true,
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/RunConfigurationsList.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/RunConfigurationsList.cs
index 5fb0b15aff..320f9ed81e 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/RunConfigurationsList.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/RunConfigurationsList.cs
@@ -38,17 +38,19 @@ namespace MonoDevelop.Ide.Projects.OptionPanels
Xwt.ListStore listStore;
Xwt.DataField<RunConfiguration> configCol = new Xwt.DataField<RunConfiguration> ();
Xwt.DataField<string> configNameCol = new Xwt.DataField<string> ();
+ Xwt.DataField<string> accessibleCol = new Xwt.DataField<string> ();
Xwt.DataField<Xwt.Drawing.Image> configIconCol = new Xwt.DataField<Xwt.Drawing.Image> ();
public RunConfigurationsList ()
{
- listStore = new Xwt.ListStore (configCol, configNameCol, configIconCol);
+ listStore = new Xwt.ListStore (configCol, configNameCol, configIconCol, accessibleCol);
list = new Xwt.ListView (listStore);
list.HeadersVisible = false;
var imgCell = new ImageCellView { ImageField = configIconCol };
var textCell = new TextCellView { MarkupField = configNameCol };
list.Columns.Add (GettextCatalog.GetString ("Name"), imgCell, textCell);
+ textCell.AccessibleFields.Label = accessibleCol;
Content = list;
@@ -64,7 +66,7 @@ namespace MonoDevelop.Ide.Projects.OptionPanels
var r = listStore.AddRow ();
var txt = "<b>" + c.Name + "</b>\n" + c.Summary;
var icon = !string.IsNullOrEmpty (c.IconId) ? ImageService.GetIcon (c.IconId) : ImageService.GetIcon ("md-prefs-play", Gtk.IconSize.Dnd);
- listStore.SetValues (r, configCol, c, configNameCol, txt, configIconCol, icon);
+ listStore.SetValues (r, configCol, c, configNameCol, txt, configIconCol, icon, accessibleCol, c.Name + " " + c.Summary);
}
if (currentRow != -1) {
if (currentRow < listStore.RowCount)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs
index 1076e98024..0fa378a615 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs
@@ -53,11 +53,11 @@ namespace MonoDevelop.Ide.Projects.OptionPanels
Solution = (Solution)dataObject;
- foreach (var rc in Solution.MultiStartupRunConfigurations)
- configs.Add (new SolutionRunConfigInfo { ProjectConfig = rc, EditedConfig = new MultiItemSolutionRunConfiguration (rc) });
-
- foreach (var c in configs)
+ foreach (var rc in Solution.MultiStartupRunConfigurations) {
+ var c = new SolutionRunConfigInfo { ProjectConfig = rc, EditedConfig = new MultiItemSolutionRunConfiguration (rc) };
+ configs.Add (c);
AddPanel (c);
+ }
ParentDialog.ExpandChildren (this);
}
@@ -120,7 +120,7 @@ namespace MonoDevelop.Ide.Projects.OptionPanels
internal void ShowConfiguration (MultiItemSolutionRunConfiguration editedConfig)
{
- var rc = configs.First (ci => ci.EditedConfig == editedConfig);
+ var rc = configs.First (ci => ci.EditedConfig.Name == editedConfig.Name);
var section = sections [rc];
ParentDialog.ShowPage (section);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs
index c301f2cbbf..5e6d401e43 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs
@@ -31,6 +31,7 @@ using MonoDevelop.Core;
using System.Collections.Generic;
using System.Text;
using MonoDevelop.Components;
+using MonoDevelop.Components.AtkCocoaHelper;
namespace MonoDevelop.Ide.Projects
{
@@ -59,6 +60,22 @@ namespace MonoDevelop.Ide.Projects
combPolicies.Active = 0;
OnRadioCustomToggled (null, null);
UpdateContentLabels ();
+
+ combPolicies.Accessible.Name = "ApplyPolicyDialog.PolicyCombo";
+ combPolicies.SetAccessibilityLabelRelationship (label2);
+ CombPolicies_Changed (null, null);
+ combPolicies.Changed += CombPolicies_Changed;
+ }
+
+ protected override void OnDestroyed ()
+ {
+ combPolicies.Changed -= CombPolicies_Changed;
+ base.OnDestroyed ();
+ }
+
+ void CombPolicies_Changed (object sender, EventArgs e)
+ {
+ combPolicies.Accessible.Description = GettextCatalog.GetString ("Select policy, current: {0}", combPolicies.ActiveText);
}
protected void OnRadioCustomToggled (object sender, System.EventArgs e)
@@ -189,7 +206,7 @@ namespace MonoDevelop.Ide.Projects
public PoliciesListSummaryTree () : base (new Gtk.ListStore (typeof (string)))
{
- CanFocus = false;
+ CanFocus = true;
HeadersVisible = false;
store = (Gtk.ListStore) Model;
this.AppendColumn ("", new Gtk.CellRendererText (), "text", 0);
@@ -204,6 +221,8 @@ namespace MonoDevelop.Ide.Projects
var win = evnt.Window;
win.Clear ();
if (string.IsNullOrEmpty (message)) {
+ if (ShowEmptyItem)
+ return base.OnExposeEvent (evnt);
return true;
}
@@ -233,7 +252,9 @@ namespace MonoDevelop.Ide.Projects
}
}
}
-
+
+ bool ShowEmptyItem { get; set; }
+
public void SetPolicies (PolicyContainer pset)
{
if (pset == null) {
@@ -279,6 +300,13 @@ namespace MonoDevelop.Ide.Projects
}
StringBuilderCache.Free (sb);
HasPolicies = sorted.Count > 0;
+ if (!HasPolicies) {
+ store.AppendValues (GettextCatalog.GetString ("No policies"));
+ ShowEmptyItem = true;
+ }
+ if (store.GetIterFirst (out var iter)) {
+ Selection.SelectIter (iter);
+ }
}
}
-} \ No newline at end of file
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs
index b10e87b681..a8574cac8c 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs
@@ -64,7 +64,6 @@ namespace MonoDevelop.Ide.Projects
tree.SetPolicies (policyProvider.Policies);
if (!tree.HasPolicies) {
- tree.Message = GettextCatalog.GetString ("No policies");
buttonOk.Sensitive = false;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportSolutionDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportSolutionDialog.cs
index 1da1730a71..91a85b8c31 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportSolutionDialog.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportSolutionDialog.cs
@@ -1,4 +1,4 @@
-// ExportProjectDialog.cs
+// ExportProjectDialog.cs
//
// Author:
// Lluis Sanchez Gual <lluis@novell.com>
@@ -43,7 +43,7 @@ namespace MonoDevelop.Ide.Projects
{
this.Build();
- labelNewFormat.Text = selectedFormat.ProductDescription;
+ labelNewFormat.Text = selectedFormat?.ProductDescription ?? item.FileFormat.ProductDescription;
formats = MSBuildFileFormat.GetSupportedFormats (item).ToArray ();
foreach (var format in formats) {
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs
index 6760e5b3dd..82622c4289 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.UI.cs
@@ -106,12 +106,6 @@ namespace MonoDevelop.Ide.Projects
Name = "wizard_dialog";
Title = GettextCatalog.GetString ("New Project");
- if (IdeApp.Workbench.RootWindow.Visible) {
- WindowPosition = WindowPosition.CenterOnParent;
- TransientFor = IdeApp.Workbench.RootWindow;
- } else
- // FIXME: align on a native toplevel if available
- WindowPosition = WindowPosition.Center;
projectConfigurationWidget = new GtkProjectConfigurationWidget ();
projectConfigurationWidget.Name = "projectConfigurationWidget";
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.cs
index cd8f235b4f..2750141b56 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/GtkNewProjectDialogBackend.cs
@@ -44,7 +44,7 @@ namespace MonoDevelop.Ide.Projects
partial class GtkNewProjectDialogBackend : INewProjectDialogBackend
{
INewProjectDialogController controller;
- Menu popupMenu;
+ Xwt.Menu popupMenu;
bool isLastPressedKeySpace;
public GtkNewProjectDialogBackend ()
@@ -98,7 +98,7 @@ namespace MonoDevelop.Ide.Projects
public void ShowDialog ()
{
- MessageService.ShowCustomDialog (this);
+ MessageService.ShowCustomDialog (this, IdeServices.DesktopService.GetFocusedTopLevelWindow ());
}
public void CloseDialog ()
@@ -150,23 +150,24 @@ namespace MonoDevelop.Ide.Projects
void HandlePopup (SolutionTemplate template, uint eventTime)
{
- if (popupMenu == null) {
- popupMenu = new Menu ();
- popupMenu.AttachToWidget (this, null);
- }
- ClearPopupMenuItems ();
- AddLanguageMenuItems (popupMenu, template);
- popupMenu.ModifyBg (StateType.Normal, Styles.NewProjectDialog.TemplateLanguageButtonBackground.ToGdkColor ());
- popupMenu.ShowAll ();
-
- MenuPositionFunc posFunc = (Menu m, out int x, out int y, out bool pushIn) => {
- Gdk.Rectangle rect = languageCellRenderer.GetLanguageRect ();
- Gdk.Rectangle screenRect = GtkUtil.ToScreenCoordinates (templatesTreeView, templatesTreeView.GdkWindow, rect);
- x = screenRect.X;
- y = screenRect.Bottom;
- pushIn = false;
- };
- popupMenu.Popup (null, null, posFunc, 0, eventTime);
+ var engine = Platform.IsMac ? Xwt.Toolkit.NativeEngine : Xwt.Toolkit.CurrentEngine;
+ var xwtParent = Xwt.Toolkit.CurrentEngine.WrapWidget (templatesTreeView);
+ engine.Invoke (() => {
+ if (popupMenu == null) {
+ popupMenu = new Xwt.Menu ();
+ }
+ ClearPopupMenuItems ();
+ AddLanguageMenuItems (popupMenu, template);
+ Gdk.Rectangle rect = languageCellRenderer.GetLanguageRect ();
+
+ try {
+ popupMenu.Popup (xwtParent, rect.X, rect.Bottom);
+ } catch {
+ // popup at mouse position if the toolkit is not supported
+ popupMenu.Popup ();
+ }
+
+ });
}
[GLib.ConnectBefore]
@@ -202,10 +203,8 @@ namespace MonoDevelop.Ide.Projects
}
void ClearPopupMenuItems ()
- {
- foreach (Widget widget in popupMenu.Children) {
- widget.Destroy ();
- }
+ {
+ popupMenu.Items.Clear ();
}
void PerformShowMenu (object sender, EventArgs args)
@@ -218,17 +217,18 @@ namespace MonoDevelop.Ide.Projects
HandlePopup (template, Gdk.EventHelper.GetTime (null));
}
- void AddLanguageMenuItems (Menu menu, SolutionTemplate template)
+ void AddLanguageMenuItems (Xwt.Menu menu, SolutionTemplate template)
{
foreach (string language in template.AvailableLanguages.OrderBy (item => item)) {
- var menuItem = new MenuItem (language);
- menuItem.Activated += (o, e) => {
+ var menuItem = new Xwt.MenuItem (language);
+ menuItem.Accessible.Label = LanguageCellRenderer.GetAccessibleLanguageName (language);
+ menuItem.Clicked += (o, e) => {
languageCellRenderer.SelectedLanguage = language;
controller.SelectedLanguage = language;
templatesTreeView.QueueDraw ();
ShowSelectedTemplate ();
};
- menu.Append (menuItem);
+ menu.Items.Add (menuItem);
}
}
@@ -283,7 +283,7 @@ namespace MonoDevelop.Ide.Projects
protected override void OnDestroyed ()
{
if (popupMenu != null) {
- popupMenu.Destroy ();
+ popupMenu.Dispose ();
popupMenu = null;
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/LanguageCellRenderer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/LanguageCellRenderer.cs
index 2f5b402682..4a9b957455 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/LanguageCellRenderer.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/LanguageCellRenderer.cs
@@ -53,9 +53,19 @@ namespace MonoDevelop.Ide.Projects
}
set {
selectedLanguage = value;
- Text = value;
+ Text = GetAccessibleLanguageName (value);
}
}
+
+ internal static string GetAccessibleLanguageName (string language)
+ {
+ switch (language) {
+ case "C#": return "C Sharp";
+ case "F#": return "F Sharp";
+ default: return language;
+ }
+ }
+
public bool RenderRecentTemplate { get; set; }
int textWidth = 0;
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewSolutionRunConfigurationDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewSolutionRunConfigurationDialog.cs
new file mode 100644
index 0000000000..e6a0986279
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewSolutionRunConfigurationDialog.cs
@@ -0,0 +1,98 @@
+//
+// NewSolutionRunConfigurationDialog.cs
+//
+// Author:
+// Rodrigo Moya <rodrigo.moya@xamarin.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 Xwt;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.Ide.Projects
+{
+ public class NewSolutionRunConfigurationDialog : Dialog
+ {
+ readonly TextEntry runConfigNameEntry;
+ readonly DialogButton createButton;
+
+ public NewSolutionRunConfigurationDialog ()
+ {
+ Title = GettextCatalog.GetString ("Create Solution Run Configuration");
+ Resizable = false;
+
+ var box = new VBox ();
+ Content = box;
+
+ var label = new Label (GettextCatalog.GetString ("Solution run configurations let you run multiple projects at once. Please provide a name to\nbe shown in the toolbar for this Solution run configuration.")) {
+ CanGetFocus = false,
+ Wrap = WrapMode.Word
+ };
+ box.PackStart (label, expand: true, marginBottom: 12);
+
+ var hbox = new HBox ();
+ hbox.PackStart (new Label (GettextCatalog.GetString ("Run configuration name:")) {
+ CanGetFocus = false
+ });
+
+ runConfigNameEntry = new TextEntry {
+ Text = GettextCatalog.GetString ("Multiple Projects")
+ };
+ runConfigNameEntry.Changed += OnConfigNameChanged;
+ hbox.PackStart (runConfigNameEntry, true, true);
+
+ box.PackStart (hbox, true, true);
+
+ createButton = new DialogButton (new Command ("create", GettextCatalog.GetString ("Create Run Configuration")));
+ Buttons.Add (Command.Cancel);
+ Buttons.Add (createButton);
+ DefaultCommand = createButton.Command;
+ }
+
+ public string RunConfigurationName => runConfigNameEntry.Text;
+
+ void OnConfigNameChanged (object sender, EventArgs e)
+ {
+ createButton.Sensitive = !string.IsNullOrEmpty (runConfigNameEntry.Text);
+ }
+
+ protected override void OnCommandActivated (Command cmd)
+ {
+ if (cmd.Id == createButton.Command.Id) {
+ Respond (cmd);
+ return;
+ }
+ base.OnCommandActivated (cmd);
+ }
+
+ protected override void OnShown ()
+ {
+ base.OnShown ();
+ runConfigNameEntry.SetFocus ();
+ }
+
+ protected override void Dispose (bool disposing)
+ {
+ runConfigNameEntry.Changed -= OnConfigNameChanged;
+ base.Dispose (disposing);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/MonoDevelopProjectCacheHostServiceFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/MonoDevelopProjectCacheHostServiceFactory.cs
index 47a1092c47..8b838368d7 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/MonoDevelopProjectCacheHostServiceFactory.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.RoslynServices/MonoDevelopProjectCacheHostServiceFactory.cs
@@ -26,6 +26,7 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
@@ -44,23 +45,22 @@ namespace MonoDevelop.Ide.RoslynServices
public IWorkspaceService CreateService (HostWorkspaceServices workspaceServices)
{
// we support active document tracking only for visual studio workspace host.
- if (workspaceServices.Workspace is MonoDevelopWorkspace) {
+ if (workspaceServices.Workspace is MonoDevelopWorkspace monoDevelopWorkspace) {
// We will finish setting this up in VisualStudioWorkspaceImpl.DeferredInitializationState
- var projectCacheService = new ProjectCacheService (workspaceServices.Workspace, ImplicitCacheTimeoutInMS);
+ var projectCacheService = new MonoDevelopProjectCacheService (monoDevelopWorkspace, ImplicitCacheTimeoutInMS);
var documentTrackingService = workspaceServices.GetService<IDocumentTrackingService> ();
// Subscribe to events so that we can cache items from the active document's project
var manager = new ActiveProjectCacheManager (documentTrackingService, projectCacheService);
+ projectCacheService.Manager = manager;
// Subscribe to requests to clear the cache
- var workspaceCacheService = workspaceServices.GetService<IWorkspaceCacheService> ();
- if (workspaceCacheService != null) {
- workspaceCacheService.CacheFlushRequested += (s, e) => manager.Clear ();
- }
+ var workspaceCacheService = workspaceServices.GetService<IWorkspaceCacheService> ();
+ projectCacheService.SubscribeToFlushRequested (workspaceCacheService);
// Also clear the cache when the solution is cleared or removed.
- workspaceServices.Workspace.WorkspaceChanged += (s, e) => {
- if (e.Kind == WorkspaceChangeKind.SolutionCleared || e.Kind == WorkspaceChangeKind.SolutionRemoved) {
+ monoDevelopWorkspace.WorkspaceChanged += (s, e) => {
+ if (e.Kind == WorkspaceChangeKind.SolutionCleared || e.Kind == WorkspaceChangeKind.SolutionRemoved) {
manager.Clear ();
}
};
@@ -71,7 +71,45 @@ namespace MonoDevelop.Ide.RoslynServices
return new ProjectCacheService (workspaceServices.Workspace);
}
- class ActiveProjectCacheManager
+ class MonoDevelopProjectCacheService : ProjectCacheService, IDisposable
+ {
+ internal ActiveProjectCacheManager Manager { get; set; }
+ IWorkspaceCacheService cacheService;
+
+ public MonoDevelopProjectCacheService (Workspace workspace) : base (workspace)
+ {
+ }
+
+ public MonoDevelopProjectCacheService (Workspace workspace, int implicitCacheTimeout) : base (workspace, implicitCacheTimeout)
+ {
+ }
+
+ public void SubscribeToFlushRequested (IWorkspaceCacheService cacheService)
+ {
+ this.cacheService = cacheService;
+ cacheService.CacheFlushRequested += OnCacheFlushRequested;
+ }
+
+ void OnCacheFlushRequested (object sender, EventArgs args)
+ {
+ Manager.Clear ();
+ }
+
+ public void Dispose()
+ {
+ if (cacheService != null) {
+ cacheService.CacheFlushRequested -= OnCacheFlushRequested;
+ cacheService = null;
+ }
+
+ Manager?.Dispose ();
+ Manager = null;
+ }
+ }
+
+ //public void GetManager
+
+ class ActiveProjectCacheManager : IDisposable
{
readonly IDocumentTrackingService _documentTrackingService;
readonly ProjectCacheService _projectCacheService;
@@ -91,6 +129,13 @@ namespace MonoDevelop.Ide.RoslynServices
}
}
+ public void Dispose ()
+ {
+ if (_documentTrackingService != null) {
+ _documentTrackingService.ActiveDocumentChanged -= UpdateCache;
+ }
+ }
+
void UpdateCache (object sender, DocumentId activeDocument)
{
lock (lockObject) {
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs
index 08ff2a23ab..811dc651ec 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplateWizard.cs
@@ -28,6 +28,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using MonoDevelop.Core;
+using MonoDevelop.Core.StringParsing;
using MonoDevelop.Ide.Projects;
using MonoDevelop.Projects;
@@ -109,6 +111,34 @@ namespace MonoDevelop.Ide.Templates
public virtual void ItemsCreated (IEnumerable<IWorkspaceFileObject> items)
{
+ if (!(items.FirstOrDefault () is Solution solution))
+ return;
+
+ CreateMultiProjectStartUp (solution);
+ }
+
+ /// <summary>
+ /// Adds MultiStartupConfiguration when there are
+ /// more than one project and one of them is a Backend project
+ /// </summary>
+ /// <param name="solution">Solution.</param>
+ void CreateMultiProjectStartUp (Solution solution)
+ {
+ if (Parameters.GetBoolValue ("CreateBackEndProject") != true || Parameters.GetBoolValue ("IncludeBackEndProject") != true)
+ return;
+
+ var config = new MultiItemSolutionRunConfiguration ("multiprojId", GettextCatalog.GetString ("Multiple Projects"));
+ foreach (var proj in solution.GetAllProjects ()) {
+ if (!proj.SupportsExecute ())
+ continue;
+ var startupItem = new StartupItem (proj, null);
+ config.Items.Add (startupItem);
+ }
+
+ solution.MultiStartupRunConfigurations.Add (config);
+ solution.StartupConfiguration = config;
+
+ solution.SaveAsync (new ProgressMonitor ()).Ignore ();
}
public virtual IEnumerable<ProjectConfigurationControl> GetFinalPageControls ()
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs
index 91f5b99b93..9619d25455 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs
@@ -71,6 +71,11 @@ namespace MonoDevelop.Ide.TypeSystem
public FoldingRegion (DocumentRegion region, FoldType type) : this (null, region, type)
{
}
+
+ public override string ToString ()
+ {
+ return string.Format ("[FoldingRegion: Name={0}, IsFoldedByDefault={1}, Region={2}, Type={3}]", Name, IsFoldedByDefault, Region, Type);
+ }
}
public enum FoldType
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/HackyWorkspaceFilesCache.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/HackyWorkspaceFilesCache.cs
new file mode 100644
index 0000000000..5660f5e906
--- /dev/null
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/HackyWorkspaceFilesCache.cs
@@ -0,0 +1,237 @@
+//
+// HackyWorkspaceFilesCache.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 System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using MonoDevelop.Core;
+using MonoDevelop.Core.FeatureConfiguration;
+using MonoDevelop.Projects;
+using Newtonsoft.Json;
+
+namespace MonoDevelop.Ide.TypeSystem
+{
+ class HackyWorkspaceFilesCache
+ {
+ const int format = 1;
+ readonly bool enabled = FeatureSwitchService.IsFeatureEnabled ("HackyCache").GetValueOrDefault ();
+
+ readonly FilePath cacheDir;
+
+ Dictionary<FilePath, ProjectCache> cachedItems = new Dictionary<FilePath, ProjectCache> ();
+
+ public HackyWorkspaceFilesCache (Solution solution)
+ {
+ if (!IdeApp.IsInitialized || !enabled || solution == null)
+ return;
+
+ LoggingService.LogDebug ("HackyWorkspaceCache enabled");
+ cacheDir = solution.GetPreferencesDirectory ().Combine ("hacky-project-cache");
+ Directory.CreateDirectory (cacheDir);
+
+ LoadCache (solution);
+ }
+
+ void LoadCache (Solution sol)
+ {
+ var solConfig = sol.GetConfiguration (IdeApp.Workspace.ActiveConfiguration);
+
+ var serializer = new JsonSerializer ();
+ foreach (var project in sol.GetAllProjects ()) {
+ var config = solConfig.GetMappedConfiguration (project);
+
+ var projectFilePath = project.FileName;
+ var cacheFilePath = GetProjectCacheFile (project, config);
+
+ try {
+ if (!File.Exists (cacheFilePath))
+ continue;
+
+ using (var sr = File.OpenText (cacheFilePath)) {
+ var value = (ProjectCache)serializer.Deserialize (sr, typeof (ProjectCache));
+
+ if (format != value.Format || value.TimeStamp != File.GetLastWriteTimeUtc (projectFilePath))
+ continue;
+
+ cachedItems [projectFilePath] = value;
+
+ }
+ } catch (Exception ex) {
+ LoggingService.LogError ("Could not deserialize project cache", ex);
+ TryDeleteFile (cacheFilePath);
+ continue;
+ }
+ }
+ }
+
+ string GetProjectCacheFile (Project proj, string configuration)
+ {
+ return cacheDir.Combine (proj.FileName.FileNameWithoutExtension + "-" + configuration + ".json");
+ }
+
+ static void TryDeleteFile (string file)
+ {
+ try {
+ File.Delete (file);
+ } catch (Exception ex) {
+ LoggingService.LogError ("Could not delete cache file", ex);
+ }
+ }
+
+ public void Update (ProjectConfiguration projConfig, Project proj, MonoDevelopWorkspace.ProjectDataMap projectMap,
+ ImmutableArray<ProjectFile> files,
+ ImmutableArray<FilePath> analyzers,
+ ImmutableArray<MonoDevelopMetadataReference> metadataReferences,
+ ImmutableArray<Microsoft.CodeAnalysis.ProjectReference> projectReferences)
+ {
+ if (!enabled)
+ return;
+
+ var paths = new string [files.Length];
+ var actions = new string [files.Length];
+
+ for (int i = 0; i < files.Length; ++i) {
+ paths [i] = files [i].FilePath;
+ actions [i] = files [i].BuildAction;
+ }
+
+ var projectRefs = new ReferenceItem [projectReferences.Length];
+ for (int i = 0; i < projectReferences.Length; ++i) {
+ var pr = projectReferences [i];
+ var mdProject = projectMap.GetMonoProject (pr.ProjectId);
+ projectRefs [i] = new ReferenceItem {
+ FilePath = mdProject.FileName,
+ Aliases = pr.Aliases.ToArray (),
+ };
+ }
+
+ var item = new ProjectCache {
+ Format = format,
+ Analyzers = analyzers.Select(x => (string)x).ToArray (),
+ Files = paths,
+ BuildActions = actions,
+ TimeStamp = File.GetLastWriteTimeUtc (proj.FileName),
+ MetadataReferences = metadataReferences.Select(x => {
+ var ri = new ReferenceItem {
+ FilePath = x.FilePath,
+ Aliases = x.Properties.Aliases.ToArray (),
+ };
+ return ri;
+ }).ToArray (),
+ ProjectReferences = projectRefs,
+ };
+
+ var cacheFile = GetProjectCacheFile (proj, projConfig.Id);
+
+ var serializer = new JsonSerializer ();
+ using (var fs = File.Open (cacheFile, FileMode.Create))
+ using (var sw = new StreamWriter (fs)) {
+ serializer.Serialize (sw, item);
+ }
+ }
+
+
+ public bool TryGetCachedItems (Project p, MonoDevelopMetadataReferenceManager provider, MonoDevelopWorkspace.ProjectDataMap projectMap,
+ out ImmutableArray<ProjectFile> files,
+ out ImmutableArray<FilePath> analyzers,
+ out ImmutableArray<MonoDevelopMetadataReference> metadataReferences,
+ out ImmutableArray<Microsoft.CodeAnalysis.ProjectReference> projectReferences)
+ {
+ files = ImmutableArray<ProjectFile>.Empty;
+ analyzers = ImmutableArray<FilePath>.Empty;
+ metadataReferences = ImmutableArray<MonoDevelopMetadataReference>.Empty;
+ projectReferences = ImmutableArray<Microsoft.CodeAnalysis.ProjectReference>.Empty;
+
+ if (!cachedItems.TryGetValue (p.FileName, out var cachedData)) {
+ return false;
+ }
+
+ cachedItems.Remove (p.FileName);
+
+ if (cachedData.TimeStamp != File.GetLastWriteTimeUtc (p.FileName))
+ return false;
+
+ var filesBuilder = ImmutableArray.CreateBuilder<ProjectFile> (cachedData.Files.Length);
+ for (int i = 0; i < cachedData.Files.Length; ++i) {
+ filesBuilder.Add(new ProjectFile (cachedData.Files [i], cachedData.BuildActions [i]) {
+ Project = p,
+ });
+ }
+
+ files = filesBuilder.MoveToImmutable ();
+
+ var analyzersBuilder = ImmutableArray.CreateBuilder<FilePath> (cachedData.Analyzers.Length);
+ foreach (var analyzer in cachedData.Analyzers)
+ analyzersBuilder.Add (analyzer);
+ analyzers = analyzersBuilder.MoveToImmutable ();
+
+ var mrBuilder = ImmutableArray.CreateBuilder<MonoDevelopMetadataReference> (cachedData.MetadataReferences.Length);
+ foreach (var item in cachedData.MetadataReferences) {
+ var aliases = item.Aliases != null ? item.Aliases.ToImmutableArray () : default;
+ var reference = provider.GetOrCreateMetadataReference (item.FilePath, new Microsoft.CodeAnalysis.MetadataReferenceProperties(aliases: aliases));
+ mrBuilder.Add (reference);
+ }
+ metadataReferences = mrBuilder.MoveToImmutable ();
+
+ var sol = p.ParentSolution;
+ var solConfig = sol.GetConfiguration (IdeApp.Workspace.ActiveConfiguration);
+ var allProjects = sol.GetAllProjects ().ToDictionary (x => x.FileName, x => x);
+
+ var prBuilder = ImmutableArray.CreateBuilder<Microsoft.CodeAnalysis.ProjectReference> (cachedData.ProjectReferences.Length);
+ foreach (var item in cachedData.ProjectReferences) {
+ if (!allProjects.TryGetValue (item.FilePath, out var mdProject))
+ return false;
+
+ var aliases = item.Aliases != null ? item.Aliases.ToImmutableArray () : default;
+ var pr = new Microsoft.CodeAnalysis.ProjectReference (projectMap.GetOrCreateId (mdProject, null), aliases.ToImmutableArray ());
+ prBuilder.Add (pr);
+ }
+ projectReferences = prBuilder.MoveToImmutable ();
+ return true;
+ }
+
+ [Serializable]
+ class ProjectCache
+ {
+ public int Format;
+ public DateTime TimeStamp;
+
+ public ReferenceItem [] ProjectReferences;
+ public ReferenceItem[] MetadataReferences;
+ public string [] Files;
+ public string [] BuildActions;
+ public string [] Analyzers;
+ }
+
+ [Serializable]
+ internal class ReferenceItem
+ {
+ public string FilePath;
+ public string [] Aliases = Array.Empty<string> ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs
index f5cc57498c..3bf82a7740 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs
@@ -40,36 +40,129 @@ using Microsoft.CodeAnalysis.SQLite;
using Microsoft.CodeAnalysis.Storage;
using Microsoft.CodeAnalysis.SolutionSize;
using MonoDevelop.Core;
+using System.Diagnostics.Contracts;
+using System.Diagnostics;
namespace MonoDevelop.Ide.TypeSystem
{
[ExportWorkspaceService (typeof (IPersistentStorageLocationService), ServiceLayer.Host), Shared]
class MonoDevelopPersistentStorageLocationService : IPersistentStorageLocationService
{
+ private readonly object _gate = new object ();
+ private WorkspaceId primaryWorkspace = WorkspaceId.Empty;
+ private SolutionId _currentSolutionId = null;
+ private string _currentWorkingFolderPath = null;
+
+ public event EventHandler<PersistentStorageLocationChangingEventArgs> StorageLocationChanging;
+
+ [ImportingConstructor]
+ [Obsolete (MefConstruction.ImportingConstructorMessage, error: true)]
+ public MonoDevelopPersistentStorageLocationService ()
+ {
+ }
+
+ public IDisposable RegisterPrimaryWorkspace (WorkspaceId id)
+ {
+ if (primaryWorkspace.Equals (WorkspaceId.Empty)) {
+ primaryWorkspace = id;
+ return new WorkspaceRegistration (this);
+ }
+ return null;
+ }
+
+ class WorkspaceRegistration : IDisposable
+ {
+ readonly MonoDevelopPersistentStorageLocationService service;
+ bool disposed;
+
+ public WorkspaceRegistration (MonoDevelopPersistentStorageLocationService service) => this.service = service;
+
+ public void Dispose ()
+ {
+ if (!disposed) {
+ service.DisconnectCurrentStorage ();
+ disposed = true;
+ }
+ }
+ }
+
public bool IsSupported (Workspace workspace) => workspace is MonoDevelopWorkspace;
- // PERF: cache for the solution location. This is needed due to roslyn querying GetStorageLocation a lot of times.
- internal ConditionalWeakTable<SolutionId, string> storageMap = new ConditionalWeakTable<SolutionId, string> ();
+ public string TryGetStorageLocation (SolutionId solutionId)
+ {
+ lock (_gate) {
+ if (solutionId == _currentSolutionId) {
+ return _currentWorkingFolderPath;
+ }
+ }
- public event EventHandler<PersistentStorageLocationChangingEventArgs> StorageLocationChanging;
+ return null;
+ }
- internal void NotifyStorageLocationChanging (SolutionId sol, string path)
+ internal void SetupSolution (MonoDevelopWorkspace visualStudioWorkspace)
{
- lock (storageMap) {
- if (storageMap.TryGetValue (sol, out string cached) && path == cached)
+ lock (_gate) {
+ // Don't trigger events for workspaces other than those we want to inspect.
+ if (!primaryWorkspace.Equals (visualStudioWorkspace.Id))
+ return;
+
+ if (visualStudioWorkspace.CurrentSolution.Id == _currentSolutionId && _currentWorkingFolderPath != null) {
return;
+ }
- StorageLocationChanging?.Invoke (this, new PersistentStorageLocationChangingEventArgs (sol, path, true));
- storageMap.Remove (sol);
- storageMap.Add (sol, path);
+ var solution = visualStudioWorkspace.MonoDevelopSolution;
+ solution.Modified += OnSolutionModified;
+ if (string.IsNullOrWhiteSpace (solution.BaseDirectory))
+ return;
+
+ var workingFolderPath = solution.GetPreferencesDirectory ();
+
+ try {
+ if (!string.IsNullOrWhiteSpace (workingFolderPath)) {
+ OnWorkingFolderChanging_NoLock (
+ new PersistentStorageLocationChangingEventArgs (
+ visualStudioWorkspace.CurrentSolution.Id,
+ workingFolderPath,
+ mustUseNewStorageLocationImmediately: false));
+ }
+ } catch {
+ // don't crash just because solution having problem getting working folder information
+ }
}
}
- public string TryGetStorageLocation (SolutionId solutionId)
+ async void OnSolutionModified (object sender, MonoDevelop.Projects.WorkspaceItemEventArgs args)
{
- lock (storageMap) {
- storageMap.TryGetValue (solutionId, out var path);
- return path;
+ var sol = (MonoDevelop.Projects.Solution)args.Item;
+ var workspace = await IdeServices.TypeSystemService.GetWorkspaceAsync (sol, CancellationToken.None);
+ if (workspace.Id.Equals (primaryWorkspace)) {
+ DisconnectCurrentStorage ();
+ }
+ }
+
+ private void OnWorkingFolderChanging_NoLock (PersistentStorageLocationChangingEventArgs eventArgs)
+ {
+ StorageLocationChanging?.Invoke (this, eventArgs);
+
+ _currentSolutionId = eventArgs.SolutionId;
+ _currentWorkingFolderPath = eventArgs.NewStorageLocation;
+ }
+
+ void DisconnectCurrentStorage ()
+ {
+ lock (_gate) {
+ var workspace = IdeServices.TypeSystemService.GetWorkspace (primaryWorkspace);
+ var solution = workspace.MonoDevelopSolution;
+ if (solution != null)
+ solution.Modified -= OnSolutionModified;
+
+ // We want to make sure everybody synchronously detaches
+ OnWorkingFolderChanging_NoLock (
+ new PersistentStorageLocationChangingEventArgs (
+ _currentSolutionId,
+ newStorageLocation: null,
+ mustUseNewStorageLocationImmediately: true));
+ primaryWorkspace = WorkspaceId.Empty;
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs
index ff49e10dd0..3d62435d25 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs
@@ -48,9 +48,10 @@ namespace MonoDevelop.Ide.TypeSystem
DocumentData = new DocumentMap (projectId);
this.metadataReferences = new List<MonoDevelopMetadataReference> (metadataReferences.Length);
- System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock));
- foreach (var metadataReference in metadataReferences) {
- AddMetadataReference_NoLock (metadataReference, ws);
+ lock (this.metadataReferences) {
+ foreach (var metadataReference in metadataReferences) {
+ AddMetadataReference_NoLock (metadataReference, ws);
+ }
}
}
@@ -61,7 +62,7 @@ namespace MonoDevelop.Ide.TypeSystem
if (!workspaceRef.TryGetTarget (out var workspace))
return;
- lock (workspace.updatingProjectDataLock) {
+ lock (metadataReferences) {
if (!RemoveMetadataReference_NoLock (reference, workspace))
return;
workspace.OnMetadataReferenceRemoved (projectId, args.OldSnapshot);
@@ -73,7 +74,7 @@ namespace MonoDevelop.Ide.TypeSystem
void AddMetadataReference_NoLock (MonoDevelopMetadataReference metadataReference, MonoDevelopWorkspace ws)
{
- System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock));
+ System.Diagnostics.Debug.Assert (Monitor.IsEntered (metadataReferences));
metadataReferences.Add (metadataReference);
metadataReference.SnapshotUpdated += OnMetadataReferenceUpdated;
@@ -81,7 +82,7 @@ namespace MonoDevelop.Ide.TypeSystem
bool RemoveMetadataReference_NoLock (MonoDevelopMetadataReference metadataReference, MonoDevelopWorkspace ws)
{
- System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock));
+ System.Diagnostics.Debug.Assert (Monitor.IsEntered (metadataReferences));
metadataReference.SnapshotUpdated -= OnMetadataReferenceUpdated;
return metadataReferences.Remove (metadataReference);
@@ -92,9 +93,10 @@ namespace MonoDevelop.Ide.TypeSystem
if (!workspaceRef.TryGetTarget (out var ws))
return;
- System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock));
- foreach (var reference in metadataReferences)
- reference.SnapshotUpdated -= OnMetadataReferenceUpdated;
+ lock (metadataReferences) {
+ foreach (var reference in metadataReferences)
+ reference.SnapshotUpdated -= OnMetadataReferenceUpdated;
+ }
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs
index 3cdf74b32b..2d28bfd99e 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs
@@ -26,6 +26,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Linq;
using Microsoft.CodeAnalysis;
namespace MonoDevelop.Ide.TypeSystem
@@ -39,8 +40,8 @@ namespace MonoDevelop.Ide.TypeSystem
ImmutableDictionary<ProjectId, MonoDevelop.Projects.Project> projectIdToMdProjectMap = ImmutableDictionary<ProjectId, MonoDevelop.Projects.Project>.Empty;
readonly Dictionary<MonoDevelop.Projects.Project, ProjectId> projectIdMap = new Dictionary<MonoDevelop.Projects.Project, ProjectId> ();
- // FIXME: Make this private
- internal readonly Dictionary<ProjectId, ProjectData> projectDataMap = new Dictionary<ProjectId, ProjectData> ();
+ readonly Dictionary<ProjectId, ProjectData> projectDataMap = new Dictionary<ProjectId, ProjectData> ();
+ readonly object updatingProjectDataLock = new object ();
public ProjectDataMap (MonoDevelopWorkspace workspace)
{
@@ -84,17 +85,36 @@ namespace MonoDevelop.Ide.TypeSystem
}
}
- internal void RemoveProject (MonoDevelop.Projects.Project project, ProjectId id)
+ internal void RemoveProject (MonoDevelop.Projects.Project project)
{
+ ProjectId projectId;
+
lock (gate) {
- if (projectIdMap.TryGetValue (project, out ProjectId val))
+ if (projectIdMap.TryGetValue (project, out projectId)) {
projectIdMap.Remove (project);
- projectIdToMdProjectMap = projectIdToMdProjectMap.Remove (val);
+ projectIdToMdProjectMap = projectIdToMdProjectMap.Remove (projectId);
+ }
+ }
+
+ if (projectId != null) {
+ RemoveData (projectId);
}
+ }
+
+ internal MonoDevelop.Projects.Project RemoveProject (ProjectId id)
+ {
+ MonoDevelop.Projects.Project actualProject;
- lock (Workspace.updatingProjectDataLock) {
- projectDataMap.Remove (id);
+ lock (gate) {
+ if (projectIdToMdProjectMap.TryGetValue (id, out actualProject)) {
+ projectIdMap.Remove (actualProject);
+ projectIdToMdProjectMap = projectIdToMdProjectMap.Remove (id);
+ }
}
+
+ RemoveData (id);
+
+ return actualProject;
}
internal MonoDevelop.Projects.Project GetMonoProject (ProjectId projectId)
@@ -106,14 +126,14 @@ namespace MonoDevelop.Ide.TypeSystem
internal bool Contains (ProjectId projectId)
{
- lock (Workspace.updatingProjectDataLock) {
+ lock (updatingProjectDataLock) {
return projectDataMap.ContainsKey (projectId);
}
}
internal ProjectData GetData (ProjectId id)
{
- lock (Workspace.updatingProjectDataLock) {
+ lock (updatingProjectDataLock) {
projectDataMap.TryGetValue (id, out ProjectData result);
return result;
}
@@ -121,7 +141,7 @@ namespace MonoDevelop.Ide.TypeSystem
internal ProjectData RemoveData (ProjectId id)
{
- lock (Workspace.updatingProjectDataLock) {
+ lock (updatingProjectDataLock) {
if (projectDataMap.TryGetValue (id, out ProjectData result)) {
projectDataMap.Remove (id);
result.Disconnect ();
@@ -132,12 +152,18 @@ namespace MonoDevelop.Ide.TypeSystem
internal ProjectData CreateData (ProjectId id, ImmutableArray<MonoDevelopMetadataReference> metadataReferences)
{
- lock (Workspace.updatingProjectDataLock) {
+ lock (updatingProjectDataLock) {
var result = new ProjectData (id, metadataReferences, Workspace);
projectDataMap [id] = result;
return result;
}
}
+ internal ProjectId[] GetProjectIds ()
+ {
+ lock (updatingProjectDataLock) {
+ return projectDataMap.Keys.ToArray ();
+ }
+ }
}
}
} \ No newline at end of file
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 b059f08a0e..1b6f1cf608 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:
@@ -36,18 +36,22 @@ using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using MonoDevelop.Core;
using MonoDevelop.Ide.Editor.Projection;
+using MonoDevelop.Projects;
namespace MonoDevelop.Ide.TypeSystem
{
public partial class MonoDevelopWorkspace
{
- internal class ProjectSystemHandler
+ internal class ProjectSystemHandler : IDisposable
{
readonly MonoDevelopWorkspace workspace;
readonly ProjectDataMap projectMap;
readonly ProjectionData projections;
readonly Lazy<MetadataReferenceHandler> metadataHandler;
readonly Lazy<HostDiagnosticUpdateSource> hostDiagnosticUpdateSource;
+ readonly HackyWorkspaceFilesCache hackyCache;
+ readonly List<MonoDevelopAnalyzer> analyzersToDispose = new List<MonoDevelopAnalyzer> ();
+ IDisposable persistentStorageLocationServiceRegistration;
bool added;
readonly object addLock = new object ();
@@ -59,9 +63,14 @@ namespace MonoDevelop.Ide.TypeSystem
this.workspace = workspace;
this.projectMap = projectMap;
this.projections = projections;
+ this.hackyCache = new HackyWorkspaceFilesCache (workspace.MonoDevelopSolution);
metadataHandler = new Lazy<MetadataReferenceHandler> (() => new MetadataReferenceHandler (workspace.MetadataReferenceManager, projectMap));
hostDiagnosticUpdateSource = new Lazy<HostDiagnosticUpdateSource> (() => new HostDiagnosticUpdateSource (workspace, workspace.compositionManager.GetExportedValue<IDiagnosticUpdateSourceRegistrationService> ()));
+
+ var persistentStorageLocationService = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> ();
+ if (workspace.MonoDevelopSolution != null)
+ persistentStorageLocationServiceRegistration = persistentStorageLocationService.RegisterPrimaryWorkspace (workspace.Id);
}
#region Solution mapping
@@ -80,26 +89,6 @@ namespace MonoDevelop.Ide.TypeSystem
return result;
}
}
-
- static async void OnSolutionModified (object sender, MonoDevelop.Projects.WorkspaceItemEventArgs args)
- {
- var sol = (MonoDevelop.Projects.Solution)args.Item;
- var workspace = await IdeApp.TypeSystemService.GetWorkspaceAsync (sol, CancellationToken.None);
- var solId = workspace.ProjectHandler.GetSolutionId (sol);
- if (solId == null)
- return;
-
- NotifySolutionModified (sol, solId, workspace);
- }
-
- static void NotifySolutionModified (MonoDevelop.Projects.Solution sol, SolutionId solId, MonoDevelopWorkspace workspace)
- {
- if (string.IsNullOrWhiteSpace (sol.BaseDirectory))
- return;
-
- var locService = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> ();
- locService.NotifyStorageLocationChanging (solId, sol.GetPreferencesDirectory ());
- }
#endregion
class SolutionData
@@ -117,51 +106,66 @@ namespace MonoDevelop.Ide.TypeSystem
if (fileName.IsNullOrEmpty)
fileName = new FilePath (p.Name + ".dll");
- var (references, projectReferences) = await metadataHandler.Value.CreateReferences (p, token);
- if (token.IsCancellationRequested)
- return null;
+ 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);
+ if (token.IsCancellationRequested)
+ return null;
- var sourceFiles = await p.GetSourceFilesAsync (config?.Selector).ConfigureAwait (false);
- if (token.IsCancellationRequested)
- return null;
+ sourceFiles = await p.GetSourceFilesAsync (config?.Selector).ConfigureAwait (false);
+ if (token.IsCancellationRequested)
+ return null;
+
+ analyzerFiles = await p.GetAnalyzerFilesAsync (config?.Selector).ConfigureAwait (false);
+
+ if (config != null)
+ hackyCache.Update (config, p, projectMap, sourceFiles, analyzerFiles, references, projectReferences);
+ }
- var analyzerFiles = await p.GetAnalyzerFilesAsync (config?.Selector).ConfigureAwait (false);
if (token.IsCancellationRequested)
return null;
var loader = workspace.Services.GetService<IAnalyzerService> ().GetLoader ();
- lock (workspace.updatingProjectDataLock) {
+ ProjectData projectData, oldProjectData;
+ List<DocumentInfo> mainDocuments, additionalDocuments;
+ try {
+ await workspace.LoadLock.WaitAsync ().ConfigureAwait (false);
//when reloading e.g. after a save, preserve document IDs
- var oldProjectData = projectMap.RemoveData (projectId);
- var projectData = projectMap.CreateData (projectId, references);
+ oldProjectData = projectMap.RemoveData (projectId);
+ projectData = projectMap.CreateData (projectId, references);
- var documents = CreateDocuments (projectData, p, token, sourceFiles, oldProjectData);
+ var documents = await CreateDocuments (projectData, p, token, sourceFiles, oldProjectData).ConfigureAwait (false);
if (documents == null)
return null;
- // TODO: Pass in the WorkspaceMetadataFileReferenceResolver
- var info = ProjectInfo.Create (
- projectId,
- VersionStamp.Create (),
- p.Name,
- fileName.FileNameWithoutExtension,
- (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp,
- p.FileName,
- fileName,
- cp?.CreateCompilationOptions (),
- cp?.CreateParseOptions (config),
- documents.Item1,
- projectReferences,
- references.Select (x => x.CurrentSnapshot),
- analyzerReferences: analyzerFiles.SelectAsArray (x => {
- var analyzer = new MonoDevelopAnalyzer (x, hostDiagnosticUpdateSource.Value, projectId, workspace, loader, LanguageNames.CSharp);
- return analyzer.GetReference ();
- }),
- additionalDocuments: documents.Item2
- );
- return info;
+ mainDocuments = documents.Item1;
+ additionalDocuments = documents.Item2;
+ } finally {
+ workspace.LoadLock.Release ();
}
+
+ // TODO: Pass in the WorkspaceMetadataFileReferenceResolver
+ var info = ProjectInfo.Create (
+ projectId,
+ VersionStamp.Create (),
+ p.Name,
+ fileName.FileNameWithoutExtension,
+ (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp,
+ p.FileName,
+ fileName,
+ cp?.CreateCompilationOptions (),
+ cp?.CreateParseOptions (config),
+ mainDocuments,
+ projectReferences,
+ references.Select (x => x.CurrentSnapshot),
+ analyzerReferences: analyzerFiles.SelectAsArray (x => {
+ var analyzer = new MonoDevelopAnalyzer (x, hostDiagnosticUpdateSource.Value, projectId, workspace, loader, LanguageNames.CSharp);
+ analyzersToDispose.Add (analyzer);
+ return analyzer.GetReference ();
+ }),
+ additionalDocuments: additionalDocuments
+ );
+ return info;
}
async Task<ConcurrentBag<ProjectInfo>> CreateProjectInfos (IEnumerable<MonoDevelop.Projects.Project> mdProjects, CancellationToken token)
@@ -268,9 +272,9 @@ namespace MonoDevelop.Ide.TypeSystem
lock (addLock) {
if (!added) {
added = true;
- solution.Modified += OnSolutionModified;
- NotifySolutionModified (solution, solutionId, workspace);
workspace.OnSolutionAdded (solutionInfo);
+ var service = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> ();
+ service.SetupSolution (workspace);
lock (workspace.generatedFiles) {
foreach (var generatedFile in workspace.generatedFiles) {
if (!workspace.IsDocumentOpen (generatedFile.Key.Id))
@@ -298,7 +302,7 @@ namespace MonoDevelop.Ide.TypeSystem
return node.Parser.CanGenerateAnalysisDocument (mimeType, f.BuildAction, p.SupportedLanguages);
}
- Tuple<List<DocumentInfo>, List<DocumentInfo>> CreateDocuments (ProjectData projectData, MonoDevelop.Projects.Project p, CancellationToken token, ImmutableArray<MonoDevelop.Projects.ProjectFile> sourceFiles, ProjectData oldProjectData)
+ async Task<Tuple<List<DocumentInfo>, List<DocumentInfo>>> CreateDocuments (ProjectData projectData, MonoDevelop.Projects.Project p, CancellationToken token, ImmutableArray<MonoDevelop.Projects.ProjectFile> sourceFiles, ProjectData oldProjectData)
{
var documents = new List<DocumentInfo> ();
// We don' add additionalDocuments anymore because they were causing slowdown of compilation generation
@@ -319,7 +323,7 @@ namespace MonoDevelop.Ide.TypeSystem
continue;
documents.Add (CreateDocumentInfo (solutionData, p.Name, projectData, f));
} else {
- foreach (var projectedDocument in GenerateProjections (f, projectData.DocumentData, p, oldProjectData, null)) {
+ foreach (var projectedDocument in await GenerateProjections (f, projectData.DocumentData, p, token, oldProjectData, null)) {
var projectedId = projectData.DocumentData.GetOrCreate (projectedDocument.FilePath, oldProjectData?.DocumentData);
if (!duplicates.Add (projectedId))
continue;
@@ -337,41 +341,44 @@ namespace MonoDevelop.Ide.TypeSystem
return Tuple.Create (documents, additionalDocuments);
}
- IEnumerable<DocumentInfo> GenerateProjections (MonoDevelop.Projects.ProjectFile f, DocumentMap documentMap, MonoDevelop.Projects.Project p, ProjectData oldProjectData, HashSet<DocumentId> duplicates)
+ async Task<List<DocumentInfo>> GenerateProjections (MonoDevelop.Projects.ProjectFile f, DocumentMap documentMap, MonoDevelop.Projects.Project p, CancellationToken token, ProjectData oldProjectData, HashSet<DocumentId> duplicates)
{
var mimeType = IdeServices.DesktopService.GetMimeTypeForUri (f.FilePath);
var node = IdeApp.TypeSystemService.GetTypeSystemParserNode (mimeType, f.BuildAction);
if (node == null || !node.Parser.CanGenerateProjection (mimeType, f.BuildAction, p.SupportedLanguages))
- yield break;
+ return new List<DocumentInfo> ();
+
var options = new ParseOptions {
FileName = f.FilePath,
Project = p,
Content = TextFileProvider.Instance.GetReadOnlyTextEditorData (f.FilePath),
};
- var generatedProjections = node.Parser.GenerateProjections (options);
+ var generatedProjections = await node.Parser.GenerateProjections (options, token);
var list = new List<Projection> ();
var entry = new ProjectionEntry {
File = f,
Projections = list,
};
- foreach (var projection in generatedProjections.Result) {
+ var result = new List<DocumentInfo> (generatedProjections.Count);
+ foreach (var projection in generatedProjections) {
list.Add (projection);
if (duplicates != null && !duplicates.Add (documentMap.GetOrCreate (projection.Document.FileName, oldProjectData?.DocumentData)))
continue;
var plainName = projection.Document.FileName.FileName;
var folders = GetFolders (p.Name, f);
- yield return DocumentInfo.Create (
+ result.Add(DocumentInfo.Create (
documentMap.GetOrCreate (projection.Document.FileName, oldProjectData?.DocumentData),
plainName,
folders,
SourceCodeKind.Regular,
TextLoader.From (TextAndVersion.Create (new MonoDevelopSourceText (projection.Document), VersionStamp.Create (), projection.Document.FileName)),
projection.Document.FileName,
- false
+ false)
);
}
projections.AddProjectionEntry (entry);
+ return result;
}
static DocumentInfo CreateDocumentInfo (SolutionData data, string projectName, ProjectData id, MonoDevelop.Projects.ProjectFile f)
@@ -396,6 +403,17 @@ namespace MonoDevelop.Ide.TypeSystem
{
return new [] { projectName }.Concat (f.ProjectVirtualPath.ParentDirectory.ToString ().Split (Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
}
+
+ public void Dispose ()
+ {
+ persistentStorageLocationServiceRegistration?.Dispose ();
+
+ solutionIdMap.Clear ();
+
+ foreach (var analyzer in analyzersToDispose)
+ analyzer.Dispose ();
+ analyzersToDispose.Clear ();
+ }
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
index 14f66e6d0d..c460b5d491 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
@@ -1,4 +1,4 @@
-//
+//
// MonoDevelopWorkspace.cs
//
// Author:
@@ -74,8 +74,9 @@ namespace MonoDevelop.Ide.TypeSystem
internal readonly WorkspaceId Id;
CancellationTokenSource src = new CancellationTokenSource ();
- bool disposed;
- readonly object updatingProjectDataLock = new object ();
+ bool disposed;
+
+ internal readonly SemaphoreSlim LoadLock = new SemaphoreSlim (1, 1);
Lazy<MonoDevelopMetadataReferenceManager> manager;
Lazy<MetadataReferenceHandler> metadataHandler;
ProjectionData Projections { get; }
@@ -83,7 +84,7 @@ namespace MonoDevelop.Ide.TypeSystem
ProjectDataMap ProjectMap { get; }
ProjectSystemHandler ProjectHandler { get; }
- public MonoDevelop.Projects.Solution MonoDevelopSolution { get; }
+ public MonoDevelop.Projects.Solution MonoDevelopSolution { get; private set; }
internal MonoDevelopMetadataReferenceManager MetadataReferenceManager => manager.Value;
@@ -260,14 +261,43 @@ namespace MonoDevelop.Ide.TypeSystem
protected internal override bool PartialSemanticsEnabled => backgroundCompiler != null;
+ // This is called by OnSolutionRemoved and on Dispose.
+ protected override void ClearSolutionData ()
+ {
+ if (MonoDevelopSolution != null) {
+ foreach (var prj in MonoDevelopSolution.GetAllProjects ()) {
+ ProjectMap.RemoveProject (prj);
+ UnloadMonoProject (prj);
+ }
+ }
+
+ base.ClearSolutionData ();
+ }
+
+ // This is called by OnProjectRemoved.
+ protected override void ClearProjectData (ProjectId projectId)
+ {
+ var actualProject = ProjectMap.RemoveProject (projectId);
+ UnloadMonoProject (actualProject);
+
+ base.ClearProjectData (projectId);
+ }
+
protected override void Dispose (bool finalize)
{
- base.Dispose (finalize);
if (disposed)
return;
- disposed = true;
+ disposed = true;
+
+ var cacheService = Services.GetService<IWorkspaceCacheService> ();
+ if (cacheService != null)
+ cacheService.CacheFlushRequested -= OnCacheFlushRequested;
+
+ var cacheHostService = Services.GetService<IProjectCacheHostService> () as IDisposable;
+ cacheHostService?.Dispose ();
+ ProjectHandler.Dispose ();
MetadataReferenceManager.ClearCache ();
TypeSystemService.EnableSourceAnalysis.Changed -= OnEnableSourceAnalysisChanged;
@@ -275,14 +305,8 @@ namespace MonoDevelop.Ide.TypeSystem
desktopService.MemoryMonitor.StatusChanged -= OnMemoryStatusChanged;
CancelLoad ();
-
- if (workspace != null)
+ if (workspace != null) {
workspace.ActiveConfigurationChanged -= HandleActiveConfigurationChanged;
-
- if (MonoDevelopSolution != null) {
- foreach (var prj in MonoDevelopSolution.GetAllProjects ()) {
- UnloadMonoProject (prj);
- }
}
var solutionCrawler = Services.GetService<ISolutionCrawlerRegistrationService> ();
@@ -290,8 +314,13 @@ namespace MonoDevelop.Ide.TypeSystem
if (backgroundCompiler != null) {
backgroundCompiler.Dispose ();
- backgroundCompiler = null; // PartialSemanticsEnabled will now return false
+ backgroundCompiler = null; // PartialSemanticsEnabled will now return false
}
+
+ base.Dispose (finalize);
+
+ // Do this at the end so solution removal from base disposal is done properly.
+ MonoDevelopSolution = null;
}
internal void InformDocumentTextChange (DocumentId id, SourceText text)
@@ -705,9 +734,9 @@ namespace MonoDevelop.Ide.TypeSystem
var projections = await node.Parser.GenerateProjections (options);
UpdateProjectionEntry (file, projections);
var projectId = GetProjectId (project);
- var projectdata = ProjectMap.GetData (projectId);
- foreach (var projected in projections) {
- OnDocumentTextChanged (projectdata.DocumentData.Get (projected.Document.FileName), new MonoDevelopSourceText (projected.Document), PreservationMode.PreserveValue);
+ var projectdata = ProjectMap.GetData (projectId);
+ foreach (var projected in projections) {
+ OnDocumentTextChanged (projectdata.DocumentData.Get (projected.Document.FileName), new MonoDevelopSourceText (projected.Document), PreservationMode.PreserveValue);
}
}
}
@@ -1070,54 +1099,49 @@ namespace MonoDevelop.Ide.TypeSystem
return project.GetAdditionalDocument (documentId);
}
- internal Task UpdateFileContent (string fileName, string text)
+ internal async Task UpdateFileContent (string fileName, string text)
{
SourceText newText = SourceText.From (text);
var tasks = new List<Task> ();
- lock (updatingProjectDataLock) {
- foreach (var kv in ProjectMap.projectDataMap) {
- var projectId = kv.Key;
- var docId = this.GetDocumentId (projectId, fileName);
- if (docId != null) {
- try {
- if (this.GetDocument (docId) != null) {
- base.OnDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity);
- } else if (this.GetAdditionalDocument (docId) != null) {
- base.OnAdditionalDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity);
- }
- } catch (Exception e) {
- LoggingService.LogWarning ("Roslyn error on text change", e);
- }
- }
- var monoProject = GetMonoProject (projectId);
- if (monoProject != null) {
- var pf = monoProject.GetProjectFile (fileName);
- if (pf != null) {
- var mimeType = desktopService.GetMimeTypeForUri (fileName);
- if (typeSystemService.CanParseProjections (monoProject, mimeType, fileName)) {
- var parseOptions = new ParseOptions { Project = monoProject, FileName = fileName, Content = new StringTextSource (text), BuildAction = pf.BuildAction };
- var task = typeSystemService.ParseProjection (parseOptions, mimeType);
- tasks.Add (task);
- }
- }
- }
- }
+ try {
+ await LoadLock.WaitAsync ();
+ foreach (var projectId in ProjectMap.GetProjectIds ()) {
+ var docId = this.GetDocumentId (projectId, fileName);
+ if (docId != null) {
+ try {
+ if (this.GetDocument (docId) != null) {
+ base.OnDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity);
+ } else if (this.GetAdditionalDocument (docId) != null) {
+ base.OnAdditionalDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity);
+ }
+ } catch (Exception e) {
+ LoggingService.LogWarning ("Roslyn error on text change", e);
+ }
+ }
+ var monoProject = GetMonoProject (projectId);
+ if (monoProject != null) {
+ var pf = monoProject.GetProjectFile (fileName);
+ if (pf != null) {
+ var mimeType = desktopService.GetMimeTypeForUri (fileName);
+ if (typeSystemService.CanParseProjections (monoProject, mimeType, fileName)) {
+ var parseOptions = new ParseOptions { Project = monoProject, FileName = fileName, Content = new StringTextSource (text), BuildAction = pf.BuildAction };
+ var task = typeSystemService.ParseProjection (parseOptions, mimeType);
+ tasks.Add (task);
+ }
+ }
+ }
+ }
+ } finally {
+ LoadLock.Release ();
}
- return Task.WhenAll (tasks);
+ await Task.WhenAll (tasks);
}
internal void RemoveProject (MonoDevelop.Projects.Project project)
{
var id = GetProjectId (project);
if (id != null) {
- foreach (var docId in GetOpenDocumentIds (id).ToList ()) {
- ClearOpenDocument (docId);
- }
-
- ProjectMap.RemoveProject (project, id);
- UnloadMonoProject (project);
-
OnProjectRemoved (id);
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs
index fbd88c1e4c..3124ad9a0d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs
@@ -84,16 +84,5 @@ namespace MonoDevelop.Ide.TypeSystem
}
return false;
}
-
- [Obsolete ("Use p.IsCompileable")]
- public static bool IsCompileableFile (ProjectFile file, out Microsoft.CodeAnalysis.SourceCodeKind sck)
- => IsCompileableFile (null, file, out sck);
-
- [Obsolete ("Use p.IsCompileable")]
- public static bool IsCompileableFile (MonoDevelop.Projects.Project p, ProjectFile file, out Microsoft.CodeAnalysis.SourceCodeKind sck)
- {
- sck = file.SourceCodeKind;
- return p.IsCompileable (file.FilePath);
- }
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs
index f3e04140e8..cf888d20a3 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs
@@ -285,11 +285,16 @@ namespace MonoDevelop.Ide.TypeSystem
var projectFile = options.Project.GetProjectFile (options.FileName);
if (projectFile != null) {
ws.UpdateProjectionEntry (projectFile, result.Projections);
- foreach (var projection in result.Projections) {
- var docId = ws.GetDocumentId (projectId, projection.Document.FileName);
- if (docId != null) {
- ws.InformDocumentTextChange (docId, new MonoDevelopSourceText (projection.Document));
+ await ws.LoadLock.WaitAsync ();
+ try {
+ foreach (var projection in result.Projections) {
+ var docId = ws.GetDocumentId (projectId, projection.Document.FileName);
+ if (docId != null) {
+ ws.InformDocumentTextChange (docId, new MonoDevelopSourceText (projection.Document));
+ }
}
+ } finally {
+ ws.LoadLock.Release ();
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/IWelcomeWindowProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/IWelcomeWindowProvider.cs
index f63f42dcc0..ffe880beb9 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/IWelcomeWindowProvider.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/IWelcomeWindowProvider.cs
@@ -32,12 +32,20 @@ namespace MonoDevelop.Ide.WelcomePage
{
public class WelcomeWindowShowOptions
{
- public WelcomeWindowShowOptions (bool closeSolution)
+ public WelcomeWindowShowOptions (bool closeSolution, bool userInitated = false)
{
CloseSolution = closeSolution;
+ UserInitiated = userInitated;
}
public bool CloseSolution { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user initiated the
+ /// operation (i.e. Window->Start Window menu)
+ /// </summary>
+ /// <value><c>true</c> if user initiated; otherwise, <c>false</c>.</value>
+ public bool UserInitiated { get; set; }
}
[TypeExtensionPoint]
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageCommands.cs
index 5d63d80e13..8ba73b2f08 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageCommands.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.WelcomePage/WelcomePageCommands.cs
@@ -29,9 +29,6 @@
using MonoDevelop.Core;
using MonoDevelop.Components.Commands;
-using MonoDevelop.Ide.Gui;
-using MonoDevelop.Ide;
-using System.Linq;
namespace MonoDevelop.Ide.WelcomePage
{
@@ -39,7 +36,7 @@ namespace MonoDevelop.Ide.WelcomePage
{
public static void Show ()
{
- WelcomePageService.ShowWelcomePageOrWindow (options: new WelcomeWindowShowOptions (false));
+ WelcomePageService.ShowWelcomePageOrWindow (options: new WelcomeWindowShowOptions (false, true));
}
protected override void Run()
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
index 779b9d6a47..069ee054b0 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj
@@ -46,8 +46,8 @@
<Reference Include="Microsoft.VisualStudio.Composition, Version=15.6.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<HintPath>..\..\..\packages\Microsoft.VisualStudio.Composition.15.6.36\lib\net45\Microsoft.VisualStudio.Composition.dll</HintPath>
</Reference>
- <Reference Include="Microsoft.VisualStudio.Threading, Version=15.6.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
- <HintPath>..\..\..\packages\Microsoft.VisualStudio.Threading.15.6.46\lib\net45\Microsoft.VisualStudio.Threading.dll</HintPath>
+ <Reference Include="Microsoft.VisualStudio.Threading, Version=15.8.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\..\..\packages\Microsoft.VisualStudio.Threading.15.8.209\lib\net45\Microsoft.VisualStudio.Threading.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.Validation, Version=15.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<HintPath>..\..\..\packages\Microsoft.VisualStudio.Validation.15.3.32\lib\net45\Microsoft.VisualStudio.Validation.dll</HintPath>
@@ -135,6 +135,7 @@
<Reference Include="YamlDotNet">
<HintPath>..\..\..\packages\YamlDotNet.Signed.5.2.1\lib\net45\YamlDotNet.dll</HintPath>
</Reference>
+ <Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MonoDevelop.Core\MonoDevelop.Core.csproj">
@@ -4276,6 +4277,9 @@
<Compile Include="MonoDevelop.Ide.Extensions\StartupInfo.cs" />
<Compile Include="MonoDevelop.Ide\IdeServices.cs" />
<Compile Include="MonoDevelop.Ide.Tasks\TaskSeverity.cs" />
+ <Compile Include="MonoDevelop.Ide.Projects\NewSolutionRunConfigurationDialog.cs" />
+ <Compile Include="MonoDevelop.Ide.TypeSystem\HackyWorkspaceFilesCache.cs" />
+ <Compile Include="MonoDevelop.Ide.Gui.Dialogs\NewFolderDialog.cs" />
</ItemGroup>
<ItemGroup>
<Data Include="options\DefaultEditingLayout.xml" />
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs
index 27fb9ebda6..1bae6697f8 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs
@@ -1,4 +1,4 @@
-//
+//
// IdeApp.DesktopService.cs
//
// Author:
@@ -144,13 +144,6 @@ namespace MonoDevelop.Ide
get { return PlatformService.Name; }
}
- [Obsolete]
- public string DefaultControlLeftRightBehavior {
- get {
- return PlatformService.DefaultControlLeftRightBehavior;
- }
- }
-
public void ShowUrl (string url)
{
PlatformService.ShowUrl (url);
@@ -364,6 +357,16 @@ namespace MonoDevelop.Ide
PlatformService.GrabDesktopFocus (window);
}
+ public Window GetParentForModalWindow ()
+ {
+ return PlatformService.GetParentForModalWindow ();
+ }
+
+ public Window GetFocusedTopLevelWindow ()
+ {
+ return PlatformService.GetFocusedTopLevelWindow ();
+ }
+
public void FocusWindow (Window window)
{
if (window != null)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs
index 4513095e1a..d2990eed1b 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdePreferences.cs
@@ -65,7 +65,14 @@ namespace MonoDevelop.Ide
Compact,
VeryCompact
}
-
+
+ public enum OnStartupBehaviour
+ {
+ ShowStartWindow,
+ LoadPreviousSolution,
+ EmptyEnvironment
+ }
+
public class IdePreferences
{
internal IdePreferences ()
@@ -95,7 +102,7 @@ namespace MonoDevelop.Ide
public readonly ConfigurationProperty<string> UserInterfaceLanguage = Runtime.Preferences.UserInterfaceLanguage;
public readonly ConfigurationProperty<string> UserInterfaceThemeName = ConfigurationProperty.Create ("MonoDevelop.Ide.UserInterfaceTheme", Platform.IsLinux ? "" : "Light");
public readonly ConfigurationProperty<WorkbenchCompactness> WorkbenchCompactness = ConfigurationProperty.Create ("MonoDevelop.Ide.WorkbenchCompactness", MonoDevelop.Ide.WorkbenchCompactness.Normal);
- public readonly ConfigurationProperty<bool> LoadPrevSolutionOnStartup = ConfigurationProperty.Create ("SharpDevelop.LoadPrevProjectOnStartup", false);
+ public readonly ConfigurationProperty<OnStartupBehaviour> StartupBehaviour = ConfigurationProperty.Create ("MonoDevelop.Ide.StartupBehaviour", OnStartupBehaviour.ShowStartWindow);
public readonly ConfigurationProperty<bool> CreateFileBackupCopies = ConfigurationProperty.Create ("SharpDevelop.CreateBackupCopy", false);
public ConfigurationProperty<bool> LoadDocumentUserProperties => IdeApp.Workbench.DocumentManager.Preferences.LoadDocumentUserProperties;
public readonly ConfigurationProperty<bool> EnableDocumentSwitchDialog = ConfigurationProperty.Create ("MonoDevelop.Core.Gui.EnableDocumentSwitchDialog", true);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs
index f02398a7d7..33d1dc875d 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs
@@ -1,4 +1,4 @@
-//
+//
// IdeStartup.cs
//
// Author:
@@ -67,6 +67,7 @@ namespace MonoDevelop.Ide
static Stopwatch timeToCodeTimer = new Stopwatch ();
static Dictionary<string, long> sectionTimings = new Dictionary<string, long> ();
static bool hideWelcomePage;
+ static StartupInfo startupInfo;
static TimeToCodeMetadata ttcMetadata;
@@ -121,7 +122,7 @@ namespace MonoDevelop.Ide
IdeTheme.InitializeGtk (BrandingService.ApplicationName, ref args);
- var startupInfo = new StartupInfo (options, args);
+ startupInfo = new StartupInfo (options, args);
IdeApp.Customizer = options.IdeCustomizer ?? new IdeCustomizer ();
try {
@@ -282,8 +283,9 @@ namespace MonoDevelop.Ide
// XBC #33699
Counters.Initialization.Trace ("Initializing IdeApp");
- hideWelcomePage = startupInfo.HasFiles;
+ hideWelcomePage = startupInfo.HasFiles || IdeApp.Preferences.StartupBehaviour.Value != OnStartupBehaviour.ShowStartWindow;
await IdeApp.Initialize (monitor);
+
sectionTimings ["AppInitialization"] = startupSectionTimer.ElapsedMilliseconds;
startupSectionTimer.Restart ();
@@ -303,7 +305,7 @@ namespace MonoDevelop.Ide
// load previous combine
RecentFile openedProject = null;
- if (IdeApp.Preferences.LoadPrevSolutionOnStartup && !startupInfo.HasSolutionFile && !IdeApp.Workspace.WorkspaceItemIsOpening && !IdeApp.Workspace.IsOpen) {
+ if (IdeApp.Preferences.StartupBehaviour.Value == OnStartupBehaviour.LoadPreviousSolution && !startupInfo.HasSolutionFile && !IdeApp.Workspace.WorkspaceItemIsOpening && !IdeApp.Workspace.IsOpen) {
openedProject = IdeServices.DesktopService.RecentFiles.MostRecentlyUsedProject;
if (openedProject != null) {
var metadata = GetOpenWorkspaceOnStartupMetadata ();
@@ -463,19 +465,21 @@ namespace MonoDevelop.Ide
WelcomePage.WelcomePageService.ShowWelcomePage ();
Counters.Initialization.Trace ("Showed welcome page");
IdeApp.Workbench.Show ();
+ } else if (hideWelcomePage && !startupInfo.OpenedFiles) {
+ IdeApp.Workbench.Show ();
}
return false;
}
- void CreateStartupMetadata (StartupInfo startupInfo, Dictionary<string, long> timings)
+ void CreateStartupMetadata (StartupInfo si, Dictionary<string, long> timings)
{
var result = IdeServices.DesktopService.PlatformTelemetry;
if (result == null) {
return;
}
- var startupMetadata = GetStartupMetadata (startupInfo, result, timings);
+ var startupMetadata = GetStartupMetadata (si, result, timings);
Counters.Startup.Inc (startupMetadata);
if (ttcMetadata != null) {
@@ -880,7 +884,8 @@ namespace MonoDevelop.Ide
IsInitialRunAfterUpgrade = IdeApp.IsInitialRunAfterUpgrade,
TimeSinceMachineStart = (long)platformDetails.TimeSinceMachineStart.TotalMilliseconds,
TimeSinceLogin = (long)platformDetails.TimeSinceLogin.TotalMilliseconds,
- Timings = timings
+ Timings = timings,
+ StartupBehaviour = IdeApp.Preferences.StartupBehaviour.Value
};
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs
index 5a41930ccf..79175fd06b 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs
@@ -134,7 +134,7 @@ namespace MonoDevelop.Ide
static Window defaultRootWindow;
public static Window RootWindow {
get {
- if (WelcomePageService.WelcomePageVisible)
+ if (WelcomePageService.WelcomeWindowVisible)
return WelcomePageService.WelcomeWindow;
return defaultRootWindow;
}
@@ -322,7 +322,7 @@ namespace MonoDevelop.Ide
// if dialog is modal, make sure it's parented on any existing modal dialog
Gtk.Dialog dialog = dlg;
if (dialog.Modal) {
- parent = GetDefaultModalParent ();
+ parent = IdeServices.DesktopService.GetParentForModalWindow ();
}
//ensure the dialog has a parent
@@ -355,10 +355,13 @@ namespace MonoDevelop.Ide
}).Wait ();
#endif
+ var initialRootWindow = Xwt.MessageDialog.RootWindow;
try {
+ Xwt.MessageDialog.RootWindow = Xwt.Toolkit.CurrentEngine.WrapWindow (dialog);
IdeApp.DisableIdleActions ();
return GtkWorkarounds.RunDialogWithNotification (dialog);
} finally {
+ Xwt.MessageDialog.RootWindow = initialRootWindow;
IdeApp.EnableIdleActions ();
}
}
@@ -381,28 +384,6 @@ namespace MonoDevelop.Ide
dialog.Shown -= HandleShown;
}
#endif
-
- /// <summary>
- /// Gets a default parent for modal dialogs.
- /// </summary>
- public static Window GetDefaultModalParent ()
- {
- foreach (Gtk.Window w in Gtk.Window.ListToplevels ())
- if (w.Visible && w.HasToplevelFocus && w.Modal)
- return w;
- return GetFocusedToplevel ();
- }
-
- static Window GetFocusedToplevel ()
- {
- // TODO: support native toplevels
- // use the first "normal" toplevel window (skipping docks, popups, etc.) or the main IDE window
- Window gtkToplevel = Gtk.Window.ListToplevels ().FirstOrDefault (w => w.HasToplevelFocus &&
- (w.TypeHint == Gdk.WindowTypeHint.Dialog ||
- w.TypeHint == Gdk.WindowTypeHint.Normal ||
- w.TypeHint == Gdk.WindowTypeHint.Utility));
- return gtkToplevel ?? RootWindow;
- }
/// <summary>
/// Positions a dialog relative to its parent on platforms where default placement is known to be poor.
@@ -413,39 +394,57 @@ namespace MonoDevelop.Ide
if (!Platform.IsMac)
return;
- Gtk.Window child = childControl;
- //modal windows should always be placed o top of existing modal windows
- if (child.Modal)
- parent = GetDefaultModalParent ();
-
- //else center on the focused toplevel
- if (parent == null)
- parent = GetFocusedToplevel ();
+ if (parent == null) {
+ if (childControl.nativeWidget is Gtk.Window gtkChild) {
+ if (gtkChild.Modal)
+ parent = IdeServices.DesktopService.GetParentForModalWindow ();
+ }
+ }
- if (parent != null)
- CenterWindow (child, parent);
+ CenterWindow (childControl, parent);
}
-
+
/// <summary>Centers a window relative to its parent.</summary>
static void CenterWindow (Window childControl, Window parentControl)
{
- // TODO: support cross-toolkit centering
- if (!(parentControl.nativeWidget is Gtk.Window)) {
- // FIXME: center on screen if no Gtk parent given for a Gtk dialog
- if (childControl.nativeWidget is Gtk.Window gtkChild)
- gtkChild.WindowPosition = Gtk.WindowPosition.Center;
+ var gtkChild = childControl?.nativeWidget as Gtk.Window;
+ var gtkParent = parentControl?.nativeWidget as Gtk.Window;
+#if MAC
+ var nsChild = childControl?.nativeWidget as NSWindow;
+ var nsParent = parentControl?.nativeWidget as NSWindow ?? parentControl;
+
+ if (nsChild != null) {
+ if (nsParent == null || !nsParent.IsVisible) {
+ nsChild.Center ();
+ } else {
+ int x = (int)(Math.Max (0, nsParent.Frame.Left + (nsParent.Frame.Width - nsChild.Frame.Width) / 2));
+ int y = (int)(Math.Max (0, nsParent.Frame.Top + (nsParent.Frame.Height - nsChild.Frame.Height) / 2));
+ nsChild.SetFrame (new CoreGraphics.CGRect (x, y, nsChild.Frame.Width, nsChild.Frame.Height), true);
+ }
+
return;
}
- Gtk.Window child = childControl;
- Gtk.Window parent = parentControl;
- child.Child.Show ();
- int w, h, winw, winh, x, y, winx, winy;
- child.GetSize (out w, out h);
- parent.GetSize (out winw, out winh);
- parent.GetPosition (out winx, out winy);
- x = Math.Max (0, (winw - w) /2) + winx;
- y = Math.Max (0, (winh - h) /2) + winy;
- child.Move (x, y);
+#endif
+ if (gtkChild != null) {
+ gtkChild.Child.Show ();
+ int x, y;
+ gtkChild.GetSize (out var w, out var h);
+ if (gtkParent != null) {
+ gtkParent.GetSize (out var winw, out var winh);
+ gtkParent.GetPosition (out var winx, out var winy);
+ x = Math.Max (0, (winw - w) / 2) + winx;
+ y = Math.Max (0, (winh - h) / 2) + winy;
+ gtkChild.Move (x, y);
+#if MAC
+ } else if (nsParent != null) {
+ x = (int)(Math.Max (0, nsParent.Frame.Left + (nsParent.Frame.Width - w) / 2));
+ y = (int)(Math.Max (0, nsParent.Frame.Top + (nsParent.Frame.Height - h) / 2));
+ gtkChild.Move (x, y);
+#endif
+ } else {
+ gtkChild.SetPosition (Gtk.WindowPosition.Center);
+ }
+ }
}
public static AlertButton GenericAlert (string icon, string primaryText, string secondaryText, params AlertButton[] buttons)
@@ -560,7 +559,7 @@ namespace MonoDevelop.Ide
return messageService.GetTextResponse (parent, question, caption, initialValue, isPassword);
}
- #region Internal GUI object
+#region Internal GUI object
static InternalMessageService mso;
static InternalMessageService messageService
{
@@ -577,7 +576,7 @@ namespace MonoDevelop.Ide
public AlertButton GenericAlert (Window parent, MessageDescription message)
{
var dialog = new AlertDialog (message) {
- TransientFor = parent ?? GetDefaultModalParent ()
+ TransientFor = parent ?? IdeServices.DesktopService.GetParentForModalWindow ()
};
return dialog.Run ();
}
@@ -589,14 +588,14 @@ namespace MonoDevelop.Ide
Caption = caption,
Value = initialValue,
IsPassword = isPassword,
- TransientFor = parent ?? GetDefaultModalParent ()
+ TransientFor = parent ?? IdeServices.DesktopService.GetParentForModalWindow ()
};
if (dialog.Run ())
return dialog.Value;
return null;
}
}
- #endregion
+#endregion
}
public class MessageDescription
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs
index e20d881b26..d9d578d147 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs
@@ -1,4 +1,4 @@
-//
+//
// ProjectOperations.cs
//
// Author:
@@ -49,22 +49,23 @@ using MonoDevelop.Projects;
using MonoDevelop.Projects.MSBuild;
using ExecutionContext = MonoDevelop.Projects.ExecutionContext;
using MonoDevelop.Ide.Gui.Documents;
+using MonoDevelop.Ide.Projects.OptionPanels;
namespace MonoDevelop.Ide
{
/// <summary>
/// This is the basic interface to the workspace.
- /// </summary>
+ /// </summary>
[DefaultServiceImplementation]
public partial class ProjectOperations: Service
{
- RootWorkspace workspace;
-
- AsyncOperation<BuildResult> currentBuildOperation = new AsyncOperation<BuildResult> (Task.FromResult (BuildResult.CreateSuccess ()), null);
+ static AsyncOperation<BuildResult> DefaultBuildOperation = new AsyncOperation<BuildResult> (Task.FromResult (BuildResult.CreateSuccess ()), null);
+ AsyncOperation<BuildResult> currentBuildOperation = DefaultBuildOperation;
MultipleAsyncOperation currentRunOperation = MultipleAsyncOperation.CompleteMultipleOperation;
IBuildTarget currentBuildOperationOwner;
List<IBuildTarget> currentRunOperationOwners = new List<IBuildTarget> ();
-
+ RootWorkspace workspace;
+
SelectReferenceDialog selDialog = null;
internal ProjectOperations ()
@@ -148,7 +149,13 @@ namespace MonoDevelop.Ide
public AsyncOperation CurrentRunOperation {
get { return currentRunOperation; }
set { AddRunOperation (value); }
- }
+ }
+
+ void ResetCurrentBuildOperation ()
+ {
+ currentBuildOperation = DefaultBuildOperation;
+ currentBuildOperationOwner = null;
+ }
public void AddRunOperation (AsyncOperation runOperation)
{
@@ -340,17 +347,18 @@ namespace MonoDevelop.Ide
public async void Export (IMSBuildFileObject item, MSBuildFileFormat format)
{
- ExportSolutionDialog dlg = new ExportSolutionDialog (item, format);
-
+ ExportSolutionDialog dlg = null;
try {
+ dlg = new ExportSolutionDialog (item, format);
+
if (MessageService.RunCustomDialog (dlg) == (int) Gtk.ResponseType.Ok) {
using (ProgressMonitor monitor = IdeServices.ProgressMonitorManager.GetToolOutputProgressMonitor (true)) {
await Services.ProjectService.Export (monitor, item.FileName, dlg.TargetFolder, dlg.Format);
}
}
} finally {
- dlg.Destroy ();
- dlg.Dispose ();
+ dlg?.Destroy ();
+ dlg?.Dispose ();
}
}
@@ -654,6 +662,35 @@ namespace MonoDevelop.Ide
}
}
}
+
+ public async void ShowRunConfiguration (Solution solution, MultiItemSolutionRunConfiguration runConfiguration)
+ {
+ var optionsDialog = new CombineOptionsDialog (IdeApp.Workbench.RootWindow, solution);
+ optionsDialog.CurrentConfig = IdeApp.Workspace.ActiveConfigurationId;
+ try {
+ optionsDialog.SelectPanel ("Run");
+ if (runConfiguration != null) {
+ void shownCallback (object sender, EventArgs args)
+ {
+ var panel = optionsDialog.GetPanel<SolutionRunConfigurationsPanel> ("General");
+ if (panel != null) {
+ panel.ShowConfiguration (runConfiguration);
+ }
+ optionsDialog.Shown -= shownCallback;
+ }
+
+ optionsDialog.Shown += shownCallback;
+ }
+
+ if (MessageService.RunCustomDialog (optionsDialog) == (int)Gtk.ResponseType.Ok) {
+ await SaveAsync (solution);
+ await IdeApp.Workspace.SavePreferences (solution);
+ }
+ } finally {
+ optionsDialog.Destroy ();
+ optionsDialog.Dispose ();
+ }
+ }
public Task<bool> NewSolution ()
{
@@ -1167,21 +1204,17 @@ namespace MonoDevelop.Ide
var t = CleanAsync (entry, monitor, tt, false, operationContext);
- t = t.ContinueWith (ta => {
- currentBuildOperationOwner = null;
- return ta.Result;
- });
-
var op = new AsyncOperation<BuildResult> (t, cs);
currentBuildOperation = op;
- currentBuildOperationOwner = entry;
+ currentBuildOperationOwner = entry;
+
+ t.ContinueWith (ta => { ResetCurrentBuildOperation (); });
+ return op;
}
catch {
tt.End ();
throw;
}
-
- return currentBuildOperation;
}
async Task<BuildResult> CleanAsync (IBuildTarget entry, ProgressMonitor monitor, ITimeTracker tt, bool isRebuilding, OperationContext operationContext)
@@ -1306,14 +1339,13 @@ namespace MonoDevelop.Ide
ProgressMonitor monitor = IdeServices.ProgressMonitorManager.GetRebuildProgressMonitor ().WithCancellationSource (cs);
var t = RebuildAsync (entry, monitor, operationContext);
- t = t.ContinueWith (ta => {
- currentBuildOperationOwner = null;
- return ta.Result;
- });
-
- var op = new AsyncOperation<BuildResult> (t, cs);
- return currentBuildOperation = op;
+ var op = new AsyncOperation<BuildResult> (t, cs);
+ currentBuildOperation = op;
+ currentBuildOperationOwner = entry;
+
+ t.ContinueWith (ta => { ResetCurrentBuildOperation (); });
+ return op;
}
async Task<BuildResult> RebuildAsync (IBuildTarget entry, ProgressMonitor monitor, OperationContext operationContext)
@@ -1604,15 +1636,19 @@ namespace MonoDevelop.Ide
cs = CancellationTokenSource.CreateLinkedTokenSource (cs.Token, cancellationToken.Value);
ProgressMonitor monitor = IdeServices.ProgressMonitorManager.GetBuildProgressMonitor ().WithCancellationSource (cs);
BeginBuild (monitor, tt, false);
- var t = BuildSolutionItemAsync (entry, monitor, tt, skipPrebuildCheck, operationContext);
- currentBuildOperation = new AsyncOperation<BuildResult> (t, cs);
- currentBuildOperationOwner = entry;
- t.ContinueWith ((ta) => currentBuildOperationOwner = null);
+
+ var t = BuildSolutionItemAsync (entry, monitor, tt, skipPrebuildCheck, operationContext);
+
+ var op = new AsyncOperation<BuildResult> (t, cs);
+ currentBuildOperation = op;
+ currentBuildOperationOwner = entry;
+
+ t.ContinueWith (ta => { ResetCurrentBuildOperation (); });
+ return op;
} catch {
tt.End ();
throw;
}
- return currentBuildOperation;
}
async Task<BuildResult> BuildSolutionItemAsync (IBuildTarget entry, ProgressMonitor monitor, ITimeTracker tt, bool skipPrebuildCheck = false, OperationContext operationContext = null)
@@ -1739,7 +1775,6 @@ namespace MonoDevelop.Ide
tt.Trace ("Begin reporting build result");
try {
if (result != null) {
- var lastResult = result;
monitor.Log.WriteLine ();
var msg = GettextCatalog.GetString (
@@ -1759,7 +1794,7 @@ namespace MonoDevelop.Ide
if (monitor.CancellationToken.IsCancellationRequested) {
monitor.ReportError (GettextCatalog.GetString ("Build canceled."), null);
- } else if (result.ErrorCount == 0 && result.WarningCount == 0 && lastResult.FailedBuildCount == 0) {
+ } else if (result.ErrorCount == 0 && result.WarningCount == 0 && result.FailedBuildCount == 0) {
monitor.ReportSuccess (GettextCatalog.GetString ("Build successful."));
} else if (result.ErrorCount == 0 && result.WarningCount > 0) {
monitor.ReportWarning(GettextCatalog.GetString("Build: ") + errorString + ", " + warningString);
@@ -1769,7 +1804,7 @@ namespace MonoDevelop.Ide
monitor.ReportError(GettextCatalog.GetString("Build failed."), null);
}
tt.Trace ("End build event");
- OnEndBuild (monitor, lastResult.FailedBuildCount == 0, lastResult, entry as SolutionFolderItem);
+ OnEndBuild (monitor, result.FailedBuildCount == 0, result, entry as SolutionFolderItem);
} else {
tt.Trace ("End build event");
OnEndBuild (monitor, false);
@@ -2550,13 +2585,14 @@ namespace MonoDevelop.Ide
public void AddOperation (AsyncOperation op)
{
Operations.Add (op);
- op.Task.ContinueWith (CheckForCompletion);
+ op.Task.ContinueWith (t => CheckForCompletion (t));
}
- void CheckForCompletion (Task obj)
+ void CheckForCompletion (Task obj)
{
- if (Operations.All (op => op.IsCompleted))
- TaskCompletionSource.SetResult (0);
+ if (Operations.All (op => op.IsCompleted)) {
+ TaskCompletionSource.SetResult (0);
+ }
}
void MultiCancel ()
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs
index dd7d7e6166..87f4b79ef3 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Services.cs
@@ -60,10 +60,7 @@ namespace MonoDevelop.Ide
internal static TimerCounter SaveAllTimer = InstrumentationService.CreateTimerCounter ("Save all documents", "IDE", id:"Ide.Shell.SaveAll");
internal static TimerCounter CloseWorkspaceTimer = InstrumentationService.CreateTimerCounter ("Workspace closed", "IDE", id:"Ide.Shell.CloseWorkspace");
internal static Counter<StartupMetadata> Startup = InstrumentationService.CreateCounter<StartupMetadata> ("IDE Startup", "IDE", id:"Ide.Startup");
- internal static TimerCounter CompositionAddinLoad = InstrumentationService.CreateTimerCounter ("MEF Composition Addin Load", "IDE", id: "Ide.Startup.Composition.ExtensionLoad");
- internal static TimerCounter CompositionDiscovery = InstrumentationService.CreateTimerCounter ("MEF Composition From Discovery", "IDE", id:"Ide.Startup.Composition.Discovery");
- internal static TimerCounter CompositionCacheControl = InstrumentationService.CreateTimerCounter ("MEF Composition Control Cache", "IDE", id: "Ide.Startup.Composition.ControlCache");
- internal static TimerCounter CompositionCache = InstrumentationService.CreateTimerCounter ("MEF Composition From Cache", "IDE", id: "Ide.Startup.Composition.Cache");
+ internal static TimerCounter<CompositionLoadMetadata> CompositionLoad = InstrumentationService.CreateTimerCounter<CompositionLoadMetadata> ("MEF Composition Load", "IDE", id: "Ide.Startup.Composition.Load");
internal static TimerCounter CompositionSave = InstrumentationService.CreateTimerCounter ("MEF Composition Save", "IDE", id: "Ide.CompositionSave");
internal static TimerCounter AnalysisTimer = InstrumentationService.CreateTimerCounter ("Code Analysis", "IDE", id:"Ide.CodeAnalysis");
internal static TimerCounter ProcessCodeCompletion = InstrumentationService.CreateTimerCounter ("Process Code Completion", "IDE", id: "Ide.ProcessCodeCompletion", logMessages:false);
@@ -153,6 +150,11 @@ namespace MonoDevelop.Ide
get => GetProperty<Dictionary<string, long>> ();
set => SetProperty (value);
}
+
+ public OnStartupBehaviour StartupBehaviour {
+ get => GetProperty<OnStartupBehaviour> ();
+ set => SetProperty (value);
+ }
}
class TimeToCodeMetadata : CounterMetadata
@@ -210,5 +212,32 @@ namespace MonoDevelop.Ide
set => SetProperty (value);
}
}
+
+ class CompositionLoadMetadata : CounterMetadata
+ {
+ public CompositionLoadMetadata ()
+ {
+ }
+
+ public CompositionLoadMetadata (Dictionary<string, long> timings)
+ {
+ Timings = timings;
+ }
+
+ public Dictionary<string, long> Timings {
+ get => GetProperty<Dictionary<string, long>> ();
+ set => SetProperty (value);
+ }
+
+ public bool ValidCache {
+ get => GetProperty<bool> ();
+ set => SetProperty (value);
+ }
+
+ public long Duration {
+ get => GetProperty<long> ();
+ set => SetProperty (value);
+ }
+ }
}
diff --git a/main/src/core/MonoDevelop.Ide/gtkrc b/main/src/core/MonoDevelop.Ide/gtkrc
index 0144ec125d..1dbc980702 100644
--- a/main/src/core/MonoDevelop.Ide/gtkrc
+++ b/main/src/core/MonoDevelop.Ide/gtkrc
@@ -1,5 +1,5 @@
# Xamarin Studio GTK Theme
-# Copyright 2012-2016 Xamarin Inc.
+# Copyright 2012-2019 Xamarin Inc.
# Authors:
# Christian Kellner <christian.kellner@lanedo.com>
# Carlos Garnacho <carlos.garnacho@lanedo.com>
@@ -13,6 +13,7 @@ gtk-color-scheme =
fg_color: #000
base_color: #fff
text_color: #000
+dim_color: #767676
selected_bg_color: #649dd6
selected_fg_color: #fff
tooltip_bg_color: #fff9e5
@@ -42,13 +43,13 @@ style "default" {
fg[NORMAL] = @fg_color
fg[PRELIGHT] = @fg_color
fg[SELECTED] = @selected_fg_color
- fg[INSENSITIVE] = darker (@bg_color)
+ fg[INSENSITIVE] = @dim_color
fg[ACTIVE] = @fg_color
text[NORMAL] = @fg_color
text[PRELIGHT] = @fg_color
text[SELECTED] = @selected_fg_color
- text[INSENSITIVE] = darker (@bg_color)
+ text[INSENSITIVE] = @dim_color
text[ACTIVE] = @fg_color
base[NORMAL] = @base_color
diff --git a/main/src/core/MonoDevelop.Ide/gtkrc.mac b/main/src/core/MonoDevelop.Ide/gtkrc.mac
index fbfc7736b6..84972e73f9 100644
--- a/main/src/core/MonoDevelop.Ide/gtkrc.mac
+++ b/main/src/core/MonoDevelop.Ide/gtkrc.mac
@@ -1,5 +1,5 @@
# Xamarin Studio Light Mac GTK Theme
-# Copyright 2012-2016 Xamarin Inc.
+# Copyright 2012-2019 Xamarin Inc.
# Authors:
# Christian Kellner <christian.kellner@lanedo.com>
# Carlos Garnacho <carlos.garnacho@lanedo.com>
@@ -14,7 +14,8 @@ fg_color: #272727
base_color: #fff
text_color: #272727
link_color: #175fde
-selected_bg_color: #5189ed
+dim_color: #767676
+selected_bg_color: #2862d9
selected_fg_color: #fff
tooltip_bg_color: #f2f2f2
tooltip_fg_color: #272727
@@ -49,13 +50,13 @@ style "default" {
fg[NORMAL] = @fg_color
fg[PRELIGHT] = @fg_color
fg[SELECTED] = @selected_fg_color
- fg[INSENSITIVE] = darker (@bg_color)
+ fg[INSENSITIVE] = @dim_color
fg[ACTIVE] = @fg_color
text[NORMAL] = @fg_color
text[PRELIGHT] = @fg_color
text[SELECTED] = @selected_fg_color
- text[INSENSITIVE] = darker (@bg_color)
+ text[INSENSITIVE] = @dim_color
text[ACTIVE] = @fg_color
base[NORMAL] = @base_color
diff --git a/main/src/core/MonoDevelop.Ide/gtkrc.mac-dark b/main/src/core/MonoDevelop.Ide/gtkrc.mac-dark
index f107bcc673..687ae9b48a 100644
--- a/main/src/core/MonoDevelop.Ide/gtkrc.mac-dark
+++ b/main/src/core/MonoDevelop.Ide/gtkrc.mac-dark
@@ -1,5 +1,5 @@
# Xamarin Studio Dark Mac GTK Theme
-# Copyright 2012-2016 Xamarin Inc.
+# Copyright 2012-2019 Xamarin Inc.
# Authors:
# Christian Kellner <christian.kellner@lanedo.com>
# Carlos Garnacho <carlos.garnacho@lanedo.com>
@@ -14,8 +14,8 @@ fg_color: #d7d7d7
base_color: #404040
text_color: #d7d7d7
link_color: #ace2ff
-dim_color: #777777
-selected_bg_color: #5189ed
+dim_color: #ababab
+selected_bg_color: #2257c9
selected_fg_color: #fff
tooltip_bg_color: #5a5a5a
tooltip_fg_color: #d2d5cd
diff --git a/main/src/core/MonoDevelop.Ide/gtkrc.win32 b/main/src/core/MonoDevelop.Ide/gtkrc.win32
index f0e6db4d26..5ebcd9b1db 100644
--- a/main/src/core/MonoDevelop.Ide/gtkrc.win32
+++ b/main/src/core/MonoDevelop.Ide/gtkrc.win32
@@ -1,5 +1,5 @@
# Xamarin Studio Light Windows GTK Theme
-# Copyright 2012-2016 Xamarin Inc.
+# Copyright 2012-2019 Xamarin Inc.
# Authors:
# Christian Kellner <christian.kellner@lanedo.com>
# Carlos Garnacho <carlos.garnacho@lanedo.com>
@@ -14,6 +14,7 @@ fg_color: #000
base_color: #fff
text_color: #000
link_color: #175fde
+dim_color: #767676
selected_bg_color: #cce8ff
selected_fg_color: #000
tooltip_bg_color: #f2f2f2
@@ -48,13 +49,13 @@ style "default" {
fg[NORMAL] = @fg_color
fg[PRELIGHT] = @fg_color
fg[SELECTED] = @selected_fg_color
- fg[INSENSITIVE] = darker (@bg_color)
+ fg[INSENSITIVE] = @dim_color
fg[ACTIVE] = @fg_color
text[NORMAL] = @fg_color
text[PRELIGHT] = @fg_color
text[SELECTED] = @selected_fg_color
- text[INSENSITIVE] = darker (@bg_color)
+ text[INSENSITIVE] = @dim_color
text[ACTIVE] = @fg_color
base[NORMAL] = @base_color
diff --git a/main/src/core/MonoDevelop.Ide/gtkrc.win32-dark b/main/src/core/MonoDevelop.Ide/gtkrc.win32-dark
index 35836699c5..074f97a9d8 100644
--- a/main/src/core/MonoDevelop.Ide/gtkrc.win32-dark
+++ b/main/src/core/MonoDevelop.Ide/gtkrc.win32-dark
@@ -1,5 +1,5 @@
# Xamarin Studio Dark Windows GTK Theme
-# Copyright 2012-2016 Xamarin Inc.
+# Copyright 2012-2019 Xamarin Inc.
# Authors:
# Christian Kellner <christian.kellner@lanedo.com>
# Carlos Garnacho <carlos.garnacho@lanedo.com>
@@ -14,7 +14,7 @@ fg_color: #d7d7d7
base_color: #404040
text_color: #d7d7d7
link_color: #ace2ff
-dim_color: #777777
+dim_color: #ababab
selected_bg_color: #4c5e6e
selected_fg_color: #bfbfbf
tooltip_bg_color: #5a5a5a
diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs
index 5deb40613d..d899b0fcf1 100644
--- a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs
+++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs
@@ -82,7 +82,7 @@ namespace MonoDevelop.Projects.MSBuild
// Pick the SDK with the highest version
foreach (var sdk in sdkFetcher ()) {
- if (sdk.Name == sdkReference.Name) {
+ if (StringComparer.OrdinalIgnoreCase.Equals (sdk.Name, sdkReference.Name)) {
if (sdk.Version != null) {
// If the sdk has a version, it must satisfy the min version requirement
if (minVersion != null && sdk.Version < minVersion)
diff --git a/main/src/core/MonoDevelop.Startup/app.config b/main/src/core/MonoDevelop.Startup/app.config
index 7f78d4ebec..2744d6fdba 100644
--- a/main/src/core/MonoDevelop.Startup/app.config
+++ b/main/src/core/MonoDevelop.Startup/app.config
@@ -160,7 +160,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.VisualStudio.Threading" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-15.6.0.0" newVersion="15.6.0.0" />
+ <bindingRedirect oldVersion="0.0.0.0-15.8.0.0" newVersion="15.8.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.VisualStudio.Validation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
diff --git a/main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs b/main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs
new file mode 100644
index 0000000000..311f852b83
--- /dev/null
+++ b/main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs
@@ -0,0 +1,144 @@
+//
+// AddinDependencyTreeWidget.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 System.Collections.Generic;
+using Mono.Addins;
+using Mono.Addins.Description;
+using Xwt;
+namespace MonoDevelop.ExtensionTools
+{
+ class AddinDependencyTreeWidget : Widget
+ {
+ readonly TreeStore treeStore;
+ readonly TreeView treeView;
+ readonly DataField<string> labelField = new DataField<string> ();
+ readonly Label summary = new Label ();
+
+ public AddinDependencyTreeWidget (Addin[] addins = null)
+ {
+ addins = addins ?? AddinManager.Registry.GetAllAddins ();
+
+ treeStore = new TreeStore (labelField);
+ treeView = new TreeView (treeStore);
+
+ var col = treeView.Columns.Add ("Name", labelField);
+ col.Expands = true;
+
+ FillData (addins);
+ treeView.ExpandAll ();
+
+ var vbox = new VBox ();
+ vbox.PackStart (summary);
+ vbox.PackStart (treeView, true);
+ Content = vbox;
+ }
+
+ void FillData(Addin[] addins)
+ {
+ var roots = MakeDependencyTree (addins);
+ var node = treeStore.AddNode ();
+
+ node.SetValue (labelField, "Root");
+ int depth = BuildTree (node, roots, new HashSet<AddinNode> (), 1);
+
+ summary.Text = $"Depth: {depth}";
+ }
+
+ int BuildTree (TreeNavigator currentPosition, IEnumerable<AddinNode> addins, HashSet<AddinNode> visited, int currentDepth)
+ {
+ int maxDepth = currentDepth;
+
+ foreach (var addinNode in addins) {
+ if (!visited.Add (addinNode))
+ continue;
+
+ var node = currentPosition.Clone ().AddChild ();
+ node.SetValue (labelField, addinNode.Label);
+
+ var childDepth = BuildTree (node, addinNode.Children, visited, currentDepth + 1);
+ maxDepth = Math.Max (maxDepth, childDepth);
+ }
+
+ return maxDepth;
+ }
+
+ List<AddinNode> MakeDependencyTree (Addin[] addins)
+ {
+ var cache = new Dictionary<Addin, AddinNode> ();
+ var roots = new List<AddinNode> ();
+
+ foreach (var addin in addins) {
+ var addinNode = GetOrCreateNode (addin);
+
+ if (addin.Description.IsRoot)
+ roots.Add (addinNode);
+ }
+
+ foreach (var kvp in cache) {
+ var addin = kvp.Key;
+ var addinNode = kvp.Value;
+
+ // TODO: handle optional dependencies and other modules
+ foreach (Dependency dep in addin.Description.MainModule.Dependencies) {
+ if (dep is AddinDependency adep) {
+ string adepid = Addin.GetFullId (addin.Namespace, adep.AddinId, adep.Version);
+
+ var addinDep = AddinManager.Registry.GetAddin (adepid);
+
+ cache [addinDep].Children.Add (addinNode);
+ }
+ }
+ }
+
+ return roots;
+
+ AddinNode GetOrCreateNode (Addin addin)
+ {
+ if (!cache.TryGetValue (addin, out var addinNode)) {
+ var addinLabel = Addin.GetIdName (addin.Id);
+ cache [addin] = addinNode = new AddinNode (addinLabel);
+ }
+ return addinNode;
+ }
+ }
+
+ class AddinNode : IEquatable<AddinNode>
+ {
+ public HashSet<AddinNode> Children { get; } = new HashSet<AddinNode> ();
+ public string Label { get; }
+
+ public AddinNode (string label)
+ {
+ Label = label;
+ }
+
+ public bool Equals (AddinNode other)
+ {
+ return Label == other.Label;
+ }
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/AddinInfo.cs b/main/src/tools/ExtensionTools/AddinInfo.cs
new file mode 100644
index 0000000000..2ca91262e8
--- /dev/null
+++ b/main/src/tools/ExtensionTools/AddinInfo.cs
@@ -0,0 +1,15 @@
+
+using System;
+using Mono.Addins;
+using Mono.Addins.Description;
+
+[assembly:Addin ("ExtensionTool",
+ Namespace = "MonoDevelop",
+ Version = MonoDevelop.BuildInfo.Version,
+ Category = "MonoDevelop Core")]
+
+[assembly:AddinName ("Extension Developer Tools")]
+[assembly:AddinDescription ("Tools used to analyze the extension model")]
+[assembly:AddinFlags (AddinFlags.Hidden)]
+
+[assembly:AddinDependency ("Core", MonoDevelop.BuildInfo.Version)]
diff --git a/main/src/tools/ExtensionTools/AddinListWidget.cs b/main/src/tools/ExtensionTools/AddinListWidget.cs
new file mode 100644
index 0000000000..d842be1e14
--- /dev/null
+++ b/main/src/tools/ExtensionTools/AddinListWidget.cs
@@ -0,0 +1,68 @@
+//
+// AddinListWidget.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 Mono.Addins;
+using Xwt;
+
+namespace MonoDevelop.ExtensionTools
+{
+ class AddinListWidget : Widget
+ {
+ readonly ListStore listStore;
+ readonly ListView listView;
+ readonly DataField<string> labelField = new DataField<string> ();
+ readonly Label summary = new Label ();
+
+ public AddinListWidget (Addin[] addins = null)
+ {
+ addins = addins ?? AddinManager.Registry.GetAllAddins (x => x.Name);
+
+ listStore = new ListStore (labelField);
+ listView = new ListView (listStore);
+
+ var col = listView.Columns.Add ("Name", labelField);
+ col.Expands = true;
+
+ FillData (addins);
+
+ var vbox = new VBox ();
+ vbox.PackStart (summary, false);
+ vbox.PackStart (listView, true);
+ Content = vbox;
+ }
+
+ void FillData (Addin[] addins)
+ {
+ summary.Text = $"Count: {addins.Length}";
+
+ foreach (var addin in addins) {
+ int row = listStore.AddRow ();
+ listStore.SetValue (row, labelField, addin.Name);
+ }
+ // TODO: clicking a node should open addin info tab
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/AddinRegistryExtensions.cs b/main/src/tools/ExtensionTools/AddinRegistryExtensions.cs
new file mode 100644
index 0000000000..493c421d59
--- /dev/null
+++ b/main/src/tools/ExtensionTools/AddinRegistryExtensions.cs
@@ -0,0 +1,64 @@
+//
+// AddinRegistryExtensions.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 System.Collections.Generic;
+using Mono.Addins;
+
+namespace MonoDevelop.ExtensionTools
+{
+ static class AddinRegistryExtensions
+ {
+ public static Addin[] GetAllAddins (this AddinRegistry registry, Func<Addin, string> sortItemSelector = null)
+ {
+ if (sortItemSelector == null)
+ sortItemSelector = x => x.Id;
+
+ var array = registry.GetModules (AddinSearchFlags.IncludeAll | AddinSearchFlags.LatestVersionsOnly);
+
+ var comparer = new NameComparer (sortItemSelector);
+ Array.Sort (array, comparer);
+
+ return array;
+ }
+
+ class NameComparer : IComparer<Addin>
+ {
+ readonly Func<Addin, string> selector;
+
+ public NameComparer (Func<Addin, string> selector)
+ {
+ this.selector = selector;
+ }
+
+ public int Compare (Addin x, Addin y)
+ {
+ var xString = selector (x);
+ var yString = selector (y);
+ return string.Compare (xString, yString, StringComparison.Ordinal);
+ }
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/Application.cs b/main/src/tools/ExtensionTools/Application.cs
new file mode 100644
index 0000000000..7403015edb
--- /dev/null
+++ b/main/src/tools/ExtensionTools/Application.cs
@@ -0,0 +1,97 @@
+//
+// MyClass.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Mono.Addins;
+using MonoDevelop.Core;
+using Xwt;
+
+namespace MonoDevelop.ExtensionTools
+{
+ public class Application: IApplication
+ {
+ // HACK: Make this proper
+ internal static LazyNotebook MainNotebook;
+
+ public Task<int> Run (string[] arguments)
+ {
+ Xwt.Application.Initialize (ToolkitType.Gtk);
+#if MAC
+ var dir = Path.GetDirectoryName (typeof (Application).Assembly.Location);
+ if (ObjCRuntime.Dlfcn.dlopen (Path.Combine (dir, "libxammac.dylib"), 0) == IntPtr.Zero) {
+ LoggingService.LogFatalError ("Unable to load libxammac");
+ return Task.FromResult (1);
+ }
+#endif
+ LoggingService.LogInfo ("Initialized toolkit");
+
+ Xwt.Toolkit.NativeEngine.Invoke (() => {
+ using (CreateWindow ()) {
+ LoggingService.LogInfo ("Showing main window");
+ Xwt.Application.Run ();
+ }
+ });
+
+ return Task.FromResult (0);
+ }
+
+ static Window CreateWindow ()
+ {
+ var window = new Window {
+ Content = CreateWindowContent (),
+ Width = 800,
+ Height = 800,
+ };
+
+ window.Closed += (_, __) => Xwt.Application.Exit ();
+ window.Show ();
+
+ return window;
+ }
+
+ static Widget CreateWindowContent ()
+ {
+ var nb = MainNotebook = new LazyNotebook ();
+
+ foreach (var (widgetFunc, title) in GetTabs ()) {
+ nb.Add (widgetFunc, title);
+ }
+
+ return nb;
+ }
+
+ static IEnumerable<(Func<Widget> widgetFunc, string title)> GetTabs ()
+ {
+ yield return (() => new AddinListWidget (), "List");
+ yield return (() => new AddinDependencyTreeWidget (), "Dependency Tree");
+ yield return (() => new ExtensionPointsWidget (), "Extension Points");
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/ExtensionNodesWidget.cs b/main/src/tools/ExtensionTools/ExtensionNodesWidget.cs
new file mode 100644
index 0000000000..d8a63369cc
--- /dev/null
+++ b/main/src/tools/ExtensionTools/ExtensionNodesWidget.cs
@@ -0,0 +1,120 @@
+//
+// ExtensionNodesWidget.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 System.Collections.Generic;
+using System.Linq;
+using Mono.Addins;
+using Mono.Addins.Description;
+using Xwt;
+
+namespace MonoDevelop.ExtensionTools
+{
+ class ExtensionNodesWidget : Widget
+ {
+ readonly TreeStore treeStore;
+ readonly TreeView treeView;
+ readonly DataField<string> labelField = new DataField<string> ();
+ readonly Label summary = new Label ();
+
+ public ExtensionNodesWidget (string path, Addin[] addins = null)
+ {
+ addins = addins ?? AddinManager.Registry.GetAllAddins ();
+
+ treeStore = new TreeStore (labelField);
+ treeView = new TreeView (treeStore);
+
+ var col = treeView.Columns.Add ("Name", labelField);
+ col.Expands = true;
+
+ FillData (path, addins);
+ treeView.ExpandAll ();
+
+ var vbox = new VBox ();
+ vbox.PackStart (summary, false);
+ vbox.PackStart (treeView, true);
+ Content = vbox;
+ }
+
+ void FillData (string path, Addin[] addins)
+ {
+ // TODO: add group by addin support.
+ var allNodes = addins
+ .SelectMany (x => x.Description.AllModules)
+ .SelectMany (x => x.Extensions)
+ .Where (x => x.Path == path)
+ .Select (x => x.ExtensionNodes);
+
+ var nav = treeStore.AddNode ();
+
+ int maxDepth = 0;
+ int count = 0;
+ foreach (var node in allNodes) {
+ int depth = BuildTree (nav, node, 1, ref count);
+ maxDepth = Math.Max (maxDepth, depth);
+ }
+
+ summary.Text = $"'{path}' Count: {count} Depth: {maxDepth}";
+ }
+
+ int BuildTree (TreeNavigator currentPosition, ExtensionNodeDescriptionCollection nodes, int currentDepth, ref int count)
+ {
+ int maxDepth = currentDepth;
+
+ // TODO: insertbefore/after
+
+ foreach (ExtensionNodeDescription node in nodes) {
+ count++;
+ var pos = currentPosition.Clone ().AddChild ();
+
+ var label = GetLabelForNode (node);
+ pos.SetValue (labelField, label);
+
+ var childDepth = BuildTree (pos, node.ChildNodes, currentDepth + 1, ref count);
+ maxDepth = Math.Max (maxDepth, childDepth);
+ }
+
+ return maxDepth;
+ }
+
+ string GetLabelForNode (ExtensionNodeDescription node)
+ {
+ if (node.IsCondition) {
+ var value = node.GetAttribute ("value");
+ if (!string.IsNullOrEmpty (value))
+ return $"Condition: {node.Id} == {value}";
+ return $"Condition: {node.Id}";
+ }
+
+ var type = node.GetAttribute ("class");
+ if (!string.IsNullOrEmpty (type))
+ return type;
+
+ return node.Id;
+ }
+
+ // TODO: add full attribute visualization
+ }
+}
diff --git a/main/src/tools/ExtensionTools/ExtensionPointsWidget.cs b/main/src/tools/ExtensionTools/ExtensionPointsWidget.cs
new file mode 100644
index 0000000000..0784222b45
--- /dev/null
+++ b/main/src/tools/ExtensionTools/ExtensionPointsWidget.cs
@@ -0,0 +1,97 @@
+//
+// ExtensionPointsWidget.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 System.Collections.Generic;
+using System.Linq;
+using Mono.Addins;
+using Mono.Addins.Description;
+using Xwt;
+namespace MonoDevelop.ExtensionTools
+{
+ class ExtensionPointsWidget : Widget
+ {
+ readonly ListStore listStore;
+ readonly ListView listView;
+ readonly DataField<string> labelField = new DataField<string> ();
+ readonly Label summary = new Label ();
+
+ public ExtensionPointsWidget (Addin[] addins = null)
+ {
+ addins = addins ?? AddinManager.Registry.GetAllAddins ();
+
+ listStore = new ListStore (labelField);
+ listView = new ListView (listStore);
+ listView.RowActivated += ListView_RowActivated;
+
+ var col = listView.Columns.Add ("Name", labelField);
+ col.Expands = true;
+
+ FillData (addins);
+
+ var vbox = new VBox ();
+ vbox.PackStart (summary, false);
+ vbox.PackStart (listView, true);
+ Content = vbox;
+ }
+
+ void ListView_RowActivated (object sender, ListViewRowEventArgs e)
+ {
+ var value = listStore.GetValue (e.RowIndex, labelField);
+
+ var window = new Window {
+ Content = new ExtensionNodesWidget (value),
+ Height = 800,
+ Width = 600,
+ };
+ window.Show ();
+ }
+
+ void FillData (Addin[] addins)
+ {
+ var points = GatherExtensionPoints (addins);
+
+ summary.Text = $"Count: {points.Length}";
+
+ foreach (var point in points) {
+ int row = listStore.AddRow ();
+ listStore.SetValue (row, labelField, point);
+ }
+ }
+
+ string[] GatherExtensionPoints (Addin[] addins)
+ {
+ var points = new HashSet<string> ();
+
+ foreach (var addin in addins) {
+ foreach (ExtensionPoint extensionPoint in addin.Description.ExtensionPoints) {
+ points.Add (extensionPoint.Path);
+ }
+ }
+
+ return points.ToSortedArray ();
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/ExtensionTools.csproj b/main/src/tools/ExtensionTools/ExtensionTools.csproj
new file mode 100644
index 0000000000..5d45e69fc3
--- /dev/null
+++ b/main/src/tools/ExtensionTools/ExtensionTools.csproj
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="..\..\..\MonoDevelop.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{E33F7901-AA45-4C25-9868-DBB6CC8DC40C}</ProjectGuid>
+ <TargetFrameworkVersion>$(MDFrameworkVersion)</TargetFrameworkVersion>
+ <OutputPath>..\..\..\build\bin</OutputPath>
+ <RootNamespace>MonoDevelop.ExtensionTools</RootNamespace>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMac|AnyCPU' " />
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMac|AnyCPU' " />
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="Xamarin.Mac" Condition=" '$(Configuration)' == 'DebugMac' OR '$(Configuration)' == 'ReleaseMac' ">
+ <HintPath>..\..\..\build\bin\Xamarin.Mac.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Application.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="AddinInfo.cs" />
+ <Compile Include="AddinDependencyTreeWidget.cs" />
+ <Compile Include="AddinListWidget.cs" />
+ <Compile Include="AddinRegistryExtensions.cs" />
+ <Compile Include="ExtensionPointsWidget.cs" />
+ <Compile Include="LazyNotebook.cs" />
+ <Compile Include="ExtensionNodesWidget.cs" />
+ <Compile Include="HashSetExtensions.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj">
+ <Project>{7525BB88-6142-4A26-93B9-A30C6983390A}</Project>
+ <Name>MonoDevelop.Core</Name>
+ <Private>False</Private>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\external\mono-addins\Mono.Addins\Mono.Addins.csproj">
+ <Project>{91DD5A2D-9FE3-4C3C-9253-876141874DAD}</Project>
+ <Name>Mono.Addins</Name>
+ <Private>False</Private>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\external\xwt\Xwt\Xwt.csproj">
+ <Project>{92494904-35FA-4DC9-BDE9-3A3E87AC49D3}</Project>
+ <Name>Xwt</Name>
+ <Private>False</Private>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="MonoDevelop.ExtensionTools.addin.xml" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project> \ No newline at end of file
diff --git a/main/src/tools/ExtensionTools/HashSetExtensions.cs b/main/src/tools/ExtensionTools/HashSetExtensions.cs
new file mode 100644
index 0000000000..138ee4c378
--- /dev/null
+++ b/main/src/tools/ExtensionTools/HashSetExtensions.cs
@@ -0,0 +1,41 @@
+//
+// HashSetExtensions.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 System.Collections.Generic;
+using System.Linq;
+
+namespace MonoDevelop.ExtensionTools
+{
+ static class HashSetExtensions
+ {
+ public static T[] ToSortedArray<T> (this HashSet<T> set)
+ {
+ var arr = set.ToArray ();
+ Array.Sort (arr);
+ return arr;
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/LazyNotebook.cs b/main/src/tools/ExtensionTools/LazyNotebook.cs
new file mode 100644
index 0000000000..bdba85338c
--- /dev/null
+++ b/main/src/tools/ExtensionTools/LazyNotebook.cs
@@ -0,0 +1,71 @@
+//
+// LazyNotebook.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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 Xwt;
+
+namespace MonoDevelop.ExtensionTools
+{
+ class LazyNotebook : Notebook
+ {
+ public void Add (Func<Widget> createWidget, string label)
+ {
+ var createWidgetChain = OnAdd (createWidget);
+ var widget = new LazyWidget (createWidgetChain);
+ Add (widget, label);
+ }
+
+ protected override void OnCurrentTabChanged (EventArgs e)
+ {
+ base.OnCurrentTabChanged (e);
+ Xwt.Application.TimeoutInvoke (0, () => {
+ Toolkit.NativeEngine.Invoke (() => {
+ var tab = Tabs [CurrentTabIndex];
+ if (tab.Child is LazyWidget lazy)
+ lazy.CreateContent ();
+ });
+ return false;
+ });
+ }
+
+ protected virtual Func<Widget> OnAdd (Func<Widget> createWidget) => createWidget;
+
+ class LazyWidget : Widget
+ {
+ readonly Func<Widget> createWidget;
+
+ public LazyWidget (Func<Widget> createWidget)
+ {
+ this.createWidget = createWidget;
+ }
+
+ public void CreateContent ()
+ {
+ if (Content == null)
+ Content = createWidget ();
+ }
+ }
+ }
+}
diff --git a/main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml b/main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml
new file mode 100644
index 0000000000..25998713d8
--- /dev/null
+++ b/main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml
@@ -0,0 +1,7 @@
+<ExtensionModel>
+
+ <Extension path = "/MonoDevelop/Core/Applications">
+ <Application id = "extension-tool" class = "MonoDevelop.ExtensionTools.Application" description = "Developer overview over the extension model"/>
+ </Extension>
+
+</ExtensionModel>
diff --git a/main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs b/main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..061f89aac8
--- /dev/null
+++ b/main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs
@@ -0,0 +1,51 @@
+//
+// AssemblyInfo.cs
+//
+// Author:
+// Marius Ungureanu <maungu@microsoft.com>
+//
+// Copyright (c) 2018 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.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle ("ExtensionTools")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("Microsoft")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("Microsoft Inc.")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion ("1.0.0")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]