yard-lint 1.2.3 → 1.3.0.rc1

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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +150 -1
  3. data/README.md +98 -4
  4. data/Rakefile +20 -0
  5. data/bin/yard-lint +71 -38
  6. data/lib/yard/lint/config.rb +5 -0
  7. data/lib/yard/lint/config_updater.rb +222 -0
  8. data/lib/yard/lint/errors.rb +6 -0
  9. data/lib/yard/lint/executor/in_process_registry.rb +130 -0
  10. data/lib/yard/lint/executor/query_executor.rb +109 -0
  11. data/lib/yard/lint/executor/result_collector.rb +55 -0
  12. data/lib/yard/lint/executor/warning_dispatcher.rb +79 -0
  13. data/lib/yard/lint/results/base.rb +2 -1
  14. data/lib/yard/lint/runner.rb +50 -38
  15. data/lib/yard/lint/templates/default_config.yml +105 -0
  16. data/lib/yard/lint/templates/strict_config.yml +105 -0
  17. data/lib/yard/lint/validators/base.rb +52 -118
  18. data/lib/yard/lint/validators/documentation/blank_line_before_definition/config.rb +25 -0
  19. data/lib/yard/lint/validators/documentation/blank_line_before_definition/messages_builder.rb +39 -0
  20. data/lib/yard/lint/validators/documentation/blank_line_before_definition/parser.rb +59 -0
  21. data/lib/yard/lint/validators/documentation/blank_line_before_definition/result.rb +61 -0
  22. data/lib/yard/lint/validators/documentation/blank_line_before_definition/validator.rb +94 -0
  23. data/lib/yard/lint/validators/documentation/blank_line_before_definition.rb +63 -0
  24. data/lib/yard/lint/validators/documentation/empty_comment_line/config.rb +24 -0
  25. data/lib/yard/lint/validators/documentation/empty_comment_line/messages_builder.rb +34 -0
  26. data/lib/yard/lint/validators/documentation/empty_comment_line/parser.rb +60 -0
  27. data/lib/yard/lint/validators/documentation/empty_comment_line/result.rb +25 -0
  28. data/lib/yard/lint/validators/documentation/empty_comment_line/validator.rb +109 -0
  29. data/lib/yard/lint/validators/documentation/empty_comment_line.rb +58 -0
  30. data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +36 -21
  31. data/lib/yard/lint/validators/documentation/markdown_syntax.rb +0 -1
  32. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +19 -29
  33. data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +0 -1
  34. data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +18 -34
  35. data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +0 -1
  36. data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +17 -25
  37. data/lib/yard/lint/validators/documentation/undocumented_objects.rb +4 -5
  38. data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +30 -21
  39. data/lib/yard/lint/validators/documentation/undocumented_options.rb +0 -1
  40. data/lib/yard/lint/validators/semantic/abstract_methods/result.rb +2 -2
  41. data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +31 -43
  42. data/lib/yard/lint/validators/semantic/abstract_methods.rb +0 -1
  43. data/lib/yard/lint/validators/tags/api_tags/validator.rb +24 -39
  44. data/lib/yard/lint/validators/tags/api_tags.rb +0 -1
  45. data/lib/yard/lint/validators/tags/collection_type/validator.rb +37 -66
  46. data/lib/yard/lint/validators/tags/collection_type.rb +0 -1
  47. data/lib/yard/lint/validators/tags/example_syntax/validator.rb +51 -64
  48. data/lib/yard/lint/validators/tags/example_syntax.rb +0 -1
  49. data/lib/yard/lint/validators/tags/informal_notation/config.rb +40 -0
  50. data/lib/yard/lint/validators/tags/informal_notation/messages_builder.rb +35 -0
  51. data/lib/yard/lint/validators/tags/informal_notation/parser.rb +55 -0
  52. data/lib/yard/lint/validators/tags/informal_notation/result.rb +26 -0
  53. data/lib/yard/lint/validators/tags/informal_notation/validator.rb +133 -0
  54. data/lib/yard/lint/validators/tags/informal_notation.rb +45 -0
  55. data/lib/yard/lint/validators/tags/invalid_types/validator.rb +57 -70
  56. data/lib/yard/lint/validators/tags/invalid_types.rb +0 -1
  57. data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +22 -54
  58. data/lib/yard/lint/validators/tags/meaningless_tag.rb +0 -1
  59. data/lib/yard/lint/validators/tags/non_ascii_type/config.rb +21 -0
  60. data/lib/yard/lint/validators/tags/non_ascii_type/messages_builder.rb +29 -0
  61. data/lib/yard/lint/validators/tags/non_ascii_type/parser.rb +59 -0
  62. data/lib/yard/lint/validators/tags/non_ascii_type/result.rb +25 -0
  63. data/lib/yard/lint/validators/tags/non_ascii_type/validator.rb +50 -0
  64. data/lib/yard/lint/validators/tags/non_ascii_type.rb +39 -0
  65. data/lib/yard/lint/validators/tags/option_tags/result.rb +2 -2
  66. data/lib/yard/lint/validators/tags/option_tags/validator.rb +25 -40
  67. data/lib/yard/lint/validators/tags/option_tags.rb +0 -1
  68. data/lib/yard/lint/validators/tags/order/validator.rb +28 -55
  69. data/lib/yard/lint/validators/tags/order.rb +0 -1
  70. data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +15 -1
  71. data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +5 -0
  72. data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +134 -100
  73. data/lib/yard/lint/validators/tags/redundant_param_description.rb +0 -1
  74. data/lib/yard/lint/validators/tags/tag_group_separator/config.rb +29 -0
  75. data/lib/yard/lint/validators/tags/tag_group_separator/messages_builder.rb +49 -0
  76. data/lib/yard/lint/validators/tags/tag_group_separator/parser.rb +67 -0
  77. data/lib/yard/lint/validators/tags/tag_group_separator/result.rb +28 -0
  78. data/lib/yard/lint/validators/tags/tag_group_separator/validator.rb +117 -0
  79. data/lib/yard/lint/validators/tags/tag_group_separator.rb +49 -0
  80. data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +53 -84
  81. data/lib/yard/lint/validators/tags/tag_type_position.rb +0 -1
  82. data/lib/yard/lint/validators/tags/type_syntax/parser.rb +7 -2
  83. data/lib/yard/lint/validators/tags/type_syntax/validator.rb +29 -59
  84. data/lib/yard/lint/validators/tags/type_syntax.rb +0 -1
  85. data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +1 -18
  86. data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +1 -18
  87. data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +1 -18
  88. data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +1 -18
  89. data/lib/yard/lint/validators/warnings/unknown_parameter_name/messages_builder.rb +243 -0
  90. data/lib/yard/lint/validators/warnings/unknown_parameter_name/result.rb +4 -3
  91. data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +1 -18
  92. data/lib/yard/lint/validators/warnings/unknown_tag/messages_builder.rb +144 -0
  93. data/lib/yard/lint/validators/warnings/unknown_tag/result.rb +4 -3
  94. data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +1 -18
  95. data/lib/yard/lint/validators/warnings/unknown_tag.rb +10 -0
  96. data/lib/yard/lint/version.rb +1 -1
  97. data/lib/yard/lint.rb +81 -13
  98. data/renovate.json +1 -8
  99. metadata +38 -2
  100. data/lib/yard/lint/command_cache.rb +0 -93
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Require executor components for in-process execution
4
+ require_relative 'executor/in_process_registry'
5
+ require_relative 'executor/result_collector'
6
+ require_relative 'executor/query_executor'
7
+ require_relative 'executor/warning_dispatcher'
8
+
3
9
  module Yard
