tailor 0.1.5 → 1.0.0.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. data/.gitignore +9 -1
  2. data/.rspec +2 -1
  3. data/.tailor +6 -0
  4. data/Gemfile.lock +47 -78
  5. data/{ChangeLog.rdoc → History.rdoc} +0 -0
  6. data/README.rdoc +157 -24
  7. data/Rakefile +0 -9
  8. data/bin/tailor +16 -69
  9. data/features/configurable.feature +78 -0
  10. data/features/horizontal_spacing.feature +262 -0
  11. data/features/indentation.feature +17 -21
  12. data/features/indentation/bad_files_with_no_trailing_newline.feature +90 -0
  13. data/features/indentation/good_files_with_no_trailing_newline.feature +206 -0
  14. data/features/name_detection.feature +72 -0
  15. data/features/step_definitions/indentation_steps.rb +10 -133
  16. data/features/support/env.rb +7 -15
  17. data/features/support/file_cases/horizontal_spacing_cases.rb +265 -0
  18. data/features/support/file_cases/indentation_cases.rb +972 -0
  19. data/features/support/file_cases/naming_cases.rb +52 -0
  20. data/features/support/file_cases/vertical_spacing_cases.rb +70 -0
  21. data/features/support/hooks.rb +8 -0
  22. data/features/support/{1_file_with_bad_operator_spacing → legacy}/bad_op_spacing.rb +0 -0
  23. data/features/support/{1_file_with_bad_ternary_colon_spacing → legacy}/bad_ternary_colon_spacing.rb +0 -0
  24. data/features/support/{1_long_file_with_indentation/my_project.rb → legacy/long_file_with_indentation.rb} +1 -1
  25. data/features/support/world.rb +14 -0
  26. data/features/vertical_spacing.feature +114 -0
  27. data/lib/ext/string_ext.rb +5 -0
  28. data/lib/tailor.rb +6 -252
  29. data/lib/tailor/cli.rb +49 -0
  30. data/lib/tailor/cli/options.rb +251 -0
  31. data/lib/tailor/composite_observable.rb +56 -0
  32. data/lib/tailor/configuration.rb +263 -0
  33. data/lib/tailor/critic.rb +162 -0
  34. data/lib/tailor/formatters/text.rb +126 -0
  35. data/lib/tailor/lexed_line.rb +246 -0
  36. data/lib/tailor/lexer.rb +428 -0
  37. data/lib/tailor/lexer/token.rb +103 -0
  38. data/lib/tailor/lexer_constants.rb +75 -0
  39. data/lib/tailor/logger.rb +28 -0
  40. data/lib/tailor/problem.rb +100 -0
  41. data/lib/tailor/reporter.rb +48 -0
  42. data/lib/tailor/ruler.rb +39 -0
  43. data/lib/tailor/rulers.rb +7 -0
  44. data/lib/tailor/rulers/allow_camel_case_methods_ruler.rb +30 -0
  45. data/lib/tailor/rulers/allow_hard_tabs_ruler.rb +22 -0
  46. data/lib/tailor/rulers/allow_screaming_snake_case_classes_ruler.rb +32 -0
  47. data/lib/tailor/rulers/allow_trailing_line_spaces_ruler.rb +33 -0
  48. data/lib/tailor/rulers/indentation_spaces_ruler.rb +199 -0
  49. data/lib/tailor/rulers/indentation_spaces_ruler/indentation_manager.rb +362 -0
  50. data/lib/tailor/rulers/max_code_lines_in_class_ruler.rb +84 -0
  51. data/lib/tailor/rulers/max_code_lines_in_method_ruler.rb +84 -0
  52. data/lib/tailor/rulers/max_line_length_ruler.rb +31 -0
  53. data/lib/tailor/rulers/spaces_after_comma_ruler.rb +83 -0
  54. data/lib/tailor/rulers/spaces_after_lbrace_ruler.rb +114 -0
  55. data/lib/tailor/rulers/spaces_after_lbracket_ruler.rb +123 -0
  56. data/lib/tailor/rulers/spaces_after_lparen_ruler.rb +116 -0
  57. data/lib/tailor/rulers/spaces_before_comma_ruler.rb +67 -0
  58. data/lib/tailor/rulers/spaces_before_lbrace_ruler.rb +93 -0
  59. data/lib/tailor/rulers/spaces_before_rbrace_ruler.rb +98 -0
  60. data/lib/tailor/rulers/spaces_before_rbracket_ruler.rb +70 -0
  61. data/lib/tailor/rulers/spaces_before_rparen_ruler.rb +70 -0
  62. data/lib/tailor/rulers/spaces_in_empty_braces_ruler.rb +94 -0
  63. data/lib/tailor/rulers/trailing_newlines_ruler.rb +36 -0
  64. data/lib/tailor/runtime_error.rb +3 -0
  65. data/lib/tailor/tailorrc.erb +88 -0
  66. data/lib/tailor/version.rb +2 -2
  67. data/spec/spec_helper.rb +7 -5
  68. data/spec/tailor/cli_spec.rb +94 -0
  69. data/spec/tailor/configuration_spec.rb +147 -0
  70. data/spec/tailor/critic_spec.rb +63 -0
  71. data/spec/tailor/lexed_line_spec.rb +569 -0
  72. data/spec/tailor/lexer/token_spec.rb +46 -0
  73. data/spec/tailor/lexer_spec.rb +181 -0
  74. data/spec/tailor/options_spec.rb +6 -0
  75. data/spec/tailor/problem_spec.rb +74 -0
  76. data/spec/tailor/reporter_spec.rb +53 -0
  77. data/spec/tailor/ruler_spec.rb +56 -0
  78. data/spec/tailor/rulers/indentation_spaces_ruler/indentation_manager_spec.rb +454 -0
  79. data/spec/tailor/rulers/indentation_spaces_ruler_spec.rb +128 -0
  80. data/spec/tailor/rulers/spaces_after_comma_spec.rb +31 -0
  81. data/spec/tailor/rulers/spaces_after_lbrace_ruler_spec.rb +145 -0
  82. data/spec/tailor/rulers/spaces_before_lbrace_ruler_spec.rb +63 -0
  83. data/spec/tailor/rulers/spaces_before_rbrace_ruler_spec.rb +63 -0
  84. data/spec/tailor/rulers_spec.rb +9 -0
  85. data/spec/tailor/version_spec.rb +6 -0
  86. data/spec/tailor_spec.rb +9 -21
  87. data/tailor.gemspec +22 -35
  88. data/tasks/features.rake +7 -0
  89. data/tasks/roodi.rake +9 -0
  90. data/tasks/roodi_config.yaml +14 -0
  91. data/tasks/spec.rake +16 -0
  92. data/tasks/yard.rake +14 -0
  93. metadata +224 -77
  94. data/features/case_checking.feature +0 -38
  95. data/features/spacing.feature +0 -97
  96. data/features/spacing/commas.feature +0 -44
  97. data/features/step_definitions/case_checking_steps.rb +0 -42
  98. data/features/step_definitions/spacing_steps.rb +0 -156
  99. data/features/support/1_file_with_bad_comma_spacing/bad_comma_spacing.rb +0 -43
  100. data/features/support/1_file_with_bad_curly_brace_spacing/bad_curly_brace_spacing.rb +0 -60
  101. data/features/support/1_file_with_bad_parenthesis/bad_parenthesis.rb +0 -4
  102. data/features/support/1_file_with_bad_square_brackets/bad_square_brackets.rb +0 -62
  103. data/features/support/1_file_with_camel_case_class/camel_case_class.rb +0 -5
  104. data/features/support/1_file_with_camel_case_method/camel_case_method.rb +0 -3
  105. data/features/support/1_file_with_hard_tabs/hard_tab.rb +0 -3
  106. data/features/support/1_file_with_long_lines/long_lines.rb +0 -5
  107. data/features/support/1_file_with_snake_case_class/snake_case_class.rb +0 -5
  108. data/features/support/1_file_with_snake_case_method/snake_case_method.rb +0 -3
  109. data/features/support/1_file_with_trailing_whitespace/trailing_whitespace.rb +0 -5
  110. data/features/support/1_good_simple_file/simple_project.rb +0 -5
  111. data/features/support/common.rb +0 -102
  112. data/features/support/matchers.rb +0 -11
  113. data/lib/tailor/file_line.rb +0 -220
  114. data/lib/tailor/indentation.rb +0 -245
  115. data/lib/tailor/spacing.rb +0 -237
  116. data/spec/file_line_spec.rb +0 -70
  117. data/spec/indentation_spec.rb +0 -259
  118. data/spec/spacing/colon_spacing_spec.rb +0 -71
  119. data/spec/spacing/comma_spacing_spec.rb +0 -159
  120. data/spec/spacing/curly_brace_spacing_spec.rb +0 -257
  121. data/spec/spacing/parentheses_spacing_spec.rb +0 -28
  122. data/spec/spacing/square_bracket_spacing_spec.rb +0 -116
  123. data/spec/spacing_spec.rb +0 -167
  124. data/tasks/metrics.rake +0 -23
