{"action":"created","comment":{"url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/comments/55809712","id":55809712,"diff_hunk":"@@ -1,395 +1,372 @@\n-//-----------------------------------------------------------------------\n-// <copyright file=\"BindingWorkflow.cs\" company=\"SonarSource SA and Microsoft Corporation\">\n-//   Copyright (c) SonarSource SA and Microsoft Corporation.  All rights reserved.\n-//   Licensed under the MIT License. See License.txt in the project root for license information.\n-// </copyright>\n-//-----------------------------------------------------------------------\n-\n-using Microsoft.Alm.Authentication;\n-using Microsoft.VisualStudio.CodeAnalysis.RuleSets;\n-using SonarLint.VisualStudio.Integration.Persistence;\n-using SonarLint.VisualStudio.Integration.Progress;\n-using SonarLint.VisualStudio.Integration.Resources;\n-using SonarLint.VisualStudio.Integration.Service;\n-using SonarLint.VisualStudio.Integration.Service.DataModel;\n-using SonarLint.VisualStudio.Integration.TeamExplorer;\n-using SonarLint.VisualStudio.Progress.Controller;\n-using System;\n-using System.Collections.Generic;\n-using System.Diagnostics;\n-using System.Globalization;\n-using System.IO;\n-using System.Linq;\n-using System.Threading;\n-\n-namespace SonarLint.VisualStudio.Integration.Binding\n-{\n-    /// <summary>\n-    /// Workflow execution for the bind command\n-    /// </summary>\n-    internal class BindingWorkflow\n-    {\n-        private readonly BindCommand owner;\n-        private readonly ProjectInformation project;\n-        private readonly IProjectSystemHelper projectSystemHelper;\n-        private readonly SolutionRuleSetWriter solutionRuleSetWriter;\n-        private readonly ProjectRuleSetWriter projectRuleSetWriter;\n-\n-        internal readonly Dictionary<string, RuleSetGroup> LanguageToGroupMapping = new Dictionary<string, RuleSetGroup>\n-        {\n-            {SonarQubeServiceWrapper.CSharpLanguage, RuleSetGroup.CSharp },\n-            {SonarQubeServiceWrapper.VBLanguage, RuleSetGroup.VB }\n-        };        \n-\n-        public BindingWorkflow(BindCommand owner, ProjectInformation project, SolutionRuleSetWriter solutionRuleSetWriter = null, ProjectRuleSetWriter projectRuleSetWriter = null, IProjectSystemHelper projectSystemHelper = null)\n-        {\n-            if (owner == null)\n-            {\n-                throw new ArgumentNullException(nameof(owner));\n-            }\n-\n-            if (project == null)\n-            {\n-                throw new ArgumentNullException(nameof(project));\n-            }\n-\n-            this.owner = owner;\n-            this.project = project;\n-            this.projectSystemHelper = projectSystemHelper ?? new ProjectSystemHelper(this.owner.ServiceProvider);\n-            this.solutionRuleSetWriter = solutionRuleSetWriter ?? new SolutionRuleSetWriter(this.project);\n-            this.projectRuleSetWriter = projectRuleSetWriter ?? new ProjectRuleSetWriter();\n-        }\n-\n-        #region Workflow state\n-\n-        public Dictionary<RuleSetGroup, RuleSet> Rulesets\n-        {\n-            get;\n-        } = new Dictionary<RuleSetGroup, RuleSet>();\n-\n-        public List<NuGetPackageInfo> NuGetPackages\n-        {\n-            get;\n-        } = new List<NuGetPackageInfo>();\n-\n-        public Dictionary<RuleSetGroup, string> SolutionRulesetPaths\n-        {\n-            get;\n-        } = new Dictionary<RuleSetGroup, string>();\n-\n-        internal RuleSetInjection.RuleSetInjector RuleSetInjector\n-        {\n-            get;\n-            private set;\n-        }\n-\n-        internal /*for testing purposes*/ bool AllNuGetPackagesInstalled\n-        {\n-            get;\n-            set;\n-        } = true;\n-\n-        #endregion\n-\n-        #region Workflow startup\n-\n-        public IProgressEvents Run()\n-        {\n-            this.owner.UserNotification.HideNotification(NotificationIds.FailedToBindId);\n-\n-            List<string> languages = new List<string>();\n-            if (this.projectSystemHelper.GetSolutionManagedProjects().Any(p => ProjectSystemHelper.IsCSharpProject(p)))\n-            {\n-                languages.Add(SonarQubeServiceWrapper.CSharpLanguage);\n-            }\n-\n-            if (this.projectSystemHelper.GetSolutionManagedProjects().Any(p => ProjectSystemHelper.IsVBProject(p)))\n-            {\n-                languages.Add(SonarQubeServiceWrapper.VBLanguage);\n-            }\n-\n-            Debug.Assert(languages.Count > 0, \"Expecting managed projects in solution\");\n-\n-            IProgressEvents progress = ProgressStepRunner.StartAsync(this.owner.ServiceProvider,\n-                this.owner.ProgressControlHost,\n-                (controller) => this.CreateWorkflowSteps(controller, languages));\n-\n-            this.DebugOnly_MonitorProgress(progress);\n-\n-            return progress;\n-        }\n-\n-        [Conditional(\"DEBUG\")]\n-        private void DebugOnly_MonitorProgress(IProgressEvents progress)\n-        {\n-            progress.RunOnFinished(r => VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, \"DEBUGONLY: Binding workflow finished, Execution result: {0}\", r));\n-        }\n-\n-        private ProgressStepDefinition[] CreateWorkflowSteps(IProgressController controller, IEnumerable<string> languages)\n-        {\n-            StepAttributes IndeterminateNonCancellableUIStep = StepAttributes.Indeterminate | StepAttributes.NonCancellable;\n-            StepAttributes HiddenIndeterminateNonImpactingNonCancellableUIStep = IndeterminateNonCancellableUIStep | StepAttributes.Hidden | StepAttributes.NoProgressImpact;\n-            StepAttributes HiddenNonImpactingBackgroundStep = StepAttributes.BackgroundThread | StepAttributes.Hidden | StepAttributes.NoProgressImpact;\n-\n-            return new ProgressStepDefinition[]\n-            {\n-                new ProgressStepDefinition(null, HiddenNonImpactingBackgroundStep,\n-                        (token, notifications) => notifications.ProgressChanged(Strings.StartedSolutionBindingWorkflow, double.NaN)),\n-\n-                new ProgressStepDefinition(null, StepAttributes.Indeterminate | StepAttributes.Hidden,\n-                        (token, notifications) => this.PromptSaveSolutionIfDirty(controller, token)),\n-\n-                new ProgressStepDefinition(null, StepAttributes.BackgroundThread | StepAttributes.Hidden | StepAttributes.Indeterminate,\n-                        (token, notifications) => this.VerifyServerPlugins(controller, token, notifications)),\n-\n-                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread,\n-                        (token, notifications) => this.DownloadQualityProfile(controller, token, notifications, languages)),\n-\n-                new ProgressStepDefinition(null, HiddenIndeterminateNonImpactingNonCancellableUIStep,\n-                        (token, notifications) => { NuGetHelper.LoadService(this.owner.ServiceProvider); /*The service needs to be loaded on UI thread*/ }),\n-\n-                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread,\n-                        (token, notifications) => this.InstallPackages(controller, token, notifications)),\n-\n-                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, IndeterminateNonCancellableUIStep,\n-                        (token, notifications) => this.PrepareRuleSetInjector(controller, notifications)),\n-\n-                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread | StepAttributes.Indeterminate,\n-                        (token, notifications) => this.PrepareRuleSets(controller, token, notifications)),\n-\n-                new ProgressStepDefinition(null, HiddenIndeterminateNonImpactingNonCancellableUIStep,\n-                        (token, notifications) => this.FinishBindingOnUIThread(controller, notifications)),\n-\n-                new ProgressStepDefinition(null, HiddenIndeterminateNonImpactingNonCancellableUIStep,\n-                        (token, notifications) => this.SilentSaveSolutionIfDirty()),\n-\n-                new ProgressStepDefinition(null, HiddenNonImpactingBackgroundStep,\n-                        (token, notifications) => this.EmitBindingCompleteMessage(notifications))\n-            };\n-        }\n-\n-        #endregion\n-\n-        #region Workflow steps\n-        private void AbortWorkflow(IProgressController controller, CancellationToken token)\n-        {\n-            bool aborted = controller.TryAbort();\n-            Debug.Assert(aborted || token.IsCancellationRequested, \"Failed to abort the workflow\");\n-        }\n-\n-        internal /*for testing purposes*/ void PromptSaveSolutionIfDirty(IProgressController controller, CancellationToken token)\n-        {\n-            if (!VsShellUtils.SaveSolution(this.owner.ServiceProvider, silent: false))\n-            {\n-                VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, Strings.SolutionSaveCancelledBindAborted);\n-\n-                this.AbortWorkflow(controller, token);\n-            }\n-        }\n-\n-        internal /*for testing purposes*/ void SilentSaveSolutionIfDirty()\n-        {\n-            bool saved = VsShellUtils.SaveSolution(this.owner.ServiceProvider, silent: true);\n-            Debug.Assert(saved, \"Should not be cancellable\");\n-        }\n-\n-        internal /*for testing purposes*/ void VerifyServerPlugins(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notifications)\n-        {\n-            var csPluginVersion = this.owner.SonarQubeService.GetPluginVersion(ServerPlugin.CSharpPluginKey, token);\n-            if (string.IsNullOrWhiteSpace(csPluginVersion) || VersionHelper.Compare(csPluginVersion, ServerPlugin.CSharpPluginMinimumVersion) < 0)\n-            {\n-                string errorMessage = string.Format(CultureInfo.CurrentCulture, Strings.ServerDoesNotHaveCorrectVersionOfCSharpPlugin, ServerPlugin.CSharpPluginMinimumVersion);\n-                VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, errorMessage);\n-\n-                this.AbortWorkflow(controller, token);\n-            }\n-        }\n-\n-        internal /*for testing purposes*/ void DownloadQualityProfile(IProgressController controller, CancellationToken cancellationToken, IProgressStepExecutionEvents notificationEvents, IEnumerable<string> languages)\n-        {\n-            Debug.Assert(controller != null);\n-            Debug.Assert(notificationEvents != null);\n-\n-            bool failed = false;\n-            Dictionary<string, RuleSet> rulesets = new Dictionary<string, RuleSet>();\n-            DeterminateStepProgressNotifier notifier = new DeterminateStepProgressNotifier(notificationEvents, languages.Count());\n-\n-            foreach (var language in languages)\n-            {\n-                notifier.NotifyCurrentProgress(string.Format(CultureInfo.CurrentCulture, Strings.DownloadingQualityProfileProgressMessage, language));\n-\n-                var export = this.owner.SonarQubeService.GetExportProfile(this.project, language, cancellationToken);\n-\n-                if (export == null)\n-                {\n-                    failed = true;\n-                    break;\n-                }\n-\n-                this.NuGetPackages.AddRange(export.Deployment.NuGetPackages);\n-\n-                var tempRuleSetFilePath = Path.GetTempFileName();\n-                File.WriteAllText(tempRuleSetFilePath, export.Configuration.RuleSet.OuterXml);\n-                RuleSet ruleSet = RuleSet.LoadFromFile(tempRuleSetFilePath);\n-\n-                rulesets[language] = ruleSet;\n-                notifier.NotifyIncrementedProgress(string.Empty);\n-                if (rulesets[language] == null)\n-                {\n-                    failed = true;\n-                    break;\n-                }\n-            }\n-\n-            if (failed)\n-            {\n-                VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, Strings.QualityProfileDownloadFailedMessage);\n-                bool aborted = controller.TryAbort();\n-                Debug.Assert(aborted || cancellationToken.IsCancellationRequested, \"Failed to abort the workflow\");\n-            }\n-            else\n-            {\n-                // Set the rule set which should be available for the following steps\n-                foreach(var keyValue in rulesets)\n-                {\n-                    this.Rulesets[this.LanguageToGroup(keyValue.Key)] = keyValue.Value;\n-                }\n-\n-                notifier.NotifyCurrentProgress(Strings.QualityProfileDownloadedSuccessfulMessage);\n-            }\n-        }\n-\n-        private RuleSetGroup LanguageToGroup(string language)\n-        {\n-            RuleSetGroup group;\n-            if (!this.LanguageToGroupMapping.TryGetValue(language, out group))\n-            {\n-                Debug.Fail(\"Unsupported language: \" + language);\n-                throw new InvalidOperationException();\n-            }\n-            return group;\n-        }\n-\n-        private void PrepareRuleSetInjector(IProgressController controller, IProgressStepExecutionEvents notificationEvents)\n-        {\n-            Debug.Assert(System.Windows.Application.Current?.Dispatcher.CheckAccess() ?? false, \"Expected to run on UI thread\");\n-\n-            notificationEvents.ProgressChanged(Strings.RuleSetGenerationProgressMessage, double.NaN);\n-            this.RuleSetInjector = new RuleSetInjection.RuleSetInjector(\n-                this.projectSystemHelper,\n-                this.SetSolutionRuleSet,\n-                this.UpdateProjectRuleSet);\n-        }\n-\n-        private void PrepareRuleSets(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notificationEvents)\n-        {\n-            this.RuleSetInjector.PrepareUpdates(token);\n-        }\n-\n-        private void FinishBindingOnUIThread(IProgressController controller, IProgressStepExecutionEvents notificationEvents)\n-        {\n-            Debug.Assert(System.Windows.Application.Current?.Dispatcher.CheckAccess() ?? false, \"Expected to run on UI thread\");\n-\n-            this.RuleSetInjector.CommitUpdates();\n-\n-            this.PersistBinding();\n-        }\n-\n-        /// <summary>\n-        /// Will persist the binding information for next time usage\n-        /// </summary>\n-        internal /*for testing purposes*/ void PersistBinding(ICredentialStore credentialStore = null, IProjectSystemHelper projectSystemHelper = null)\n-        {\n-            Debug.Assert(this.owner.SonarQubeService.CurrentConnection != null, \"Connection expected\");\n-            ConnectionInformation connection = this.owner.SonarQubeService.CurrentConnection;\n-\n-            BasicAuthCredentials credentials = connection.UserName == null? null : new BasicAuthCredentials(connection.UserName, connection.Password);\n-\n-            SolutionBinding binding = new SolutionBinding(this.owner.ServiceProvider, credentialStore, projectSystemHelper);\n-            binding.WriteSolutionBinding(new BoundSonarQubeProject(connection.ServerUri, this.project.Key, credentials));\n-        }\n-\n-        /// <summary>\n-        /// Will install the NuGet packages for the current managed projects.\n-        /// The packages that will be installed will be based on the information from <see cref=\"Analyzer.GetRequiredNuGetPackages\"/> \n-        /// and is specific to the <see cref=\"RuleSet\"/>.\n-        /// </summary>\n-        internal /*for testing purposes*/ void InstallPackages(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notificationEvents)\n-        {\n-            if (!this.NuGetPackages.Any())\n-            {\n-                return;\n-            }\n-\n-            Debug.Assert(this.NuGetPackages.Count == this.NuGetPackages.Distinct().Count(), \"Duplicate NuGet packages specified\");\n-\n-            var managedProjects = this.projectSystemHelper.GetSolutionManagedProjects().ToArray();\n-            if (!managedProjects.Any())\n-            {\n-                Debug.Fail(\"Not expected to be called when there are no managed projects\");\n-                return;\n-            }\n-\n-            DeterminateStepProgressNotifier progressNotifier = new DeterminateStepProgressNotifier(notificationEvents, managedProjects.Length * this.NuGetPackages.Count);\n-            foreach (var project in managedProjects)\n-            {\n-                foreach (var packageInfo in this.NuGetPackages)\n-                {\n-                    if (token.IsCancellationRequested)\n-                    {\n-                        break;\n-                    }\n-\n-                    string message = string.Format(CultureInfo.CurrentCulture, Strings.EnsuringNugetPackagesProgressMessage, packageInfo.Id, project.Name);\n-                    progressNotifier.NotifyCurrentProgress(message);\n-\n-                    // TODO: SVS-33 (https://jira.sonarsource.com/browse/SVS-33) Trigger a Team Explorer warning notification to investigate the partial binding in the output window.\n-                    this.AllNuGetPackagesInstalled &= NuGetHelper.TryInstallPackage(this.owner.ServiceProvider, project, packageInfo.Id, packageInfo.Version);\n-\n-                    progressNotifier.NotifyIncrementedProgress(string.Empty);\n-                }\n-            }\n-        }\n-\n-        internal /*for testing purposes*/ string SetSolutionRuleSet(RuleSetGroup group, string solutionFullPath)\n-        {\n-            Debug.Assert(!string.IsNullOrWhiteSpace(solutionFullPath), \"Expecting a solution file path\");\n-            Debug.Assert(this.Rulesets.ContainsKey(group) && this.Rulesets[group] != null, $\"Rule set should have been stashed by previous step ({nameof(DownloadQualityProfile)})\");\n-\n-            RuleSet ruleset;\n-            string path = null;\n-            if (this.Rulesets.TryGetValue(group, out ruleset) && ruleset!=null)\n-            {\n-                path = this.solutionRuleSetWriter.WriteSolutionLevelRuleSet(solutionFullPath, ruleset, fileNameSuffix: group.ToString());\n-                this.SolutionRulesetPaths[group] = path;\n-            }\n-\n-            return path;\n-        }\n-\n-        internal /*for testing purposes*/ string UpdateProjectRuleSet(RuleSetGroup group, string projectFullPath, string configurationName, string currentRuleSet)\n-        {\n-            Debug.Assert(!string.IsNullOrWhiteSpace(projectFullPath), \"Expecting a project full path\");\n-            Debug.Assert(this.SolutionRulesetPaths.ContainsKey(group) && this.SolutionRulesetPaths[group] != null, $\"Rule set should have been stashed by previous step ({nameof(DownloadQualityProfile)})\");\n-\n-            string solutionRuleSetPath = null;\n-            string projectRuleSetPath = null;\n-            if (this.SolutionRulesetPaths.TryGetValue(group, out solutionRuleSetPath))\n-            {\n-                projectRuleSetPath = this.projectRuleSetWriter.WriteProjectLevelRuleSet(projectFullPath, configurationName, solutionRuleSetPath, currentRuleSet);\n-            }\n-\n-            return projectRuleSetPath;\n-        }\n-\n-        internal /*for testing purposes*/ void EmitBindingCompleteMessage(IProgressStepExecutionEvents notifications)\n-        {\n-            var message = this.AllNuGetPackagesInstalled\n-                ? Strings.FinishedSolutionBindingWorkflowSuccessful\n-                : Strings.FinishedSolutionBindingWorkflowNotAllPackagesInstalled;\n-            notifications.ProgressChanged(message, double.NaN);\n-        }\n-\n-        #endregion\n-    }\n-}\n+//-----------------------------------------------------------------------\r\n+// <copyright file=\"BindingWorkflow.cs\" company=\"SonarSource SA and Microsoft Corporation\">\r\n+//   Copyright (c) SonarSource SA and Microsoft Corporation.  All rights reserved.\r\n+//   Licensed under the MIT License. See License.txt in the project root for license information.\r\n+// </copyright>\r\n+//-----------------------------------------------------------------------\r\n+\r\n+using Microsoft.Alm.Authentication;\r\n+using Microsoft.VisualStudio.CodeAnalysis.RuleSets;\r\n+using SonarLint.VisualStudio.Integration.Persistence;\r\n+using SonarLint.VisualStudio.Integration.Progress;\r\n+using SonarLint.VisualStudio.Integration.Resources;\r\n+using SonarLint.VisualStudio.Integration.Service;\r\n+using SonarLint.VisualStudio.Integration.Service.DataModel;\r\n+using SonarLint.VisualStudio.Integration.TeamExplorer;\r\n+using SonarLint.VisualStudio.Progress.Controller;\r\n+using System;\r\n+using System.Collections.Generic;\r\n+using System.Diagnostics;\r\n+using System.Globalization;\r\n+using System.IO;\r\n+using System.Linq;\r\n+using System.Threading;\r\n+\r\n+namespace SonarLint.VisualStudio.Integration.Binding\r\n+{\r\n+    /// <summary>\r\n+    /// Workflow execution for the bind command\r\n+    /// </summary>\r\n+    internal class BindingWorkflow\r\n+    {\r\n+        private readonly BindCommand owner;\r\n+        private readonly ProjectInformation project;\r\n+        private readonly IProjectSystemHelper projectSystemHelper;\r\n+        private readonly IBindingOperation solutionBindingOperation;\r\n+        private readonly ISolutionRuleStore solutionRuleStore;\r\n+\r\n+        internal readonly Dictionary<string, RuleSetGroup> LanguageToGroupMapping = new Dictionary<string, RuleSetGroup>\r\n+        {\r\n+            {SonarQubeServiceWrapper.CSharpLanguage, RuleSetGroup.CSharp },\r\n+            {SonarQubeServiceWrapper.VBLanguage, RuleSetGroup.VB }\r\n+        };\r\n+\r\n+        public BindingWorkflow(BindCommand owner, ProjectInformation project)\r\n+            : this(owner, project, null, null, null)\r\n+        {\r\n+\r\n+        }\r\n+\r\n+        internal /*for testing purposes*/ BindingWorkflow(BindCommand owner, ProjectInformation project, IProjectSystemHelper projectSystemHelper, IBindingOperation solutionBindingOperation, ISolutionRuleStore solutionRuleStore)\r\n+        {\r\n+            if (owner == null)\r\n+            {\r\n+                throw new ArgumentNullException(nameof(owner));\r\n+            }\r\n+\r\n+            if (project == null)\r\n+            {\r\n+                throw new ArgumentNullException(nameof(project));\r\n+            }\r\n+\r\n+            this.owner = owner;\r\n+            this.project = project;\r\n+            this.projectSystemHelper = projectSystemHelper ?? new ProjectSystemHelper(this.owner.ServiceProvider);\r\n+\r\n+            // This instance will be \"wasted\" only when created for testing purposes\r\n+            var solutionBinding = new SolutionBindingOperation(\r\n+                    this.owner.ServiceProvider,\r\n+                    this.projectSystemHelper,\r\n+                    this.project.Key);\r\n+\r\n+            this.solutionBindingOperation = solutionBindingOperation ?? solutionBinding;\r\n+            this.solutionRuleStore = solutionRuleStore ?? solutionBinding;\r\n+        }\r\n+\r\n+        #region Workflow state\r\n+\r\n+        public Dictionary<RuleSetGroup, RuleSet> Rulesets\r\n+        {\r\n+            get;\r\n+        } = new Dictionary<RuleSetGroup, RuleSet>();\r\n+\r\n+        public List<NuGetPackageInfo> NuGetPackages\r\n+        {\r\n+            get;\r\n+        } = new List<NuGetPackageInfo>();\r\n+\r\n+        public Dictionary<RuleSetGroup, string> SolutionRulesetPaths\r\n+        {\r\n+            get;\r\n+        } = new Dictionary<RuleSetGroup, string>();\r\n+\r\n+\r\n+        internal /*for testing purposes*/ bool AllNuGetPackagesInstalled\r\n+        {\r\n+            get;\r\n+            set;\r\n+        } = true;\r\n+\r\n+        #endregion\r\n+\r\n+        #region Workflow startup\r\n+\r\n+        public IProgressEvents Run()\r\n+        {\r\n+            this.owner.UserNotification.HideNotification(NotificationIds.FailedToBindId);\r\n+\r\n+            List<string> languages = new List<string>();\r\n+            if (this.projectSystemHelper.GetSolutionManagedProjects().Any(p => ProjectSystemHelper.IsCSharpProject(p)))\r\n+            {\r\n+                languages.Add(SonarQubeServiceWrapper.CSharpLanguage);\r\n+            }\r\n+\r\n+            if (this.projectSystemHelper.GetSolutionManagedProjects().Any(p => ProjectSystemHelper.IsVBProject(p)))\r\n+            {\r\n+                languages.Add(SonarQubeServiceWrapper.VBLanguage);\r\n+            }\r\n+\r\n+            Debug.Assert(languages.Count > 0, \"Expecting managed projects in solution\");\r\n+\r\n+            IProgressEvents progress = ProgressStepRunner.StartAsync(this.owner.ServiceProvider,\r\n+                this.owner.ProgressControlHost,\r\n+                (controller) => this.CreateWorkflowSteps(controller, languages));\r\n+\r\n+            this.DebugOnly_MonitorProgress(progress);\r\n+\r\n+            return progress;\r\n+        }\r\n+\r\n+        [Conditional(\"DEBUG\")]\r\n+        private void DebugOnly_MonitorProgress(IProgressEvents progress)\r\n+        {\r\n+            progress.RunOnFinished(r => VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, \"DEBUGONLY: Binding workflow finished, Execution result: {0}\", r));\r\n+        }\r\n+\r\n+        private ProgressStepDefinition[] CreateWorkflowSteps(IProgressController controller, IEnumerable<string> languages)\r\n+        {\r\n+            StepAttributes IndeterminateNonCancellableUIStep = StepAttributes.Indeterminate | StepAttributes.NonCancellable;\r\n+            StepAttributes HiddenIndeterminateNonImpactingNonCancellableUIStep = IndeterminateNonCancellableUIStep | StepAttributes.Hidden | StepAttributes.NoProgressImpact;\r\n+            StepAttributes HiddenNonImpactingBackgroundStep = StepAttributes.BackgroundThread | StepAttributes.Hidden | StepAttributes.NoProgressImpact;\r\n+\r\n+            return new ProgressStepDefinition[]\r\n+            {\r\n+                new ProgressStepDefinition(null, HiddenNonImpactingBackgroundStep,\r\n+                        (token, notifications) => notifications.ProgressChanged(Strings.StartedSolutionBindingWorkflow, double.NaN)),\r\n+\r\n+                new ProgressStepDefinition(null, StepAttributes.Indeterminate | StepAttributes.Hidden,\r\n+                        (token, notifications) => this.PromptSaveSolutionIfDirty(controller, token)),\r\n+\r\n+                new ProgressStepDefinition(null, StepAttributes.BackgroundThread | StepAttributes.Hidden | StepAttributes.Indeterminate,\r\n+                        (token, notifications) => this.VerifyServerPlugins(controller, token, notifications)),\r\n+\r\n+                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread,\r\n+                        (token, notifications) => this.DownloadQualityProfile(controller, token, notifications, languages)),\r\n+\r\n+                new ProgressStepDefinition(null, HiddenIndeterminateNonImpactingNonCancellableUIStep,\r\n+                        (token, notifications) => { NuGetHelper.LoadService(this.owner.ServiceProvider); /*The service needs to be loaded on UI thread*/ }),\r\n+\r\n+                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread,\r\n+                        (token, notifications) => this.InstallPackages(controller, token, notifications)),\r\n+\r\n+                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, IndeterminateNonCancellableUIStep,\r\n+                        (token, notifications) => this.InitializeSolutionBindingOnUIThread(notifications)),\r\n+\r\n+                new ProgressStepDefinition(Strings.BindingProjectsDisplayMessage, StepAttributes.BackgroundThread | StepAttributes.Indeterminate,\r\n+                        (token, notifications) => this.PrepareSolutionBinding(token)),\r\n+\r\n+                new ProgressStepDefinition(null, HiddenIndeterminateNonImpactingNonCancellableUIStep,\r\n+                        (token, notifications) => this.FinishSolutionBindingOnUIThread()),\r\n+\r\n+                new ProgressStepDefinition(null, HiddenIndeterminateNonImpactingNonCancellableUIStep,\r\n+                        (token, notifications) => this.SilentSaveSolutionIfDirty()),\r\n+\r\n+                new ProgressStepDefinition(null, HiddenNonImpactingBackgroundStep,\r\n+                        (token, notifications) => this.EmitBindingCompleteMessage(notifications))\r\n+            };\r\n+        }\r\n+        #endregion\r\n+\r\n+        #region Workflow steps\r\n+        internal /*for testing purposes*/ void PromptSaveSolutionIfDirty(IProgressController controller, CancellationToken token)\r\n+        {\r\n+            if (!VsShellUtils.SaveSolution(this.owner.ServiceProvider, silent: false))\r\n+            {\r\n+                VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, Strings.SolutionSaveCancelledBindAborted);\r\n+\r\n+                this.AbortWorkflow(controller, token);\r\n+            }\r\n+        }\r\n+\r\n+        internal /*for testing purposes*/ void SilentSaveSolutionIfDirty()\r\n+        {\r\n+            bool saved = VsShellUtils.SaveSolution(this.owner.ServiceProvider, silent: true);\r\n+            Debug.Assert(saved, \"Should not be cancellable\");\r\n+        }\r\n+\r\n+        internal /*for testing purposes*/ void VerifyServerPlugins(IProgressController controller, CancellationToken token, IProgressStepExecutionEvents notifications)\r\n+        {\r\n+            var csPluginVersion = this.owner.SonarQubeService.GetPluginVersion(ServerPlugin.CSharpPluginKey, token);\r\n+            if (string.IsNullOrWhiteSpace(csPluginVersion) || VersionHelper.Compare(csPluginVersion, ServerPlugin.CSharpPluginMinimumVersion) < 0)\r\n+            {\r\n+                string errorMessage = string.Format(CultureInfo.CurrentCulture, Strings.ServerDoesNotHaveCorrectVersionOfCSharpPlugin, ServerPlugin.CSharpPluginMinimumVersion);\r\n+                VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, errorMessage);\r\n+\r\n+                this.AbortWorkflow(controller, token);\r\n+            }\r\n+        }\r\n+\r\n+        internal /*for testing purposes*/ void DownloadQualityProfile(IProgressController controller, CancellationToken cancellationToken, IProgressStepExecutionEvents notificationEvents, IEnumerable<string> languages)\r\n+        {\r\n+            Debug.Assert(controller != null);\r\n+            Debug.Assert(notificationEvents != null);\r\n+\r\n+            bool failed = false;\r\n+            Dictionary<string, RuleSet> rulesets = new Dictionary<string, RuleSet>();\r\n+            DeterminateStepProgressNotifier notifier = new DeterminateStepProgressNotifier(notificationEvents, languages.Count());\r\n+\r\n+            foreach (var language in languages)\r\n+            {\r\n+                notifier.NotifyCurrentProgress(string.Format(CultureInfo.CurrentCulture, Strings.DownloadingQualityProfileProgressMessage, language));\r\n+\r\n+                var export = this.owner.SonarQubeService.GetExportProfile(this.project, language, cancellationToken);\r\n+\r\n+                if (export == null)\r\n+                {\r\n+                    failed = true;\r\n+                    break;\r\n+                }\r\n+\r\n+                this.NuGetPackages.AddRange(export.Deployment.NuGetPackages);\r\n+\r\n+                var tempRuleSetFilePath = Path.GetTempFileName();\r\n+                File.WriteAllText(tempRuleSetFilePath, export.Configuration.RuleSet.OuterXml);\r\n+                RuleSet ruleSet = RuleSet.LoadFromFile(tempRuleSetFilePath);\r\n+\r\n+                rulesets[language] = ruleSet;\r\n+                notifier.NotifyIncrementedProgress(string.Empty);\r\n+                if (rulesets[language] == null)\r\n+                {\r\n+                    failed = true;\r\n+                    break;\r\n+                }\r\n+            }\r\n+\r\n+            if (failed)\r\n+            {\r\n+                VsShellUtils.WriteToGeneralOutputPane(this.owner.ServiceProvider, Strings.QualityProfileDownloadFailedMessage);\r\n+                bool aborted = controller.TryAbort();\r\n+                Debug.Assert(aborted || cancellationToken.IsCancellationRequested, \"Failed to abort the workflow\");\r","path":"src/Integration/Binding/BindingWorkflow.cs","position":644,"original_position":644,"commit_id":"4203b5272afe2e66268dc72b491d0a212495fdfb","original_commit_id":"4203b5272afe2e66268dc72b491d0a212495fdfb","user":{"login":"mjcheetham","id":5658207,"avatar_url":"https://avatars.githubusercontent.com/u/5658207?v=3","gravatar_id":"","url":"https://api.github.com/users/mjcheetham","html_url":"https://github.com/mjcheetham","followers_url":"https://api.github.com/users/mjcheetham/followers","following_url":"https://api.github.com/users/mjcheetham/following{/other_user}","gists_url":"https://api.github.com/users/mjcheetham/gists{/gist_id}","starred_url":"https://api.github.com/users/mjcheetham/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mjcheetham/subscriptions","organizations_url":"https://api.github.com/users/mjcheetham/orgs","repos_url":"https://api.github.com/users/mjcheetham/repos","events_url":"https://api.github.com/users/mjcheetham/events{/privacy}","received_events_url":"https://api.github.com/users/mjcheetham/received_events","type":"User","site_admin":false},"body":"Replace with call to `AbortWorkflow`","created_at":"2016-03-11T09:51:45Z","updated_at":"2016-03-11T09:51:45Z","html_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio/pull/30#discussion_r55809712","pull_request_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30","_links":{"self":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/comments/55809712"},"html":{"href":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio/pull/30#discussion_r55809712"},"pull_request":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30"}}},"pull_request":{"url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30","id":62402395,"html_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio/pull/30","diff_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio/pull/30.diff","patch_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio/pull/30.patch","issue_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/30","number":30,"state":"open","locked":false,"title":"[Svs-17] Refactor the code to support the bug fix + the actual fix","user":{"login":"eyalantzMS","id":16574964,"avatar_url":"https://avatars.githubusercontent.com/u/16574964?v=3","gravatar_id":"","url":"https://api.github.com/users/eyalantzMS","html_url":"https://github.com/eyalantzMS","followers_url":"https://api.github.com/users/eyalantzMS/followers","following_url":"https://api.github.com/users/eyalantzMS/following{/other_user}","gists_url":"https://api.github.com/users/eyalantzMS/gists{/gist_id}","starred_url":"https://api.github.com/users/eyalantzMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/eyalantzMS/subscriptions","organizations_url":"https://api.github.com/users/eyalantzMS/orgs","repos_url":"https://api.github.com/users/eyalantzMS/repos","events_url":"https://api.github.com/users/eyalantzMS/events{/privacy}","received_events_url":"https://api.github.com/users/eyalantzMS/received_events","type":"User","site_admin":false},"body":"Refactoring\r\nIntroduced Solution and Project binding operations that have more responsibility than before (some of the responsibility was at the hands of the injector which is gone now)\r\nThe solution binding operation is responsible to new up project binding operations, which in turn responsible to manage the configuration based ruleset generation (this allowed me to fix the bug which ultimately reduces the number of rulesets).\r\n\r\nThe tests were refactored as well and now cover the extra cases required by the bug fix.\r\n\r\nNote:\r\n1. if a project has the same ruleset applied to all the configurations - only one ruleset will be created {project name}.ruleset\r\n2. In other cases, the default rule set will end up in {project name}.ruleset and the configuration specific will be in {project name}.{configuration name}.ruleset.","created_at":"2016-03-10T15:04:01Z","updated_at":"2016-03-11T09:51:45Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3b147561c334f17db8269af6b18e3a25012803af","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30/commits","review_comments_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30/comments","review_comment_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/comments{/number}","comments_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/30/comments","statuses_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/statuses/4203b5272afe2e66268dc72b491d0a212495fdfb","head":{"label":"SonarSource-VisualStudio:SvS-17","ref":"SvS-17","sha":"4203b5272afe2e66268dc72b491d0a212495fdfb","user":{"login":"SonarSource-VisualStudio","id":16780579,"avatar_url":"https://avatars.githubusercontent.com/u/16780579?v=3","gravatar_id":"","url":"https://api.github.com/users/SonarSource-VisualStudio","html_url":"https://github.com/SonarSource-VisualStudio","followers_url":"https://api.github.com/users/SonarSource-VisualStudio/followers","following_url":"https://api.github.com/users/SonarSource-VisualStudio/following{/other_user}","gists_url":"https://api.github.com/users/SonarSource-VisualStudio/gists{/gist_id}","starred_url":"https://api.github.com/users/SonarSource-VisualStudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SonarSource-VisualStudio/subscriptions","organizations_url":"https://api.github.com/users/SonarSource-VisualStudio/orgs","repos_url":"https://api.github.com/users/SonarSource-VisualStudio/repos","events_url":"https://api.github.com/users/SonarSource-VisualStudio/events{/privacy}","received_events_url":"https://api.github.com/users/SonarSource-VisualStudio/received_events","type":"Organization","site_admin":false},"repo":{"id":50510037,"name":"sonarlint-visualstudio","full_name":"SonarSource-VisualStudio/sonarlint-visualstudio","owner":{"login":"SonarSource-VisualStudio","id":16780579,"avatar_url":"https://avatars.githubusercontent.com/u/16780579?v=3","gravatar_id":"","url":"https://api.github.com/users/SonarSource-VisualStudio","html_url":"https://github.com/SonarSource-VisualStudio","followers_url":"https://api.github.com/users/SonarSource-VisualStudio/followers","following_url":"https://api.github.com/users/SonarSource-VisualStudio/following{/other_user}","gists_url":"https://api.github.com/users/SonarSource-VisualStudio/gists{/gist_id}","starred_url":"https://api.github.com/users/SonarSource-VisualStudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SonarSource-VisualStudio/subscriptions","organizations_url":"https://api.github.com/users/SonarSource-VisualStudio/orgs","repos_url":"https://api.github.com/users/SonarSource-VisualStudio/repos","events_url":"https://api.github.com/users/SonarSource-VisualStudio/events{/privacy}","received_events_url":"https://api.github.com/users/SonarSource-VisualStudio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio","description":"","fork":false,"url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio","forks_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/forks","keys_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/teams","hooks_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/hooks","issue_events_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/events{/number}","events_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/events","assignees_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/assignees{/user}","branches_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/branches{/branch}","tags_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/tags","blobs_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/refs{/sha}","trees_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/statuses/{sha}","languages_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/languages","stargazers_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/stargazers","contributors_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/contributors","subscribers_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/subscribers","subscription_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/subscription","commits_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/commits{/sha}","git_commits_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/commits{/sha}","comments_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/comments{/number}","issue_comment_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/comments{/number}","contents_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/contents/{+path}","compare_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/merges","archive_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/downloads","issues_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues{/number}","pulls_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls{/number}","milestones_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/milestones{/number}","notifications_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/labels{/name}","releases_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/releases{/id}","deployments_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/deployments","created_at":"2016-01-27T13:44:12Z","updated_at":"2016-03-09T13:59:22Z","pushed_at":"2016-03-10T19:03:29Z","git_url":"git://github.com/SonarSource-VisualStudio/sonarlint-visualstudio.git","ssh_url":"git@github.com:SonarSource-VisualStudio/sonarlint-visualstudio.git","clone_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio.git","svn_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio","homepage":null,"size":841,"stargazers_count":3,"watchers_count":3,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":3,"forks":5,"open_issues":3,"watchers":3,"default_branch":"master"}},"base":{"label":"SonarSource-VisualStudio:master","ref":"master","sha":"dd71796980c1f3e18bec26793d31171c1ff0bfc0","user":{"login":"SonarSource-VisualStudio","id":16780579,"avatar_url":"https://avatars.githubusercontent.com/u/16780579?v=3","gravatar_id":"","url":"https://api.github.com/users/SonarSource-VisualStudio","html_url":"https://github.com/SonarSource-VisualStudio","followers_url":"https://api.github.com/users/SonarSource-VisualStudio/followers","following_url":"https://api.github.com/users/SonarSource-VisualStudio/following{/other_user}","gists_url":"https://api.github.com/users/SonarSource-VisualStudio/gists{/gist_id}","starred_url":"https://api.github.com/users/SonarSource-VisualStudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SonarSource-VisualStudio/subscriptions","organizations_url":"https://api.github.com/users/SonarSource-VisualStudio/orgs","repos_url":"https://api.github.com/users/SonarSource-VisualStudio/repos","events_url":"https://api.github.com/users/SonarSource-VisualStudio/events{/privacy}","received_events_url":"https://api.github.com/users/SonarSource-VisualStudio/received_events","type":"Organization","site_admin":false},"repo":{"id":50510037,"name":"sonarlint-visualstudio","full_name":"SonarSource-VisualStudio/sonarlint-visualstudio","owner":{"login":"SonarSource-VisualStudio","id":16780579,"avatar_url":"https://avatars.githubusercontent.com/u/16780579?v=3","gravatar_id":"","url":"https://api.github.com/users/SonarSource-VisualStudio","html_url":"https://github.com/SonarSource-VisualStudio","followers_url":"https://api.github.com/users/SonarSource-VisualStudio/followers","following_url":"https://api.github.com/users/SonarSource-VisualStudio/following{/other_user}","gists_url":"https://api.github.com/users/SonarSource-VisualStudio/gists{/gist_id}","starred_url":"https://api.github.com/users/SonarSource-VisualStudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SonarSource-VisualStudio/subscriptions","organizations_url":"https://api.github.com/users/SonarSource-VisualStudio/orgs","repos_url":"https://api.github.com/users/SonarSource-VisualStudio/repos","events_url":"https://api.github.com/users/SonarSource-VisualStudio/events{/privacy}","received_events_url":"https://api.github.com/users/SonarSource-VisualStudio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio","description":"","fork":false,"url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio","forks_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/forks","keys_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/teams","hooks_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/hooks","issue_events_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/events{/number}","events_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/events","assignees_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/assignees{/user}","branches_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/branches{/branch}","tags_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/tags","blobs_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/refs{/sha}","trees_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/statuses/{sha}","languages_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/languages","stargazers_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/stargazers","contributors_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/contributors","subscribers_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/subscribers","subscription_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/subscription","commits_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/commits{/sha}","git_commits_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/git/commits{/sha}","comments_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/comments{/number}","issue_comment_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/comments{/number}","contents_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/contents/{+path}","compare_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/merges","archive_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/downloads","issues_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues{/number}","pulls_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls{/number}","milestones_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/milestones{/number}","notifications_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/labels{/name}","releases_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/releases{/id}","deployments_url":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/deployments","created_at":"2016-01-27T13:44:12Z","updated_at":"2016-03-09T13:59:22Z","pushed_at":"2016-03-10T19:03:29Z","git_url":"git://github.com/SonarSource-VisualStudio/sonarlint-visualstudio.git","ssh_url":"git@github.com:SonarSource-VisualStudio/sonarlint-visualstudio.git","clone_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio.git","svn_url":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio","homepage":null,"size":841,"stargazers_count":3,"watchers_count":3,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":3,"forks":5,"open_issues":3,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30"},"html":{"href":"https://github.com/SonarSource-VisualStudio/sonarlint-visualstudio/pull/30"},"issue":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/30"},"comments":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/issues/30/comments"},"review_comments":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30/comments"},"review_comment":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/pulls/30/commits"},"statuses":{"href":"https://api.github.com/repos/SonarSource-VisualStudio/sonarlint-visualstudio/statuses/4203b5272afe2e66268dc72b491d0a212495fdfb"}}}}