4
10
  module Lint
5
11
  # Main runner class that orchestrates the YARD validation process
@@ -26,8 +32,8 @@ module Yard
26
32
 
27
33
  private
28
34
 
29
- # Run all validators
30
- # Automatically runs all validators from ConfigLoader::ALL_VALIDATORS if enabled
35
+ # Run all validators using in-process YARD execution.
36
+ # Parses files once and shares the registry across all validators.
31
37
  # @return [Hash] hash with raw results from all validators
32
38
  def run_validators
33
39
  results = {}
@@ -37,50 +43,55 @@ module Yard
37
43
 
38
44
  @progress_formatter&.start(enabled_validators.size)
39
45
 
40
- # Iterate through all registered validators
46
+ # Initialize in-process infrastructure
47
+ registry = Executor::InProcessRegistry.new
48
+ registry.parse(selection)
49
+
50
+ query_executor = Executor::QueryExecutor.new(registry)
51
+ warning_dispatcher = Executor::WarningDispatcher.new
52
+ dispatched_warnings = warning_dispatcher.dispatch(registry.warnings)
53
+
54
+ # Process each enabled validator
41
55
  enabled_validators.each_with_index do |validator_name, index|
42
- # Get the validator namespace and config
43
56
  validator_namespace = ConfigLoader.validator_module(validator_name)
