1
+ using System . Collections . Generic ;
2
+ using System . Linq ;
3
+ using System . Threading ;
4
+ using System . Threading . Tasks ;
5
+ using NLog ;
6
+ using Rubberduck . Parsing . Symbols ;
7
+ using Rubberduck . Parsing . VBA ;
8
+ using Rubberduck . Parsing . VBA . Extensions ;
9
+ using Rubberduck . VBEditor ;
10
+
11
+ namespace Rubberduck . Parsing . Rewriter
12
+ {
13
+ public class MemberAttributeRecoverer : IMemberAttributeRecoverer
14
+ {
15
+ private readonly IDeclarationFinderProvider _declarationFinderProvider ;
16
+ private readonly IParseManager _parseManager ;
17
+ private readonly IAttributesUpdater _attributesUpdater ;
18
+ private IRewritingManager _rewritingManager ;
19
+
20
+ private readonly
21
+ IDictionary < QualifiedModuleName , IDictionary < string , HashSet < AttributeNode > > > _attributesToRecover
22
+ = new Dictionary < QualifiedModuleName , IDictionary < string , HashSet < AttributeNode > > > ( ) ;
23
+
24
+ private readonly Logger _logger = LogManager . GetCurrentClassLogger ( ) ;
25
+
26
+ public MemberAttributeRecoverer ( IDeclarationFinderProvider declarationFinderProvider ,
27
+ IParseManager parseManager , IAttributesUpdater attributesUpdater )
28
+ {
29
+ _declarationFinderProvider = declarationFinderProvider ;
30
+ _parseManager = parseManager ;
31
+ _attributesUpdater = attributesUpdater ;
32
+ }
33
+
34
+ //This needs to be property injected because this class will be constructor injected into the RewritingManager that needs to set itself as the dependency here.
35
+ public IRewritingManager RewritingManager
36
+ {
37
+ set => _rewritingManager = value ;
38
+ }
39
+
40
+ public void RecoverCurrentMemberAttributesAfterNextParse ( IEnumerable < QualifiedMemberName > members )
41
+ {
42
+ var declarationsByModule = MemberDeclarationsByModule ( members ) ;
43
+ RecoverCurrentMemberAttributesAfterNextParse ( declarationsByModule ) ;
44
+ }
45
+
46
+ private IDictionary < QualifiedModuleName , IEnumerable < Declaration > > MemberDeclarationsByModule ( IEnumerable < QualifiedMemberName > members )
47
+ {
48
+ var membersByModule = members . GroupBy ( member => member . QualifiedModuleName )
49
+ . ToDictionary ( group => group . Key , group => group . ToHashSet ( ) ) ;
50
+ var declarationFinder = _declarationFinderProvider . DeclarationFinder ;
51
+ var memberDeclarationsPerModule = new Dictionary < QualifiedModuleName , IEnumerable < Declaration > > ( ) ;
52
+ foreach ( var module in membersByModule . Keys )
53
+ {
54
+ var memberDeclarationsInModule = declarationFinder . Members ( module )
55
+ . Where ( declaration => membersByModule [ module ] . Contains ( declaration . QualifiedName )
56
+ && declaration . ParentScopeDeclaration . DeclarationType . HasFlag ( DeclarationType . Module ) ) ;
57
+ memberDeclarationsPerModule . Add ( module , memberDeclarationsInModule ) ;
58
+ }
59
+
60
+ return memberDeclarationsPerModule ;
61
+ }
62
+
63
+ private void RecoverCurrentMemberAttributesAfterNextParse (
64
+ IDictionary < QualifiedModuleName , IEnumerable < Declaration > > declarationsByModule )
65
+ {
66
+ SaveAttributesToRecover ( declarationsByModule ) ;
67
+
68
+ if ( _attributesToRecover . Any ( ) )
69
+ {
70
+ PrimeRecoveryOfAttributes ( ) ;
71
+ }
72
+ }
73
+
74
+ private void SaveAttributesToRecover ( IDictionary < QualifiedModuleName , IEnumerable < Declaration > > declarationsByModule )
75
+ {
76
+ _attributesToRecover . Clear ( ) ;
77
+ foreach ( var module in declarationsByModule . Keys )
78
+ {
79
+ var attributesByMember = declarationsByModule [ module ]
80
+ . Where ( decl => decl . Attributes . Any ( ) )
81
+ . ToDictionary (
82
+ decl => decl . IdentifierName ,
83
+ decl => ( HashSet < AttributeNode > ) decl . Attributes ) ;
84
+ _attributesToRecover . Add ( module , attributesByMember ) ;
85
+ }
86
+ }
87
+
88
+ private void PrimeRecoveryOfAttributes ( )
89
+ {
90
+ _parseManager . StateChanged += ExecuteRecoveryOfAttributes ;
91
+ }
92
+
93
+ private void ExecuteRecoveryOfAttributes ( object sender , ParserStateEventArgs e )
94
+ {
95
+ if ( e . State != ParserState . ResolvedDeclarations )
96
+ {
97
+ return ;
98
+ }
99
+
100
+ StopRecoveringAttributesOnNextParse ( ) ;
101
+ CancelTheCurrentParse ( ) ;
102
+
103
+ var rewriteSession = _rewritingManager . CheckOutAttributesSession ( ) ;
104
+ foreach ( var module in _attributesToRecover . Keys )
105
+ {
106
+ RecoverAttributes ( rewriteSession , module , _attributesToRecover [ module ] ) ;
107
+ }
108
+
109
+ Task . Run ( ( ) => rewriteSession . TryRewrite ( ) ) ;
110
+
111
+ EndTheCurrentParse ( e . Token ) ;
112
+ }
113
+
114
+ private void StopRecoveringAttributesOnNextParse ( )
115
+ {
116
+ _parseManager . StateChanged -= ExecuteRecoveryOfAttributes ;
117
+ }
118
+
119
+ private void CancelTheCurrentParse ( )
120
+ {
121
+ _parseManager . OnParseCancellationRequested ( this ) ;
122
+ }
123
+
124
+ private void RecoverAttributes ( IRewriteSession rewriteSession , QualifiedModuleName module , IDictionary < string , HashSet < AttributeNode > > attributesByMember )
125
+ {
126
+ var membersWithAttributesToRecover = attributesByMember . Keys . ToHashSet ( ) ;
127
+ var declarationFinder = _declarationFinderProvider . DeclarationFinder ;
128
+ var declarationsWithAttributesToRecover = declarationFinder . Members ( module )
129
+ . Where ( decl => membersWithAttributesToRecover . Contains ( decl . IdentifierName )
130
+ && decl . ParentScopeDeclaration . DeclarationType . HasFlag ( DeclarationType . Module ) )
131
+ . ToList ( ) ;
132
+
133
+ if ( membersWithAttributesToRecover . Count != declarationsWithAttributesToRecover . Count )
134
+ {
135
+ LogFailureToRecoverAllAttributes ( module , membersWithAttributesToRecover , declarationsWithAttributesToRecover ) ;
136
+ }
137
+
138
+ foreach ( var declaration in declarationsWithAttributesToRecover )
139
+ {
140
+ RecoverAttributes ( rewriteSession , declaration , attributesByMember [ declaration . IdentifierName ] ) ;
141
+ }
142
+ }
143
+
144
+ private void LogFailureToRecoverAllAttributes ( QualifiedModuleName module , IEnumerable < string > membersWithAttributesToRecover , List < Declaration > declarationsWithAttributesToRecover )
145
+ {
146
+ _logger . Warn ( "Could not recover the attributes for all members because one or more members could no longer be found." ) ;
147
+
148
+ var membersWithoutDeclarations = membersWithAttributesToRecover . ToHashSet ( ) ;
149
+ membersWithoutDeclarations . ExceptWith ( declarationsWithAttributesToRecover . Select ( decl => decl . IdentifierName ) ) ;
150
+ foreach ( var member in membersWithoutDeclarations )
151
+ {
152
+ _logger . Trace ( $ "Could not recover the attributes for member { member } in module { module } because a member of that name exists no longer.") ;
153
+ }
154
+ }
155
+
156
+ private void RecoverAttributes ( IRewriteSession rewriteSession , Declaration declaration , IEnumerable < AttributeNode > attributes )
157
+ {
158
+ foreach ( var attribute in attributes )
159
+ {
160
+ if ( ! declaration . Attributes . Contains ( attribute ) )
161
+ {
162
+ _attributesUpdater . AddAttribute ( rewriteSession , declaration , attribute . Name , attribute . Values ) ;
163
+ }
164
+ }
165
+ }
166
+
167
+ private void EndTheCurrentParse ( CancellationToken currentCancellationToken )
168
+ {
169
+ currentCancellationToken . ThrowIfCancellationRequested ( ) ;
170
+ }
171
+
172
+ public void RecoverCurrentMemberAttributesAfterNextParse ( IEnumerable < QualifiedModuleName > modules )
173
+ {
174
+ var declarationsByModule = MemberDeclarationsByModule ( modules ) ;
175
+ RecoverCurrentMemberAttributesAfterNextParse ( declarationsByModule ) ;
176
+ }
177
+
178
+ private IDictionary < QualifiedModuleName , IEnumerable < Declaration > > MemberDeclarationsByModule ( IEnumerable < QualifiedModuleName > modules )
179
+ {
180
+ var declarationFinder = _declarationFinderProvider . DeclarationFinder ;
181
+ var declarationsByModule = modules . ToDictionary (
182
+ module => module ,
183
+ module => declarationFinder . Members ( module )
184
+ . Where ( decl => ! decl . DeclarationType . HasFlag ( DeclarationType . Module )
185
+ && decl . ParentScopeDeclaration . DeclarationType . HasFlag ( DeclarationType . Module ) ) ) ;
186
+ return declarationsByModule ;
187
+ }
188
+ }
189
+ }
0 commit comments