1
1
using System ;
2
2
using System . Collections . Generic ;
3
3
using System . Linq ;
4
- using Antlr4 . Runtime ;
5
4
using Antlr4 . Runtime . Misc ;
6
5
using Antlr4 . Runtime . Tree ;
7
6
using Rubberduck . Inspections . Abstract ;
7
+ using Rubberduck . Parsing . Annotations ;
8
8
using Rubberduck . Parsing . Grammar ;
9
9
using Rubberduck . Parsing . Inspections ;
10
10
using Rubberduck . Parsing . Inspections . Abstract ;
11
11
using Rubberduck . Parsing . Rewriter ;
12
+ using Rubberduck . Parsing . Symbols ;
12
13
using Rubberduck . Parsing . VBA ;
14
+ using Rubberduck . Parsing . VBA . Parsing ;
13
15
14
16
namespace Rubberduck . Inspections . QuickFixes
15
17
{
@@ -29,98 +31,109 @@ public IgnoreOnceQuickFix(RubberduckParserState state, IEnumerable<IInspection>
29
31
30
32
public override void Fix ( IInspectionResult result , IRewriteSession rewriteSession )
31
33
{
32
- var annotationText = $ "'@Ignore { result . Inspection . AnnotationName } ";
33
-
34
- int annotationLine ;
35
- //TODO: Make this use the parse tree instead of the code module.
36
- var component = _state . ProjectsProvider . Component ( result . QualifiedSelection . QualifiedName ) ;
37
- using ( var module = component . CodeModule )
34
+ if ( result . Target ? . DeclarationType . HasFlag ( DeclarationType . Module ) ?? false )
38
35
{
39
- annotationLine = result . QualifiedSelection . Selection . StartLine ;
40
- while ( annotationLine != 1 && module . GetLines ( annotationLine - 1 , 1 ) . EndsWith ( " _" ) )
41
- {
42
- annotationLine -- ;
43
- }
36
+ FixModule ( result , rewriteSession ) ;
44
37
}
45
-
46
- RuleContext treeRoot = result . Context ;
47
- while ( treeRoot . Parent != null )
38
+ else
48
39
{
49
- treeRoot = treeRoot . Parent ;
40
+ FixNonModule ( result , rewriteSession ) ;
50
41
}
42
+ }
51
43
52
- var listener = new CommentOrAnnotationListener ( ) ;
53
- ParseTreeWalker . Default . Walk ( listener , treeRoot ) ;
54
- var commentContext = listener . Contexts . LastOrDefault ( i => i . Stop . TokenIndex <= result . Context . Start . TokenIndex ) ;
55
- var commented = commentContext ? . Stop . Line + 1 == annotationLine ;
44
+ private void FixNonModule ( IInspectionResult result , IRewriteSession rewriteSession )
45
+ {
46
+ int insertionIndex ;
47
+ string insertText ;
48
+ var annotationText = $ "'@Ignore { result . Inspection . AnnotationName } ";
56
49
57
- var rewriter = rewriteSession . CheckOutModuleRewriter ( result . QualifiedSelection . QualifiedName ) ;
50
+ var module = result . QualifiedSelection . QualifiedName ;
51
+ var parseTree = _state . GetParseTree ( module , CodeKind . CodePaneCode ) ;
52
+ var eolListener = new EndOfLineListener ( ) ;
53
+ ParseTreeWalker . Default . Walk ( eolListener , parseTree ) ;
54
+ var previousEol = eolListener . Contexts
55
+ . OrderBy ( eol => eol . Start . TokenIndex )
56
+ . LastOrDefault ( eol => eol . Start . Line < result . QualifiedSelection . Selection . StartLine ) ;
58
57
59
- if ( commented )
58
+ var rewriter = rewriteSession . CheckOutModuleRewriter ( module ) ;
59
+
60
+ if ( previousEol == null )
60
61
{
61
- var annotation = commentContext . annotationList ( ) ? . annotation ( 0 ) ;
62
- if ( annotation != null && annotation . GetText ( ) . StartsWith ( "Ignore" ) )
63
- {
64
- rewriter . InsertAfter ( annotation . annotationName ( ) . Stop . TokenIndex , $ " { result . Inspection . AnnotationName } ,") ;
65
- }
66
- else
67
- {
68
- var indent = new string ( Enumerable . Repeat ( ' ' , commentContext . Start . Column ) . ToArray ( ) ) ;
69
- rewriter . InsertAfter ( commentContext . Stop . TokenIndex , $ "{ indent } { annotationText } { Environment . NewLine } ") ;
70
- }
62
+ // The context to get annotated is on the first line; we need to insert before token index 0.
63
+ insertionIndex = 0 ;
64
+ insertText = annotationText + Environment . NewLine ;
65
+ rewriter . InsertBefore ( insertionIndex , insertText ) ;
66
+ return ;
71
67
}
72
- else
68
+
69
+ var commentContext = previousEol . commentOrAnnotation ( ) ;
70
+ if ( commentContext == null )
71
+ {
72
+ insertionIndex = previousEol . Start . TokenIndex ;
73
+ var indent = WhitespaceAfter ( previousEol ) ;
74
+ insertText = $ "{ Environment . NewLine } { indent } { annotationText } ";
75
+ rewriter . InsertBefore ( insertionIndex , insertText ) ;
76
+ return ;
77
+ }
78
+
79
+ var ignoreAnnotation = commentContext . annotationList ( ) ? . annotation ( )
80
+ . FirstOrDefault ( annotationContext => annotationContext . annotationName ( ) . GetText ( ) == AnnotationType . Ignore . ToString ( ) ) ;
81
+ if ( ignoreAnnotation == null )
73
82
{
74
- int insertIndex ;
75
-
76
- // this value is used when the annotation should be on line 1--we need to insert before token index 0
77
- if ( annotationLine == 1 )
78
- {
79
- insertIndex = 0 ;
80
- annotationText += Environment . NewLine ;
81
- }
82
- else
83
- {
84
- var eol = new EndOfLineListener ( ) ;
85
- ParseTreeWalker . Default . Walk ( eol , treeRoot ) ;
86
-
87
- // we subtract 2 here to get the insertion index to A) account for VBE's one-based indexing
88
- // and B) to get the newline token that introduces that line
89
- var eolContext = eol . Contexts . OrderBy ( o => o . Start . TokenIndex ) . ElementAt ( annotationLine - 2 ) ;
90
- insertIndex = eolContext . Start . TokenIndex ;
91
-
92
- annotationText = Environment . NewLine + annotationText ;
93
- }
94
-
95
- rewriter . InsertBefore ( insertIndex , annotationText ) ;
83
+ insertionIndex = commentContext . Stop . TokenIndex ;
84
+ var indent = WhitespaceAfter ( previousEol ) ;
85
+ insertText = $ "{ indent } { annotationText } { Environment . NewLine } ";
86
+ rewriter . InsertAfter ( insertionIndex , insertText ) ;
87
+ return ;
96
88
}
89
+
90
+ insertionIndex = ignoreAnnotation . annotationName ( ) . Stop . TokenIndex ;
91
+ insertText = $ " { result . Inspection . AnnotationName } ,";
92
+ rewriter . InsertAfter ( insertionIndex , insertText ) ;
97
93
}
98
94
99
- public override string Description ( IInspectionResult result ) => Resources . Inspections . QuickFixes . IgnoreOnce ;
95
+ private static string WhitespaceAfter ( VBAParser . EndOfLineContext endOfLine )
96
+ {
97
+ var individualEndOfStatement = ( VBAParser . IndividualNonEOFEndOfStatementContext ) endOfLine . Parent ;
98
+ var whiteSpaceOnNextLine = individualEndOfStatement . whiteSpace ( 0 ) ;
99
+ return whiteSpaceOnNextLine != null
100
+ ? whiteSpaceOnNextLine . GetText ( )
101
+ : string . Empty ;
102
+ }
100
103
101
- private class CommentOrAnnotationListener : VBAParserBaseListener
104
+ private void FixModule ( IInspectionResult result , IRewriteSession rewriteSession )
102
105
{
103
- private readonly IList < VBAParser . CommentOrAnnotationContext > _contexts = new List < VBAParser . CommentOrAnnotationContext > ( ) ;
104
- public IEnumerable < VBAParser . CommentOrAnnotationContext > Contexts => _contexts ;
106
+ var module = result . QualifiedSelection . QualifiedName ;
107
+ var moduleAnnotations = _state . GetModuleAnnotations ( module ) ;
108
+ var firstIgnoreModuleAnnotation = moduleAnnotations
109
+ . Where ( annotation => annotation . AnnotationType == AnnotationType . IgnoreModule )
110
+ . OrderBy ( annotation => annotation . Context . Start . TokenIndex )
111
+ . FirstOrDefault ( ) ;
105
112
106
- public override void ExitCommentOrAnnotation ( [ NotNull ] VBAParser . CommentOrAnnotationContext context )
113
+ var rewriter = rewriteSession . CheckOutModuleRewriter ( module ) ;
114
+
115
+ int insertionIndex ;
116
+ string insertText ;
117
+
118
+ if ( firstIgnoreModuleAnnotation == null )
107
119
{
108
- _contexts . Add ( context ) ;
120
+ insertionIndex = 0 ;
121
+ insertText = $ "'@IgnoreModule { result . Inspection . AnnotationName } { Environment . NewLine } ";
122
+ rewriter . InsertBefore ( insertionIndex , insertText ) ;
123
+ return ;
109
124
}
125
+
126
+ insertionIndex = firstIgnoreModuleAnnotation . Context . annotationName ( ) . Stop . TokenIndex ;
127
+ insertText = $ " { result . Inspection . AnnotationName } ,";
128
+ rewriter . InsertAfter ( insertionIndex , insertText ) ;
110
129
}
111
130
131
+ public override string Description ( IInspectionResult result ) => Resources . Inspections . QuickFixes . IgnoreOnce ;
132
+
112
133
private class EndOfLineListener : VBAParserBaseListener
113
134
{
114
- private readonly IList < ParserRuleContext > _contexts = new List < ParserRuleContext > ( ) ;
115
- public IEnumerable < ParserRuleContext > Contexts => _contexts ;
116
-
117
- public override void ExitWhiteSpace ( [ NotNull ] VBAParser . WhiteSpaceContext context )
118
- {
119
- if ( context . GetText ( ) . Contains ( Environment . NewLine ) )
120
- {
121
- _contexts . Add ( context ) ;
122
- }
123
- }
135
+ private readonly IList < VBAParser . EndOfLineContext > _contexts = new List < VBAParser . EndOfLineContext > ( ) ;
136
+ public IEnumerable < VBAParser . EndOfLineContext > Contexts => _contexts ;
124
137
125
138
public override void ExitEndOfLine ( [ NotNull ] VBAParser . EndOfLineContext context )
126
139
{
0 commit comments