44
57
  validator_cfg = ConfigLoader.validator_config(validator_name)
45
58
 
46
- # Show progress before running validator
47
59
  @progress_formatter&.update(index + 1, validator_name)
48
60
 
49
- # Run the validator if it has a module
50
- # (validators with modules have Validator classes)
51
- if validator_namespace
52
- run_and_store_validator(validator_namespace, validator_cfg, results, validator_name)
53
- end
61
+ next unless validator_namespace
62
+
63
+ validator_class = validator_namespace::Validator
64
+ validator_selection = filter_files_for_validator(validator_name, selection)
65
+
66
+ result = if warning_dispatcher.warning_validator?(validator_name)
67
+ # Use dispatched warnings for warning validators
68
+ warning_dispatcher.format_for_validator(
69
+ dispatched_warnings[validator_name] || []
70
+ )
71
+ else
72
+ # Use in-process execution
73
+ validator = validator_class.new(config, validator_selection)
74
+ in_process_result = query_executor.execute(validator, file_selection: validator_selection)
75
+
76
+ # Tags/Order requires special result wrapping for its parser
77
+ if validator_name == 'Tags/Order'
78
+ tags_order = config.validator_config('Tags/Order', 'EnforcedOrder')
79
+ in_process_result[:stdout] = {
80
+ result: in_process_result[:stdout],
81
+ tags_order: tags_order
82
+ }
83
+ end
84
+
85
+ in_process_result
86
+ end
87
+
88
+ results[validator_cfg.id] = result
54
89
  end
55
90
 
56
91
  @progress_formatter&.finish
57
-
58
92
  results
59
93
  end
60
94
 
61
- # Run a validator and store its result using the module's ID
62
- # @param validator_namespace [Module] validator namespace module (e.g., Validators::Tags::Order)
63
- # @param validator_config [Class] validator config class
64
- # @param results [Hash] hash to store results in
65
- # @param validator_name [String] full validator name for per-validator exclusions
66
- def run_and_store_validator(
67
- validator_namespace, validator_config, results, validator_name
68
- )
69
- results[validator_config.id] = run_validator(
70
- validator_namespace::Validator,
71
- validator_name
72
- )
73
- end
74
-
75
- # Run a single validator with per-validator file filtering
76
- # @param validator_class [Class] validator class to instantiate and run
77
- # @param validator_name [String] full validator name for exclusions
78
- # @return [Hash] hash with stdout, stderr and exit_code keys
79
- def run_validator(validator_class, validator_name)
80
- validator_selection = filter_files_for_validator(validator_name, selection)
81
- validator_class.new(config, validator_selection).call
82
- end
83
-
84
95
  # Filter files for a specific validator based on per-validator exclusions
85
96
  # @param validator_name [String] full validator name
86
97
  # @param files [Array<String>] array of file paths
@@ -106,7 +117,6 @@ module Yard
106
117
  return result if validator_excludes.empty?
107
118
 
108
119
  working_dir = Dir.pwd
109
- filtered_keys = %i[severity type name message location_line]
110
120
 
