@@ -41,186 +41,188 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4141 throw new ArgumentNullException ( "ast" ) ;
4242 }
4343
44- // TODO Given that we need to make exceptions for
45- // ScriptBlockExpressionAst and NamedBlockAst, a
46- // simpler approach using only tokens seems more
47- // robust - for every close brace, check the position
48- // of its corresponding open brace. If the open brace
49- // is on a line by itself, use its identation to decide
50- // close brace's indentation. If the open brace is
51- // preceded by any non new line token then find the
52- // fist keyword on the line and use its indentation for
53- // the close brace
44+ // TODO Should have the following options
45+ // * no-empty-line-before
46+ // * on-new-line
47+ // * align
5448
55- var tokens = Helper . Instance . Tokens . ToList ( ) ;
56- var astTokenMap = new Dictionary < Ast , List < Token > > ( ) ;
57- var violationTokens = new HashSet < Token > ( ) ;
49+ var tokens = Helper . Instance . Tokens ;
5850 var diagnosticRecords = new List < DiagnosticRecord > ( ) ;
59- var astItems = ast . FindAll ( x => x is ScriptBlockAst
60- || x is StatementBlockAst
61- || x is NamedBlockAst ,
62- true ) ;
63- foreach ( var astItem in astItems )
51+ var openBracePosStack = new Stack < int > ( ) ;
52+
53+ for ( int k = 0 ; k < tokens . Length ; k ++ )
6454 {
65- var astTokens = GetTokens ( astItem , tokens , ref astTokenMap ) ;
66- AddToDiagnosticRecords (
67- GetViolationForBraceOnSameLine ( astItem , astTokens , fileName , ref violationTokens ) ,
68- ref diagnosticRecords ) ;
55+ var token = tokens [ k ] ;
56+ if ( token . Kind == TokenKind . LCurly )
57+ {
58+ openBracePosStack . Push ( k ) ;
59+ continue ;
60+ }
6961
70- AddToDiagnosticRecords (
71- GetViolationForEmptyLineBeforeBrace ( astItem , astTokens , fileName , ref violationTokens ) ,
72- ref diagnosticRecords ) ;
62+ if ( token . Kind == TokenKind . RCurly )
63+ {
64+ if ( openBracePosStack . Count > 0 )
65+ {
66+ var openBracePos = openBracePosStack . Pop ( ) ;
67+ AddToDiagnosticRecords (
68+ GetViolationForBraceOnSameLine ( tokens , k , openBracePos , fileName ) ,
69+ ref diagnosticRecords ) ;
70+ AddToDiagnosticRecords (
71+ GetViolationForEmptyLineBeforeBrace ( tokens , k , openBracePos , fileName ) ,
72+ ref diagnosticRecords ) ;
73+ }
74+ else
75+ {
76+ break ;
77+ }
78+ }
7379 }
7480
7581 return diagnosticRecords ;
7682 }
7783
78- private void AddToDiagnosticRecords (
79- DiagnosticRecord diagnosticRecord ,
80- ref List < DiagnosticRecord > diagnosticRecords )
84+ private DiagnosticRecord GetViolationForEmptyLineBeforeBrace ( Token [ ] tokens , int closeBracePos , int openBracePos , string fileName )
8185 {
82- if ( diagnosticRecord != null )
86+ if ( tokens . Length > 2 && tokens . Length > closeBracePos )
8387 {
84- diagnosticRecords . Add ( diagnosticRecord ) ;
88+ var closeBraceToken = tokens [ closeBracePos ] ;
89+ var newLineToken = tokens [ closeBracePos - 1 ] ;
90+ var extraNewLineToken = tokens [ closeBracePos - 2 ] ;
91+ if ( newLineToken . Kind == TokenKind . NewLine
92+ && extraNewLineToken . Kind == TokenKind . NewLine )
93+ {
94+ return new DiagnosticRecord (
95+ "Extra new line before close brace" ,
96+ closeBraceToken . Extent ,
97+ GetName ( ) ,
98+ GetDiagnosticSeverity ( ) ,
99+ fileName ,
100+ null ,
101+ GetSuggestedCorrectionsForEmptyLineBeforeBrace ( tokens , closeBracePos , openBracePos , fileName ) ) ;
102+ }
85103 }
104+
105+ return null ;
86106 }
87107
88- private List < Token > GetTokens ( Ast ast , List < Token > tokens , ref Dictionary < Ast , List < Token > > astTokenMap )
108+ private List < CorrectionExtent > GetSuggestedCorrectionsForEmptyLineBeforeBrace ( Token [ ] tokens , int closeBracePos , int openBracePos , string fileName )
89109 {
90- if ( astTokenMap . Keys . Contains ( ast ) )
91- {
92- return astTokenMap [ ast ] ;
93- }
110+ var corrections = new List < CorrectionExtent > ( ) ;
111+ var newLineToken = tokens [ closeBracePos - 2 ] ;
112+ var closeBraceToken = tokens [ closeBracePos ] ;
113+ corrections . Add ( new CorrectionExtent (
114+ newLineToken . Extent . StartLineNumber ,
115+ closeBraceToken . Extent . EndLineNumber ,
116+ newLineToken . Extent . StartColumnNumber ,
117+ closeBraceToken . Extent . EndColumnNumber ,
118+ newLineToken . Text + GetIndentation ( tokens , closeBracePos , openBracePos ) + closeBraceToken . Text ,
119+ fileName ) ) ;
120+ return corrections ;
121+ }
94122
95- // check if any parent upstream is in the cache
96- var parentAst = ast . Parent ;
97- while ( parentAst != null
98- && ! astTokenMap . Keys . Contains ( parentAst ) )
123+ private string GetIndentation ( Token [ ] tokens , int closeBracePos , int openBracePos )
124+ {
125+ // if open brace on a new line by itself, use its indentation
126+ var openBraceToken = tokens [ openBracePos ] ;
127+ if ( tokens [ openBracePos - 1 ] . Kind == TokenKind . NewLine )
99128 {
100- parentAst = parentAst . Parent ;
129+ return new String ( ' ' , openBraceToken . Extent . StartColumnNumber - 1 ) ;
101130 }
102131
103- List < Token > tokenSuperSet = parentAst == null ? tokens : astTokenMap [ parentAst ] ;
104- var tokenSet = new List < Token > ( ) ;
105- foreach ( var token in tokenSuperSet )
132+ // if open brace follows any keywords use the identation of the first keyword
133+ // on the line containing the open brace
134+ Token firstTokenOnOpenBraceLine = openBraceToken ;
135+ for ( int k = openBracePos ; k > 0 ; -- k )
106136 {
107- if ( Helper . ContainsExtent ( ast . Extent , token . Extent ) )
137+ if ( tokens [ k ] . Extent . StartLineNumber == firstTokenOnOpenBraceLine . Extent . StartLineNumber )
108138 {
109- tokenSet . Add ( token ) ;
139+ firstTokenOnOpenBraceLine = tokens [ k ] ;
110140 }
111- }
112-
113- astTokenMap [ ast ] = tokenSet ;
114- return tokenSet ;
115- }
116- private DiagnosticRecord GetViolationForEmptyLineBeforeBrace (
117- Ast ast ,
118- List < Token > tokens ,
119- string fileName ,
120- ref HashSet < Token > violationTokens )
121- {
122- if ( tokens . Count >= 3 )
123- {
124- var closeBraceToken = tokens . Last ( ) ;
125- var extraNewLineToken = tokens [ tokens . Count - 2 ] ;
126- var newLineToken = tokens [ tokens . Count - 3 ] ;
127- if ( ! violationTokens . Contains ( closeBraceToken )
128- && closeBraceToken . Kind == TokenKind . RCurly
129- && extraNewLineToken . Kind == TokenKind . NewLine
130- && newLineToken . Kind == TokenKind . NewLine )
141+ else
131142 {
132- violationTokens . Add ( closeBraceToken ) ;
133- return new DiagnosticRecord (
134- "Extra new line before close brace" ,
135- closeBraceToken . Extent ,
136- GetName ( ) ,
137- GetDiagnosticSeverity ( ) ,
138- fileName ,
139- null ,
140- GetSuggestedCorrectionsForEmptyLineBeforeBrace ( ast , closeBraceToken , newLineToken , fileName ) ) ;
143+ break ;
141144 }
142145 }
143146
144- return null ;
147+ return new String ( ' ' , firstTokenOnOpenBraceLine . Extent . StartColumnNumber - 1 ) ;
145148 }
146149
147- private DiagnosticRecord GetViolationForBraceOnSameLine (
148- Ast ast ,
149- List < Token > tokens ,
150- string fileName ,
151- ref HashSet < Token > violationTokens )
150+ private DiagnosticRecord GetViolationForBraceOnSameLine ( Token [ ] tokens , int closeBracePos , int openBracePos , string fileName )
152151 {
153- if ( tokens . Count >= 2 )
152+ if ( tokens . Length > 1 && tokens . Length > closeBracePos )
154153 {
155- var closeBraceToken = tokens . Last ( ) ;
156- if ( ! violationTokens . Contains ( closeBraceToken )
157- && closeBraceToken . Kind == TokenKind . RCurly
158- && tokens [ tokens . Count - 2 ] . Kind != TokenKind . NewLine )
154+ var closeBraceToken = tokens [ closeBracePos ] ;
155+ if ( tokens [ closeBracePos - 1 ] . Kind != TokenKind . NewLine )
159156 {
160- violationTokens . Add ( closeBraceToken ) ;
161157 return new DiagnosticRecord (
162158 GetError ( ) ,
163159 closeBraceToken . Extent ,
164160 GetName ( ) ,
165161 GetDiagnosticSeverity ( ) ,
166162 fileName ,
167163 null ,
168- GetSuggestedCorrectionsForBraceOnSameLine ( ast , closeBraceToken , fileName ) ) ;
164+ GetSuggestedCorrectionsForBraceOnSameLine ( tokens , closeBracePos , openBracePos , fileName ) ) ;
169165 }
170166 }
171167
172168 return null ;
173169 }
174170
175171 private List < CorrectionExtent > GetSuggestedCorrectionsForBraceOnSameLine (
176- Ast ast ,
177- Token closeBraceToken ,
172+ Token [ ] tokens ,
173+ int closeBracePos ,
174+ int openBracePos ,
178175 string fileName )
179176 {
180177 var corrections = new List < CorrectionExtent > ( ) ;
181- corrections . Add (
182- new CorrectionExtent (
183- closeBraceToken . Extent . StartLineNumber ,
184- closeBraceToken . Extent . EndLineNumber ,
185- closeBraceToken . Extent . StartColumnNumber ,
186- closeBraceToken . Extent . EndColumnNumber ,
187- Environment . NewLine + GetIndentation ( ast ) + closeBraceToken . Text ,
188- fileName ) ) ;
178+ var closeBraceToken = tokens [ closeBracePos ] ;
179+ corrections . Add ( new CorrectionExtent (
180+ closeBraceToken . Extent . StartLineNumber ,
181+ closeBraceToken . Extent . EndLineNumber ,
182+ closeBraceToken . Extent . StartColumnNumber ,
183+ closeBraceToken . Extent . EndColumnNumber ,
184+ Environment . NewLine + GetIndentation ( tokens , closeBracePos , openBracePos ) + closeBraceToken . Extent . Text ,
185+ fileName ) ) ;
189186 return corrections ;
190187 }
191188
192- private string GetIndentation ( Ast ast )
189+ private void AddToDiagnosticRecords (
190+ DiagnosticRecord diagnosticRecord ,
191+ ref List < DiagnosticRecord > diagnosticRecords )
193192 {
194- var targetAst = ast ;
195- if ( ! ( targetAst is NamedBlockAst )
196- && targetAst . Parent != null )
193+ if ( diagnosticRecord != null )
197194 {
198- targetAst = targetAst . Parent ;
199- if ( targetAst is ScriptBlockExpressionAst )
200- {
201- targetAst = targetAst . Parent ?? targetAst ;
202- }
195+ diagnosticRecords . Add ( diagnosticRecord ) ;
203196 }
204-
205- return new String ( ' ' , targetAst . Extent . StartColumnNumber - 1 ) ;
206197 }
207198
208- private List < CorrectionExtent > GetSuggestedCorrectionsForEmptyLineBeforeBrace (
209- Ast ast ,
210- Token closeBraceToken ,
211- Token newLineToken ,
212- string fileName )
199+ private List < Token > GetTokens ( Ast ast , List < Token > tokens , ref Dictionary < Ast , List < Token > > astTokenMap )
213200 {
214- var corrections = new List < CorrectionExtent > ( ) ;
215- corrections . Add (
216- new CorrectionExtent (
217- newLineToken . Extent . StartLineNumber ,
218- closeBraceToken . Extent . EndLineNumber ,
219- newLineToken . Extent . StartColumnNumber ,
220- closeBraceToken . Extent . EndColumnNumber ,
221- Environment . NewLine + GetIndentation ( ast ) + closeBraceToken . Text ,
222- fileName ) ) ;
223- return corrections ;
201+ if ( astTokenMap . Keys . Contains ( ast ) )
202+ {
203+ return astTokenMap [ ast ] ;
204+ }
205+
206+ // check if any parent upstream is in the cache
207+ var parentAst = ast . Parent ;
208+ while ( parentAst != null
209+ && ! astTokenMap . Keys . Contains ( parentAst ) )
210+ {
211+ parentAst = parentAst . Parent ;
212+ }
213+
214+ List < Token > tokenSuperSet = parentAst == null ? tokens : astTokenMap [ parentAst ] ;
215+ var tokenSet = new List < Token > ( ) ;
216+ foreach ( var token in tokenSuperSet )
217+ {
218+ if ( Helper . ContainsExtent ( ast . Extent , token . Extent ) )
219+ {
220+ tokenSet . Add ( token ) ;
221+ }
222+ }
223+
224+ astTokenMap [ ast ] = tokenSet ;
225+ return tokenSet ;
224226 }
225227
226228 /// <summary>
0 commit comments