Browse Source

Make Find References multi-threaded.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
3bf2871b51
  1. 41
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs
  2. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs
  3. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs
  4. 51
      src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferenceSearchScope.cs
  5. 72
      src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs
  6. 1
      src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  7. 12
      src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs
  8. 2
      src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs

41
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Editor; using ICSharpCode.Editor;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
@ -25,12 +26,11 @@ namespace CSharpBinding
{ {
IProject project; IProject project;
FindReferences fr = new FindReferences(); FindReferences fr = new FindReferences();
IList<FindReferences.SearchScope> searchScopes; IList<IFindReferenceSearchScope> searchScopes;
IList<string>[] interestingFileNames; IList<string>[] interestingFileNames;
int workAmount; int workAmount;
double workAmountInverse; double workAmountInverse;
Action<Reference> callback; Action<Reference> callback;
FileName currentFileName;
public CSharpSymbolSearch(IProject project, IEntity entity) public CSharpSymbolSearch(IProject project, IEntity entity)
{ {
@ -84,40 +84,45 @@ namespace CSharpBinding
if (this.callback != null) if (this.callback != null)
throw new InvalidOperationException("Cannot call FindReferences() twice"); throw new InvalidOperationException("Cannot call FindReferences() twice");
this.callback = callback; this.callback = callback;
fr.ReferenceFound += OnReferenceFound;
for (int i = 0; i < searchScopes.Count; i++) { for (int i = 0; i < searchScopes.Count; i++) {
FindReferences.SearchScope searchScope = searchScopes[i]; IFindReferenceSearchScope searchScope = searchScopes[i];
foreach (string file in interestingFileNames[i]) { object progressLock = new object();
currentFileName = FileName.Create(file); Parallel.ForEach(
FindReferencesInCurrentFile(args, searchScope); interestingFileNames[i],
args.ProgressMonitor.Progress += workAmountInverse; 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) void FindReferencesInFile(SymbolSearchArgs args, IFindReferenceSearchScope searchScope, FileName fileName)
{
callback(new Reference(new DomRegion(currentFileName, node.StartLocation, node.EndLocation), result));
}
void FindReferencesInCurrentFile(SymbolSearchArgs args, FindReferences.SearchScope searchScope)
{ {
ITextSource textSource = args.ParseableFileContentFinder.Create(currentFileName); ITextSource textSource = args.ParseableFileContentFinder.Create(fileName);
if (textSource == null) if (textSource == null)
return; return;
if (searchScope.SearchTerm != null) { if (searchScope.SearchTerm != null) {
// TODO: do a fast check with IndexOf() // TODO: do a fast check with IndexOf()
} }
ParseInformation parseInfo = ParserService.Parse(currentFileName, textSource); ParseInformation parseInfo = ParserService.Parse(fileName, textSource);
if (parseInfo == null) if (parseInfo == null)
return; return;
ParsedFile parsedFile = parseInfo.ParsedFile as ParsedFile; ParsedFile parsedFile = parseInfo.ParsedFile as ParsedFile;
CompilationUnit cu = parseInfo.Annotation<CompilationUnit>(); CompilationUnit cu = parseInfo.Annotation<CompilationUnit>();
if (parsedFile == null || cu == null) if (parsedFile == null || cu == null)
return; 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));
});
} }
} }
} }

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin.cs