111
121
  filtered_offenses = result.offenses.reject do |offense|
112
122
  file_path = offense[:location] || offense[:file]
@@ -128,9 +138,11 @@ module Yard
128
138
  # Return nil if no offenses remain after filtering
129
139
  return nil if filtered_offenses.empty?
130
140
 
131
- # Create a new result object with filtered offenses
132
- # We need to preserve the result class and config
133
- result.class.new(filtered_offenses.map { |o| o.except(*filtered_keys) }, result.config)
141
+ # Instead of creating a new Result object (which would rebuild messages),
142
+ # just modify the existing result object's offenses array
143
+ # This preserves all the processed offense data including enhanced messages
144
+ result.offenses = filtered_offenses
145
+ result
134
146
  end
135
147
 
136
148
  # Parse raw results from validators and create Result objects
@@ -57,6 +57,23 @@ Documentation/MarkdownSyntax:
57
57
  Enabled: true
58
58
  Severity: warning
59
59
 
60
+ Documentation/EmptyCommentLine:
61
+ Description: 'Detects empty comment lines at the start or end of documentation blocks.'
62
+ Enabled: true
63
+ Severity: convention
64
+ EnabledPatterns:
65
+ Leading: true
66
+ Trailing: true
67
+
68
+ Documentation/BlankLineBeforeDefinition:
69
+ Description: 'Detects blank lines between YARD documentation and method definition.'
70
+ Enabled: true
71
+ Severity: convention
72
+ OrphanedSeverity: convention
73
+ EnabledPatterns:
74
+ SingleBlankLine: true
75
+ OrphanedDocs: true
76
+
60
77
  # Tags validators
61
78
  Tags/Order:
62
79
  Description: 'Enforces consistent ordering of YARD tags.'
@@ -65,9 +82,15 @@ Tags/Order:
65
82
  EnforcedOrder:
66
83
  - param
67
84
  - option
85
+ - yield
86
+ - yieldparam
87
+ - yieldreturn
68
88
  - return
69
89
  - raise
90
+ - see
70
91
  - example
92
+ - note
93
+ - todo
71
94
 
72
95
  Tags/InvalidTypes:
73
96
  Description: 'Validates type definitions in @param, @return, @option tags.'
@@ -136,6 +159,88 @@ Tags/OptionTags:
136
159
  Enabled: true
137
160
  Severity: warning
138
161
 
162
+ Tags/ExampleSyntax:
163
+ Description: 'Validates Ruby syntax in @example tags.'
164
+ Enabled: true
165
+ Severity: warning
166
+
167
+ Tags/RedundantParamDescription:
168
+ Description: 'Detects meaningless parameter descriptions that add no value.'
169
+ Enabled: true
170
+ Severity: convention
171
+ CheckedTags:
172
+ - param
173
+ - option
174
+ Articles:
175
+ - The
176
+ - the
177
+ - A
178
+ - a
179
+ - An
180
+ - an
181
+ MaxRedundantWords: 6
182
+ GenericTerms:
183
+ - object
184
+ - instance
185
+ - value
186
+ - data
187
+ - item
188
+ - element
189
+ EnabledPatterns:
190
+ ArticleParam: true
191
+ PossessiveParam: true
192
+ TypeRestatement: true
193
+ ParamToVerb: true
194
+ IdPattern: true
195
+ DirectionalDate: true
196
+ TypeGeneric: true
197
+
198
+ Tags/InformalNotation:
199
+ Description: 'Detects informal tag notation patterns like "Note:" instead of @note.'
200
+ Enabled: true
201
+ Severity: warning
202
+ CaseSensitive: false
203
+ RequireStartOfLine: true
204
+ Patterns:
205
+ Note: '@note'
206
+ Todo: '@todo'
207
+ TODO: '@todo'
208
+ FIXME: '@todo'
209
+ See: '@see'
210
+ See also: '@see'
211
+ Warning: '@deprecated'
212
+ Deprecated: '@deprecated'
213
+ Author: '@author'
214
+ Version: '@version'
215
+ Since: '@since'
216
+ Returns: '@return'
217
+ Raises: '@raise'
218
+ Example: '@example'
219
+
220
+ Tags/NonAsciiType:
221
+ Description: 'Detects non-ASCII characters in type annotations.'
222
+ Enabled: true
223
+ Severity: warning
224
+ ValidatedTags:
225
+ - param
226
+ - option
227
+ - return
228
+ - yieldreturn
229
+ - yieldparam
230
+
231
+ Tags/TagGroupSeparator:
232
+ Description: 'Enforces blank line separators between different YARD tag groups.'
233
+ Enabled: false # Opt-in validator
234
+ Severity: convention
235
+ TagGroups:
236
+ param: [param, option]
237
+ return: [return]
238
+ error: [raise, throws]
239
+ example: [example]
240
+ meta: [see, note, todo, deprecated, since, version, api]
241
+ yield: [yield, yieldparam, yieldreturn]
242
+ RequireAfterDescription: false
243
+
139
244
  # Warnings validators - catches YARD parser errors
