1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Diagnostics ;
4+ using System . Linq ;
5+ using MonoDevelop . Core ;
6+ using MonoDevelop . Ide . Gui . Pads . ProjectPad ;
7+ using MonoDevelop . Projects ;
8+ using VisualStudioMac . SolutionTreeFilter . Helpers ;
9+ using VisualStudioMac . SolutionTreeFilter . Helpers . ExtensionSettings ;
10+
11+ namespace VisualStudioMac . SolutionTreeFilter . Cache
12+ {
13+ public static class FilteredProjectCache
14+ {
15+ struct TreeItem
16+ {
17+ public FilePath ProjectVirtualPath ;
18+ public bool IsExpanded ;
19+ public bool IsEnabled ;
20+ }
21+
22+ private static Dictionary < string , DateTime > _projectsDictionary = new Dictionary < string , DateTime > ( ) ;
23+ private static Dictionary < string , TreeItem > _treeDictionary = new Dictionary < string , TreeItem > ( ) ;
24+
25+ private static string _lastFilter = string . Empty ;
26+
27+ public static void ScanProjectForFiles ( Project project )
28+ {
29+ lock ( _projectsDictionary )
30+ {
31+ var filter = FilterSettings . SolutionFilter ;
32+
33+ // check if cache is still current according to filter used
34+ if ( _lastFilter != filter )
35+ {
36+ _projectsDictionary . Clear ( ) ;
37+ _treeDictionary . Clear ( ) ;
38+ _lastFilter = filter ;
39+ }
40+
41+ // check if cache is still current
42+ DateTime lastDate ;
43+ if ( _projectsDictionary . TryGetValue ( project . Name , out lastDate ) )
44+ {
45+ var d = DateTime . Now - lastDate ;
46+ if ( d . TotalSeconds < 60 )
47+ return ;
48+ }
49+ _projectsDictionary [ project . Name ] = DateTime . Now ;
50+ var filterArray = FilterSettings . SolutionFilterArray ;
51+ var pinnedDocuments = FilterSettings . PinnedDocuments ;
52+
53+ // clear all entries related to project.
54+ ClearCacheOfProject ( project ) ;
55+
56+ // Look for files that are in this project and comply with the filter. Add them to dictionary
57+ foreach ( var file in project . Files )
58+ {
59+ FilePath path ;
60+
61+ //If the file is pinned in the Workbench, add it to the cache.
62+ if ( filterArray . Count ( ) > 0 && pinnedDocuments . Contains ( file . FilePath ) )
63+ RegisterFile ( project . Name , file . ProjectVirtualPath . FileName , file . ProjectVirtualPath . ParentDirectory , true , filterArray , true ) ;
64+
65+ if ( ! file . Visible || file . Flags . HasFlag ( ProjectItemFlags . Hidden ) || file . Subtype == Subtype . Directory )
66+ continue ;
67+
68+ path = file . IsLink ? project . BaseDirectory . Combine ( file . ProjectVirtualPath ) : file . FilePath ;
69+
70+ if ( filterArray . Length > 0 )
71+ {
72+ foreach ( var key in filterArray )
73+ {
74+
75+ if ( file . ProjectVirtualPath . ToString ( ) . ToLower ( ) . Contains ( key ) )
76+ {
77+ #if DEBUG
78+ if ( file . FilePath . FileName == "StageProgressBar.designer.cs" )
79+ {
80+ Debug . WriteLine ( "BINGO !!" ) ;
81+ }
82+ #endif
83+
84+
85+ // if this file depends on another, make sure that that parent file in the cache is marked as expanded too.
86+
87+
88+ // Found file or folder that fits the filter
89+ var filenameInFilter = false ;
90+ var filenameToTest = file . FilePath . FileName . ToLower ( ) ;
91+ foreach ( var key1 in filterArray )
92+ {
93+ if ( filenameToTest . Contains ( key1 ) )
94+ {
95+ filenameInFilter = true ;
96+ break ;
97+ }
98+ }
99+
100+ var filename = file . ProjectVirtualPath . FileName ;
101+ var folder = file . ProjectVirtualPath . ParentDirectory ;
102+ RegisterFile ( project . Name , filename , folder , filenameInFilter , filterArray ) ;
103+
104+ break ;
105+ }
106+ }
107+ }
108+ }
109+ }
110+ Debug . WriteLine ( ToString ( ) ) ;
111+ }
112+
113+ private static void ClearCacheOfProject ( Project project )
114+ {
115+ foreach ( var s in _treeDictionary . Where ( item => item . Key . StartsWith ( project . Name ) ) . ToList ( ) )
116+ {
117+ _treeDictionary . Remove ( s . Key ) ;
118+ }
119+ }
120+
121+ /// <summary>
122+ /// Recursive routine which registers an entry for each folder in the file's path
123+ /// </summary>
124+ private static void RegisterFile ( string projectname , string filename , FilePath folder , bool filenameInFilter , string [ ] filter , bool isPinnedDoc = false )
125+ {
126+ RegisterFileForFolder ( projectname , filename , folder , filenameInFilter , filter ) ;
127+
128+ // Register file
129+ var foldername = projectname + "/" + folder . ToString ( ) + "/" + filename ;
130+ foldername = foldername . Replace ( "//" , "/" ) ;
131+ TreeItem item ;
132+ if ( ! _treeDictionary . TryGetValue ( foldername , out item ) )
133+ item = new TreeItem { ProjectVirtualPath = folder } ;
134+
135+ item . IsExpanded = item . IsExpanded || filenameInFilter ;
136+ item . IsEnabled = item . IsEnabled || filenameInFilter ;
137+ _treeDictionary [ foldername ] = item ;
138+ }
139+
140+ /// <summary>
141+ /// Recursive routine which registers an entry for each folder in the file's path
142+ /// </summary>
143+ private static string RegisterFileForFolder ( string projectname , string filename , FilePath folder , bool filenameInFilter , string [ ] filter )
144+ {
145+ if ( folder == null || folder . IsEmpty )
146+ return projectname ;
147+
148+ // Enter recursion
149+ var foldername = RegisterFileForFolder ( projectname , filename , folder . ParentDirectory , filenameInFilter , filter ) + $ "/" + folder . FileName ;
150+ foldername = foldername . Replace ( "//" , "/" ) ;
151+ // Register file in dictionary
152+ TreeItem item ;
153+ if ( ! _treeDictionary . TryGetValue ( foldername , out item ) )
154+ item = new TreeItem { ProjectVirtualPath = folder } ;
155+ item . IsExpanded = item . IsExpanded || filenameInFilter || IsFilePathExpanded ( folder , filter ) ;
156+ item . IsEnabled = true ;
157+ _treeDictionary [ foldername ] = item ;
158+
159+ return foldername ;
160+ }
161+
162+ private static bool IsFilePathExpanded ( FilePath folder , string [ ] filter )
163+ {
164+ foreach ( var key in filter )
165+ {
166+ if ( folder . FileName . ToLower ( ) . Contains ( key ) )
167+ return true ;
168+ }
169+ return false ;
170+ }
171+
172+ private static string GetFoldername ( ProjectFolder folder )
173+ {
174+ var parent = folder . Parent ;
175+ if ( parent is Project project )
176+ return project . Name + "/" + folder . Name ;
177+
178+ // Enter recursion
179+ return GetFoldername ( parent as ProjectFolder ) + $ "/" + folder . Name ;
180+ }
181+
182+ private static string GetKeyFor ( object dataObject )
183+ {
184+ string key ;
185+ switch ( dataObject )
186+ {
187+ case ProjectFile file :
188+ key = file . Project . Name + "/" + file . ProjectVirtualPath . ToString ( ) ;
189+ break ;
190+ case ProjectFolder folder :
191+ key = GetFoldername ( folder ) ;
192+ break ;
193+ case Project project :
194+ key = project . Name ;
195+ break ;
196+ default :
197+ key = string . Empty ;
198+ break ;
199+ }
200+ key = key . Replace ( "//" , "/" ) ;
201+ return key ;
202+ }
203+
204+ public static bool IsProjectItemVisible ( object dataItem )
205+ {
206+ if ( dataItem is ProjectFile || dataItem is ProjectFolder )
207+ {
208+ var key = GetKeyFor ( dataItem ) ;
209+ return _treeDictionary . TryGetValue ( key , out _ ) ;
210+ }
211+ else if ( dataItem is Project project )
212+ {
213+ return _treeDictionary . Keys . FindIndex ( ( key ) => key . Contains ( project . Name ) ) != - 1 ;
214+ }
215+ return false ;
216+ }
217+
218+ public static bool IsProjectItemEnabled ( object dataItem )
219+ {
220+ if ( dataItem is ProjectFile || dataItem is ProjectFolder )
221+ {
222+ var key = GetKeyFor ( dataItem ) ;
223+ TreeItem item ;
224+ if ( ! _treeDictionary . TryGetValue ( key , out item ) )
225+ return false ;
226+ return item . IsEnabled ;
227+ }
228+ else if ( dataItem is Project project )
229+ {
230+ return true ;
231+ }
232+ return false ;
233+ }
234+
235+ public static bool IsProjectItemExpanded ( object dataItem )
236+ {
237+ if ( dataItem is ProjectFile || dataItem is ProjectFolder )
238+ {
239+ var key = GetKeyFor ( dataItem ) ;
240+ TreeItem item ;
241+
242+ if ( dataItem is ProjectFile pf && pf . DependentChildren . Where ( f => f . FilePath . FileName . EndsWith ( ".designer.cs" ) ) . Any ( ) )
243+ return false ;
244+
245+ var found = _treeDictionary . TryGetValue ( key , out item ) ;
246+ return found && item . IsExpanded ;
247+ }
248+ else if ( dataItem is Project project )
249+ {
250+ return _treeDictionary . Keys . FindIndex ( ( key ) => key . Contains ( project . Name ) ) != - 1 ;
251+ }
252+ return false ;
253+ }
254+
255+ public static new string ToString ( )
256+ {
257+ var str = "" ;
258+ var maxLength = 0 ;
259+
260+ foreach ( var item in _treeDictionary )
261+ {
262+ if ( item . Key . Length > maxLength )
263+ maxLength = item . Key . Length ;
264+ }
265+ maxLength += 1 ;
266+
267+ str += $ "\n {{FilteredProjectCache - TreeDictionary}}\n ";
268+ foreach ( var item in _treeDictionary )
269+ {
270+ str += $ "\t - { item . Key . PadRight ( maxLength , '.' ) } \t => IsExpanded: { item . Value . IsExpanded } \t => IsEnabled: { item . Value . IsEnabled } \n ";
271+ }
272+ return str ;
273+ }
274+ }
275+ }
0 commit comments