@@ -0,0 +1,75 @@
1
+ class Tailor
2
+
3
+ # These are important tokens that key certain styling events. They are taken
4
+ # from:
5
+ # https://github.com/ruby/ruby/blob/trunk/ext/ripper/eventids2.c
6
+ # https://github.com/ruby/ruby/blob/trunk/parse.y
7
+ module LexerConstants
8
+ KEYWORDS_TO_INDENT = [
9
+ 'begin',
10
+ 'case',
11
+ 'class',
12
+ 'def',
13
+ 'do',
14
+ 'else',
15
+ 'elsif',
16
+ 'ensure',
17
+ 'for',
18
+ 'if',
19
+ 'module',
20
+ 'rescue',
21
+ 'unless',
22
+ 'until',
23
+ 'when',
24
+ 'while'
25
+ ]
26
+
27
+ CONTINUATION_KEYWORDS = [
28
+ 'elsif',
29
+ 'else',
30
+ 'ensure',
31
+ 'rescue',
32
+ 'when'
33
+ ]
34
+
35
+ KEYWORDS_AND_MODIFIERS = [
36
+ 'if',
37
+ 'unless',
38
+ 'until',
39
+ 'while'
40
+ ]
41
+
42
+ MODIFIERS = {
43
+ 'if' => :if_mod,
44
+ 'rescue' => :rescue_mod,
45
+ 'unless' => :unless_mod,
46
+ 'until' => :until_mod,
47
+ 'while' => :while_mod
48
+ }
49
+
50
+ MULTILINE_OPERATORS = [
51
+ '+', '-', '*', '**', '/', '%', # +, -, tSTAR, tPOW, /, %
52
+ '<', '>', '<=', '>=', # <, >, tLEQ, tGEQ
53
+ '=', '+=', '-=', '*=', '**=', '/=', '%=',
54
+ '&&=', '||=', '<<=', # ...tOP_ASGN...
55
+ '>>', '<<', # tRSHFT, tLSHFT
56
+ '!', '&', '?', ':', '^', '~', # !, tAMPER, ?, :, ^, ~
57
+ #'|',
58
+ '&&', '||', # tANDOP, tOROP
59
+ '==', '===', '<=>', '!=', # tEQ, tEQQ, tCMP, tNEQ
60
+ '=~', '!~', # tMATCH, tNMATCH
61
+ '..', '...', # tDOT2, tDOT3
62
+ '::', # tCOLON2 (not sure about tCOLON3)
63
+ #'[]', '[]=', # tAREF, tASET (not sure about these)
64
+ '=>', # tASSOC
65
+ '->', # tLAMBDA
66
+ '~>' # gem_version op
67
+ ]
68
+
69
+ LOOP_KEYWORDS = [
70
+ 'for',
71
+ 'until',
72
+ 'while'
73
+ ]
74
+ end
75
+ end
@@ -0,0 +1,28 @@
1
+ require 'log_switch'
2
+
3
+ class Tailor
4
+ class Logger
5
+ extend LogSwitch
6
+
7
+ def self.logger
8
+ return @logger if @logger
9
+ @logger ||= ::Logger.new $stdout
10
+
11
+ def @logger.format_message(level, time, progname, msg)
12
+ "[#{time.strftime("%Y-%m-%d %H:%M:%S")}] #{msg}\n"
13
+ end
14
+
15
+ @logger
16
+ end
17
+
18
+ module Mixin
19
+ def self.included(base)
20
+ define_method :log do |*args|
21
+ class_minus_main_name = self.class.to_s.sub(/^.*::/, '')
22
+ args.first.insert(0, "<#{class_minus_main_name}> ")
23
+ Tailor::Logger.log(*args)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,100 @@
1
+ require_relative 'logger'
2
+ require_relative 'runtime_error'
3
+
4
+ class Tailor
5
+
6
+ # A Hashed data structure that abstracts out data (especially the error
7
+ # message) to build reports from.
8
+ class Problem < Hash
9
+ include LogSwitch::Mixin
10
+
11
+ # @param [Symbol] type The problem type.
12
+ # @param [Binding] binding The context that the problem was discovered in.
13
+ def initialize(type, line, column, options={})
14
+ @type = type
15
+ @line = line
16
+ @column = column
17
+ @options = options
18
+ set_values
19
+ subclass_name = self.class.to_s.sub(/^Tailor::/, '')
20
+ msg = "<#{subclass_name}> #{self[:line]}[#{self[:column]}]: "
21
+ msg << "ERROR[:#{self[:type]}] #{self[:message]}"
22
+ log msg
23
+ end
24
+
25
+ # Sets the standard values for the problem based on the type and binding.
26
+ def set_values
27
+ self[:type] = @type
28
+ self[:line] = @line
29
+ self[:column] = @column
30
+ self[:message] = message(@type)
31
+ end
32
+
33
+ # Builds the message for the problem type, based on the info provided in
34
+ # the +@binding+.
35
+ #
36
+ # @param [Symbol] type The type of problem.
37
+ # @return [String] The error message.
38
+ def message(type)
39
+ case type
40
+ when :camel_case_method
41
+ "Camel-case method name found."
42
+ when :code_lines_in_class
43
+ msg = "Class/module has #{@options[:actual_count]} code lines, but "
44
+ msg << "should have no more than #{@options[:should_be_at]}."
45
+ when :code_lines_in_method
46
+ msg = "Method has #{@options[:actual_count]} code lines, but "
47
+ msg << "should have no more than #{@options[:should_be_at]}."
48
+ when :hard_tab
49
+ "Hard tab found."
50
+ when :indentation
51
+ self[:column] = @options[:actual_indentation]
52
+ msg = "Line is indented to #{@options[:actual_indentation]}, "
53
+ msg << "but should be at #{@options[:should_be_at]}."
54
+ when :line_length
55
+ msg = "Line is #{@options[:actual_length]} chars long, "
56
+ msg << "but should be #{@options[:should_be_at]}."
57
+ when :screaming_snake_case_class_name
58
+ "Screaming-snake-case class/module found."
59
+ when :spaces_after_comma
60
+ msg = "Line has #{@options[:actual_spaces]} space(s) after a comma, "
61
+ msg << "but should have #{@options[:should_have]}."
62
+ when :spaces_before_comma
63
+ msg = "Line has #{@options[:actual_spaces]} space(s) before a comma, "
64
+ msg << "but should have #{@options[:should_have]}."
65
+ when :spaces_after_lbrace
66
+ msg = "Line has #{@options[:actual_spaces]} space(s) after a {, "
67
+ msg << "but should have #{@options[:should_have]}."
68
+ when :spaces_after_lbracket
69
+ msg = "Line has #{@options[:actual_spaces]} space(s) after a [, "
70
+ msg << "but should have #{@options[:should_have]}."
71
+ when :spaces_after_lparen
72
+ msg = "Line has #{@options[:actual_spaces]} space(s) after a (, "
73
+ msg << "but should have #{@options[:should_have]}."
74
+ when :spaces_before_lbrace
75
+ msg = "Line has #{@options[:actual_spaces]} space(s) before a {, "
76
+ msg << "but should have #{@options[:should_have]}."
77
+ when :spaces_before_rbrace
78
+ msg = "Line has #{@options[:actual_spaces]} space(s) before a }, "
79
+ msg << "but should have #{@options[:should_have]}."
80
+ when :spaces_before_rbracket
81
+ msg = "Line has #{@options[:actual_spaces]} space(s) before a ], "
82
+ msg << "but should have #{@options[:should_have]}."
83
+ when :spaces_before_rparen
84
+ msg = "Line has #{@options[:actual_spaces]} space(s) before a ), "
85
+ msg << "but should have #{@options[:should_have]}."
86
+ when :spaces_in_empty_braces
87
+ msg = "Line has #{@options[:actual_spaces]} space(s) in between empty "
88
+ msg << "braces, but should have #{@options[:should_have]}."
89
+ when :trailing_newlines
90
+ msg = "File has #{@options[:actual_trailing_newlines]} trailing "
91
+ msg << "newlines, but should have #{@options[:should_have]}."
92
+ when :trailing_spaces
93
+ "Line has #{@options[:actual_trailing_spaces]} trailing spaces."
94
+ else
95
+ raise Tailor::RuntimeError,
96
+ "Problem type '#{type}' doesn't exist."
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,48 @@
1
+ class Tailor
2
+
3
+ # Objects of this type are responsible for sending the right data to report
4
+ # formatters.
5
+ class Reporter
6
+ attr_reader :formatters
7
+
8
+ # For each in +formats+, it creates a new
9
+ # +Tailor::Formatter::#{formatter.capitalize}+ object and adds it to
10
+ # +@formatters+.
11
+ #
12
+ # @param [Array] formats A list of formatters to use for generating reports.
13
+ def initialize(*formats)
14
+ @formatters = []
15
+
16
+ formats.flatten.each do |formatter|
17
+ require_relative "formatters/#{formatter}"
18
+ @formatters << eval("Tailor::Formatter::#{formatter.capitalize}.new")
19
+ end
20
+ end
21
+
22
+ # Sends the data to each +@formatters+ to generate the report of problems
23
+ # for the file that was just critiqued. A problem is in the format:
24
+ #
25
+ # { 'path/to/file.rb' => [Problem1, Problem2, etc.]}
26
+ #
27
+ # ...where Problem1 and Problem2 are of type {Tailor::Problem}.
28
+ #
29
+ # @param [Hash] file_problems
30
+ # @param [Symbol,String] label The label of the file_set that defines the
31
+ # problems in +file_problems+.
32
+ def file_report(file_problems, label)
33
+ @formatters.each do |formatter|
34
+ formatter.file_report(file_problems, label)
35
+ end
36
+ end
37
+
38
+ # Sends the data to each +@formatters+ to generate the reports of problems
39
+ # for all files that were just critiqued.
40
+ #
41
+ # @param [Hash] all_problems
42
+ def summary_report(all_problems)
43
+ @formatters.each do |formatter|
44
+ formatter.summary_report(all_problems)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ require_relative 'logger'
2
+ require_relative 'problem'
3
+ require_relative 'runtime_error'
4
+
5
+ class Tailor
6
+ class Ruler
7
+ include Tailor::Logger::Mixin
8
+
9
+ attr_reader :cli_option
10
+
11
+ def initialize(config={})
12
+ @config = config
13
+ @problems = []
14
+ @child_rulers = []
15
+ @cli_option = ""
16
+ @do_measurement = true
17
+ log "Ruler initialized with style setting: #{@config}"
18
+ end
19
+
20
+ def add_child_ruler(ruler)
21
+ @child_rulers << ruler
22
+ log "Added child: #{ruler}"
23
+ end
24
+
25
+ def problems
26
+ @problems = @child_rulers.inject(@problems) do |problems, ruler|
27
+ problems + ruler.problems
28
+ end
29
+
30
+ @problems.sort_by! { |problem| problem[:line].to_i }
31
+ end
32
+
33
+ # Each ruler should redefine this for its needs.
34
+ def measure(*args)
35
+ raise RuntimeError,
36
+ "Ruler#measure called, but should be redefined by a real ruler."
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ Dir.glob(File.dirname(__FILE__) + '/rulers/*', &method(:require))
2
+
3
+ class Tailor
4
+ module Rulers
5
+
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../ruler'
2
+
3
+ class Tailor
4
+ module Rulers
5
+ class AllowCamelCaseMethodsRuler < Tailor::Ruler
6
+ def ident_update(token, lexed_line, lineno, column)
7
+ ident_index = lexed_line.event_index(column)
8
+ previous_event = lexed_line.event_at(ident_index - 2)
9
+ log "previous event: #{previous_event}"
10
+
11
+ return if previous_event.nil?
12
+
13
+ if previous_event[1] == :on_kw && previous_event.last == "def"
14
+ measure(token, lineno, column)
15
+ end
16
+ end
17
+
18
+ # Checks to see if the method name contains capital letters.
19
+ #
20
+ # @param [Fixnum] token The method name.
21
+ # @param [Fixnum] lineno Line the problem was found on.
22
+ # @param [Fixnum] column Column the problem was found on.
23
+ def measure(token, lineno, column)
24
+ if token.contains_capital_letter?
25
+ @problems << Problem.new(:camel_case_method, lineno, column)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../ruler'
2
+
3
+ class Tailor
4
+ module Rulers
5
+ class AllowHardTabsRuler < Tailor::Ruler
6
+ def sp_update(token, lineno, column)
7
+ measure(token, lineno, column)
8
+ end
9
+
10
+ # Checks to see if the space(s) contains hard tabs.
11
+ #
12
+ # @param [Fixnum] token The space(s).
13
+ # @param [Fixnum] lineno Line the problem was found on.
14
+ # @param [Fixnum] column Column the problem was found on.
15
+ def measure(token, lineno, column)
16
+ if token.contains_hard_tab?
17
+ @problems << Problem.new(:hard_tab, lineno, column)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../ruler'
2
+
3
+ class Tailor
4
+ module Rulers
5
+ class AllowScreamingSnakeCaseClassesRuler < Tailor::Ruler
6
+ def const_update(token, lexed_line, lineno, column)
7
+ ident_index = lexed_line.event_index(column)
8
+ previous_event = lexed_line.event_at(ident_index - 2)
9
+ log "previous event: #{previous_event}"
10
+
11
+ return if previous_event.nil?
12
+
13
+ if previous_event[1] == :on_kw &&
14
+ (previous_event.last == "class" || previous_event.last == "module")
15
+ measure(token, lineno, column)
16
+ end
17
+ end
18
+
19
+ # Checks to see if the class name matches /[A-Z].*_/.
20
+ #
21
+ # @param [Fixnum] token The space(s).
22
+ # @param [Fixnum] lineno Line the potential problem is on.
23
+ # @param [Fixnum] column Column the potential problem is on.
24
+ def measure(token, lineno, column)
25
+ if token.screaming_snake_case?
26
+ @problems << Problem.new(:screaming_snake_case_class_name,
27
+ lineno, column)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../ruler'
2
+
3
+ class Tailor
4
+ module Rulers
5
+ class AllowTrailingLineSpacesRuler < Tailor::Ruler
6
+ def ignored_nl_update(lexed_line, lineno, column)
7
+ log "Last event: #{lexed_line.last_non_line_feed_event}"
8
+ log "Line ends with space: #{lexed_line.ends_with_sp?}"
9
+
10
+ measure(lexed_line, lineno, column)
11
+ end
12
+
13
+ def nl_update(lexed_line, lineno, column)
14
+ ignored_nl_update(lexed_line, lineno, column)
15
+ end
16
+
17
+ # Checks to see if the line contains trailing spaces.
18
+ #
19
+ # @param [LexedLine] lexed_line The line to check for trailing spaces.
20
+ # @param [Fixnum] lineno Line the potential problem is on.
21
+ # @param [Fixnum] column Column the potential problem is on.
22
+ def measure(lexed_line, lineno, column)
23
+ if lexed_line.ends_with_sp?
24
+ options = {
25
+ actual_trailing_spaces:
26
+ lexed_line.last_non_line_feed_event.last.size
27
+ }
28
+ @problems << Problem.new(:trailing_spaces, lineno, column, options)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,199 @@
1
+ require_relative '../ruler'
2
+ require_relative '../lexed_line'
3
+ require_relative '../lexer/token'
4
+ require_relative 'indentation_spaces_ruler/indentation_manager'
5
+
6
+ class Tailor
7
+ module Rulers
8
+ class IndentationSpacesRuler < Tailor::Ruler
9
+ def initialize(config)
10
+ super(config)
11
+ @manager = IndentationManager.new(@config)
12
+ end
13
+
14
+ def comment_update(token, lexed_line, file_text, lineno, column)
15
+ # trailing comment?
16
+ if token.ends_with_newline?
17
+ log "Comment ends with newline. Removing comment..."
18
+ log "Old lexed line: #{lexed_line.inspect}"
19
+
20
+ new_lexed_line = lexed_line.remove_trailing_comment(file_text)
21
+
22
+ log "New lexed line: #{new_lexed_line.inspect}"
23
+
24
+ if new_lexed_line.ends_with_ignored_nl?
25
+ log "New lexed line ends with :on_ignored_nl."
26
+ ignored_nl_update(new_lexed_line, lineno, column)
27
+ elsif new_lexed_line.ends_with_nl?
28
+ log "New lexed line ends with :on_nl."
29
+ nl_update(new_lexed_line, lineno, column)
30
+ end
31
+ end
32
+ end
33
+
34
+ def embexpr_beg_update
35
+ @manager.embexpr_beg = true
36
+ end
37
+
38
+ def embexpr_end_update
39
+ @manager.embexpr_beg = false
40
+ end
41
+
42
+ def ignored_nl_update(current_lexed_line, lineno, column)
43
+ log "indent reasons on entry: #{@manager.indent_reasons}"
44
+ stop if @manager.tstring_nesting.size > 0
45
+
46
+ if current_lexed_line.only_spaces?
47
+ log "Line of only spaces. Moving on."
48
+ # todo: maybe i shouldn't return here? ...do transitions?
49
+ return
50
+ end
51
+
52
+ if @manager.line_ends_with_single_token_indenter?(current_lexed_line)
53
+ log "Line ends with single-token indent token."
54
+
55
+ unless @manager.comma_is_part_of_enclosed_statement?(current_lexed_line, lineno)
56
+ log "Line-ending single-token indenter found."
57
+ token_event = current_lexed_line.last_non_line_feed_event
58
+
59
+ unless @manager.line_ends_with_same_as_last token_event
60
+ log "Line ends with different type of single-token indenter: #{token_event}"
61
+ @manager.add_indent_reason(token_event[1], token_event.last, lineno)
62
+ end
63
+ end
64
+ end
65
+
66
+ @manager.update_actual_indentation(current_lexed_line)
67
+ @manager.set_up_line_transition
68
+ measure(lineno, column)
69
+
70
+ log "indent reasons on exit: #{@manager.indent_reasons}"
71
+ # prep for next line
72
+ @manager.transition_lines
73
+ end
74
+
75
+ def kw_update(token, lexed_line, lineno, column)
76
+ if token == "end"
77
+ @manager.update_for_closing_reason(:on_kw, lexed_line, lineno)
78
+ return
79
+ end
80
+
81
+ if token.continuation_keyword?
82
+ log "Continuation keyword found: '#{token}'."
83
+ @manager.update_for_continuation_reason(token, lexed_line, lineno)
84
+ return
85
+ end
86
+
87
+ if token.keyword_to_indent?
88
+ log "Indent keyword found: '#{token}'."
89
+ @manager.update_for_opening_reason(:on_kw, token, lineno)
90
+ end
91
+ end
92
+
93
+ def lbrace_update(lexed_line, lineno, column)
94
+ token = Tailor::Lexer::Token.new('{')
95
+ @manager.update_for_opening_reason(:on_lbrace, token, lineno)
96
+ end
97
+
98
+ def lbracket_update(lexed_line, lineno, column)
99
+ token = Tailor::Lexer::Token.new('[')
100
+ @manager.update_for_opening_reason(:on_lbracket, token, lineno)
101
+ end
102
+
103
+ def lparen_update(lineno, column)
104
+ token = Tailor::Lexer::Token.new('(')
105
+ @manager.update_for_opening_reason(:on_lparen, token, lineno)
106
+ end
107
+
108
+ def nl_update(current_lexed_line, lineno, column)
109
+ log "indent reasons on entry: #{@manager.indent_reasons}"
110
+ @manager.update_actual_indentation(current_lexed_line)
111
+
112
+ if @manager.last_indent_reason_type != :on_kw &&
113
+ @manager.last_indent_reason_type != :on_lbrace &&
114
+ @manager.last_indent_reason_type != :on_lbracket &&
115
+ @manager.last_indent_reason_type != :on_lparen &&
116
+ !@manager.last_indent_reason_type.nil?
117
+ log "last indent reason type: #{@manager.last_indent_reason_type}"
118
+ log "I think this is a single-token closing line..."
119
+
120
+ @manager.update_for_closing_reason(@manager.indent_reasons.last[:event_type],
121
+ current_lexed_line, lineno)
122
+ end
123
+
124
+ @manager.set_up_line_transition
125
+
126
+ unless current_lexed_line.end_of_multi_line_string?
127
+ measure(lineno, column)
128
+ end
129
+
130
+ log "indent reasons on exit: #{@manager.indent_reasons}"
131
+ @manager.transition_lines
132
+ end
133
+
134
+ def rbrace_update(current_lexed_line, lineno, column)
135
+ if @manager.multi_line_braces?(lineno)
136
+ log "End of multi-line braces!"
137
+
138
+ if current_lexed_line.only_rbrace?
139
+ @manager.amount_to_change_this -= 1
140
+ log "lonely rbrace. change_this -= 1 -> #{@manager.amount_to_change_this}"
141
+ end
142
+ end
143
+
144
+ @manager.update_for_closing_reason(:on_rbrace, current_lexed_line, lineno)
145
+ end
146
+
147
+ def rbracket_update(current_lexed_line, lineno, column)
148
+ if @manager.multi_line_brackets?(lineno)
149
+ log "End of multi-line brackets!"
150
+
151
+ if current_lexed_line.only_rbracket?
152
+ @manager.amount_to_change_this -= 1
153
+ log "lonely rbracket. change_this -= 1 -> #{@manager.amount_to_change_this}"
154
+ end
155
+ end
156
+
157
+ @manager.update_for_closing_reason(:on_rbracket, current_lexed_line, lineno)
158
+ end
159
+
160
+ def rparen_update(current_lexed_line, lineno, column)
161
+ if @manager.multi_line_parens?(lineno)
162
+ log "End of multi-line parens!"
163
+
164
+ if current_lexed_line.only_rparen?
165
+ @manager.amount_to_change_this -= 1
166
+ log "lonely rparen. change_this -= 1 -> #{@manager.amount_to_change_this}"
167
+ end
168
+ end
169
+
170
+ @manager.update_for_closing_reason(:on_rparen, current_lexed_line, lineno)
171
+ end
172
+
173
+ def tstring_beg_update(lineno)
174
+ @manager.tstring_nesting << lineno
175
+ @manager.stop
176
+ end
177
+
178
+ def tstring_end_update
179
+ @manager.tstring_nesting.pop
180
+ @manager.start unless @manager.in_tstring?
181
+ end
182
+
183
+ # Checks if the line's indentation level is appropriate.
184
+ #
185
+ # @param [Fixnum] lineno The line the potential problem is on.
186
+ # @param [Fixnum] column The column the potential problem is on.
187
+ def measure(lineno, column)
188
+ log "Measuring..."
189
+
190
+ if @manager.actual_indentation != @manager.should_be_at
191
+ @problems << Problem.new(:indentation, lineno, column,
192
+ { actual_indentation: @manager.actual_indentation,
193
+ should_be_at: @manager.should_be_at }
194
+ )
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end