140
245
  Warnings/UnknownTag:
141
246
  Description: 'Detects unknown YARD tags.'
@@ -61,6 +61,23 @@ Documentation/MarkdownSyntax:
61
61
  Enabled: true
62
62
  Severity: error
63
63
 
64
+ Documentation/EmptyCommentLine:
65
+ Description: 'Detects empty comment lines at the start or end of documentation blocks.'
66
+ Enabled: true
67
+ Severity: error
68
+ EnabledPatterns:
69
+ Leading: true
70
+ Trailing: true
71
+
72
+ Documentation/BlankLineBeforeDefinition:
73
+ Description: 'Detects blank lines between YARD documentation and method definition.'
74
+ Enabled: true
75
+ Severity: error
76
+ OrphanedSeverity: error
77
+ EnabledPatterns:
78
+ SingleBlankLine: true
79
+ OrphanedDocs: true
80
+
64
81
  # Tags validators
65
82
  Tags/Order:
66
83
  Description: 'Enforces consistent ordering of YARD tags.'
@@ -69,9 +86,15 @@ Tags/Order:
69
86
  EnforcedOrder:
70
87
  - param
71
88
  - option
89
+ - yield
90
+ - yieldparam
91
+ - yieldreturn
72
92
  - return
73
93
  - raise
94
+ - see
74
95
  - example
96
+ - note
97
+ - todo
75
98
 
76
99
  Tags/InvalidTypes:
77
100
  Description: 'Validates type definitions in @param, @return, @option tags.'
@@ -140,6 +163,88 @@ Tags/OptionTags:
140
163
  Enabled: true
141
164
  Severity: error
142
165
 
166
+ Tags/ExampleSyntax:
167
+ Description: 'Validates Ruby syntax in @example tags.'
168
+ Enabled: true
169
+ Severity: error
170
+
171
+ Tags/RedundantParamDescription:
172
+ Description: 'Detects meaningless parameter descriptions that add no value.'
173
+ Enabled: true
174
+ Severity: error
175
+ CheckedTags:
176
+ - param
177
+ - option
178
+ Articles:
179
+ - The
180
+ - the
181
+ - A
182
+ - a
183
+ - An
184
+ - an
185
+ MaxRedundantWords: 6
186
+ GenericTerms:
187
+ - object
188
+ - instance
189
+ - value
190
+ - data
191
+ - item
192
+ - element
193
+ EnabledPatterns:
194
+ ArticleParam: true
195
+ PossessiveParam: true
196
+ TypeRestatement: true
197
+ ParamToVerb: true
198
+ IdPattern: true
199
+ DirectionalDate: true
200
+ TypeGeneric: true
201
+
202
+ Tags/InformalNotation:
203
+ Description: 'Detects informal tag notation patterns like "Note:" instead of @note.'
204
+ Enabled: true
205
+ Severity: error
206
+ CaseSensitive: false
207
+ RequireStartOfLine: true
208
+ Patterns:
209
+ Note: '@note'
210
+ Todo: '@todo'
211
+ TODO: '@todo'
212
+ FIXME: '@todo'
213
+ See: '@see'
214
+ See also: '@see'
215
+ Warning: '@deprecated'
216
+ Deprecated: '@deprecated'
217
+ Author: '@author'
218
+ Version: '@version'
219
+ Since: '@since'
220
+ Returns: '@return'
221
+ Raises: '@raise'
222
+ Example: '@example'
223
+
224
+ Tags/NonAsciiType:
225
+ Description: 'Detects non-ASCII characters in type annotations.'
226
+ Enabled: true
227
+ Severity: error
228
+ ValidatedTags:
229
+ - param
230
+ - option
231
+ - return
232
+ - yieldreturn
233
+ - yieldparam
234
+
235
+ Tags/TagGroupSeparator:
236
+ Description: 'Enforces blank line separators between different YARD tag groups.'
237
+ Enabled: false # Opt-in validator
238
+ Severity: error
239
+ TagGroups:
240
+ param: [param, option]
241
+ return: [return]
242
+ error: [raise, throws]
243
+ example: [example]
244
+ meta: [see, note, todo, deprecated, since, version, api]
245
+ yield: [yield, yieldparam, yieldreturn]
246
+ RequireAfterDescription: false
247
+
143
248
  # Warnings validators - catches YARD parser errors
