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
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad')
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AbstractGroupingProvider.cs115
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ActionSummary.cs65
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisState.cs37
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisStateChangeEventArgs.cs50
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/BatchFixer.cs141
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CategoryGroupingProvider.cs47
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeAnalysisBatchRunner.cs163
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeIssuePad.cs534
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ExactIssueMatcher.cs52
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/FileGroupingProvider.cs46
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingDescriptionAttribute.cs40
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderChainControl.cs153
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderEventArgs.cs52
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IActionMatcher.cs36
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IGroupingProvider.cs67
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueSummarySink.cs40
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueTreeNode.cs87
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroup.cs326
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroupEventArgs.cs40
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueMatch.cs38
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueSummary.cs236
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueTreeNodeEventArgs.cs43
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/NullGroupingProvider.cs82
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProjectGroupingProvider.cs45
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProviderGroupingProvider.cs45
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/AnalysisJobQueue.cs121
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/CodeIssueEventArgs.cs70
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IAnalysisJob.cs93
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IJobContext.cs35
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobContext.cs66
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobSlice.cs162
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobStatus.cs86
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/ProgressMonitorWrapperJob.cs126
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/SimpleAnalysisJob.cs142
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SeverityGroupingProvider.cs45
-rw-r--r--main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SolutionAnalysisJob.cs72
36 files changed, 3598 insertions, 0 deletions
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AbstractGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AbstractGroupingProvider.cs
new file mode 100644
index 0000000000..3f7cd390c7
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AbstractGroupingProvider.cs
@@ -0,0 +1,115 @@
+//
+// AbstractGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public abstract class AbstractGroupingProvider<T>: IGroupingProvider
+ {
+ /// <summary>
+ /// Associates a parent group and grouping key to a child group.
+ /// </summary>
+ readonly Dictionary<Tuple<IssueGroup, T>, IssueGroup> groups = new Dictionary<Tuple<IssueGroup, T>, IssueGroup> ();
+
+ protected AbstractGroupingProvider()
+ {
+ Next = NullGroupingProvider.Instance;
+ }
+
+ #region IGroupingProvider implementation
+
+ protected abstract T GetGroupingKey (IssueSummary issue);
+
+ protected abstract string GetGroupName (IssueSummary issue);
+
+ public IssueGroup GetIssueGroup (IssueGroup parentGroup, IssueSummary issue)
+ {
+ IssueGroup group;
+ var providerCategory = GetGroupingKey (issue);
+ if (providerCategory == null)
+ return null;
+ var key = Tuple.Create (parentGroup, providerCategory);
+ if (!groups.TryGetValue (key, out group)) {
+ group = new IssueGroup (Next, GetGroupName(issue));
+ groups.Add (key, group);
+ }
+ return group;
+ }
+
+ public void Reset ()
+ {
+ groups.Clear ();
+ Next.Reset ();
+ }
+
+ IGroupingProvider next;
+ public IGroupingProvider Next
+ {
+ get {
+ return next;
+ }
+ set {
+ var eventArgs = new GroupingProviderEventArgs (this, next);
+ next = value;
+ foreach (var group in groups.Values) {
+ group.GroupingProvider = value;
+ }
+ OnNextChanged (eventArgs);
+ }
+ }
+
+ protected virtual void OnNextChanged (GroupingProviderEventArgs eventArgs)
+ {
+ var handler = nextChanged;
+ if (handler != null) {
+ handler (this, eventArgs);
+ }
+ }
+
+ event EventHandler<GroupingProviderEventArgs> nextChanged;
+
+ event EventHandler<GroupingProviderEventArgs> IGroupingProvider.NextChanged
+ {
+ add {
+ nextChanged += value;
+ }
+ remove {
+ nextChanged -= value;
+ }
+ }
+
+ public bool SupportsNext
+ {
+ get {
+ return true;
+ }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ActionSummary.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ActionSummary.cs
new file mode 100644
index 0000000000..7a560e69fd
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ActionSummary.cs
@@ -0,0 +1,65 @@
+//
+// IGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Ide.Editor;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class ActionSummary
+ {
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ /// <value>The title.</value>
+ public string Title { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sibling key.
+ /// </summary>
+ /// <value>The sibling key.</value>
+ public object SiblingKey { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="MonoDevelop.CodeIssues.ActionSummary"/> is batchable.
+ /// </summary>
+ /// <value><c>true</c> if batchable; otherwise, <c>false</c>.</value>
+ public bool Batchable { get; set; }
+
+ public DocumentRegion Region { get; set; }
+
+ /// <summary>
+ /// Gets or sets the <see cref="IssueSummary"/> representing the source of this action.
+ /// </summary>
+ /// <value>The issue summary.</value>
+ public IssueSummary IssueSummary { get; set; }
+
+ public override string ToString ()
+ {
+ return string.Format ("[ActionSummary: Title={0}, Batchable={1}, Region={2}]", Title, Batchable, Region);
+ }
+ }
+
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisState.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisState.cs
new file mode 100644
index 0000000000..221e11d819
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisState.cs
@@ -0,0 +1,37 @@
+//
+// AnalysisState.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.
+
+namespace MonoDevelop.CodeIssues
+{
+ public enum AnalysisState {
+ NeverStarted,
+ Running,
+ Completed,
+ Cancelled,
+ Error
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisStateChangeEventArgs.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisStateChangeEventArgs.cs
new file mode 100644
index 0000000000..7531189f08
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/AnalysisStateChangeEventArgs.cs
@@ -0,0 +1,50 @@
+//
+// AnalysisStateChangeEventArgs.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class AnalysisStateChangeEventArgs : EventArgs
+ {
+ public AnalysisStateChangeEventArgs (AnalysisState oldState, AnalysisState newState)
+ {
+ OldState = oldState;
+ NewState = newState;
+ }
+
+ public AnalysisState OldState {
+ get;
+ private set;
+ }
+
+ public AnalysisState NewState {
+ get;
+ private set;
+ }
+ }
+
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/BatchFixer.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/BatchFixer.cs
new file mode 100644
index 0000000000..c4c6491f05
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/BatchFixer.cs
@@ -0,0 +1,141 @@
+//
+// BatchFixer.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Collections.Generic;
+using MonoDevelop.Ide;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+using MonoDevelop.Core;
+using System.Threading.Tasks;
+using MonoDevelop.Refactoring;
+using ICSharpCode.NRefactory.Refactoring;
+using System.Threading;
+using MonoDevelop.Projects;
+using MonoDevelop.Ide.TypeSystem;
+using ICSharpCode.NRefactory.CSharp;
+using System.Text;
+using MonoDevelop.Ide.Editor;
+using MonoDevelop.Core.Text;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class BatchFixer
+ {
+ readonly IActionMatcher matcher;
+
+ readonly ProgressMonitor monitor;
+
+ public BatchFixer (IActionMatcher matcher, ProgressMonitor monitor)
+ {
+ this.matcher = matcher;
+ this.monitor = monitor;
+ }
+
+ /// <summary>
+ /// Tries to apply the actions passed in <paramref name="actions"/>.
+ /// </summary>
+ /// <param name="actions">The actions to apply.</param>
+ /// <returns>The fixed code actions.</returns>
+ public IEnumerable<ActionSummary> TryFixIssues (IEnumerable<ActionSummary> actions)
+ {
+ if (actions == null)
+ throw new ArgumentNullException ("actions");
+
+ // enumerate once
+ var actionSummaries = actions as IList<ActionSummary> ?? actions.ToList ();
+ var issueSummaries = actionSummaries.Select (action => action.IssueSummary).ToList ();
+ var files = issueSummaries.Select (issue => issue.File).Distinct ().ToList ();
+ monitor.BeginTask ("Applying fixes", files.Count);
+
+ var appliedActions = new List<ActionSummary> (issueSummaries.Count);
+ Parallel.ForEach (files, file => {
+ monitor.Step (1);
+
+ var fileSummaries = issueSummaries.Where (summary => summary.File == file);
+ var inspectorIds = new HashSet<string> (fileSummaries.Select (summary => summary.InspectorIdString));
+
+ bool isOpen;
+ var data = TextFileProvider.Instance.GetTextEditorData (file.FilePath, out isOpen);
+ IRefactoringContext refactoringContext;
+ var realActions = GetIssues (data, file, inspectorIds, out refactoringContext).SelectMany (issue => issue.Actions).ToList ();
+ if (realActions.Count == 0 || refactoringContext == null)
+ return;
+
+ var fileActionSummaries = actionSummaries.Where (summary => summary.IssueSummary.File == file).ToList ();
+ var matches = matcher.Match (fileActionSummaries, realActions).ToList ();
+
+ var appliedFixes = RefactoringService.ApplyFixes (matches.Select (match => match.Action), refactoringContext);
+ appliedActions.AddRange (matches.Where (match => appliedFixes.Contains (match.Action)).Select (match => match.Summary));
+
+ if (!isOpen) {
+ // If the file is open we leave it to the user to explicitly save the file
+ data.Save ();
+ }
+ });
+ return appliedActions;
+ }
+
+ static IList<CodeIssue> GetIssues (ITextDocument data, ProjectFile file, ISet<string> inspectorIds, out IRefactoringContext refactoringContext)
+ {
+ var issues = new List<CodeIssue> ();
+//
+// var document = TypeSystemService.ParseFile (file.Project, data);
+// if (document == null) {
+ refactoringContext = null;
+// return issues;
+// }
+//
+// var content = TypeSystemService.GetProjectContext (file.Project);
+// var compilation = content.AddOrUpdateFiles (document.ParsedFile).CreateCompilation ();
+// var resolver = new CSharpAstResolver (compilation, document.GetAst<SyntaxTree> (), document.ParsedFile as ICSharpCode.NRefactory.CSharp.TypeSystem.CSharpUnresolvedFile);
+//
+// refactoringContext = document.CreateRefactoringContextWithEditor (data, resolver, CancellationToken.None);
+// var context = refactoringContext;
+// foreach (var provider in GetInspectors (data, inspectorIds)) {
+// var severity = provider.GetSeverity ();
+// if (severity == Severity.None || !provider.GetIsEnabled ())
+// continue;
+// try {
+// lock (issues) {
+// issues.AddRange (provider.GetIssues (context, CancellationToken.None));
+// }
+// } catch (Exception ex) {
+// LoggingService.LogError ("Error while running code issue on: " + data.FileName, ex);
+// }
+// }
+ return issues;
+ }
+
+ static IList<CodeIssueProvider> GetInspectors (IReadonlyTextDocument editor, ICollection<string> inspectorIds)
+ {
+ var inspectors = RefactoringService.GetInspectors (editor.MimeType).ToList ();
+ return inspectors
+ .Where (inspector => inspectorIds.Contains (inspector.IdString))
+ .ToList ();
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CategoryGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CategoryGroupingProvider.cs
new file mode 100644
index 0000000000..bb7094f20d
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CategoryGroupingProvider.cs
@@ -0,0 +1,47 @@
+//
+// ProjectGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.
+
+namespace MonoDevelop.CodeIssues
+{
+ // TODO: should this be a threadsafe class?
+ // Our current usage is fine, but could be nice anyway (and might avoid future bugs)
+ [GroupingDescription("Category")]
+ public class CategoryGroupingProvider: AbstractGroupingProvider<string>
+ {
+ #region implemented abstract members of AbstractGroupingProvider
+ protected override string GetGroupingKey (IssueSummary issue)
+ {
+ return issue.ProviderCategory;
+ }
+
+ protected override string GetGroupName (IssueSummary issue)
+ {
+ return issue.ProviderCategory;
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeAnalysisBatchRunner.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeAnalysisBatchRunner.cs
new file mode 100644
index 0000000000..a2e9b91a5e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeAnalysisBatchRunner.cs
@@ -0,0 +1,163 @@
+//
+// CodeIssueBatchRunner.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Threading;
+using MonoDevelop.Ide;
+using MonoDevelop.Projects;
+using MonoDevelop.Ide.TypeSystem;
+using System.Threading.Tasks;
+using System.IO;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.Refactoring;
+using MonoDevelop.Core;
+using System.Collections.Concurrent;
+using ICSharpCode.NRefactory.TypeSystem;
+using MonoDevelop.Core.Instrumentation;
+using MonoDevelop.Refactoring;
+using System.Collections.Generic;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class CodeAnalysisBatchRunner
+ {
+ private readonly object _lock = new object ();
+
+ private int workerCount;
+
+ private readonly AnalysisJobQueue jobQueue = new AnalysisJobQueue ();
+
+ public IJobContext QueueJob (IAnalysisJob job)
+ {
+ jobQueue.Add (job);
+ EnsureRunning ();
+ return new JobContext (job, jobQueue, this);
+ }
+
+ private void EnsureRunning ()
+ {
+ for (; Interlocked.Add (ref workerCount, 1) < Environment.ProcessorCount;) {
+ new Thread (() => {
+ try {
+ ProcessQueue ();
+ }
+ finally {
+ Interlocked.Add (ref workerCount, -1);
+ }
+ }).Start ();
+ }
+ }
+
+ private void ProcessQueue ()
+ {
+ while (true) {
+ try {
+ using (var slice = GetSlice ()) {
+ if (slice == null)
+ // TODO: Do something smarter if the queue is empty
+ return;
+ AnalyzeFile (slice, slice.GetJobs ().SelectMany (job => job.GetIssueProviders (slice.File)));
+ }
+ } catch (Exception e) {
+ LoggingService.LogError ("Unhandled exception", e);
+ MessageService.ShowException (e);
+ }
+ }
+ }
+
+ private JobSlice GetSlice ()
+ {
+ lock (_lock) {
+ return jobQueue.Dequeue (1).FirstOrDefault ();
+ }
+ }
+
+ void AnalyzeFile (JobSlice item, IEnumerable<BaseCodeIssueProvider> codeIssueProviders)
+ {
+// var file = item.File;
+//
+// if (file.BuildAction != BuildAction.Compile)
+// return;
+//
+// if (!(file.Project is DotNetProject))
+// return;
+//
+// TextEditorData editor;
+// try {
+// editor = TextFileProvider.Instance.GetReadOnlyTextEditorData (file.FilePath);
+// } catch (FileNotFoundException) {
+// // Swallow exception and ignore this file
+// return;
+// }
+// var document = TypeSystemService.ParseFile (file.Project, editor);
+// if (document == null)
+// return;
+//
+// var content = TypeSystemService.GetProjectContext (file.Project);
+// var compilation = content.AddOrUpdateFiles (document.ParsedFile).CreateCompilation ();
+//
+// CSharpAstResolver resolver;
+// using (var timer = ExtensionMethods.ResolveCounter.BeginTiming ()) {
+// resolver = new CSharpAstResolver (compilation, document.GetAst<SyntaxTree> (), document.ParsedFile as ICSharpCode.NRefactory.CSharp.TypeSystem.CSharpUnresolvedFile);
+// try {
+// resolver.ApplyNavigator (new ExtensionMethods.ConstantModeResolveVisitorNavigator (ResolveVisitorNavigationMode.Resolve, null));
+// } catch (Exception e) {
+// LoggingService.LogError ("Error while applying navigator", e);
+// }
+// }
+// var context = document.CreateRefactoringContextWithEditor (editor, resolver, CancellationToken.None);
+//
+// foreach (var provider in codeIssueProviders) {
+// if (item.CancellationToken.IsCancellationRequested)
+// break;
+// IList<IAnalysisJob> jobs;
+// lock (_lock)
+// jobs = item.GetJobs ().ToList ();
+// var jobsForProvider = jobs.Where (j => j.GetIssueProviders (file).Contains (provider)).ToList();
+// try {
+// var issues = provider.GetIssues (context, CancellationToken.None).ToList ();
+// foreach (var job in jobsForProvider) {
+// // Call AddResult even if issues.Count == 0, to enable a job implementation to keep
+// // track of progress information.
+// job.AddResult (file, provider, issues);
+// }
+// } catch (OperationCanceledException) {
+// // The operation was cancelled, no-op as the user-visible parts are
+// // handled elsewhere
+// } catch (Exception e) {
+// foreach (var job in jobsForProvider) {
+// job.AddError (file, provider);
+// }
+// }
+// }
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeIssuePad.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeIssuePad.cs
new file mode 100644
index 0000000000..c875894512
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/CodeIssuePad.cs
@@ -0,0 +1,534 @@
+//
+// CodeIssuePad.cs
+//
+// Author:
+// Mike Krüger <mkrueger@xamarin.com>
+//
+// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
+//
+// 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.Ide.Gui;
+using Xwt;
+using MonoDevelop.Ide;
+using MonoDevelop.Projects;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory.TypeSystem;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using MonoDevelop.Refactoring;
+using Xwt.Drawing;
+using IconSize = Gtk.IconSize;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class CodeIssuePadControl : VBox
+ {
+ const int UpdatePeriod = 500;
+ const int BatchChoiceCount = 5;
+
+ readonly TreeView view = new TreeView ();
+ readonly DataField<string> textField = new DataField<string> ();
+ readonly DataField<IIssueTreeNode> nodeField = new DataField<IIssueTreeNode> ();
+ readonly Button runButton = new Button ("Run");
+ readonly Button cancelButton = new Button ("Cancel");
+
+ readonly IssueGroup rootGroup;
+
+ readonly TreeStore store;
+
+ readonly ISet<IIssueTreeNode> syncedNodes = new HashSet<IIssueTreeNode> ();
+ readonly Dictionary<IIssueTreeNode, TreePosition> nodePositions = new Dictionary<IIssueTreeNode, TreePosition> ();
+
+ bool runPeriodicUpdate;
+ readonly object queueLock = new object ();
+ readonly Queue<IIssueTreeNode> updateQueue = new Queue<IIssueTreeNode> ();
+
+ IJobContext currentJobContext;
+
+ public IJobContext CurrentJobContext {
+ get {
+ return currentJobContext;
+ }
+ set {
+ currentJobContext = value;
+ bool working = currentJobContext != null;
+ runButton.Sensitive = !working;
+ cancelButton.Sensitive = working;
+ }
+ }
+
+ static readonly Type[] groupingProviders = {
+ typeof(CategoryGroupingProvider),
+ typeof(ProviderGroupingProvider),
+ typeof(SeverityGroupingProvider),
+ typeof(ProjectGroupingProvider),
+ typeof(FileGroupingProvider)
+ };
+
+ public CodeIssuePadControl ()
+ {
+ var buttonRow = new HBox();
+ runButton.Image = ImageService.GetIcon (Ide.Gui.Stock.Execute, IconSize.Menu);
+ runButton.Clicked += StartAnalyzation;
+ buttonRow.PackStart (runButton);
+
+ cancelButton.Image = ImageService.GetIcon (Ide.Gui.Stock.Stop, IconSize.Menu);
+ cancelButton.Clicked += StopAnalyzation;
+ cancelButton.Sensitive = false;
+ buttonRow.PackStart (cancelButton);
+ var groupingProvider = new CategoryGroupingProvider {
+ Next = new ProviderGroupingProvider()
+ };
+ rootGroup = new IssueGroup (groupingProvider, "root group");
+ var groupingProviderControl = new GroupingProviderChainControl (rootGroup, groupingProviders);
+ buttonRow.PackStart (groupingProviderControl);
+
+ PackStart (buttonRow);
+
+ store = new TreeStore (textField, nodeField);
+ view.DataSource = store;
+ view.HeadersVisible = false;
+ view.Columns.Add ("Name", textField);
+ view.SelectionMode = SelectionMode.Multiple;
+
+ view.RowActivated += OnRowActivated;
+ view.RowExpanding += OnRowExpanding;
+ view.ButtonPressed += HandleButtonPressed;
+ view.ButtonReleased += HandleButtonReleased;
+ PackStart (view, true);
+
+ IIssueTreeNode node = rootGroup;
+ node.ChildrenInvalidated += (sender, group) => {
+ Application.Invoke (delegate {
+ ClearSiblingNodes (store.GetFirstNode ());
+ store.Clear ();
+ foreach(var child in ((IIssueTreeNode)rootGroup).Children) {
+ var navigator = store.AddNode ();
+ SetNode (navigator, child);
+ SyncNode (navigator);
+ }
+ });
+ };
+ node.ChildAdded += HandleRootChildAdded;
+
+ IdeApp.Workspace.LastWorkspaceItemClosed += HandleLastWorkspaceItemClosed;
+ }
+
+ void HandleLastWorkspaceItemClosed (object sender, EventArgs e)
+ {
+ ClearState ();
+ }
+
+ void ClearState ()
+ {
+ store.Clear ();
+ rootGroup.ClearStatistics ();
+ rootGroup.EnableProcessing ();
+
+ syncedNodes.Clear ();
+ nodePositions.Clear ();
+ lock (queueLock) {
+ updateQueue.Clear ();
+ }
+ }
+
+ void StartPeriodicUpdate ()
+ {
+ Debug.Assert (!runPeriodicUpdate);
+ runPeriodicUpdate = true;
+ Application.TimeoutInvoke (UpdatePeriod, RunPeriodicUpdate);
+ }
+
+ void ProcessUpdateQueue ()
+ {
+ IList<IIssueTreeNode> nodes;
+ lock (queueLock) {
+ nodes = new List<IIssueTreeNode> (updateQueue);
+ updateQueue.Clear ();
+ }
+ foreach (var node in nodes) {
+ TreePosition position;
+ if (!nodePositions.TryGetValue (node, out position)) {
+ // This might be an event for a group that has been invalidated and removed
+ continue;
+ }
+ var navigator = store.GetNavigatorAt (position);
+ if (!node.Visible) {
+ // Check above means node is always in nodePositions
+ nodePositions.Remove (node);
+ if (syncedNodes.Contains (node)) {
+ syncedNodes.Remove (node);
+ }
+ ClearChildNodes (navigator);
+ navigator.Remove ();
+ continue;
+ }
+ UpdateText (navigator, node);
+ if (!syncedNodes.Contains (node) && node.HasVisibleChildren) {
+ if (navigator.MoveToChild ()) {
+ navigator.MoveToParent ();
+ }
+ else {
+ AddDummyChild (navigator);
+ }
+ }
+ }
+ }
+
+ bool RunPeriodicUpdate ()
+ {
+ ProcessUpdateQueue ();
+ return runPeriodicUpdate;
+ }
+
+ void EndPeriodicUpdate ()
+ {
+ Debug.Assert (runPeriodicUpdate);
+ runPeriodicUpdate = false;
+ }
+
+ void HandleRootChildAdded (object sender, IssueTreeNodeEventArgs e)
+ {
+ Application.Invoke (delegate {
+ Debug.Assert (e.Parent == rootGroup);
+ var navigator = store.AddNode ();
+ SetNode (navigator, e.Child);
+ SyncNode (navigator);
+ });
+ }
+
+ void StartAnalyzation (object sender, EventArgs e)
+ {
+ var solution = IdeApp.ProjectOperations.CurrentSelectedSolution;
+ if (solution == null)
+ return;
+
+ ClearState ();
+
+ var job = new SolutionAnalysisJob (solution);
+ job.CodeIssueAdded += HandleCodeIssueAdded;
+ job.Completed += delegate {
+ CurrentJobContext = null;
+ };
+ CurrentJobContext = RefactoringService.QueueCodeIssueAnalysis (job, "Analyzing solution");
+ StartPeriodicUpdate ();
+ }
+
+ void HandleCodeIssueAdded (object sender, CodeIssueEventArgs e)
+ {
+ foreach (var issue in e.CodeIssues) {
+ var summary = IssueSummary.FromCodeIssue (e.File, e.Provider, issue);
+ rootGroup.AddIssue (summary);
+ }
+ }
+
+ void StopAnalyzation (object sender, EventArgs e)
+ {
+ if (CurrentJobContext != null) {
+ CurrentJobContext.CancelJob ();
+ CurrentJobContext = null;
+ }
+ EndPeriodicUpdate ();
+ }
+
+ void SetNode (TreeNavigator navigator, IIssueTreeNode node)
+ {
+ if (navigator == null)
+ throw new ArgumentNullException ("navigator");
+ if (node == null)
+ throw new ArgumentNullException ("node");
+
+ navigator.SetValue (nodeField, node);
+ Debug.Assert (!nodePositions.ContainsKey (node));
+ var position = navigator.CurrentPosition;
+ nodePositions.Add (node, position);
+
+ node.ChildAdded += (sender, e) => {
+ Debug.Assert (e.Parent == node);
+ Application.Invoke (delegate {
+ var newNavigator = store.GetNavigatorAt (position);
+ newNavigator.AddChild ();
+ SetNode (newNavigator, e.Child);
+ SyncNode (newNavigator);
+ });
+ };
+ node.ChildrenInvalidated += (sender, e) => {
+ Application.Invoke (delegate {
+ SyncNode (store.GetNavigatorAt (position));
+ });
+ };
+ node.TextChanged += (sender, e) => {
+ lock (queueLock) {
+ if (!updateQueue.Contains (e.Node)) {
+ updateQueue.Enqueue (e.Node);
+ }
+ }
+ };
+ node.VisibleChanged += (sender, e) => {
+ lock (queueLock) {
+ if (!updateQueue.Contains (e.Node)) {
+ updateQueue.Enqueue (e.Node);
+ }
+ }
+ };
+ }
+
+ void ClearSiblingNodes (TreeNavigator navigator)
+ {
+ if (navigator.CurrentPosition == null)
+ return;
+
+ do {
+ var node = navigator.GetValue (nodeField);
+ if (node != null) {
+ if (syncedNodes.Contains (node)) {
+ syncedNodes.Remove (node);
+ }
+ if (nodePositions.ContainsKey (node)) {
+ nodePositions.Remove (node);
+ }
+ }
+ ClearChildNodes (navigator);
+ } while (navigator.MoveNext ());
+ }
+
+ void ClearChildNodes (TreeNavigator navigator)
+ {
+ if (navigator.MoveToChild ()) {
+ ClearSiblingNodes (navigator);
+ navigator.MoveToParent ();
+ }
+ }
+
+ void SyncNode (TreeNavigator navigator, bool forceExpansion = false)
+ {
+ var node = navigator.GetValue (nodeField);
+ UpdateText (navigator, node);
+ bool isExpanded = forceExpansion || view.IsRowExpanded (navigator.CurrentPosition);
+ ClearChildNodes (navigator);
+ syncedNodes.Remove (node);
+ navigator.RemoveChildren ();
+ if (!node.HasVisibleChildren)
+ return;
+ if (isExpanded) {
+ foreach (var childNode in node.Children.Where (child => child.Visible)) {
+ navigator.AddChild ();
+ SetNode (navigator, childNode);
+ SyncNode (navigator);
+ navigator.MoveToParent ();
+ }
+ } else {
+ AddDummyChild (navigator);
+ }
+
+ if (isExpanded) {
+ syncedNodes.Add (node);
+ view.ExpandRow (navigator.CurrentPosition, false);
+ }
+
+ }
+
+ void UpdateText (TreeNavigator navigator, IIssueTreeNode node)
+ {
+ navigator.SetValue (textField, node.Text);
+ }
+
+ void AddDummyChild (TreeNavigator navigator)
+ {
+ navigator.AddChild ();
+ navigator.SetValue (textField, "Loading...");
+ navigator.MoveToParent ();
+ }
+
+ EventHandler<IssueGroupEventArgs> GetChildrenInvalidatedHandler (TreePosition position)
+ {
+ return (sender, eventArgs) => {
+ Application.Invoke(delegate {
+ var expanded = view.IsRowExpanded (position);
+ var newNavigator = store.GetNavigatorAt (position);
+ newNavigator.RemoveChildren ();
+ SyncNode (newNavigator, expanded);
+ if (expanded) {
+ view.ExpandRow (position, false);
+ }
+ });
+ };
+ }
+
+ void OnRowActivated (object sender, TreeViewRowEventArgs e)
+ {
+ var position = e.Position;
+ var node = store.GetNavigatorAt (position).GetValue (nodeField);
+
+ var issueSummary = node as IssueSummary;
+ if (issueSummary != null) {
+ var region = issueSummary.Region;
+ IdeApp.Workbench.OpenDocument (region.FileName, region.BeginLine, region.BeginColumn);
+ } else {
+ if (!view.IsRowExpanded (position)) {
+ view.ExpandRow (position, false);
+ } else {
+ view.CollapseRow (position);
+ }
+ }
+ }
+
+ void OnRowExpanding (object sender, TreeViewRowEventArgs e)
+ {
+ var navigator = store.GetNavigatorAt (e.Position);
+ var node = navigator.GetValue (nodeField);
+ if (!syncedNodes.Contains (node)) {
+ SyncNode (navigator, true);
+ }
+ }
+
+ #region Button event handlers
+ // Event handling of right click on the TreeView is split in two parts
+ // This is because no single handler can support intuitive behavior regarding
+ // what happens to the selection when the right mouse button is pressed:
+ // if only a single row is selected: change the selection and then show menu
+ // if multiple rows are selected: show the menu directly
+
+ void HandleButtonReleased (object sender, ButtonEventArgs e)
+ {
+ if (e.Button != PointerButton.Right || handledByPress)
+ return;
+
+ var rows = view.SelectedRows;
+ if (rows.Length <= 1) {
+ // Single row or no row
+ ShowBatchFixContextMenu (e.X, e.Y, view.SelectedRows);
+ }
+ }
+
+ bool handledByPress;
+
+ void HandleButtonPressed (object sender, ButtonEventArgs e)
+ {
+ if (e.Button != PointerButton.Right)
+ return;
+
+ var rows = view.SelectedRows;
+ if (rows.Length > 1) {
+ // this is a multiple selection
+ // waiting in this case means the selection disappears
+ ShowBatchFixContextMenu (e.X, e.Y, rows);
+
+ // Don't let the selection be reset
+ e.Handled = true;
+ handledByPress = true;
+ } else {
+ handledByPress = false;
+ }
+ }
+
+ #endregion
+
+ void UpdateParents (TreeNavigator navigator)
+ {
+ do {
+ var node = navigator.GetValue (nodeField);
+ UpdateText (navigator, node);
+ } while (navigator.MoveToParent ());
+ }
+
+ void ShowBatchFixContextMenu (double x, double y, IEnumerable<TreePosition> rows)
+ {
+ var possibleFixes = rows
+ .Select (row => store.GetNavigatorAt (row).GetValue (nodeField))
+ .Where (node1 => node1 != null)
+ .SelectMany (node2 => node2.AllChildren.Union (new [] { node2 }))
+ .Where (node3 => node3.Visible)
+ .OfType<IssueSummary> ()
+ .Where (issue => issue.Actions.Any (a => a.Batchable))
+ .Distinct()
+ .GroupBy(issue => issue.InspectorIdString)
+ .OrderBy (group => -group.Count ());
+
+ var groups = possibleFixes.Take (BatchChoiceCount).ToList ();
+ if (!groups.Any ())
+ return;
+
+ if (groups.Count == 1) {
+ CreateIssueMenu (groups.First ()).Popup (view, x, y);
+ } else {
+ var menu = new Menu ();
+ foreach (var g in groups) {
+ var menuItem = new MenuItem (g.First ().ProviderTitle);
+ menuItem.SubMenu = CreateIssueMenu (g);
+ menu.Items.Add (menuItem);
+ }
+ menu.Popup (view, x, y);
+ }
+ }
+
+ Menu CreateIssueMenu (IEnumerable<IssueSummary> issues)
+ {
+ var allIssues = issues as IList<IssueSummary> ?? issues.ToList ();
+ var issueMenu = new Menu ();
+
+ var actionGroups = allIssues
+ .SelectMany (issue => issue.Actions)
+ .GroupBy (action => action.SiblingKey);
+ foreach (var _actionGroup in actionGroups) {
+ var actionGroup = _actionGroup;
+
+ var actionMenuItem = new MenuItem (actionGroup.First ().Title);
+ actionMenuItem.Clicked += delegate {
+ ThreadPool.QueueUserWorkItem (delegate {
+ try {
+ using (var monitor = IdeApp.Workbench.ProgressMonitors.GetStatusProgressMonitor ("Applying fixes", null, false)) {
+ var fixer = new BatchFixer (new ExactIssueMatcher (), monitor);
+ var appliedActions = fixer.TryFixIssues (actionGroup);
+ foreach (var action in appliedActions) {
+ ((IIssueTreeNode)action.IssueSummary).Visible = false;
+ }
+ }
+ Application.Invoke (delegate {
+ ProcessUpdateQueue ();
+ });
+ } catch (Exception e) {
+ LoggingService.LogInternalError (e);
+ }
+ });
+ };
+ issueMenu.Items.Add (actionMenuItem);
+ }
+ return issueMenu;
+ }
+
+ }
+
+ public class CodeIssuePad : AbstractPadContent
+ {
+ CodeIssuePadControl issueControl;
+
+ public override Gtk.Widget Control {
+ get {
+ if (issueControl == null)
+ issueControl = new CodeIssuePadControl ();
+ return (Gtk.Widget)Toolkit.CurrentEngine.GetNativeWidget (issueControl);
+ }
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ExactIssueMatcher.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ExactIssueMatcher.cs
new file mode 100644
index 0000000000..3f1db8df72
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ExactIssueMatcher.cs
@@ -0,0 +1,52 @@
+//
+// ExactIssueMatcher.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Collections.Generic;
+using MonoDevelop.CodeActions;
+using System.Linq;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class ExactIssueMatcher: IActionMatcher
+ {
+ #region IIssueMatcher implementation
+
+ public IEnumerable<IssueMatch> Match (IList<ActionSummary> summaries, IList<CodeAction> realActions)
+ {
+ var summaryLookup = summaries.ToLookup (summary => summary.Region);
+ foreach (var action in realActions) {
+ if (summaryLookup.Contains (action.DocumentRegion)) {
+ yield return new IssueMatch {
+ Action = action,
+ Summary = summaryLookup[action.DocumentRegion].First ()
+ };
+ }
+ }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/FileGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/FileGroupingProvider.cs
new file mode 100644
index 0000000000..aa1d64d7cf
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/FileGroupingProvider.cs
@@ -0,0 +1,46 @@
+//
+// FileGroupingProvider.cs
+//
+// Author:
+// Marius Ungureanu <marius.ungureanu@xamarin.com>
+//
+// Copyright (c) 2013 Marius Ungureanu
+//
+// 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.Projects;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CodeIssues
+{
+ [GroupingDescription("File")]
+ public class FileGroupingProvider : AbstractGroupingProvider<ProjectFile>
+ {
+ #region implemented abstract members of AbstractGroupingProvider
+ protected override ProjectFile GetGroupingKey (IssueSummary issue)
+ {
+ return issue.File;
+ }
+ protected override string GetGroupName (IssueSummary issue)
+ {
+ return issue.File.FilePath.ToRelative (issue.Project.BaseDirectory);
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingDescriptionAttribute.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingDescriptionAttribute.cs
new file mode 100644
index 0000000000..720084149e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingDescriptionAttribute.cs
@@ -0,0 +1,40 @@
+//
+// GroupingDescriptionAttribute.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class GroupingDescriptionAttribute: Attribute
+ {
+ public string Title { get; private set; }
+
+ public GroupingDescriptionAttribute (string title)
+ {
+ this.Title = title;
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderChainControl.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderChainControl.cs
new file mode 100644
index 0000000000..3449d7b24b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderChainControl.cs
@@ -0,0 +1,153 @@
+//
+// GroupingProviderChainControl.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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 Xwt;
+using System.Collections.Generic;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class GroupingProviderChainControl: HBox
+ {
+ readonly IList<Type> availableProviders;
+
+ readonly IList<ComboBox> providerPickers = new List<ComboBox>();
+ readonly IList<Label> labels = new List<Label>();
+
+ public GroupingProviderChainControl(IssueGroup rootGroup, IEnumerable<Type> providers)
+ {
+ this.RootGroupingProvider = new GroupingProvider (rootGroup);
+ availableProviders = providers.ToList();
+
+ BuildUi ();
+ }
+
+ public IGroupingProvider RootGroupingProvider {
+ get;
+ private set;
+ }
+
+ void BuildUi ()
+ {
+ Clear ();
+ var label = new Label ("Group by:");
+ labels.Add (label);
+ PackStart (label);
+ BuildProviderSelectors (RootGroupingProvider, RootGroupingProvider.Next);
+ }
+
+ void BuildProviderSelectors (IGroupingProvider previousProvider, IGroupingProvider selectedProvider)
+ {
+ var combo = MakeSelector (selectedProvider);
+ combo.SelectionChanged += (sender, e) => {
+ var selectedType = combo.SelectedItem as Type;
+ if (selectedType == null)
+ return;
+ var newProvider = (IGroupingProvider)Activator.CreateInstance(selectedType);
+
+ if (newProvider.SupportsNext && selectedProvider.SupportsNext) {
+ newProvider.Next = selectedProvider.Next;
+ }
+ previousProvider.Next = newProvider;
+ BuildUi ();
+ };
+ providerPickers.Add (combo);
+ PackStart (combo);
+
+ if (selectedProvider.SupportsNext) {
+ PackStart (new Label ("then by"));
+ BuildProviderSelectors (selectedProvider, selectedProvider.Next);
+ }
+ }
+
+ ComboBox MakeSelector (IGroupingProvider selectedProvider)
+ {
+ var combo = new ComboBox ();
+ combo.Items.Add (typeof(NullGroupingProvider), "Nothing");
+ combo.Items.Add (ItemSeparator.Instance);
+ foreach (var providerType in availableProviders) {
+ var metadata = (GroupingDescriptionAttribute)providerType.GetCustomAttributes (false).FirstOrDefault (attr => attr is GroupingDescriptionAttribute);
+ if (metadata == null) {
+ LoggingService.LogWarning ("Grouping provider '{0}' does not have a metadata attribute, ignoring provider.", providerType.FullName);
+ continue;
+ }
+ combo.Items.Add (providerType, metadata.Title);
+ }
+ if (selectedProvider != null) {
+ combo.SelectedItem = selectedProvider.GetType ();
+ } else {
+ combo.SelectedItem = typeof(NullGroupingProvider);
+ }
+ return combo;
+ }
+
+ class GroupingProvider: IGroupingProvider
+ {
+ readonly IssueGroup rootGroup;
+
+ public GroupingProvider (IssueGroup rootGroup)
+ {
+ this.rootGroup = rootGroup;
+ next = rootGroup.GroupingProvider;
+ }
+
+ #region IGroupingProvider implementation
+
+ public event EventHandler<GroupingProviderEventArgs> NextChanged;
+
+ public IssueGroup GetIssueGroup (IssueGroup parent, IssueSummary issue)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Reset ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ IGroupingProvider next;
+ public IGroupingProvider Next {
+ get {
+ return next;
+ }
+ set {
+ next = value;
+ rootGroup.GroupingProvider = value;
+ }
+ }
+
+ public bool SupportsNext {
+ get {
+ return true;
+ }
+ }
+ #endregion
+
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderEventArgs.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderEventArgs.cs
new file mode 100644
index 0000000000..f9eee0fc87
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/GroupingProviderEventArgs.cs
@@ -0,0 +1,52 @@
+//
+// IGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class GroupingProviderEventArgs : EventArgs
+ {
+ public GroupingProviderEventArgs(IGroupingProvider provider, IGroupingProvider oldNext)
+ {
+ GroupingProvider = provider;
+ OldNext = oldNext;
+ }
+
+ /// <summary>
+ /// The <see cref="IGroupingProvider"/> whose Next reference has changed.
+ /// </summary>
+ /// <value>The grouping provider.</value>
+ public IGroupingProvider GroupingProvider { get; private set; }
+
+ /// <summary>
+ /// The old value of the Next reference.
+ /// </summary>
+ /// <value>The old value.</value>
+ public IGroupingProvider OldNext { get; private set; }
+ }
+
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IActionMatcher.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IActionMatcher.cs
new file mode 100644
index 0000000000..8e732f596a
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IActionMatcher.cs
@@ -0,0 +1,36 @@
+//
+// IIssueMatcher.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Collections.Generic;
+using MonoDevelop.CodeActions;
+
+namespace MonoDevelop.CodeIssues
+{
+ public interface IActionMatcher
+ {
+ IEnumerable<IssueMatch> Match (IList<ActionSummary> summaries, IList<CodeAction> realIssues);
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IGroupingProvider.cs
new file mode 100644
index 0000000000..19b0001bf4
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IGroupingProvider.cs
@@ -0,0 +1,67 @@
+//
+// IGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public interface IGroupingProvider
+ {
+ /// <summary>
+ /// Gets the issue group for the <see cref="IssueGroup"/> specified in <paramref name="issue"/>.
+ /// </summary>
+ /// <returns>The issue group.</returns>
+ /// <param name="parentGroup">The parent group.</param>
+ /// <param name="issue">The <see cref="IssueSummary"/> to return a group for.</param>
+ IssueGroup GetIssueGroup(IssueGroup parentGroup, IssueSummary issue);
+
+ /// <summary>
+ /// Removes the set of cached groups.
+ /// </summary>
+ void Reset ();
+
+ /// <summary>
+ /// The <see cref="IGroupingProvider"/> to be applied after the current instance. Never returns null.
+ /// </summary>
+ /// <value>The next.</value>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown by both accessors if <see cref="SupportsNext"/> is false.
+ /// </exception>
+ IGroupingProvider Next { get; set; }
+
+ /// <summary>
+ /// Occurs when <see cref="Next"/> changes.
+ /// </summary>
+ event EventHandler<GroupingProviderEventArgs> NextChanged;
+
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="MonoDevelop.CodeIssues.IGroupingProvider"/>
+ /// supports the usage of <see cref="Next"/> .
+ /// </summary>
+ /// <value>True if <see cref="Next"/> can be used.</value>
+ bool SupportsNext { get; }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueSummarySink.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueSummarySink.cs
new file mode 100644
index 0000000000..af1cb51c0c
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueSummarySink.cs
@@ -0,0 +1,40 @@
+//
+// IAnalysisResultSink.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public interface IIssueSummarySink
+ {
+
+ /// <summary>
+ /// Adds a <see cref="IssueSummary"/> to the sink.
+ /// </summary>
+ /// <param name="issueSummary">The Issue summary.</param>
+ void AddIssue (IssueSummary issueSummary);
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueTreeNode.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueTreeNode.cs
new file mode 100644
index 0000000000..68cf43ef50
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IIssueTreeNode.cs
@@ -0,0 +1,87 @@
+//
+// IIssueTreeNode.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+using System.Collections.Generic;
+
+namespace MonoDevelop.CodeIssues
+{
+ public interface IIssueTreeNode
+ {
+ /// <summary>
+ /// The text that should be displayed in the ui.
+ /// </summary>
+ /// <value>The text.</value>
+ string Text { get; }
+
+ /// <summary>
+ /// The children of this instance.
+ /// </summary>
+ /// <value>The children.</value>
+ ICollection<IIssueTreeNode> Children { get; }
+
+ /// <summary>
+ /// Indicates whether this instance has any children.
+ /// </summary>
+ /// <value><c>true</c> if this instance has children; otherwise, <c>false</c>.</value>
+ bool HasVisibleChildren {
+ get;
+ }
+
+ /// <summary>
+ /// Indicates whether this node should be shown in the ui.
+ /// </summary>
+ /// <value><c>true</c> if the current instance should be shown; otherwise, <c>false</c>.</value>
+ bool Visible { get; set; }
+
+ /// <summary>
+ /// Gets all children including nested children of this instance.
+ /// </summary>
+ /// <value>All children.</value>
+ ICollection<IIssueTreeNode> AllChildren { get; }
+
+ /// <summary>
+ /// Occurs when children of this node are invalidated.
+ /// </summary>
+ event EventHandler<IssueGroupEventArgs> ChildrenInvalidated;
+
+ /// <summary>
+ /// Occurs when children of this node are invalidated.
+ /// </summary>
+ event EventHandler<IssueTreeNodeEventArgs> ChildAdded;
+
+ /// <summary>
+ /// Occurs when <see cref="Text"/> is updated.
+ /// </summary>
+ event EventHandler<IssueGroupEventArgs> TextChanged;
+
+ /// <summary>
+ /// Occurs when <see cref="Visible"/> is updated.
+ /// </summary>
+ event EventHandler<IssueGroupEventArgs> VisibleChanged;
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroup.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroup.cs
new file mode 100644
index 0000000000..10e261b7fc
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroup.cs
@@ -0,0 +1,326 @@
+//
+// IGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Collections.Generic;
+
+namespace MonoDevelop.CodeIssues
+{
+ /// <summary>
+ /// Represents the cached information saved for each issue detected in a source file.
+ /// </summary>
+ /// <remarks>
+ /// This class is thread safe.
+ /// </remarks>
+ public class IssueGroup : IIssueTreeNode, IIssueSummarySink
+ {
+ readonly object _lock = new object ();
+ bool processingEnabled;
+ /// <summary>
+ /// A list of groups produced by the <see cref="groupingProvider"/>.
+ /// </summary>
+ readonly ISet<IssueGroup> groups = new HashSet<IssueGroup>();
+ readonly IList<IIssueTreeNode> children = new List<IIssueTreeNode>();
+ readonly ISet<IssueSummary> allIssues = new HashSet<IssueSummary>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.CodeIssues.IssueGroup"/> class.
+ /// </summary>
+ /// <param name="nextProvider">
+ /// The <see cref="IGroupingProvider"/> to use when grouping <see cref="IssueSummary"/> instances.
+ /// </param>
+ /// <param name="description">A string describing the contents of this group.</param>
+ public IssueGroup (IGroupingProvider nextProvider, string description)
+ {
+ groupingProvider = nextProvider;
+ Description = description;
+ processingEnabled = false;
+ }
+
+ IGroupingProvider groupingProvider;
+
+ public IGroupingProvider GroupingProvider {
+ get {
+ return groupingProvider;
+ }
+ set {
+ lock(_lock) {
+ processingEnabled = false;
+ groupingProvider = value;
+ groups.Clear ();
+ children.Clear ();
+ }
+ OnChildrenInvalidated (new IssueGroupEventArgs(this));
+ }
+ }
+
+ #region IIssueTreeNode implementation
+
+ string IIssueTreeNode.Text {
+ get {
+ lock (_lock) {
+ return string.Format ("{0} ({1})", Description, allIssues.Count (issue => ((IIssueTreeNode)issue).Visible));
+ }
+ }
+ }
+
+ ICollection<IIssueTreeNode> IIssueTreeNode.Children {
+ get {
+ EnableProcessing ();
+ lock (_lock) {
+ return new List<IIssueTreeNode> (children);
+ }
+ }
+ }
+
+ bool IIssueTreeNode.HasVisibleChildren {
+ get {
+ lock (_lock) {
+ return allIssues.Any (issue => ((IIssueTreeNode)issue).Visible);
+ }
+ }
+ }
+
+ bool IIssueTreeNode.Visible {
+ get {
+ return ((IIssueTreeNode)this).HasVisibleChildren;
+ }
+
+ set {
+ throw new InvalidOperationException ("Not supported");
+ }
+ }
+
+ ICollection<IIssueTreeNode> IIssueTreeNode.AllChildren {
+ get {
+ lock (_lock) {
+ return new List<IIssueTreeNode> (allIssues);
+ }
+ }
+ }
+
+ event EventHandler<IssueGroupEventArgs> childrenInvalidated;
+
+ event EventHandler<IssueGroupEventArgs> IIssueTreeNode.ChildrenInvalidated {
+ add {
+ childrenInvalidated += value;
+ }
+ remove {
+ childrenInvalidated -= value;
+ }
+ }
+
+ protected virtual void OnChildrenInvalidated (IssueGroupEventArgs eventArgs)
+ {
+ var handler = childrenInvalidated;
+ if (handler != null) {
+ handler (this, eventArgs);
+ }
+ }
+
+ event EventHandler<IssueTreeNodeEventArgs> childAdded;
+
+ event EventHandler<IssueTreeNodeEventArgs> IIssueTreeNode.ChildAdded {
+ add {
+ childAdded += value;
+ }
+ remove {
+ childAdded -= value;
+ }
+ }
+
+ protected virtual void OnChildAdded (IssueTreeNodeEventArgs eventArgs)
+ {
+ var handler = childAdded;
+ if (handler != null) {
+ handler (this, eventArgs);
+ }
+ }
+
+ event EventHandler<IssueGroupEventArgs> textChanged;
+
+ event EventHandler<IssueGroupEventArgs> IIssueTreeNode.TextChanged {
+ add {
+ textChanged += value;
+ }
+ remove {
+ textChanged -= value;
+ }
+ }
+
+ protected virtual void OnTextChanged (IssueGroupEventArgs eventArgs)
+ {
+ var handler = textChanged;
+ if (handler != null) {
+ handler (this, eventArgs);
+ }
+ }
+
+ event EventHandler<IssueGroupEventArgs> visibleChanged;
+
+ event EventHandler<IssueGroupEventArgs> IIssueTreeNode.VisibleChanged {
+ add {
+ visibleChanged += value;
+ }
+ remove {
+ visibleChanged -= value;
+ }
+ }
+
+ protected virtual void OnVisibleChanged (IssueGroupEventArgs eventArgs)
+ {
+ var handler = visibleChanged;
+ if (handler != null) {
+ handler (this, eventArgs);
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ /// <value>The description.</value>
+ public string Description {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance issue count.
+ /// </summary>
+ /// <value><c>true</c> if this instance issue count; otherwise, <c>false</c>.</value>
+ public int IssueCount {
+ get;
+ private set;
+ }
+
+ public void ClearStatistics ()
+ {
+ lock (_lock) {
+ groups.Clear ();
+ children.Clear ();
+ allIssues.Clear ();
+ groupingProvider.Reset ();
+ IssueCount = 0;
+ processingEnabled = false;
+ }
+ }
+
+ /// <summary>
+ /// Makes this instance start processing issues.
+ /// </summary>
+ public void EnableProcessing ()
+ {
+ lock (_lock) {
+ if (!processingEnabled) {
+ processingEnabled = true;
+
+ // Now process the existing issues
+ foreach (var issue in allIssues) {
+ IssueGroup group;
+ ProcessIssue (issue, out group);
+
+ // TODO: Could this be run without holding the lock and is it worth it?
+ if (group != null) {
+ group.AddIssue (issue);
+ }
+ }
+ }
+ }
+ }
+
+ #region IIssueSummarySink implementation
+
+ public void AddIssue (IssueSummary issue)
+ {
+ IssueGroup group = null;
+ bool groupAdded = false;
+ bool issueAdded = false;
+ lock (_lock) {
+ if (!allIssues.Contains (issue)) {
+ IssueCount++;
+ allIssues.Add (issue);
+ ((IIssueTreeNode) issue).VisibleChanged += HandleVisibleChanged;
+ issueAdded = true;
+ }
+ if (processingEnabled) {
+ groupAdded = ProcessIssue (issue, out group);
+ }
+ }
+ if (issueAdded) {
+ OnTextChanged (new IssueGroupEventArgs (this));
+ }
+ if (!processingEnabled)
+ return;
+ if (groupAdded) {
+ OnChildAdded (new IssueTreeNodeEventArgs (this, group));
+ } else if (group == null) {
+ OnChildAdded (new IssueTreeNodeEventArgs (this, issue));
+ }
+ if (group != null) {
+ group.AddIssue (issue);
+ }
+
+ }
+
+ void HandleVisibleChanged (object sender, IssueGroupEventArgs e)
+ {
+ lock (_lock) {
+ var visibleChildren = children.Any (child => child.Visible);
+ if ((e.Node.Visible && visibleChildren) || (!e.Node.Visible && !visibleChildren)) {
+ OnVisibleChanged (new IssueGroupEventArgs (this));
+ }
+ OnTextChanged (new IssueGroupEventArgs (this));
+ }
+ }
+
+ #endregion
+
+ bool ProcessIssue (IssueSummary issue, out IssueGroup group)
+ {
+ bool groupAdded = false;
+ group = null;
+ if (groupingProvider != null) {
+ group = groupingProvider.GetIssueGroup (this, issue);
+ }
+ if (group == null) {
+ children.Add (issue);
+ } else if (!groups.Contains (group)) {
+ groupAdded = true;
+ groups.Add (group);
+ children.Add (group);
+ }
+ return groupAdded;
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[IssueGroup: Description={1}, IssueCount={2}, GroupingProvider={0}]", GroupingProvider, Description, IssueCount);
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroupEventArgs.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroupEventArgs.cs
new file mode 100644
index 0000000000..42787d4560
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueGroupEventArgs.cs
@@ -0,0 +1,40 @@
+//
+// IssueGroupEventArgs.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class IssueGroupEventArgs: EventArgs
+ {
+ public IssueGroupEventArgs (IIssueTreeNode node)
+ {
+ Node = node;
+ }
+
+ public IIssueTreeNode Node { get; private set; }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueMatch.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueMatch.cs
new file mode 100644
index 0000000000..fbdaafcd57
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueMatch.cs
@@ -0,0 +1,38 @@
+//
+// IIssueMatcher.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.CodeActions;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class IssueMatch
+ {
+ public ActionSummary Summary { get; set; }
+
+ public CodeAction Action { get; set; }
+ }
+
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueSummary.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueSummary.cs
new file mode 100644
index 0000000000..5e7c4d201c
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueSummary.cs
@@ -0,0 +1,236 @@
+//
+// IGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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 ICSharpCode.NRefactory.TypeSystem;
+using ICSharpCode.NRefactory.Refactoring;
+using MonoDevelop.Projects;
+using System.Collections.Generic;
+using System.IO;
+using System;
+using System.Linq;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class IssueSummary: IIssueTreeNode
+ {
+ public static IssueSummary FromCodeIssue(ProjectFile file, BaseCodeIssueProvider provider, CodeIssue codeIssue)
+ {
+ var topLevelProvider = (provider as CodeIssueProvider) ?? provider.Parent;
+ if (topLevelProvider == null)
+ throw new ArgumentException ("must be a CodeIssueProvider or a BaseCodeIssueProvider with Parent != null", "provider");
+ var issueSummary = new IssueSummary {
+ IssueDescription = codeIssue.Description,
+ Region = codeIssue.Region,
+ ProviderTitle = topLevelProvider.Title,
+ ProviderDescription = topLevelProvider.Description,
+ ProviderCategory = topLevelProvider.Category,
+ Severity = topLevelProvider.GetSeverity (),
+ IssueMarker = codeIssue.IssueMarker,
+ File = file,
+ Project = file.Project,
+ InspectorIdString = codeIssue.InspectorIdString
+ };
+ issueSummary.Actions = codeIssue.Actions.Select (a => new ActionSummary {
+// Batchable = a.SupportsBatchRunning,
+// SiblingKey = a.SiblingKey,
+// Title = a.,
+// Region = a.DocumentRegion,
+ IssueSummary = issueSummary
+ }).ToList ();
+ return issueSummary;
+ }
+
+ #region IIssueTreeNode implementation
+
+ string IIssueTreeNode.Text {
+ get {
+ string lineDescription;
+ if (Region.BeginLine == Region.EndLine) {
+ lineDescription = Region.BeginLine.ToString ();
+ } else {
+ lineDescription = string.Format ("{0}-{1}", Region.BeginLine, Region.EndLine);
+ }
+ var fileName = Path.GetFileName (File.Name);
+ return string.Format ("{0} [{1}:{2}]", IssueDescription, fileName, lineDescription);
+ }
+ }
+
+ static readonly ICollection<IIssueTreeNode> emptyCollection = new IIssueTreeNode[0];
+
+ ICollection<IIssueTreeNode> IIssueTreeNode.Children {
+ get {
+ return emptyCollection;
+ }
+ }
+
+ bool IIssueTreeNode.HasVisibleChildren {
+ get {
+ return false;
+ }
+ }
+
+ bool visible = true;
+ bool IIssueTreeNode.Visible {
+ get {
+ return visible;
+ }
+
+ set {
+ if (visible != value) {
+ visible = value;
+ OnVisibleChanged (new IssueGroupEventArgs (this));
+ }
+ }
+ }
+
+ ICollection<IIssueTreeNode> IIssueTreeNode.AllChildren {
+ get {
+ return emptyCollection;
+ }
+ }
+
+ event EventHandler<IssueGroupEventArgs> visibleChanged;
+ event EventHandler<IssueGroupEventArgs> IIssueTreeNode.VisibleChanged {
+ add {
+ visibleChanged += value;
+ }
+ remove {
+ visibleChanged -= value;
+ }
+ }
+
+ protected virtual void OnVisibleChanged (IssueGroupEventArgs eventArgs)
+ {
+ var handler = visibleChanged;
+ if (handler != null) {
+ handler (this, eventArgs);
+ }
+ }
+
+ // no-op events, these never happen in this implementation
+ event EventHandler<IssueGroupEventArgs> IIssueTreeNode.ChildrenInvalidated {
+ add {
+ }
+ remove {
+ }
+ }
+
+ event EventHandler<IssueTreeNodeEventArgs> IIssueTreeNode.ChildAdded {
+ add {
+ }
+ remove {
+ }
+ }
+
+ event EventHandler<IssueGroupEventArgs> IIssueTreeNode.TextChanged {
+ add {
+ }
+ remove {
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// The description of the issue.
+ /// </summary>
+ public string IssueDescription { get; set; }
+
+ /// <summary>
+ /// The region.
+ /// </summary>
+ public DomRegion Region { get; set; }
+
+ /// <summary>
+ /// Gets or sets the category of the issue provider.
+ /// </summary>
+ public string ProviderCategory { get; set; }
+
+ /// <summary>
+ /// Gets or sets the title of the issue provider.
+ /// </summary>
+ public string ProviderTitle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description of the issue provider.
+ /// </summary>
+ public string ProviderDescription { get; set; }
+
+ /// <summary>
+ /// Gets or sets the severity.
+ /// </summary>
+ public Severity Severity { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating how this issue should be marked inside the text editor.
+ /// Note: There is only one code issue provider generated therfore providers need to be state less.
+ /// </summary>
+ public IssueMarker IssueMarker { get; set; }
+
+ /// <summary>
+ /// Gets or sets the file that this issue was found in.
+ /// </summary>
+ /// <value>The file.</value>
+ public ProjectFile File { get; set; }
+
+ /// <summary>
+ /// Gets or sets the project this issue was found in.
+ /// </summary>
+ /// <value>The project.</value>
+ public Project Project { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type of the inspector that was the source of this issue.
+ /// </summary>
+ /// <value>The type of the inspector.</value>
+ public string InspectorIdString { get; set; }
+
+ IList<ActionSummary> actions;
+
+ /// <summary>
+ /// Gets or sets the actions available to fix this issue.
+ /// </summary>
+ /// <value>The actions.</value>
+ public IList<ActionSummary> Actions {
+ get {
+ if (actions == null) {
+ Actions = new List<ActionSummary> ();
+ }
+ return actions;
+ }
+ set {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ actions = value;
+ }
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("[IssueSummary: ProviderTitle={2}, Region={0}, ProviderCategory={1}]", Region, ProviderCategory, ProviderTitle);
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueTreeNodeEventArgs.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueTreeNodeEventArgs.cs
new file mode 100644
index 0000000000..37d663f252
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/IssueTreeNodeEventArgs.cs
@@ -0,0 +1,43 @@
+//
+// IssueSummaryEventArgs.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class IssueTreeNodeEventArgs: EventArgs
+ {
+ public IssueTreeNodeEventArgs (IIssueTreeNode parent, IIssueTreeNode child)
+ {
+ Parent = parent;
+ Child = child;
+ }
+
+ public IIssueTreeNode Parent { get; private set; }
+
+ public IIssueTreeNode Child { get; private set; }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/NullGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/NullGroupingProvider.cs
new file mode 100644
index 0000000000..677d9989c6
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/NullGroupingProvider.cs
@@ -0,0 +1,82 @@
+//
+// NullGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class NullGroupingProvider: IGroupingProvider
+ {
+ static readonly Lazy<NullGroupingProvider> instance = new Lazy<NullGroupingProvider>();
+ public static IGroupingProvider Instance
+ {
+ get {
+ return instance.Value;
+ }
+ }
+
+ #region IGroupingProvider implementation
+
+ public IssueGroup GetIssueGroup (IssueGroup parent, IssueSummary issue)
+ {
+ return null;
+ }
+
+ public void Reset ()
+ {
+ // no-op
+ }
+
+ public IGroupingProvider Next {
+ get {
+ throw new InvalidOperationException ();
+ }
+ set {
+ throw new InvalidOperationException ();
+ }
+ }
+
+ event EventHandler<GroupingProviderEventArgs> nextChanged;
+
+ event EventHandler<GroupingProviderEventArgs> IGroupingProvider.NextChanged
+ {
+ add {
+ nextChanged += value;
+ }
+ remove {
+ nextChanged -= value;
+ }
+ }
+
+ public bool SupportsNext {
+ get {
+ return false;
+ }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProjectGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProjectGroupingProvider.cs
new file mode 100644
index 0000000000..7ca19ba063
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProjectGroupingProvider.cs
@@ -0,0 +1,45 @@
+//
+// ProjectGroupingProvider.cs
+//
+// Author:
+// Marius Ungureanu <marius.ungureanu@xamarin.com>
+//
+// Copyright (c) 2013 Marius Ungureanu
+//
+// 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.Projects;
+
+namespace MonoDevelop.CodeIssues
+{
+ [GroupingDescription("Project")]
+ public class ProjectGroupingProvider : AbstractGroupingProvider<Project>
+ {
+ #region implemented abstract members of AbstractGroupingProvider
+ protected override Project GetGroupingKey (IssueSummary issue)
+ {
+ return issue.Project;
+ }
+ protected override string GetGroupName (IssueSummary issue)
+ {
+ return issue.Project.Name;
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProviderGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProviderGroupingProvider.cs
new file mode 100644
index 0000000000..7cdf46e461
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/ProviderGroupingProvider.cs
@@ -0,0 +1,45 @@
+//
+// ProviderGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.
+
+namespace MonoDevelop.CodeIssues
+{
+ [GroupingDescription("CodeIssue")]
+ public class ProviderGroupingProvider: AbstractGroupingProvider<string>
+ {
+ #region implemented abstract members of AbstractGroupingProvider
+ protected override string GetGroupingKey (IssueSummary issue)
+ {
+ return issue.ProviderTitle;
+ }
+
+ protected override string GetGroupName (IssueSummary issue)
+ {
+ return issue.ProviderTitle;
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/AnalysisJobQueue.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/AnalysisJobQueue.cs
new file mode 100644
index 0000000000..cdb3182fbb
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/AnalysisJobQueue.cs
@@ -0,0 +1,121 @@
+//
+// AnalysisJobQueue.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Collections.Generic;
+using System.Linq;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class AnalysisJobQueue
+ {
+ readonly object _lock = new object();
+
+ /// <summary>
+ /// The list of items in the queue.
+ /// </summary>
+ readonly List<JobSlice> slices = new List<JobSlice>();
+
+ /// <summary>
+ /// Indicates whether queueItems is sorted.
+ /// </summary>
+ bool sorted;
+
+ /// <summary>
+ /// Adds the specified job to the queue.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ public void Add (IAnalysisJob job)
+ {
+ lock (_lock) {
+ var jobStatus = new JobStatus (job);
+ foreach (var file in job.GetFiles()) {
+ JobSlice slice = slices.FirstOrDefault (j => j.File == file);
+ if (slice == null) {
+ slice = new JobSlice (file);
+ slices.Add (slice);
+ }
+ jobStatus.AddSlice (slice);
+ slice.AddJob (job, jobStatus);
+ }
+ InvalidateSort ();
+ }
+ }
+
+ /// <summary>
+ /// Remove the specified job from the queue.
+ /// </summary>
+ /// <param name="job">The job to remove.</param>
+ public void Remove (IAnalysisJob job)
+ {
+ lock (_lock) {
+ foreach (var file in job.GetFiles()) {
+ JobSlice queueItem = slices.FirstOrDefault (j => j.File == file);
+ if (queueItem == null)
+ // The file might have been processed already, carry on
+ continue;
+ queueItem.RemoveJob (job);
+ if (!queueItem.GetJobs ().Any ())
+ slices.Remove (queueItem);
+ }
+ InvalidateSort ();
+ }
+ }
+
+ /// <summary>
+ /// Dequeues a number of elements less than or equal to <paramref name="maxNumber"/>.
+ /// </summary>
+ /// <param name="maxNumber">The index.</param>
+ public IEnumerable<JobSlice> Dequeue (int maxNumber)
+ {
+ lock (_lock) {
+ EnsureSorted ();
+ var taken = slices.Take (maxNumber).ToList ();
+ foreach (var item in taken)
+ slices.Remove (item);
+ return taken;
+ }
+ }
+
+ /// <summary>
+ /// Notifies the rest of the class that <see cref="slices"/> is no longer sorted.
+ /// </summary>
+ void InvalidateSort ()
+ {
+ sorted = false;
+ }
+
+ /// <summary>
+ /// Ensures that <see cref="slices"/> is sorted.
+ /// </summary>
+ void EnsureSorted ()
+ {
+ if (!sorted) {
+ slices.Sort ();
+ sorted = true;
+ }
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/CodeIssueEventArgs.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/CodeIssueEventArgs.cs
new file mode 100644
index 0000000000..53e3a29f8b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/CodeIssueEventArgs.cs
@@ -0,0 +1,70 @@
+//
+// CodeIssueEventArgs.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.CodeIssues;
+using System.Collections.Generic;
+using System.Linq;
+using MonoDevelop.Projects;
+
+namespace MonoDevelop.CodeIssues
+{
+ /// <summary>
+ /// Code issue event arguments.
+ /// </summary>
+ public class CodeIssueEventArgs : EventArgs
+ {
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CodeIssueEventArgs"/> class.
+ /// </summary>
+ /// <param name="codeIssues">The code issues.</param>
+ public CodeIssueEventArgs (ProjectFile file, BaseCodeIssueProvider provider, IEnumerable<CodeIssue> codeIssues)
+ {
+ File = file;
+ Provider = provider;
+ CodeIssues = codeIssues as IList<CodeIssue> ?? codeIssues.ToList ();
+ }
+
+ /// <summary>
+ /// Gets the analyzed file.
+ /// </summary>
+ /// <value>The analyzed file.</value>
+ public ProjectFile File { get; private set; }
+
+ /// <summary>
+ /// Gets the provider.
+ /// </summary>
+ /// <value>The code issue provider that provided the issues.</value>
+ public BaseCodeIssueProvider Provider { get; private set; }
+
+ /// <summary>
+ /// Gets the code issues.
+ /// </summary>
+ /// <value>The new code issues.</value>
+ public IList<CodeIssue> CodeIssues { get; private set; }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IAnalysisJob.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IAnalysisJob.cs
new file mode 100644
index 0000000000..0ab2553b4f
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IAnalysisJob.cs
@@ -0,0 +1,93 @@
+//
+// IAnalysisJob.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Collections.Generic;
+using MonoDevelop.Projects;
+using System;
+
+namespace MonoDevelop.CodeIssues
+{
+ /// <summary>
+ /// Represents an analysis job. Implementations must be thread safe.
+ /// </summary>
+ public interface IAnalysisJob
+ {
+ /// <summary>
+ /// Gets the file names affected by this job.
+ /// </summary>
+ /// <remarks>
+ /// The return value of this method may not change after the initial invocation.
+ /// </remarks>
+ /// <returns>The file names.</returns>
+ IEnumerable<ProjectFile> GetFiles ();
+
+ /// <summary>
+ /// Gets the issue providers for the file specified in <paramref name="file"/>.
+ /// </summary>
+ /// <returns>The issue providers to run on the specified file.</returns>
+ /// <param name="file">The file.</param>
+ IEnumerable<BaseCodeIssueProvider> GetIssueProviders (ProjectFile file);
+
+ /// <summary>
+ /// Adds the results to this job.
+ /// </summary>
+ /// <param name="file">The file that the results apply to.</param>
+ /// <param name="provider">The provider that provided the issues.</param>
+ /// <param name="issues">The issues detected in the specified file.</param>
+ void AddResult (ProjectFile file, BaseCodeIssueProvider provider, IEnumerable<CodeIssue> issues);
+
+ /// <summary>
+ /// Notifies the job that there was an error running the specified provider on the specified file.
+ /// </summary>
+ /// <param name="file">The file.</param>
+ /// <param name="provider">The provider.</param>
+ void AddError (ProjectFile file, BaseCodeIssueProvider provider);
+
+ /// <summary>
+ /// Occurs when new code issues are added.
+ /// </summary>
+ event EventHandler<CodeIssueEventArgs> CodeIssueAdded;
+
+ /// <summary>
+ /// Called when this job is cancelled to notify the instance about that change.
+ /// </summary>
+ /// <remarks>
+ /// The caller does NOT have to ensure that this is the last method called.
+ /// Specifically, <see cref="AddResult"/> can be called after this method has been invoked.
+ /// </remarks>
+ void NotifyCancelled ();
+
+ /// <summary>
+ /// Notifies the job that all files have been processed.
+ /// </summary>
+ void SetCompleted ();
+
+ /// <summary>
+ /// Occurs when the job is completed.
+ /// </summary>
+ event EventHandler<EventArgs> Completed;
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IJobContext.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IJobContext.cs
new file mode 100644
index 0000000000..4027081b81
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/IJobContext.cs
@@ -0,0 +1,35 @@
+//
+// IJobContext.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.
+
+namespace MonoDevelop.CodeIssues
+{
+ public interface IJobContext
+ {
+ IAnalysisJob Job { get; }
+ void CancelJob ();
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobContext.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobContext.cs
new file mode 100644
index 0000000000..e15c83c88e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobContext.cs
@@ -0,0 +1,66 @@
+//
+// JobContext.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class JobContext : IJobContext
+ {
+ readonly IAnalysisJob job;
+
+ readonly AnalysisJobQueue queue;
+
+ readonly CodeAnalysisBatchRunner runner;
+
+ public JobContext(IAnalysisJob job, AnalysisJobQueue queue, CodeAnalysisBatchRunner runner)
+ {
+ if (job == null)
+ throw new ArgumentNullException ("job");
+ if (queue == null)
+ throw new ArgumentNullException ("queue");
+ if (runner == null)
+ throw new ArgumentNullException ("runner");
+ this.job = job;
+ this.queue = queue;
+ this.runner = runner;
+ }
+
+ #region IJobContext implementation
+ public void CancelJob ()
+ {
+ job.NotifyCancelled ();
+ queue.Remove (job);
+ }
+
+ public IAnalysisJob Job {
+ get {
+ return job;
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobSlice.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobSlice.cs
new file mode 100644
index 0000000000..7802e21ada
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobSlice.cs
@@ -0,0 +1,162 @@
+//
+// QueueItem.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Projects;
+using System.Threading;
+using System.Linq;
+
+namespace MonoDevelop.CodeIssues
+{
+ /// <summary>
+ /// Represents a unit of analysis at the time of analysis. Essentially this
+ /// maps a file to the jobs that wants to run analysis on it so the runner
+ /// can parse the file once and then make progress on all the jobs.
+ /// This class is not thread safe.
+ /// </summary>
+ public class JobSlice : IComparable<JobSlice>, IDisposable
+ {
+ bool disposed;
+ readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
+
+ /// <summary>
+ /// The jobs to run on the file specified in <see cref="FileName"/>.
+ /// </summary>
+ readonly IList<IAnalysisJob> jobs = new List<IAnalysisJob>();
+
+ /// <summary>
+ /// The status to report to when this slice is complete.
+ /// </summary>
+ readonly IList<JobStatus> statuses = new List<JobStatus>();
+
+ /// <summary>
+ /// The name of a file to be analyzed.
+ /// </summary>
+ /// <value>The name of the file.</value>
+ public ProjectFile File { get; private set; }
+
+ /// <summary>
+ /// Gets the cancellation token for this work unit.
+ /// </summary>
+ /// <value>A cancellation token.</value>
+ public CancellationToken CancellationToken {
+ get {
+ return tokenSource.Token;
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.CodeIssues.JobSlice"/> class.
+ /// </summary>
+ /// <param name="file">The file.</param>
+ public JobSlice (ProjectFile file)
+ {
+ File = file;
+ }
+
+ ~JobSlice ()
+ {
+ Dispose (false);
+ }
+
+ /// <summary>
+ /// Adds a job to be run on this file.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ /// <param name = "status">The status of the job.</param>
+ public void AddJob (IAnalysisJob job, JobStatus status)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().FullName);
+
+ statuses.Add (status);
+ jobs.Add (job);
+ }
+
+ /// <summary>
+ /// Gets the current jobs.
+ /// </summary>
+ /// <returns>The jobs.</returns>
+ public IEnumerable<IAnalysisJob> GetJobs ()
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().FullName);
+
+ return new List<IAnalysisJob> (jobs);
+ }
+
+ /// <summary>
+ /// Removes the specified job.
+ /// If the job is the last job in this instance it requests cancellation of it's CancellationToken.
+ /// </summary>
+ /// <param name="job">The job to remove.</param>
+ public void RemoveJob(IAnalysisJob job)
+ {
+ if (disposed)
+ throw new ObjectDisposedException (GetType ().FullName);
+
+ jobs.Remove (job);
+ if (!jobs.Any ())
+ tokenSource.Cancel ();
+ }
+
+ void MarkAsComplete ()
+ {
+ foreach (var status in statuses) {
+ status.MarkAsComplete (this);
+ }
+ }
+
+ #region IComparable implementation
+
+ public int CompareTo (JobSlice other)
+ {
+ return jobs.Count.CompareTo (other.jobs.Count);
+ }
+
+ #endregion
+
+ #region IDisposable implementation
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (disposed)
+ return;
+
+ MarkAsComplete ();
+
+ disposed = true;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobStatus.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobStatus.cs
new file mode 100644
index 0000000000..02ca3eb1fd
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/JobStatus.cs
@@ -0,0 +1,86 @@
+//
+// JobStatus.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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;
+
+namespace MonoDevelop.CodeIssues
+{
+ /// <summary>
+ /// Keeps track of the status of a possibly partially executed job.
+ /// </summary>
+ public class JobStatus
+ {
+ readonly object _lock = new object();
+
+ /// <summary>
+ /// The job.
+ /// </summary>
+ readonly IAnalysisJob job;
+
+ /// <summary>
+ /// The slices that the job has been split into.
+ /// </summary>
+ readonly ISet<JobSlice> slices = new HashSet<JobSlice> ();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.CodeIssues.JobStatus"/> class.
+ /// </summary>
+ /// <param name="job">The job.</param>
+ public JobStatus (IAnalysisJob job)
+ {
+ if (job == null)
+ throw new ArgumentNullException ("job");
+
+ this.job = job;
+ }
+
+ /// <summary>
+ /// Adds another slice. This method should not be called after <see cref="MarkAsComplete"/> has been called.
+ /// </summary>
+ /// <param name="slice">Slice.</param>
+ public void AddSlice (JobSlice slice)
+ {
+ lock (_lock) {
+ slices.Add (slice);
+ }
+ }
+
+ /// <summary>
+ /// Marks a slice of the job as complete and marks the job as completed if all slices have been completed.
+ /// </summary>
+ /// <param name="slice">The completed slice.</param>
+ public void MarkAsComplete(JobSlice slice)
+ {
+ lock (_lock) {
+ slices.Remove (slice);
+ if (slices.Count == 0) {
+ job.SetCompleted ();
+ }
+ }
+ }
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/ProgressMonitorWrapperJob.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/ProgressMonitorWrapperJob.cs
new file mode 100644
index 0000000000..b1eea307cf
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/ProgressMonitorWrapperJob.cs
@@ -0,0 +1,126 @@
+//
+// ProgressReportingWrapperJob.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Linq;
+using MonoDevelop.Ide;
+using MonoDevelop.Core;
+using MonoDevelop.Projects;
+using System.Collections.Generic;
+using System;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class ProgressMonitorWrapperJob : IAnalysisJob
+ {
+ readonly IAnalysisJob wrappedJob;
+
+ ProgressMonitor monitor;
+
+ int reportingThinningFactor = 100;
+
+ int completedWork;
+
+ public ProgressMonitorWrapperJob (IAnalysisJob wrappedJob, string message)
+ {
+ this.wrappedJob = wrappedJob;
+ monitor = IdeApp.Workbench.ProgressMonitors.GetStatusProgressMonitor (message, null, false);
+ var work = wrappedJob.GetFiles ().Sum (f => wrappedJob.GetIssueProviders (f).Count ());
+
+ monitor.BeginTask (message, work);
+ }
+
+ #region IAnalysisJob implementation
+
+ public event EventHandler<CodeIssueEventArgs> CodeIssueAdded {
+ add {
+ wrappedJob.CodeIssueAdded += value;
+ }
+ remove {
+ wrappedJob.CodeIssueAdded -= value;
+ }
+ }
+
+ public IEnumerable<ProjectFile> GetFiles ()
+ {
+ return wrappedJob.GetFiles ();
+ }
+
+ public IEnumerable<BaseCodeIssueProvider> GetIssueProviders (ProjectFile file)
+ {
+ return wrappedJob.GetIssueProviders (file);
+ }
+
+ public void AddResult (ProjectFile file, BaseCodeIssueProvider provider, IEnumerable<CodeIssue> issues)
+ {
+ Step ();
+ wrappedJob.AddResult (file, provider, issues);
+ }
+
+ public void AddError (ProjectFile file, BaseCodeIssueProvider provider)
+ {
+ Step ();
+ wrappedJob.AddError (file, provider);
+ }
+
+ public event EventHandler<EventArgs> Completed {
+ add {
+ wrappedJob.Completed += value;
+ }
+ remove {
+ wrappedJob.Completed -= value;
+ }
+ }
+
+ public void SetCompleted ()
+ {
+ StopReporting ();
+ wrappedJob.SetCompleted ();
+ }
+
+ void Step ()
+ {
+ completedWork++;
+ if (monitor != null && completedWork % reportingThinningFactor == 0) {
+ monitor.Step (reportingThinningFactor);
+ }
+ }
+
+ void StopReporting ()
+ {
+ if (monitor != null) {
+ monitor.Dispose ();
+ monitor = null;
+ }
+ }
+
+ public void NotifyCancelled ()
+ {
+ StopReporting ();
+ }
+
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/SimpleAnalysisJob.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/SimpleAnalysisJob.cs
new file mode 100644
index 0000000000..15878be01b
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/Runner/SimpleAnalysisJob.cs
@@ -0,0 +1,142 @@
+//
+// AbstractAnalysisJob.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Projects;
+using MonoDevelop.Refactoring;
+using MonoDevelop.Ide;
+using System.Linq;
+using ICSharpCode.NRefactory.Refactoring;
+
+namespace MonoDevelop.CodeIssues
+{
+ /// <summary>
+ /// A simple analysis job.
+ /// </summary>
+ public class SimpleAnalysisJob : IAnalysisJob
+ {
+ object _lock = new object ();
+
+ readonly IList<ProjectFile> files;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MonoDevelop.CodeIssues.SimpleAnalysisJob"/> class.
+ /// </summary>
+ /// <param name="files">The files to analyze.</param>
+ public SimpleAnalysisJob (IList<ProjectFile> files)
+ {
+ if (files == null)
+ throw new ArgumentNullException ("files");
+ this.files = files;
+ }
+
+ public bool IsCompleted {
+ get;
+ private set;
+ }
+
+ bool IsCancelled {
+ get;
+ set;
+ }
+
+ #region IAnalysisJob implementation
+
+ public event EventHandler<CodeIssueEventArgs> CodeIssueAdded;
+
+ protected virtual void OnCodeIssueAdded (CodeIssueEventArgs args)
+ {
+ var handler = CodeIssueAdded;
+ if (handler != null)
+ handler(this, args);
+ }
+
+ public IEnumerable<ProjectFile> GetFiles ()
+ {
+ return files;
+ }
+
+ public IEnumerable<BaseCodeIssueProvider> GetIssueProviders (ProjectFile file)
+ {
+ return RefactoringService.GetInspectors (DesktopService.GetMimeTypeForUri (file.Name))
+ .Where (provider => {
+ var severity = provider.GetSeverity ();
+ if (severity == Severity.None || !provider.GetIsEnabled ())
+ return false;
+ return true;
+ });
+ }
+
+ public void AddResult (ProjectFile file, BaseCodeIssueProvider provider, IEnumerable<CodeIssue> issues)
+ {
+ OnCodeIssueAdded (new CodeIssueEventArgs(file, provider, issues));
+ }
+
+ public void AddError (ProjectFile file, BaseCodeIssueProvider provider)
+ {
+ }
+
+ public void NotifyCancelled ()
+ {
+ lock (_lock) {
+ IsCancelled = true;
+ }
+ }
+
+ event EventHandler<EventArgs> completed;
+ public event EventHandler<EventArgs> Completed {
+ add {
+ completed += value;
+ if (IsCompleted && !IsCancelled)
+ OnCompleted (new EventArgs());
+ }
+ remove {
+ completed += value;
+ }
+ }
+
+ protected virtual void OnCompleted (EventArgs e)
+ {
+ var handler = completed;
+ if (handler != null)
+ handler (this, e);
+ }
+
+ public void SetCompleted ()
+ {
+ bool runEventHandler;
+ lock (_lock) {
+ IsCompleted = true;
+ runEventHandler = !IsCancelled && !IsCompleted;
+ }
+ if (runEventHandler)
+ OnCompleted (new EventArgs());
+ }
+
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SeverityGroupingProvider.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SeverityGroupingProvider.cs
new file mode 100644
index 0000000000..dc129bb35e
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SeverityGroupingProvider.cs
@@ -0,0 +1,45 @@
+//
+// SeverityGroupingProvider.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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 ICSharpCode.NRefactory.Refactoring;
+
+namespace MonoDevelop.CodeIssues
+{
+ [GroupingDescription("Severity")]
+ public class SeverityGroupingProvider : AbstractGroupingProvider<Severity>
+ {
+ #region implemented abstract members of AbstractGroupingProvider
+ protected override Severity GetGroupingKey (IssueSummary issue)
+ {
+ return issue.Severity;
+ }
+ protected override string GetGroupName (IssueSummary issue)
+ {
+ return issue.Severity.ToString ();
+ }
+ #endregion
+ }
+}
+
diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SolutionAnalysisJob.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SolutionAnalysisJob.cs
new file mode 100644
index 0000000000..785c037dcd
--- /dev/null
+++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeIssues/Pad/SolutionAnalysisJob.cs
@@ -0,0 +1,72 @@
+//
+// SolutionAnalysisJob.cs
+//
+// Author:
+// Simon Lindgren <simon.n.lindgren@gmail.com>
+//
+// Copyright (c) 2013 Simon Lindgren
+//
+// 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.Projects;
+using System.Linq;
+using System.Collections.Generic;
+using MonoDevelop.Ide;
+
+namespace MonoDevelop.CodeIssues
+{
+ public class SolutionAnalysisJob : SimpleAnalysisJob
+ {
+ public SolutionAnalysisJob (Solution solution) : base(GetApplicableFiles(solution))
+ {
+ }
+
+ static IList<ProjectFile> GetApplicableFiles (Solution solution)
+ {
+ var configurationSelector = IdeApp.Workspace.ActiveConfiguration;
+ var config = solution.GetConfiguration (configurationSelector);
+ return solution.GetAllProjects ()
+ .Where (config.BuildEnabledForItem)
+ .SelectMany (p => p.Files)
+ .Where (f => f.BuildAction == BuildAction.Compile)
+ .Distinct (new FilePathComparer())
+ .ToList ();
+ }
+
+ class FilePathComparer : IEqualityComparer<ProjectFile>
+ {
+ #region IEqualityComparer implementation
+
+ public bool Equals (ProjectFile x, ProjectFile y)
+ {
+ return x == y ||
+ (x != null &&
+ y != null &&
+ x.Name == y.Name);
+ }
+
+ public int GetHashCode (ProjectFile obj)
+ {
+ return obj == null ? -1 : obj.Name.GetHashCode ();
+ }
+
+ #endregion
+ }
+ }
+}
+