# Public: Check the manifest tokens for correct indent levels and # record a warning for each instance found. PuppetLint.new_check(:'strict_indent') do def match(tokens) open = { :LBRACE => [], :LBRACK => [], :LPAREN => [], } matches = {} tokens.each do |token| if [:LBRACE, :LBRACK, :LPAREN].include?(token.type) open[token.type] << token elsif [:RBRACE, :RBRACK, :RPAREN].include?(token.type) match = open[("L" + token.type.to_s[1..-1]).to_sym].pop if not match.nil? matches[token] = match matches[match] = token end end end matches end def check chars_per_indent = PuppetLint.configuration.chars_per_indent || 2 indent = 0 colon_indent = nil matches = match(tokens) tokens.select { |token| token.type == :NEWLINE }.reject { |token| # ignore newline at end of code token.next_token.nil? }.each do |token| temp_indent = 0 # indent for open groups in the previous line open_groups = 0 prev_token = token.prev_token while not prev_token.nil? and prev_token.type != :NEWLINE if [:LBRACE, :LBRACK, :LPAREN].include?(prev_token.type) if matches[prev_token].nil? or matches[prev_token].line > prev_token.line # left braces not matched in the same line increase indent open_groups += 1 end end prev_token = prev_token.prev_token end indent += open_groups # reset prev_token to last non-whitespace token on previous line prev_token = token.prev_token while not prev_token.nil? and prev_token.type == :WHITESPACE prev_token = prev_token.prev_token end # get type if available prev_type = prev_token.nil? ? nil : prev_token.type # handle change in indent based on last token case prev_type when :COLON if open_groups == 0 if colon_indent.nil? # only indent for a colon when you haven't indented yet colon_indent = prev_token.line indent += 1 else # you probably missed a semicolon two lines ago end end when :SEMIC if not colon_indent.nil? # only unindent for a semicolon when we've indented for a colon colon_indent = nil indent -= 1 end when :EQUALS, :FARROW temp_indent += 1 end # unindent for closing brackets in the current line next_token = token.next_token while not next_token.nil? and next_token.type != :NEWLINE if [:RBRACE, :RBRACK, :RPAREN].include?(next_token.type) if not matches[next_token].nil? and matches[next_token].line < next_token.line # right braces matched in a previous line decrease indent indent -= 1 end if next_token.type == :RBRACE and not colon_indent.nil? if not matches[next_token].nil? and matches[next_token].line < colon_indent # unindent at the end of resources if needed indent -= 1 colon_indent = nil end end end next_token = next_token.next_token end # obviously we have a problem if indent < 0 notify :error, { :message => 'Error calculating indent. Please file an issue at https://github.com/relud/puppet-lint-indent-check/issues', :line => token.next_token.line, :column => token.next_token.column, } # stop parsing indent break end # get actual indent actual = 0 if token.next_token.type == :INDENT actual = token.next_token.value.length else actual = 0 end # expected indent expected = (indent + temp_indent) * chars_per_indent # oh no! incorrect indent! if actual != expected # no one cares if blank lines and comments are indented correctly if not [:COMMENT, :NEWLINE].include?(token.next_token.type) notify :warning, { :message => "indent should be #{expected} chars and is #{actual}", :line => token.line, :column => token.column, :token => token, :indent => expected, } end end end end def fix(problem) char_for_indent = ' ' if [:INDENT,:WHITESPACE].include?(problem[:token].next_token.type) problem[:token].next_token.value = char_for_indent * problem[:indent] else tokens.insert( tokens.find_index(problem[:token]) + 1, PuppetLint::Lexer::Token.new(:INDENT, char_for_indent * problem[:indent], problem[:line], problem[:column]), ) end end end