144
249
  Warnings/UnknownTag:
145
250
  Description: 'Detects unknown YARD tags.'
@@ -6,47 +6,52 @@ module Yard
6
6
  module Validators
7
7
  # Base YARD validator class
8
8
  class Base
9
- # Class-level cache shared across ALL validator classes
10
- # Must be stored on Base itself, not on subclasses
11
- @shared_command_cache = nil
12
-
13
- # Default YARD command options that we need to use
14
- DEFAULT_OPTIONS = [
15
- '--charset utf-8',
16
- '--markup markdown',
17
- '--no-progress'
18
- ].freeze
19
-
20
- # Base temp directory for YARD databases
21
- # Each unique set of arguments gets its own subdirectory to prevent contamination
22
- YARDOC_BASE_TEMP_DIR = Dir.mktmpdir.freeze
23
-
24
- private_constant :YARDOC_BASE_TEMP_DIR
9
+ # Class-level settings for in-process execution
10
+ # These must be set on each subclass, not on Base
11
+ @in_process_enabled = nil
12
+ @in_process_visibility = nil
25
13
 
26
14
  attr_reader :config, :selection
27
15
 
28
16
  class << self
29
- # Lazy-initialized command cache shared across all validator instances
30
- # This allows different validators to reuse results from identical YARD commands
31
- # @return [CommandCache] the command cache instance
32
- def command_cache
33
- # Use Base's cache, not subclass's cache
34
- Base.instance_variable_get(:@shared_command_cache) ||
35
- Base.instance_variable_set(:@shared_command_cache, CommandCache.new)
17
+ # Declare that this validator supports in-process execution
18
+ # @param visibility [Symbol] visibility filter for objects (:public or :all)
19
+ # :public - only include public methods (default, no --private/--protected)
20
+ # :all - include all methods (equivalent to --private --protected)
21
+ # @return [void]
22
+ # @example
23
+ # class Validator < Base
24
+ # in_process visibility: :all
25
+ # end
26
+ def in_process(visibility: :public)
27
+ @in_process_enabled = true
28
+ @in_process_visibility = visibility
36
29
  end
37
30
 
38
- # Reset the command cache (primarily for testing)
39
- # @return [void]
40
- def reset_command_cache!
41
- Base.instance_variable_set(:@shared_command_cache, nil)
31
+ # Check if this validator supports in-process execution
32
+ # @return [Boolean]
33
+ def in_process?
34
+ @in_process_enabled == true
42
35
  end
43
36
 
44
- # Clear all YARD databases (primarily for testing)
45
- # @return [void]
46
- def clear_yard_database!
47
- return unless defined?(YARDOC_BASE_TEMP_DIR)
37
+ # Get the visibility setting for in-process execution
38
+ # @return [Symbol, nil] :public, :all, or nil if not set
39
+ def in_process_visibility
40
+ @in_process_visibility
41
+ end
48
42
 
