2424using System . Threading ;
2525using System . Threading . Tasks ;
2626
27+ using ICSharpCode . NRefactory . Analysis ;
2728using CSharpBinding . Parser ;
2829using ICSharpCode . AvalonEdit . Document ;
2930using ICSharpCode . AvalonEdit . Highlighting ;
@@ -56,22 +57,44 @@ public class CSharpSymbolSearch : ISymbolSearch
5657 FindReferences fr = new FindReferences ( ) ;
5758 IList < IFindReferenceSearchScope > searchScopes ;
5859 IList < string > [ ] interestingFileNames ;
60+ Dictionary < string , IList < IFindReferenceSearchScope > > searchScopesPerFile ;
5961 int workAmount ;
6062 double workAmountInverse ;
6163
6264 public CSharpSymbolSearch ( IProject project , ISymbol entity )
6365 {
6466 this . project = project ;
65- searchScopes = fr . GetSearchScopes ( entity ) ;
6667 compilation = SD . ParserService . GetCompilation ( project ) ;
68+ var relatedSymbols = GetRelatedSymbols ( entity ) ;
69+ if ( ( relatedSymbols != null ) && relatedSymbols . Any ( ) ) {
70+ searchScopes = relatedSymbols . SelectMany ( e => fr . GetSearchScopes ( e ) ) . ToList ( ) ;
71+ } else {
72+ searchScopes = fr . GetSearchScopes ( entity ) ;
73+ }
74+
75+ searchScopesPerFile = new Dictionary < string , IList < IFindReferenceSearchScope > > ( ) ;
6776 interestingFileNames = new IList < string > [ searchScopes . Count ] ;
6877 for ( int i = 0 ; i < searchScopes . Count ; i ++ ) {
69- interestingFileNames [ i ] = fr . GetInterestingFiles ( searchScopes [ i ] , compilation ) . Select ( f => f . FileName ) . ToList ( ) ;
70- workAmount += interestingFileNames [ i ] . Count ;
78+ var thisSearchScope = searchScopes [ i ] ;
79+ var interestingFiles = fr . GetInterestingFiles ( thisSearchScope , compilation ) . Select ( f => f . FileName ) . ToList ( ) ;
80+ foreach ( var file in interestingFiles ) {
81+ if ( ! searchScopesPerFile . ContainsKey ( file ) )
82+ searchScopesPerFile [ file ] = new List < IFindReferenceSearchScope > ( ) ;
83+ searchScopesPerFile [ file ] . Add ( thisSearchScope ) ;
84+ }
85+ interestingFileNames [ i ] = interestingFiles . ToList ( ) ;
86+ workAmount += interestingFiles . Count ;
7187 }
7288 workAmountInverse = 1.0 / workAmount ;
7389 }
7490
91+ IEnumerable < ISymbol > GetRelatedSymbols ( ISymbol entity )
92+ {
93+ TypeGraph typeGraph = new TypeGraph ( new [ ] { compilation . MainAssembly } ) ;
94+ var symbolCollector = new SymbolCollector ( ) ;
95+ return symbolCollector . GetRelatedSymbols ( typeGraph , entity ) ;
96+ }
97+
7598 public double WorkAmount {
7699 get { return workAmount ; }
77100 }
@@ -83,38 +106,36 @@ public Task FindReferencesAsync(SymbolSearchArgs args, Action<SearchedFile> call
83106 var cancellationToken = args . ProgressMonitor . CancellationToken ;
84107 return Task . Run (
85108 ( ) => {
86- for ( int i = 0 ; i < searchScopes . Count ; i ++ ) {
87- IFindReferenceSearchScope searchScope = searchScopes [ i ] ;
88- object progressLock = new object ( ) ;
89- Parallel . ForEach (
90- interestingFileNames [ i ] ,
91- new ParallelOptions {
92- MaxDegreeOfParallelism = Environment . ProcessorCount ,
93- CancellationToken = cancellationToken
94- } ,
95- delegate ( string fileName ) {
96- try {
97- FindReferencesInFile ( args , searchScope , FileName . Create ( fileName ) , callback , cancellationToken ) ;
98- } catch ( OperationCanceledException ) {
99- throw ;
100- } catch ( Exception ex ) {
101- throw new ApplicationException ( "Error searching in file '" + fileName + "'" , ex ) ;
102- }
103- lock ( progressLock )
104- args . ProgressMonitor . Progress += workAmountInverse ;
105- } ) ;
106- }
109+ object progressLock = new object ( ) ;
110+ Parallel . ForEach (
111+ searchScopesPerFile . Keys ,
112+ new ParallelOptions {
113+ MaxDegreeOfParallelism = Environment . ProcessorCount ,
114+ CancellationToken = cancellationToken
115+ } ,
116+ delegate ( string fileName ) {
117+ try {
118+ FindReferencesInFile ( args , searchScopesPerFile [ fileName ] , FileName . Create ( fileName ) , callback , cancellationToken ) ;
119+ } catch ( OperationCanceledException ) {
120+ throw ;
121+ } catch ( Exception ex ) {
122+ throw new ApplicationException ( "Error searching in file '" + fileName + "'" , ex ) ;
123+ }
124+ lock ( progressLock )
125+ args . ProgressMonitor . Progress += workAmountInverse ;
126+ } ) ;
107127 } , cancellationToken
108128 ) ;
109129 }
110130
111- void FindReferencesInFile ( SymbolSearchArgs args , IFindReferenceSearchScope searchScope , FileName fileName , Action < SearchedFile > callback , CancellationToken cancellationToken )
131+ void FindReferencesInFile ( SymbolSearchArgs args , IList < IFindReferenceSearchScope > searchScopeList , FileName fileName , Action < SearchedFile > callback , CancellationToken cancellationToken )
112132 {
113133 ITextSource textSource = args . ParseableFileContentFinder . Create ( fileName ) ;
114134 if ( textSource == null )
115135 return ;
116- if ( searchScope . SearchTerm != null ) {
117- if ( textSource . IndexOf ( searchScope . SearchTerm , 0 , textSource . TextLength , StringComparison . Ordinal ) < 0 )
136+ if ( searchScopeList != null ) {
137+ if ( ! searchScopeList . Any (
138+ scope => ( scope . SearchTerm == null ) || ( textSource . IndexOf ( scope . SearchTerm , 0 , textSource . TextLength , StringComparison . Ordinal ) >= 0 ) ) )
118139 return ;
119140 }
120141
@@ -134,7 +155,7 @@ void FindReferencesInFile(SymbolSearchArgs args, IFindReferenceSearchScope searc
134155 }
135156
136157 fr . FindReferencesInFile (
137- searchScope , unresolvedFile , parseInfo . SyntaxTree , compilation ,
158+ searchScopeList , unresolvedFile , parseInfo . SyntaxTree , compilation ,
138159 delegate ( AstNode node , ResolveResult result ) {
139160 if ( document == null ) {
140161 document = new ReadOnlyDocument ( textSource , fileName ) ;
@@ -154,8 +175,18 @@ void FindReferencesInFile(SymbolSearchArgs args, IFindReferenceSearchScope searc
154175 if ( highlighter != null ) {
155176 highlighter . Dispose ( ) ;
156177 }
157- if ( results . Count > 0 )
158- callback ( new SearchedFile ( fileName , results ) ) ;
178+ if ( results . Count > 0 ) {
179+ // Remove overlapping results
180+ List < SearchResultMatch > fixedResults = new List < SearchResultMatch > ( ) ;
181+ int lastEndOffset = 0 ;
182+ foreach ( var result in results . OrderBy ( m => m . StartOffset ) ) {
183+ if ( result . StartOffset >= lastEndOffset ) {
184+ fixedResults . Add ( result ) ;
185+ lastEndOffset = result . EndOffset ;
186+ }
187+ }
188+ callback ( new SearchedFile ( fileName , fixedResults ) ) ;
189+ }
159190 }
160191
161192 public Task RenameAsync ( SymbolRenameArgs args , Action < PatchedFile > callback , Action < Error > errorCallback )
@@ -166,32 +197,30 @@ public Task RenameAsync(SymbolRenameArgs args, Action<PatchedFile> callback, Act
166197 return Task . Run (
167198 ( ) => {
168199 bool isNameValid = Mono . CSharp . Tokenizer . IsValidIdentifier ( args . NewName ) ;
169- for ( int i = 0 ; i < searchScopes . Count ; i ++ ) {
170- IFindReferenceSearchScope searchScope = searchScopes [ i ] ;
171- object progressLock = new object ( ) ;
172- Parallel . ForEach (
173- interestingFileNames [ i ] ,
174- new ParallelOptions {
175- MaxDegreeOfParallelism = Environment . ProcessorCount ,
176- CancellationToken = cancellationToken
177- } ,
178- delegate ( string fileName ) {
179- RenameReferencesInFile ( args , searchScope , FileName . Create ( fileName ) , callback , errorCallback , isNameValid , cancellationToken ) ;
180- lock ( progressLock )
181- args . ProgressMonitor . Progress += workAmountInverse ;
182- } ) ;
183- }
200+ object progressLock = new object ( ) ;
201+ Parallel . ForEach (
202+ searchScopesPerFile . Keys ,
203+ new ParallelOptions {
204+ MaxDegreeOfParallelism = Environment . ProcessorCount ,
205+ CancellationToken = cancellationToken
206+ } ,
207+ delegate ( string fileName ) {
208+ RenameReferencesInFile ( args , searchScopesPerFile [ fileName ] , FileName . Create ( fileName ) , callback , errorCallback , isNameValid , cancellationToken ) ;
209+ lock ( progressLock )
210+ args . ProgressMonitor . Progress += workAmountInverse ;
211+ } ) ;
184212 } , cancellationToken
185213 ) ;
186214 }
187215
188- void RenameReferencesInFile ( SymbolRenameArgs args , IFindReferenceSearchScope searchScope , FileName fileName , Action < PatchedFile > callback , Action < Error > errorCallback , bool isNameValid , CancellationToken cancellationToken )
216+ void RenameReferencesInFile ( SymbolRenameArgs args , IList < IFindReferenceSearchScope > searchScopeList , FileName fileName , Action < PatchedFile > callback , Action < Error > errorCallback , bool isNameValid , CancellationToken cancellationToken )
189217 {
190218 ITextSource textSource = args . ParseableFileContentFinder . Create ( fileName ) ;
191219 if ( textSource == null )
192220 return ;
193- if ( searchScope . SearchTerm != null ) {
194- if ( textSource . IndexOf ( searchScope . SearchTerm , 0 , textSource . TextLength , StringComparison . Ordinal ) < 0 )
221+ if ( searchScopeList != null ) {
222+ if ( ! searchScopeList . Any (
223+ scope => ( scope . SearchTerm == null ) || ( textSource . IndexOf ( scope . SearchTerm , 0 , textSource . TextLength , StringComparison . Ordinal ) >= 0 ) ) )
195224 return ;
196225 }
197226
@@ -213,7 +242,7 @@ void RenameReferencesInFile(SymbolRenameArgs args, IFindReferenceSearchScope sea
213242 CSharpAstResolver resolver = new CSharpAstResolver ( compilation , parseInfo . SyntaxTree , unresolvedFile ) ;
214243
215244 fr . RenameReferencesInFile (
216- new [ ] { searchScope } , args . NewName , resolver ,
245+ searchScopeList , args . NewName , resolver ,
217246 delegate ( RenameCallbackArguments callbackArgs ) {
218247 var node = callbackArgs . NodeToReplace ;
219248 string newCode = callbackArgs . NewNode . ToString ( ) ;
@@ -249,10 +278,16 @@ void RenameReferencesInFile(SymbolRenameArgs args, IFindReferenceSearchScope sea
249278 }
250279 IDocument changedDocument = new TextDocument ( document ) ;
251280 var oldVersion = changedDocument . Version ;
281+ List < SearchResultMatch > fixedResults = new List < SearchResultMatch > ( ) ;
282+ int lastStartOffset = changedDocument . TextLength + 1 ;
252283 foreach ( var result in results . OrderByDescending ( m => m . StartOffset ) ) {
253- changedDocument . Replace ( result . StartOffset , result . Length , result . NewCode ) ;
284+ if ( result . EndOffset <= lastStartOffset ) {
285+ changedDocument . Replace ( result . StartOffset , result . Length , result . NewCode ) ;
286+ fixedResults . Add ( result ) ;
287+ lastStartOffset = result . StartOffset ;
288+ }
254289 }
255- callback ( new PatchedFile ( fileName , results , oldVersion , changedDocument . Version ) ) ;
290+ callback ( new PatchedFile ( fileName , fixedResults , oldVersion , changedDocument . Version ) ) ;
256291 }
257292 }
258293 }
0 commit comments