diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs index 621b2bd315..d47ebefff7 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; using ICSharpCode.Core; using ICSharpCode.Editor; using ICSharpCode.NRefactory.CSharp; @@ -25,12 +26,11 @@ namespace CSharpBinding { IProject project; FindReferences fr = new FindReferences(); - IList searchScopes; + IList searchScopes; IList[] interestingFileNames; int workAmount; double workAmountInverse; Action callback; - FileName currentFileName; public CSharpSymbolSearch(IProject project, IEntity entity) { @@ -84,40 +84,45 @@ namespace CSharpBinding if (this.callback != null) throw new InvalidOperationException("Cannot call FindReferences() twice"); this.callback = callback; - fr.ReferenceFound += OnReferenceFound; for (int i = 0; i < searchScopes.Count; i++) { - FindReferences.SearchScope searchScope = searchScopes[i]; - foreach (string file in interestingFileNames[i]) { - currentFileName = FileName.Create(file); - FindReferencesInCurrentFile(args, searchScope); - args.ProgressMonitor.Progress += workAmountInverse; - } + IFindReferenceSearchScope searchScope = searchScopes[i]; + object progressLock = new object(); + Parallel.ForEach( + interestingFileNames[i], + new ParallelOptions { + MaxDegreeOfParallelism = Environment.ProcessorCount + }, + delegate (string file) { + FindReferencesInFile(args, searchScope, FileName.Create(file)); + lock (progressLock) + args.ProgressMonitor.Progress += workAmountInverse; + }); } } - void OnReferenceFound(AstNode node, ResolveResult result) - { - callback(new Reference(new DomRegion(currentFileName, node.StartLocation, node.EndLocation), result)); - } - - void FindReferencesInCurrentFile(SymbolSearchArgs args, FindReferences.SearchScope searchScope) + void FindReferencesInFile(SymbolSearchArgs args, IFindReferenceSearchScope searchScope, FileName fileName) { - ITextSource textSource = args.ParseableFileContentFinder.Create(currentFileName); + ITextSource textSource = args.ParseableFileContentFinder.Create(fileName); if (textSource == null) return; if (searchScope.SearchTerm != null) { // TODO: do a fast check with IndexOf() } - ParseInformation parseInfo = ParserService.Parse(currentFileName, textSource); + ParseInformation parseInfo = ParserService.Parse(fileName, textSource); if (parseInfo == null) return; ParsedFile parsedFile = parseInfo.ParsedFile as ParsedFile; CompilationUnit cu = parseInfo.Annotation(); if (parsedFile == null || cu == null) return; - fr.FindReferencesInFile(searchScope, parsedFile, cu, project.TypeResolveContext); + fr.FindReferencesInFile( + searchScope, parsedFile, cu, project.TypeResolveContext, + delegate (AstNode node, ResolveResult result) { + var region = new DomRegion(fileName, node.StartLocation, node.EndLocation); + callback(new Reference(region, result)); + }); } } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs index 51b5de4694..5db27a8031 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs @@ -200,7 +200,7 @@ namespace ICSharpCode.AvalonEdit.AddIn differ.editor.Visibility = Visibility.Collapsed; differ.copyButton.Visibility = Visibility.Collapsed; } else { - var baseDocument = new TextDocument(DocumentUtilitites.GetTextSource(changeWatcher.BaseDocument)); + var baseDocument = new TextDocument(changeWatcher.BaseDocument); if (differ.editor.SyntaxHighlighting != null) { var mainHighlighter = new DocumentHighlighter(baseDocument, differ.editor.SyntaxHighlighting.MainRuleSet); var popupHighlighter = differ.editor.TextArea.GetService(typeof(IHighlighter)) as DocumentHighlighter; diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs index ec84362a91..4378267497 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs @@ -25,8 +25,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { IResolveVisitorNavigator[] navigators; - public CompositeResolveVisitorNavigator(IResolveVisitorNavigator[] navigators) + public CompositeResolveVisitorNavigator(params IResolveVisitorNavigator[] navigators) { + if (navigators == null) + throw new ArgumentNullException("navigators"); this.navigators = navigators; } diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferenceSearchScope.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferenceSearchScope.cs new file mode 100644 index 0000000000..80cc8d7934 --- /dev/null +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferenceSearchScope.cs @@ -0,0 +1,51 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// 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 ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents a scope in which references are searched. + /// + public interface IFindReferenceSearchScope + { + /// + /// Gets the search term. Only files that contain this identifier need to be parsed. + /// Can return null if all files need to be parsed. + /// + string SearchTerm { get; } + + /// + /// Gets the accessibility that defines the search scope. + /// + Accessibility Accessibility { get; } + + /// + /// Gets the top-level entity that defines the search scope. + /// + ITypeDefinition TopLevelTypeDefinition { get; } + + /// + /// Creates a navigator that can find references to this entity and reports + /// them to the specified callback. + /// + IResolveVisitorNavigator GetNavigator(FoundReferenceCallback callback); + } +} diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs index 302bb853fe..92b40721bd 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs @@ -26,16 +26,13 @@ using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.Resolver { + public delegate void FoundReferenceCallback(AstNode astNode, ResolveResult result); + /// /// 'Find references' implementation. /// public class FindReferences { - /// - /// Callback that is invoked whenever a reference is found. - /// - public event Action ReferenceFound; - #region Properties /// /// Gets/Sets the cancellation token. @@ -95,28 +92,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region class SearchScope - public abstract class SearchScope : IResolveVisitorNavigator + abstract class SearchScope : IResolveVisitorNavigator, IFindReferenceSearchScope { protected string searchTerm; internal Accessibility accessibility; internal ITypeDefinition topLevelTypeDefinition; - internal FindReferences findReferences; - /// - /// Gets the search term. Only files that contain this identifier need to be parsed. - /// Can return null if all files need to be parsed. - /// - public string SearchTerm { get { return searchTerm; } } + FoundReferenceCallback callback; - /// - /// Gets the accessibility that defines the search scope. - /// - public Accessibility Accessibility { get { return accessibility; } } + IResolveVisitorNavigator IFindReferenceSearchScope.GetNavigator(FoundReferenceCallback callback) + { + SearchScope n = (SearchScope)MemberwiseClone(); + n.callback = callback; + return n; + } - /// - /// Gets the top-level entity that defines the search scope. - /// - public ITypeDefinition TopLevelTypeDefinition { get { return topLevelTypeDefinition; } } + string IFindReferenceSearchScope.SearchTerm { + get { return searchTerm; } + } + + Accessibility IFindReferenceSearchScope.Accessibility { + get { return accessibility; } + } + + ITypeDefinition IFindReferenceSearchScope.TopLevelTypeDefinition { + get { return topLevelTypeDefinition; } + } internal abstract bool CanMatch(AstNode node); internal abstract bool IsMatch(ResolveResult rr); @@ -147,15 +148,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver protected void ReportMatch(AstNode node, ResolveResult result) { - var referenceFound = findReferences.ReferenceFound; - if (referenceFound != null) - referenceFound(node, result); + if (callback != null) + callback(node, result); } } #endregion #region GetSearchScopes - public IList GetSearchScopes(IEntity entity) + public IList GetSearchScopes(IEntity entity) { if (entity == null) throw new ArgumentNullException("entity"); @@ -196,19 +196,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver additionalScope = new FindChainedConstructorReferences(ctor); break; case EntityType.Destructor: - return EmptyList.Instance; + return EmptyList.Instance; default: throw new ArgumentException("Unknown entity type " + entity.EntityType); } if (scope.accessibility == Accessibility.None) scope.accessibility = effectiveAccessibility; scope.topLevelTypeDefinition = topLevelTypeDefinition; - scope.findReferences = this; if (additionalScope != null) { if (additionalScope.accessibility == Accessibility.None) additionalScope.accessibility = effectiveAccessibility; additionalScope.topLevelTypeDefinition = topLevelTypeDefinition; - additionalScope.findReferences = this; return new[] { scope, additionalScope }; } else { return new[] { scope }; @@ -220,7 +218,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// /// Gets the file names that possibly contain references to the element being searched for. /// - public IList GetInterestingFileNames(SearchScope searchScope, IEnumerable allTypes, ITypeResolveContext context) + public IList GetInterestingFileNames(IFindReferenceSearchScope searchScope, IEnumerable allTypes, ITypeResolveContext context) { IEnumerable interestingTypes; if (searchScope.TopLevelTypeDefinition != null) { @@ -276,11 +274,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// The type system representation of the file being searched. /// The compilation unit of the file being searched. /// The type resolve context to use for resolving the file. - public void FindReferencesInFile(SearchScope searchScope, ParsedFile parsedFile, CompilationUnit compilationUnit, ITypeResolveContext context) + /// Callback used to report the references that were found. + public void FindReferencesInFile(IFindReferenceSearchScope searchScope, ParsedFile parsedFile, CompilationUnit compilationUnit, + ITypeResolveContext context, FoundReferenceCallback callback) { if (searchScope == null) throw new ArgumentNullException("searchScope"); - FindReferencesInFile(new[] { searchScope }, parsedFile, compilationUnit, context); + FindReferencesInFile(new[] { searchScope }, parsedFile, compilationUnit, context, callback); } /// @@ -290,7 +290,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// The type system representation of the file being searched. /// The compilation unit of the file being searched. /// The type resolve context to use for resolving the file. - public void FindReferencesInFile(IList searchScopes, ParsedFile parsedFile, CompilationUnit compilationUnit, ITypeResolveContext context) + /// Callback used to report the references that were found. + public void FindReferencesInFile(IList searchScopes, ParsedFile parsedFile, CompilationUnit compilationUnit, + ITypeResolveContext context, FoundReferenceCallback callback) { if (searchScopes == null) throw new ArgumentNullException("searchScopes"); @@ -303,16 +305,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver this.CancellationToken.ThrowIfCancellationRequested(); if (searchScopes.Count == 0) return; - foreach (SearchScope scope in searchScopes) { - if (scope.findReferences != this) - throw new ArgumentException("Cannot use a search scope that was created by another FindReferences instance"); - } using (var ctx = context.Synchronize()) { IResolveVisitorNavigator navigator; if (searchScopes.Count == 1) - navigator = searchScopes[0]; + navigator = searchScopes[0].GetNavigator(callback); else - navigator = new CompositeResolveVisitorNavigator(searchScopes.ToArray()); + navigator = new CompositeResolveVisitorNavigator(searchScopes.Select(s => s.GetNavigator(callback)).ToArray()); navigator = new DetectSkippableNodesNavigator(navigator, compilationUnit); CSharpResolver resolver = new CSharpResolver(ctx, this.CancellationToken); ResolveVisitor v = new ResolveVisitor(resolver, parsedFile, navigator); diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 5d0087768b..4d7ea32385 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -104,6 +104,7 @@ + diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs index 724359d46e..3b173f5994 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs @@ -50,8 +50,20 @@ namespace ICSharpCode.SharpDevelop.Refactoring return symbolSearches; } + /// + /// Finds all references to the specified entity. + /// The results are reported using the callback. + /// FindReferences may internally use parallelism, and may invoke the callback on multiple + /// threads in parallel. + /// public static void FindReferences(IEntity entity, IProgressMonitor progressMonitor, Action callback) { + if (entity == null) + throw new ArgumentNullException("entity"); + if (progressMonitor == null) + throw new ArgumentNullException("progressMonitor"); + if (callback == null) + throw new ArgumentNullException("callback"); if (ParserService.LoadSolutionProjectsThreadRunning) { progressMonitor.ShowingDialog = true; MessageService.ShowMessage("${res:SharpDevelop.Refactoring.LoadSolutionProjectsThreadRunning}"); diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs index 5cf975d7fe..37045d3f3c 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs @@ -512,7 +512,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring string entityName = (entity.DeclaringTypeDefinition != null ? entity.DeclaringTypeDefinition.Name + "." + entity.Name : entity.Name); using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("${res:SharpDevelop.Refactoring.FindReferences}", true)) { List references = new List(); - FindReferenceService.FindReferences(entity, monitor, references.Add); + FindReferenceService.FindReferences(entity, monitor, r => { lock (references) references.Add(r); }); FindReferencesAndRenameHelper.ShowAsSearchResults( StringParser.Parse("${res:SharpDevelop.Refactoring.ReferencesTo}", new StringTagPair("Name", entityName)),