49
- FileUtils.rm_rf(Dir.glob(File.join(YARDOC_BASE_TEMP_DIR, '*')))
43
+ # Get the validator name from the class namespace
44
+ # @return [String, nil] validator name like 'Tags/Order' or nil
45
+ # @example
46
+ # Yard::Lint::Validators::Tags::Order::Validator.validator_name
47
+ # # => 'Tags/Order'
48
+ def validator_name
49
+ name&.split('::')&.then do |parts|
50
+ idx = parts.index('Validators')
51
+ return nil unless idx && parts[idx + 1] && parts[idx + 2]
52
+
53
+ "#{parts[idx + 1]}/#{parts[idx + 2]}"
54
+ end
50
55
  end
51
56
  end
52
57
 
@@ -57,105 +62,34 @@ module Yard
57
62
  @selection = selection
58
63
  end
59
64
 
60
- # Performs the validation and returns raw results
61
- # @return [Hash] hash with stdout, stderr and exit_code keys
62
- def call
63
- # There might be a case when there were no files because someone ignored all
64
- # then we need to return empty result
65
- return raw if selection.nil? || selection.empty?
66
-
67
- # Anything that goes to shell needs to be escaped
68
- escaped_file_names = escape(selection)
69
-
70
- # Use a unique YARD database per set of arguments to prevent contamination
71
- # between validators with different file selections or options
72
- yardoc_dir = yardoc_temp_dir_for_arguments(escaped_file_names.join(' '))
73
-
74
- # For large file lists, use a temporary file to avoid ARG_MAX limits
75
- # Write file paths to temp file, one per line
76
- Tempfile.create(['yard_files', '.txt']) do |f|
77
- escaped_file_names.each { |file| f.puts(file) }
78
- f.flush
79
-
80
- yard_cmd(yardoc_dir, f.path)
81
- end
65
+ # Execute query for a single object during in-process execution.
66
+ # Override this method in validators that support in-process execution.
67
+ # @param object [YARD::CodeObjects::Base] the code object to query
68
+ # @param collector [Executor::ResultCollector] collector for output
69
+ # @return [void]
70
+ # @example
71
+ # def in_process_query(object, collector)
72
+ # return unless object.docstring.all.empty?
73
+ # collector.puts "#{object.file}:#{object.line}: #{object.title}"
74
+ # end
75
+ def in_process_query(object, collector)
76
+ raise NotImplementedError, "#{self.class} must implement in_process_query for in-process execution"
82
77
  end
83
78
 
84
79
  private
85
80
 
86
- # Returns a unique YARD database directory for the given arguments
87
- # Uses SHA256 hash of the normalized arguments to ensure different file sets
88
- # get separate databases, preventing contamination
89
- # @param escaped_file_names [String] escaped file names to process
90
- # @return [String] path to the YARD database directory
91
- def yardoc_temp_dir_for_arguments(escaped_file_names)
92
- # Combine all arguments that affect YARD output
93
- all_args = "#{shell_arguments} #{escaped_file_names}"
94
-
95
- # Create a hash of the arguments for a unique directory name
96
- args_hash = Digest::SHA256.hexdigest(all_args)
97
-
98
- # Create subdirectory under base temp dir
99
- dir = File.join(YARDOC_BASE_TEMP_DIR, args_hash)
100
- FileUtils.mkdir_p(dir) unless File.directory?(dir)
101
-
102
- dir
103
- end
104
-
105
- # @return [String] all arguments with which YARD command should be executed
106
- def shell_arguments
107
- validator_name = self.class.name&.split('::')&.then do |parts|
108
- idx = parts.index('Validators')
109
- next config.options unless idx && parts[idx + 1] && parts[idx + 2]
110
-
111
- "#{parts[idx + 1]}/#{parts[idx + 2]}"
112
- end || config.options
113
-
114
- yard_options = config.validator_yard_options(validator_name)
115
- args = escape(yard_options).join(' ')
116
- "#{args} #{DEFAULT_OPTIONS.join(' ')}"
117
- end
118
-
119
- # @param array [Array] escape all elements in an array
120
- # @return [Array] array with escaped elements
121
- def escape(array)
122
- array.map { |cmd| Shellwords.escape(cmd) }
123
- end
124
-
125
- # Builds a raw hash that can be used for further processing
126
- # @param stdout [String, Hash, Array] anything that we want to return as stdout
127
- # @param stderr [String, Hash, Array] any errors that occurred
128
- # @param exit_code [Integer, false] result exit code or false if we want to decide it based
129
- # on the stderr content
130
- # @return [Hash] hash with stdout, stderr and exit_code keys
131
- def raw(stdout = '', stderr = '', exit_code = false)
132
- {
133
- stdout: stdout,
134
- stderr: stderr,
135
- exit_code: exit_code || (stderr.empty? ? 0 : 1)
136
- }
137
- end
138
-
139
- # Executes a shell command and returns the result
140
- # Routes through command cache to avoid duplicate executions
141
- # @param cmd [String] shell command to execute
142
- # @return [Hash] hash with stdout, stderr and exit_code keys
143
- def shell(cmd)
144
- self.class.command_cache.execute(cmd)
145
- end
146
-
147
81
  # Retrieves configuration value with fallback to default
