@@ -23,11 +23,20 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2323 /// <summary>
2424 /// A class to walk an AST to check for [violation]
2525 /// </summary>
26- #if ! CORECLR
26+ #if ! CORECLR
2727 [ Export ( typeof ( IScriptRule ) ) ]
2828#endif
2929 class UseConsistentIndentation : IScriptRule
3030 {
31+ private readonly int unitsPerIndentationLevel ;
32+
33+ UseConsistentIndentation ( )
34+ {
35+ // TODO Add a parameter for indentation kind {Tab, Space}
36+ // TODO make this configurable
37+ unitsPerIndentationLevel = 4 ;
38+ }
39+
3140 /// <summary>
3241 /// Analyzes the given ast to find the [violation]
3342 /// </summary>
@@ -41,8 +50,103 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4150 throw new ArgumentNullException ( "ast" ) ;
4251 }
4352
44- // your code goes here
45- yield return new DiagnosticRecord ( ) ;
53+ var tokens = Helper . Instance . Tokens ;
54+ var diagnosticRecords = new List < DiagnosticRecord > ( ) ;
55+ var openBracePosStack = new Stack < int > ( ) ;
56+ bool onNewLine = true ;
57+ for ( int k = 0 ; k < tokens . Length ; k ++ )
58+ {
59+ var token = tokens [ k ] ;
60+ var curIndentationLevel = GetIndentationLevel ( openBracePosStack ) ;
61+ var curIndentation = GetIndentation ( curIndentationLevel ) ;
62+ switch ( token . Kind )
63+ {
64+ case TokenKind . LCurly :
65+ AddViolation ( token , curIndentationLevel , diagnosticRecords , ref onNewLine ) ;
66+ openBracePosStack . Push ( k ) ;
67+ break ;
68+
69+ case TokenKind . RCurly :
70+ if ( openBracePosStack . Count > 0 )
71+ {
72+ openBracePosStack . Pop ( ) ;
73+ }
74+ AddViolation ( token , GetIndentationLevel ( openBracePosStack ) , diagnosticRecords , ref onNewLine ) ;
75+ break ;
76+
77+ case TokenKind . NewLine :
78+ onNewLine = true ;
79+ break ;
80+
81+ default :
82+ // we do not want to make a call for every token, hence
83+ // we add this redundant check
84+ if ( onNewLine )
85+ {
86+ AddViolation ( token , curIndentationLevel , diagnosticRecords , ref onNewLine ) ;
87+ }
88+ break ;
89+ }
90+ }
91+
92+ return diagnosticRecords ;
93+ }
94+
95+ private void AddViolation (
96+ Token token ,
97+ int curIndentationLevel ,
98+ List < DiagnosticRecord > diagnosticRecords ,
99+ ref bool onNewLine )
100+ {
101+ if ( onNewLine )
102+ {
103+ onNewLine = false ;
104+ if ( token . Extent . StartColumnNumber - 1 != GetIndentation ( curIndentationLevel ) )
105+ {
106+ var fileName = token . Extent . File ;
107+ var extent = token . Extent ;
108+ var violationExtent = extent = new ScriptExtent (
109+ new ScriptPosition (
110+ fileName ,
111+ extent . StartLineNumber ,
112+ 1 , // first column in the line
113+ extent . StartScriptPosition . Line ) ,
114+ new ScriptPosition (
115+ fileName ,
116+ extent . StartLineNumber ,
117+ extent . StartColumnNumber ,
118+ extent . StartScriptPosition . Line ) ) ;
119+ diagnosticRecords . Add (
120+ new DiagnosticRecord (
121+ "not correct indenation" , // TODO replace with localized string
122+ violationExtent ,
123+ GetName ( ) ,
124+ GetDiagnosticSeverity ( ) ,
125+ fileName ,
126+ null ,
127+ null ) ) ;
128+ }
129+ }
130+ }
131+
132+ private static int ClipNegative ( int x )
133+ {
134+ return x > 0 ? x : 0 ;
135+ }
136+
137+ private int GetIndentationColumnNumber ( int indentationLevel )
138+ {
139+ return GetIndentation ( indentationLevel ) + 1 ;
140+ }
141+
142+ private int GetIndentation ( int indentationLevel )
143+ {
144+ return indentationLevel * this . unitsPerIndentationLevel ;
145+ }
146+
147+ private int GetIndentationLevel ( Stack < int > openBracePosStack )
148+ {
149+ return openBracePosStack . Count ;
46150 }
47151
48152 /// <summary>
0 commit comments