@ -200,7 +200,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
differ.editor.Visibility = Visibility.Collapsed; differ.editor.Visibility = Visibility.Collapsed;
differ.copyButton.Visibility = Visibility.Collapsed; differ.copyButton.Visibility = Visibility.Collapsed;
} else { } else {
var baseDocument = new TextDocument(DocumentUtilitites.GetTextSource(changeWatcher.BaseDocument)); var baseDocument = new TextDocument(changeWatcher.BaseDocument);
if (differ.editor.SyntaxHighlighting != null) { if (differ.editor.SyntaxHighlighting != null) {
var mainHighlighter = new DocumentHighlighter(baseDocument, differ.editor.SyntaxHighlighting.MainRuleSet); var mainHighlighter = new DocumentHighlighter(baseDocument, differ.editor.SyntaxHighlighting.MainRuleSet);
var popupHighlighter = differ.editor.TextArea.GetService(typeof(IHighlighter)) as DocumentHighlighter; var popupHighlighter = differ.editor.TextArea.GetService(typeof(IHighlighter)) as DocumentHighlighter;

4
src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs

@ -25,8 +25,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
IResolveVisitorNavigator[] navigators; IResolveVisitorNavigator[] navigators;
public CompositeResolveVisitorNavigator(IResolveVisitorNavigator[] navigators) public CompositeResolveVisitorNavigator(params IResolveVisitorNavigator[] navigators)
{ {
if (navigators == null)
throw new ArgumentNullException("navigators");
this.navigators = navigators; this.navigators = navigators;
} }

51
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
{
/// <summary>
/// Represents a scope in which references are searched.
/// </summary>
public interface IFindReferenceSearchScope
{
/// <summary>
/// Gets the search term. Only files that contain this identifier need to be parsed.
/// Can return null if all files need to be parsed.
/// </summary>
string SearchTerm { get; }
/// <summary>
/// Gets the accessibility that defines the search scope.
/// </summary>
Accessibility Accessibility { get; }
/// <summary>
/// Gets the top-level entity that defines the search scope.
/// </summary>
ITypeDefinition TopLevelTypeDefinition { get; }
/// <summary>
/// Creates a navigator that can find references to this entity and reports
/// them to the specified callback.
/// </summary>
IResolveVisitorNavigator GetNavigator(FoundReferenceCallback callback);
}
}

72
src/Libraries/NRefactory/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs

@ -26,16 +26,13 @@ using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.NRefactory.CSharp.Resolver namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
public delegate void FoundReferenceCallback(AstNode astNode, ResolveResult result);
/// <summary> /// <summary>
/// 'Find references' implementation. /// 'Find references' implementation.
/// </summary> /// </summary>
public class FindReferences public class FindReferences
{ {
/// <summary>
/// Callback that is invoked whenever a reference is found.
/// </summary>
public event Action<AstNode, ResolveResult> ReferenceFound;
#region Properties #region Properties
/// <summary> /// <summary>
/// Gets/Sets the cancellation token. /// Gets/Sets the cancellation token.
@ -95,28 +92,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion #endregion
#region class SearchScope #region class SearchScope
public abstract class SearchScope : IResolveVisitorNavigator abstract class SearchScope : IResolveVisitorNavigator, IFindReferenceSearchScope
{ {
protected string searchTerm; protected string searchTerm;
internal Accessibility accessibility; internal Accessibility accessibility;
internal ITypeDefinition topLevelTypeDefinition; internal ITypeDefinition topLevelTypeDefinition;
internal FindReferences findReferences;
/// <summary> FoundReferenceCallback callback;
/// Gets the search term. Only files that contain this identifier need to be parsed.
/// Can return null if all files need to be parsed.
/// </summary>
public string SearchTerm { get { return searchTerm; } }
/// <summary> IResolveVisitorNavigator IFindReferenceSearchScope.GetNavigator(FoundReferenceCallback callback)
/// Gets the accessibility that defines the search scope. {
/// </summary> SearchScope n = (SearchScope)MemberwiseClone();
public Accessibility Accessibility { get { return accessibility; } } n.callback = callback;
return n;
}
/// <summary> string IFindReferenceSearchScope.SearchTerm {
/// Gets the top-level entity that defines the search scope. get { return searchTerm; }
/// </summary> }
public ITypeDefinition TopLevelTypeDefinition { get { return topLevelTypeDefinition; } }
Accessibility IFindReferenceSearchScope.Accessibility {
get { return accessibility; }
}
ITypeDefinition IFindReferenceSearchScope.TopLevelTypeDefinition {
get { return topLevelTypeDefinition; }
}
internal abstract bool CanMatch(AstNode node); internal abstract bool CanMatch(AstNode node);
internal abstract bool IsMatch(ResolveResult rr); internal abstract bool IsMatch(ResolveResult rr);
@ -147,15 +148,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
protected void ReportMatch(AstNode node, ResolveResult result) protected void ReportMatch(AstNode node, ResolveResult result)
{ {
var referenceFound = findReferences.ReferenceFound; if (callback != null)
if (referenceFound != null) callback(node, result);
referenceFound(node, result);
} }
} }
#endregion #endregion
#region GetSearchScopes #region GetSearchScopes
public IList<SearchScope> GetSearchScopes(IEntity entity) public IList<IFindReferenceSearchScope> GetSearchScopes(IEntity entity)
{ {
if (entity == null) if (entity == null)
throw new ArgumentNullException("entity"); throw new ArgumentNullException("entity");
@ -196,19 +196,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
additionalScope = new FindChainedConstructorReferences(ctor); additionalScope = new FindChainedConstructorReferences(ctor);
break; break;
case EntityType.Destructor: case EntityType.Destructor:
return EmptyList<SearchScope>.Instance; return EmptyList<IFindReferenceSearchScope>.Instance;
default: default:
throw new ArgumentException("Unknown entity type " + entity.EntityType); throw new ArgumentException("Unknown entity type " + entity.EntityType);
} }
if (scope.accessibility == Accessibility.None) if (scope.accessibility == Accessibility.None)
scope.accessibility = effectiveAccessibility; scope.accessibility = effectiveAccessibility;
scope.topLevelTypeDefinition = topLevelTypeDefinition; scope.topLevelTypeDefinition = topLevelTypeDefinition;
scope.findReferences = this;
if (additionalScope != null) { if (additionalScope != null) {
if (additionalScope.accessibility == Accessibility.None) if (additionalScope.accessibility == Accessibility.None)
additionalScope.accessibility = effectiveAccessibility; additionalScope.accessibility = effectiveAccessibility;
additionalScope.topLevelTypeDefinition = topLevelTypeDefinition; additionalScope.topLevelTypeDefinition = topLevelTypeDefinition;
additionalScope.findReferences = this;
return new[] { scope, additionalScope }; return new[] { scope, additionalScope };
} else { } else {
return new[] { scope }; return new[] { scope };
@ -220,7 +218,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary> /// <summary>
/// Gets the file names that possibly contain references to the element being searched for. /// Gets the file names that possibly contain references to the element being searched for.
/// </summary> /// </summary>
public IList<string> GetInterestingFileNames(SearchScope searchScope, IEnumerable<ITypeDefinition> allTypes, ITypeResolveContext context) public IList<string> GetInterestingFileNames(IFindReferenceSearchScope searchScope, IEnumerable<ITypeDefinition> allTypes, ITypeResolveContext context)
{ {
IEnumerable<ITypeDefinition> interestingTypes; IEnumerable<ITypeDefinition> interestingTypes;
if (searchScope.TopLevelTypeDefinition != null) { if (searchScope.TopLevelTypeDefinition != null) {
@ -276,11 +274,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <param name="parsedFile">The type system representation of the file being searched.</param> /// <param name="parsedFile">The type system representation of the file being searched.</param>
/// <param name="compilationUnit">The compilation unit of the file being searched.</param> /// <param name="compilationUnit">The compilation unit of the file being searched.</param>
/// <param name="context">The type resolve context to use for resolving the file.</param> /// <param name="context">The type resolve context to use for resolving the file.</param>
public void FindReferencesInFile(SearchScope searchScope, ParsedFile parsedFile, CompilationUnit compilationUnit, ITypeResolveContext context) /// <param name="callback">Callback used to report the references that were found.</param>
public void FindReferencesInFile(IFindReferenceSearchScope searchScope, ParsedFile parsedFile, CompilationUnit compilationUnit,
ITypeResolveContext context, FoundReferenceCallback callback)
{ {
if (searchScope == null) if (searchScope == null)
throw new ArgumentNullException("searchScope"); throw new ArgumentNullException("searchScope");
FindReferencesInFile(new[] { searchScope }, parsedFile, compilationUnit, context); FindReferencesInFile(new[] { searchScope }, parsedFile, compilationUnit, context, callback);
} }
/// <summary> /// <summary>
@ -290,7 +290,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <param name="parsedFile">The type system representation of the file being searched.</param> /// <param name="parsedFile">The type system representation of the file being searched.</param>
/// <param name="compilationUnit">The compilation unit of the file being searched.</param> /// <param name="compilationUnit">The compilation unit of the file being searched.</param>
/// <param name="context">The type resolve context to use for resolving the file.</param> /// <param name="context">The type resolve context to use for resolving the file.</param>
public void FindReferencesInFile(IList<SearchScope> searchScopes, ParsedFile parsedFile, CompilationUnit compilationUnit, ITypeResolveContext context) /// <param name="callback">Callback used to report the references that were found.</param>
public void FindReferencesInFile(IList<IFindReferenceSearchScope> searchScopes, ParsedFile parsedFile, CompilationUnit compilationUnit,
ITypeResolveContext context, FoundReferenceCallback callback)
{ {
if (searchScopes == null) if (searchScopes == null)
throw new ArgumentNullException("searchScopes"); throw new ArgumentNullException("searchScopes");
@ -303,16 +305,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.CancellationToken.ThrowIfCancellationRequested(); this.CancellationToken.ThrowIfCancellationRequested();
if (searchScopes.Count == 0) if (searchScopes.Count == 0)
return; 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()) { using (var ctx = context.Synchronize()) {
IResolveVisitorNavigator navigator; IResolveVisitorNavigator navigator;
if (searchScopes.Count == 1) if (searchScopes.Count == 1)
navigator = searchScopes[0]; navigator = searchScopes[0].GetNavigator(callback);
else else
navigator = new CompositeResolveVisitorNavigator(searchScopes.ToArray()); navigator = new CompositeResolveVisitorNavigator(searchScopes.Select(s => s.GetNavigator(callback)).ToArray());
navigator = new DetectSkippableNodesNavigator(navigator, compilationUnit); navigator = new DetectSkippableNodesNavigator(navigator, compilationUnit);
CSharpResolver resolver = new CSharpResolver(ctx, this.CancellationToken); CSharpResolver resolver = new CSharpResolver(ctx, this.CancellationToken);
ResolveVisitor v = new ResolveVisitor(resolver, parsedFile, navigator); ResolveVisitor v = new ResolveVisitor(resolver, parsedFile, navigator);

1
src/Libraries/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -104,6 +104,7 @@
<Compile Include="CSharp\Resolver\DetectSkippableNodesNavigator.cs" /> <Compile Include="CSharp\Resolver\DetectSkippableNodesNavigator.cs" />
<Compile Include="CSharp\Resolver\FindReferencedEntities.cs" /> <Compile Include="CSharp\Resolver\FindReferencedEntities.cs" />
<Compile Include="CSharp\Resolver\FindReferences.cs" /> <Compile Include="CSharp\Resolver\FindReferences.cs" />
<Compile Include="CSharp\Resolver\FindReferenceSearchScope.cs" />
<Compile Include="CSharp\Resolver\Log.cs" /> <Compile Include="CSharp\Resolver\Log.cs" />
<Compile Include="CSharp\Resolver\OperatorResolveResult.cs" /> <Compile Include="CSharp\Resolver\OperatorResolveResult.cs" />
<Compile Include="CSharp\Resolver\ConversionResolveResult.cs" /> <Compile Include="CSharp\Resolver\ConversionResolveResult.cs" />

12
src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs

@ -50,8 +50,20 @@ namespace ICSharpCode.SharpDevelop.Refactoring
return symbolSearches; return symbolSearches;
} }
/// <summary>
/// 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.
/// </summary>
public static void FindReferences(IEntity entity, IProgressMonitor progressMonitor, Action<Reference> callback) public static void FindReferences(IEntity entity, IProgressMonitor progressMonitor, Action<Reference> 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) { if (ParserService.LoadSolutionProjectsThreadRunning) {
progressMonitor.ShowingDialog = true; progressMonitor.ShowingDialog = true;
MessageService.ShowMessage("${res:SharpDevelop.Refactoring.LoadSolutionProjectsThreadRunning}"); MessageService.ShowMessage("${res:SharpDevelop.Refactoring.LoadSolutionProjectsThreadRunning}");

2
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); string entityName = (entity.DeclaringTypeDefinition != null ? entity.DeclaringTypeDefinition.Name + "." + entity.Name : entity.Name);
using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("${res:SharpDevelop.Refactoring.FindReferences}", true)) { using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("${res:SharpDevelop.Refactoring.FindReferences}", true)) {
List<Reference> references = new List<Reference>(); List<Reference> references = new List<Reference>();
FindReferenceService.FindReferences(entity, monitor, references.Add); FindReferenceService.FindReferences(entity, monitor, r => { lock (references) references.Add(r); });
FindReferencesAndRenameHelper.ShowAsSearchResults( FindReferencesAndRenameHelper.ShowAsSearchResults(
StringParser.Parse("${res:SharpDevelop.Refactoring.ReferencesTo}", StringParser.Parse("${res:SharpDevelop.Refactoring.ReferencesTo}",
new StringTagPair("Name", entityName)), new StringTagPair("Name", entityName)),

Loading…
Cancel
Save