148
82
  # Automatically determines the validator name from the class namespace
149
83
  #
150
84
  # @param key [String] the configuration key to retrieve
151
85
  # @return [Object] the configured value or default value from the validator's Config.defaults
152
- # @note The validator name is automatically extracted from the class namespace.
153
- # For example, Yard::Lint::Validators::Tags::RedundantParamDescription::Validator
154
- # becomes 'Tags/RedundantParamDescription'
155
86
  # @example Usage in a validator (e.g., Tags::RedundantParamDescription)
156
87
  # def config_articles
157
88
  # config_or_default('Articles')
158
89
  # end
90
+ # @note The validator name is automatically extracted from the class namespace.
91
+ # For example, Yard::Lint::Validators::Tags::RedundantParamDescription::Validator
92
+ # becomes 'Tags/RedundantParamDescription'
159
93
  def config_or_default(key)
160
94
  validator_name = self.class.name&.split('::')&.then do |parts|
161
95
  idx = parts.index('Validators')
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module BlankLineBeforeDefinition
8
+ # Configuration for BlankLineBeforeDefinition validator
9
+ class Config < ::Yard::Lint::Validators::Config
10
+ self.id = :blank_line_before_definition
11
+ self.defaults = {
12
+ 'Enabled' => true,
13
+ 'Severity' => 'convention',
14
+ 'OrphanedSeverity' => 'convention',
15
+ 'EnabledPatterns' => {
16
+ 'SingleBlankLine' => true,
17
+ 'OrphanedDocs' => true
18
+ }
19
+ }.freeze
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yard
4
+ module Lint
5
+ module Validators
6
+ module Documentation
7
+ module BlankLineBeforeDefinition
8
+ # Builds human-readable messages for blank line before definition violations
9
+ class MessagesBuilder
10
+ # Maps violation types to human-readable descriptions
11
+ ERROR_DESCRIPTIONS = {
12
+ 'single' => 'Blank line between documentation and definition',
13
+ 'orphaned' => 'Documentation is orphaned (YARD ignores it due to blank lines)'
14
+ }.freeze
15
+
16
+ class << self
17
+ # Formats a violation message
18
+ # @param offense [Hash] the offense details
19
+ # @return [String] formatted message
20
+ def call(offense)
21
+ type = offense[:violation_type]
22
+ object_name = offense[:object_name]
23
+ blank_count = offense[:blank_count]
24
+
25
+ description = ERROR_DESCRIPTIONS[type] || 'Blank line before definition'
26
+
27
+ if type == 'orphaned'
28
+ "#{description} for '#{object_name}' (#{blank_count} blank lines)"
29
+ else
30
+ "#{description} for '#{object_name}'"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end