yard-lint 1.2.3 → 1.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +150 -1
- data/README.md +98 -4
- data/Rakefile +20 -0
- data/bin/yard-lint +71 -38
- data/lib/yard/lint/config.rb +5 -0
- data/lib/yard/lint/config_updater.rb +222 -0
- data/lib/yard/lint/errors.rb +6 -0
- data/lib/yard/lint/executor/in_process_registry.rb +130 -0
- data/lib/yard/lint/executor/query_executor.rb +109 -0
- data/lib/yard/lint/executor/result_collector.rb +55 -0
- data/lib/yard/lint/executor/warning_dispatcher.rb +79 -0
- data/lib/yard/lint/ext/irb_notifier_shim.rb +19 -6
- data/lib/yard/lint/results/base.rb +2 -1
- data/lib/yard/lint/runner.rb +50 -38
- data/lib/yard/lint/templates/default_config.yml +105 -0
- data/lib/yard/lint/templates/strict_config.yml +105 -0
- data/lib/yard/lint/validators/base.rb +52 -118
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/config.rb +25 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/messages_builder.rb +39 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/parser.rb +59 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/result.rb +61 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition/validator.rb +94 -0
- data/lib/yard/lint/validators/documentation/blank_line_before_definition.rb +63 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/config.rb +24 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/messages_builder.rb +34 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/parser.rb +60 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/result.rb +25 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line/validator.rb +109 -0
- data/lib/yard/lint/validators/documentation/empty_comment_line.rb +58 -0
- data/lib/yard/lint/validators/documentation/markdown_syntax/validator.rb +36 -21
- data/lib/yard/lint/validators/documentation/markdown_syntax.rb +0 -1
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods/validator.rb +19 -29
- data/lib/yard/lint/validators/documentation/undocumented_boolean_methods.rb +0 -1
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments/validator.rb +18 -34
- data/lib/yard/lint/validators/documentation/undocumented_method_arguments.rb +0 -1
- data/lib/yard/lint/validators/documentation/undocumented_objects/validator.rb +17 -25
- data/lib/yard/lint/validators/documentation/undocumented_objects.rb +4 -5
- data/lib/yard/lint/validators/documentation/undocumented_options/validator.rb +30 -21
- data/lib/yard/lint/validators/documentation/undocumented_options.rb +0 -1
- data/lib/yard/lint/validators/semantic/abstract_methods/result.rb +2 -2
- data/lib/yard/lint/validators/semantic/abstract_methods/validator.rb +31 -43
- data/lib/yard/lint/validators/semantic/abstract_methods.rb +0 -1
- data/lib/yard/lint/validators/tags/api_tags/validator.rb +24 -39
- data/lib/yard/lint/validators/tags/api_tags.rb +0 -1
- data/lib/yard/lint/validators/tags/collection_type/validator.rb +37 -66
- data/lib/yard/lint/validators/tags/collection_type.rb +0 -1
- data/lib/yard/lint/validators/tags/example_syntax/validator.rb +51 -64
- data/lib/yard/lint/validators/tags/example_syntax.rb +0 -1
- data/lib/yard/lint/validators/tags/informal_notation/config.rb +40 -0
- data/lib/yard/lint/validators/tags/informal_notation/messages_builder.rb +35 -0
- data/lib/yard/lint/validators/tags/informal_notation/parser.rb +55 -0
- data/lib/yard/lint/validators/tags/informal_notation/result.rb +26 -0
- data/lib/yard/lint/validators/tags/informal_notation/validator.rb +133 -0
- data/lib/yard/lint/validators/tags/informal_notation.rb +45 -0
- data/lib/yard/lint/validators/tags/invalid_types/validator.rb +57 -70
- data/lib/yard/lint/validators/tags/invalid_types.rb +0 -1
- data/lib/yard/lint/validators/tags/meaningless_tag/validator.rb +22 -54
- data/lib/yard/lint/validators/tags/meaningless_tag.rb +0 -1
- data/lib/yard/lint/validators/tags/non_ascii_type/config.rb +21 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/messages_builder.rb +29 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/parser.rb +59 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/result.rb +25 -0
- data/lib/yard/lint/validators/tags/non_ascii_type/validator.rb +50 -0
- data/lib/yard/lint/validators/tags/non_ascii_type.rb +39 -0
- data/lib/yard/lint/validators/tags/option_tags/result.rb +2 -2
- data/lib/yard/lint/validators/tags/option_tags/validator.rb +25 -40
- data/lib/yard/lint/validators/tags/option_tags.rb +0 -1
- data/lib/yard/lint/validators/tags/order/validator.rb +28 -55
- data/lib/yard/lint/validators/tags/order.rb +0 -1
- data/lib/yard/lint/validators/tags/redundant_param_description/config.rb +15 -1
- data/lib/yard/lint/validators/tags/redundant_param_description/messages_builder.rb +5 -0
- data/lib/yard/lint/validators/tags/redundant_param_description/validator.rb +134 -100
- data/lib/yard/lint/validators/tags/redundant_param_description.rb +0 -1
- data/lib/yard/lint/validators/tags/tag_group_separator/config.rb +29 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/messages_builder.rb +49 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/parser.rb +67 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/result.rb +28 -0
- data/lib/yard/lint/validators/tags/tag_group_separator/validator.rb +117 -0
- data/lib/yard/lint/validators/tags/tag_group_separator.rb +49 -0
- data/lib/yard/lint/validators/tags/tag_type_position/validator.rb +53 -84
- data/lib/yard/lint/validators/tags/tag_type_position.rb +0 -1
- data/lib/yard/lint/validators/tags/type_syntax/parser.rb +7 -2
- data/lib/yard/lint/validators/tags/type_syntax/validator.rb +29 -59
- data/lib/yard/lint/validators/tags/type_syntax.rb +0 -1
- data/lib/yard/lint/validators/warnings/duplicated_parameter_name/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/invalid_directive_format/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/invalid_tag_format/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_directive/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/messages_builder.rb +243 -0
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/result.rb +4 -3
- data/lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_tag/messages_builder.rb +144 -0
- data/lib/yard/lint/validators/warnings/unknown_tag/result.rb +4 -3
- data/lib/yard/lint/validators/warnings/unknown_tag/validator.rb +1 -18
- data/lib/yard/lint/validators/warnings/unknown_tag.rb +10 -0
- data/lib/yard/lint/version.rb +1 -1
- data/lib/yard/lint.rb +81 -13
- data/lib/yard-lint.rb +1 -1
- data/renovate.json +1 -8
- metadata +38 -2
- data/lib/yard/lint/command_cache.rb +0 -93
|
@@ -5,25 +5,8 @@ module Yard
|
|
|
5
5
|
module Validators
|
|
6
6
|
module Warnings
|
|
7
7
|
module UnknownParameterName
|
|
8
|
-
#
|
|
8
|
+
# Validator for detecting unknown parameter name warnings from YARD
|
|
9
9
|
class Validator < Base
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
# Runs YARD stats command with proper settings on a given dir and files
|
|
13
|
-
# @param dir [String] dir where we should generate the temp docs
|
|
14
|
-
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
15
|
-
# @return [Hash] shell command execution hash results
|
|
16
|
-
def yard_cmd(dir, file_list_path)
|
|
17
|
-
cmd = <<~CMD
|
|
18
|
-
cat #{Shellwords.escape(file_list_path)} | xargs yard stats \
|
|
19
|
-
#{shell_arguments} \
|
|
20
|
-
--compact \
|
|
21
|
-
-b #{Shellwords.escape(dir)}
|
|
22
|
-
CMD
|
|
23
|
-
cmd = cmd.tr("\n", ' ')
|
|
24
|
-
|
|
25
|
-
shell(cmd)
|
|
26
|
-
end
|
|
27
10
|
end
|
|
28
11
|
end
|
|
29
12
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yard
|
|
4
|
+
module Lint
|
|
5
|
+
module Validators
|
|
6
|
+
module Warnings
|
|
7
|
+
module UnknownTag
|
|
8
|
+
# Builds enhanced messages with "did you mean" suggestions for unknown tags
|
|
9
|
+
class MessagesBuilder
|
|
10
|
+
class << self
|
|
11
|
+
# Dynamically fetch list of valid YARD meta-data tags from YARD::Tags::Library
|
|
12
|
+
# This ensures we're always in sync with the installed YARD version
|
|
13
|
+
# @return [Array<String>] array of tag names (without @ prefix)
|
|
14
|
+
def known_tags
|
|
15
|
+
@known_tags ||= begin
|
|
16
|
+
lib = ::YARD::Tags::Library.instance
|
|
17
|
+
lib.methods
|
|
18
|
+
.grep(/_tag$/)
|
|
19
|
+
.map { |m| m.to_s.sub(/_tag$/, '') }
|
|
20
|
+
.sort
|
|
21
|
+
.freeze
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Dynamically fetch list of valid YARD directives from YARD::Tags::Library
|
|
26
|
+
# This ensures we're always in sync with the installed YARD version
|
|
27
|
+
# @return [Array<String>] array of directive names (without @! prefix)
|
|
28
|
+
def known_directives
|
|
29
|
+
@known_directives ||= begin
|
|
30
|
+
lib = ::YARD::Tags::Library.instance
|
|
31
|
+
lib.methods
|
|
32
|
+
.grep(/_directive$/)
|
|
33
|
+
.map { |m| m.to_s.sub(/_directive$/, '') }
|
|
34
|
+
.sort
|
|
35
|
+
.freeze
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Combined list of all known tags and directives
|
|
40
|
+
# @return [Array<String>] array of all valid tag and directive names
|
|
41
|
+
def all_known_tags
|
|
42
|
+
@all_known_tags ||= (known_tags + known_directives).freeze
|
|
43
|
+
end
|
|
44
|
+
# Build message with suggestion for unknown tag
|
|
45
|
+
# @param offense [Hash] offense data with :message, :location (file), :line keys
|
|
46
|
+
# @return [String] formatted message with suggestion if available
|
|
47
|
+
def call(offense)
|
|
48
|
+
message = offense[:message] || 'Unknown tag detected'
|
|
49
|
+
|
|
50
|
+
# Extract the unknown tag name from the message
|
|
51
|
+
# Format: "Unknown tag @tagname in file..."
|
|
52
|
+
match = message.match(/Unknown tag @(\w+)/)
|
|
53
|
+
return message unless match
|
|
54
|
+
|
|
55
|
+
unknown_tag = match[1]
|
|
56
|
+
|
|
57
|
+
# Find best suggestion using did_you_mean
|
|
58
|
+
suggestion = find_suggestion(unknown_tag)
|
|
59
|
+
|
|
60
|
+
if suggestion
|
|
61
|
+
# Replace just the descriptive part before "in file"
|
|
62
|
+
message.sub(/Unknown tag @\w+/, "Unknown tag @#{unknown_tag} (did you mean '@#{suggestion}'?)")
|
|
63
|
+
else
|
|
64
|
+
message
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Find the best suggestion using DidYouMean spell checker
|
|
71
|
+
# @param unknown_tag [String] the unknown tag name (without @ prefix)
|
|
72
|
+
# @return [String, nil] suggested tag name or nil
|
|
73
|
+
def find_suggestion(unknown_tag)
|
|
74
|
+
return nil if unknown_tag.nil? || unknown_tag.empty?
|
|
75
|
+
|
|
76
|
+
# Use DidYouMean::SpellChecker for smart suggestions
|
|
77
|
+
spell_checker = DidYouMean::SpellChecker.new(dictionary: all_known_tags)
|
|
78
|
+
suggestions = spell_checker.correct(unknown_tag)
|
|
79
|
+
|
|
80
|
+
# If DidYouMean found suggestions, return the best one
|
|
81
|
+
return suggestions.first unless suggestions.empty?
|
|
82
|
+
|
|
83
|
+
# Otherwise, fallback to Levenshtein distance
|
|
84
|
+
find_suggestion_fallback(unknown_tag)
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
# Fallback to simple Levenshtein distance if DidYouMean fails
|
|
87
|
+
warn "DidYouMean failed: #{e.message}, using fallback" if ENV['DEBUG']
|
|
88
|
+
find_suggestion_fallback(unknown_tag)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Fallback suggestion finder using simple Levenshtein distance
|
|
92
|
+
# @param unknown_tag [String] the unknown tag name
|
|
93
|
+
# @return [String, nil] suggested tag name or nil
|
|
94
|
+
def find_suggestion_fallback(unknown_tag)
|
|
95
|
+
# Calculate Levenshtein distance for each tag
|
|
96
|
+
distances = all_known_tags.map do |tag|
|
|
97
|
+
[tag, levenshtein_distance(unknown_tag, tag)]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Sort by distance and get the closest match
|
|
101
|
+
best_match = distances.min_by { |_tag, distance| distance }
|
|
102
|
+
|
|
103
|
+
# Only suggest if the distance is reasonable (less than half the length)
|
|
104
|
+
return nil unless best_match
|
|
105
|
+
|
|
106
|
+
tag, distance = best_match
|
|
107
|
+
max_distance = [unknown_tag.length, tag.length].max / 2
|
|
108
|
+
|
|
109
|
+
distance <= max_distance ? tag : nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Calculate Levenshtein distance between two strings
|
|
113
|
+
# @param str1 [String] first string
|
|
114
|
+
# @param str2 [String] second string
|
|
115
|
+
# @return [Integer] Levenshtein distance
|
|
116
|
+
def levenshtein_distance(str1, str2)
|
|
117
|
+
return str2.length if str1.empty?
|
|
118
|
+
return str1.length if str2.empty?
|
|
119
|
+
|
|
120
|
+
matrix = Array.new(str1.length + 1) { Array.new(str2.length + 1) }
|
|
121
|
+
|
|
122
|
+
(0..str1.length).each { |i| matrix[i][0] = i }
|
|
123
|
+
(0..str2.length).each { |j| matrix[0][j] = j }
|
|
124
|
+
|
|
125
|
+
(1..str1.length).each do |i|
|
|
126
|
+
(1..str2.length).each do |j|
|
|
127
|
+
cost = str1[i - 1] == str2[j - 1] ? 0 : 1
|
|
128
|
+
matrix[i][j] = [
|
|
129
|
+
matrix[i - 1][j] + 1, # deletion
|
|
130
|
+
matrix[i][j - 1] + 1, # insertion
|
|
131
|
+
matrix[i - 1][j - 1] + cost # substitution
|
|
132
|
+
].min
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
matrix[str1.length][str2.length]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -12,10 +12,11 @@ module Yard
|
|
|
12
12
|
self.offense_name = 'UnknownTag'
|
|
13
13
|
|
|
14
14
|
# Build human-readable message for unknown tag offense
|
|
15
|
-
#
|
|
16
|
-
# @
|
|
15
|
+
# Uses MessagesBuilder to add "did you mean" suggestions
|
|
16
|
+
# @param offense [Hash] offense data with :message, :location, :line keys
|
|
17
|
+
# @return [String] formatted message with suggestion if available
|
|
17
18
|
def build_message(offense)
|
|
18
|
-
offense
|
|
19
|
+
MessagesBuilder.call(offense)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
end
|
|
@@ -5,25 +5,8 @@ module Yard
|
|
|
5
5
|
module Validators
|
|
6
6
|
module Warnings
|
|
7
7
|
module UnknownTag
|
|
8
|
-
#
|
|
8
|
+
# Validator for detecting unknown YARD tag warnings
|
|
9
9
|
class Validator < Base
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
# Runs YARD stats command with proper settings on a given dir and files
|
|
13
|
-
# @param dir [String] dir where we should generate the temp docs
|
|
14
|
-
# @param file_list_path [String] path to temp file containing file paths (one per line)
|
|
15
|
-
# @return [Hash] shell command execution hash results
|
|
16
|
-
def yard_cmd(dir, file_list_path)
|
|
17
|
-
cmd = <<~CMD
|
|
18
|
-
cat #{Shellwords.escape(file_list_path)} | xargs yard stats \
|
|
19
|
-
#{shell_arguments} \
|
|
20
|
-
--compact \
|
|
21
|
-
-b #{Shellwords.escape(dir)}
|
|
22
|
-
CMD
|
|
23
|
-
cmd = cmd.tr("\n", ' ')
|
|
24
|
-
|
|
25
|
-
shell(cmd)
|
|
26
|
-
end
|
|
27
10
|
end
|
|
28
11
|
end
|
|
29
12
|
end
|
|
@@ -12,18 +12,28 @@ module Yard
|
|
|
12
12
|
# doesn't recognize, which could indicate typos or unsupported tags.
|
|
13
13
|
# This validator is enabled by default.
|
|
14
14
|
#
|
|
15
|
+
# Provides intelligent "did you mean" suggestions for common typos using
|
|
16
|
+
# Ruby's did_you_mean gem with Levenshtein distance fallback.
|
|
17
|
+
#
|
|
15
18
|
# @example Bad - Misspelled or non-existent tags
|
|
16
19
|
# # @param name [String] typo in param tag
|
|
17
20
|
# # @returns [String] should be @return not @returns
|
|
21
|
+
# # @raises [Error] should be @raise not @raises
|
|
18
22
|
# def process(name)
|
|
19
23
|
# end
|
|
20
24
|
#
|
|
21
25
|
# @example Good - Standard YARD tags
|
|
22
26
|
# # @param name [String] the name
|
|
23
27
|
# # @return [String] the result
|
|
28
|
+
# # @raise [Error] the error
|
|
24
29
|
# def process(name)
|
|
25
30
|
# end
|
|
26
31
|
#
|
|
32
|
+
# **Output with suggestions:**
|
|
33
|
+
#
|
|
34
|
+
# lib/foo.rb:10: [error] Unknown tag @returns (did you mean '@return'?)
|
|
35
|
+
# lib/foo.rb:11: [error] Unknown tag @raises (did you mean '@raise'?)
|
|
36
|
+
#
|
|
27
37
|
# ## Configuration
|
|
28
38
|
#
|
|
29
39
|
# To disable this validator:
|
data/lib/yard/lint/version.rb
CHANGED
data/lib/yard/lint.rb
CHANGED
|
@@ -6,6 +6,9 @@ require 'open3'
|
|
|
6
6
|
require 'tempfile'
|
|
7
7
|
require 'tmpdir'
|
|
8
8
|
require 'digest'
|
|
9
|
+
require 'did_you_mean'
|
|
10
|
+
require 'yard'
|
|
11
|
+
require 'set'
|
|
9
12
|
|
|
10
13
|
module Yard
|
|
11
14
|
# YARD Lint module providing linting functionality for YARD documentation
|
|
@@ -59,6 +62,9 @@ module Yard
|
|
|
59
62
|
# @param config [Yard::Lint::Config] configuration object
|
|
60
63
|
# @return [Array<String>] array of absolute file paths
|
|
61
64
|
def get_diff_files(diff, path, config)
|
|
65
|
+
# Determine the base directory for relative path calculations
|
|
66
|
+
base_dir = determine_base_dir(path)
|
|
67
|
+
|
|
62
68
|
# Get changed files from git based on mode
|
|
63
69
|
git_files = case diff[:mode]
|
|
64
70
|
when :ref
|
|
@@ -72,11 +78,7 @@ module Yard
|
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
# Apply exclusion patterns
|
|
75
|
-
git_files.reject
|
|
76
|
-
config.exclude.any? do |pattern|
|
|
77
|
-
File.fnmatch(pattern, file, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
78
|
-
end
|
|
79
|
-
end
|
|
81
|
+
git_files.reject { |file| excluded_file?(file, config.exclude, base_dir) }
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
# Expand path/glob patterns into an array of files
|
|
@@ -84,28 +86,94 @@ module Yard
|
|
|
84
86
|
# @param config [Yard::Lint::Config] configuration object
|
|
85
87
|
# @return [Array<String>] array of absolute file paths
|
|
86
88
|
def expand_path(path, config)
|
|
89
|
+
# Determine the base directory for relative path calculations
|
|
90
|
+
base_dir = determine_base_dir(path)
|
|
91
|
+
|
|
92
|
+
files = discover_ruby_files(path)
|
|
93
|
+
|
|
94
|
+
# Convert to absolute paths for YARD
|
|
95
|
+
files = files.map { |file| File.expand_path(file) }
|
|
96
|
+
|
|
97
|
+
# Filter out excluded files
|
|
98
|
+
files.reject { |file| excluded_file?(file, config.exclude, base_dir) }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Discover Ruby files from path/glob patterns
|
|
102
|
+
# @param path [String, Array<String>] path or array of paths
|
|
103
|
+
# @return [Array<String>] array of discovered Ruby file paths
|
|
104
|
+
# @raise [Errors::FileNotFoundError] if a specified path does not exist
|
|
105
|
+
def discover_ruby_files(path)
|
|
87
106
|
files = Array(path).flat_map do |p|
|
|
88
107
|
if p.include?('*')
|
|
89
108
|
Dir.glob(p)
|
|
90
109
|
elsif File.directory?(p)
|
|
91
110
|
Dir.glob(File.join(p, '**/*.rb'))
|
|
92
111
|
else
|
|
112
|
+
validate_path_exists!(p)
|
|
93
113
|
p
|
|
94
114
|
end
|
|
95
115
|
end
|
|
96
116
|
|
|
97
|
-
files
|
|
117
|
+
files.select { |file| File.file?(file) && file.end_with?('.rb') }
|
|
118
|
+
end
|
|
98
119
|
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
# Validate that a path exists
|
|
121
|
+
# @param path [String] file or directory path to check for existence
|
|
122
|
+
# @raise [Errors::FileNotFoundError] if path does not exist
|
|
123
|
+
def validate_path_exists!(path)
|
|
124
|
+
return if File.exist?(path)
|
|
101
125
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
raise Errors::FileNotFoundError, "No such file or directory: #{path}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Determine base directory for relative path calculations
|
|
130
|
+
# @param path [String, Array<String>] path or array of paths
|
|
131
|
+
# @return [String] absolute base directory path
|
|
132
|
+
def determine_base_dir(path)
|
|
133
|
+
first_path = Array(path).first
|
|
134
|
+
return Dir.pwd unless first_path
|
|
135
|
+
|
|
136
|
+
absolute_path = File.expand_path(first_path)
|
|
137
|
+
File.directory?(absolute_path) ? absolute_path : File.dirname(absolute_path)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Check if a file matches any exclusion pattern
|
|
141
|
+
# Patterns are matched against both absolute and relative paths
|
|
142
|
+
# @param file [String] absolute file path
|
|
143
|
+
# @param patterns [Array<String>] exclusion patterns
|
|
144
|
+
# @param base_dir [String] base directory for relative path calculation
|
|
145
|
+
# @return [Boolean] true if file should be excluded
|
|
146
|
+
def excluded_file?(file, patterns, base_dir)
|
|
147
|
+
relative_path = relative_path_from(file, base_dir)
|
|
148
|
+
|
|
149
|
+
patterns.any? do |pattern|
|
|
150
|
+
match_path?(pattern, file, relative_path)
|
|
107
151
|
end
|
|
108
152
|
end
|
|
153
|
+
|
|
154
|
+
# Calculate relative path from base directory
|
|
155
|
+
# @param file [String] absolute file path
|
|
156
|
+
# @param base_dir [String] base directory
|
|
157
|
+
# @return [String] relative path
|
|
158
|
+
def relative_path_from(file, base_dir)
|
|
159
|
+
if file.start_with?("#{base_dir}/")
|
|
160
|
+
file.sub("#{base_dir}/", '')
|
|
161
|
+
else
|
|
162
|
+
file
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Check if a pattern matches a file path
|
|
167
|
+
# Tries matching against both relative and absolute paths
|
|
168
|
+
# @param pattern [String] glob pattern
|
|
169
|
+
# @param absolute_path [String] absolute file path
|
|
170
|
+
# @param relative_path [String] relative file path
|
|
171
|
+
# @return [Boolean] true if pattern matches
|
|
172
|
+
def match_path?(pattern, absolute_path, relative_path)
|
|
173
|
+
flags = File::FNM_PATHNAME | File::FNM_EXTGLOB
|
|
174
|
+
|
|
175
|
+
File.fnmatch(pattern, relative_path, flags) || File.fnmatch(pattern, absolute_path, flags)
|
|
176
|
+
end
|
|
109
177
|
end
|
|
110
178
|
end
|
|
111
179
|
end
|
data/lib/yard-lint.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Load IRB notifier shim before YARD to avoid IRB dependency in Ruby 3.5+
|
|
3
|
+
# Load IRB notifier shim before YARD to avoid IRB dependency in Ruby 3.5+/4.0+
|
|
4
4
|
# This must be loaded before any YARD code is required
|
|
5
5
|
require_relative 'yard/lint/ext/irb_notifier_shim'
|
|
6
6
|
|
data/renovate.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"extends": [
|
|
4
4
|
"config:recommended"
|
|
5
5
|
],
|
|
6
|
+
"minimumReleaseAge": "7 days",
|
|
6
7
|
"github-actions": {
|
|
7
8
|
"enabled": true,
|
|
8
9
|
"pinDigests": true
|
|
@@ -10,13 +11,5 @@
|
|
|
10
11
|
"includePaths": [
|
|
11
12
|
"Gemfile",
|
|
12
13
|
"yard-lint.gemspec"
|
|
13
|
-
],
|
|
14
|
-
"packageRules": [
|
|
15
|
-
{
|
|
16
|
-
"matchManagers": [
|
|
17
|
-
"github-actions"
|
|
18
|
-
],
|
|
19
|
-
"minimumReleaseAge": "7 days"
|
|
20
|
-
}
|
|
21
14
|
]
|
|
22
15
|
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yard-lint
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maciej Mensfeld
|
|
@@ -54,11 +54,15 @@ files:
|
|
|
54
54
|
- bin/yard-lint
|
|
55
55
|
- lib/yard-lint.rb
|
|
56
56
|
- lib/yard/lint.rb
|
|
57
|
-
- lib/yard/lint/command_cache.rb
|
|
58
57
|
- lib/yard/lint/config.rb
|
|
59
58
|
- lib/yard/lint/config_generator.rb
|
|
60
59
|
- lib/yard/lint/config_loader.rb
|
|
60
|
+
- lib/yard/lint/config_updater.rb
|
|
61
61
|
- lib/yard/lint/errors.rb
|
|
62
|
+
- lib/yard/lint/executor/in_process_registry.rb
|
|
63
|
+
- lib/yard/lint/executor/query_executor.rb
|
|
64
|
+
- lib/yard/lint/executor/result_collector.rb
|
|
65
|
+
- lib/yard/lint/executor/warning_dispatcher.rb
|
|
62
66
|
- lib/yard/lint/ext/irb_notifier_shim.rb
|
|
63
67
|
- lib/yard/lint/formatters/progress.rb
|
|
64
68
|
- lib/yard/lint/git.rb
|
|
@@ -74,6 +78,18 @@ files:
|
|
|
74
78
|
- lib/yard/lint/templates/strict_config.yml
|
|
75
79
|
- lib/yard/lint/validators/base.rb
|
|
76
80
|
- lib/yard/lint/validators/config.rb
|
|
81
|
+
- lib/yard/lint/validators/documentation/blank_line_before_definition.rb
|
|
82
|
+
- lib/yard/lint/validators/documentation/blank_line_before_definition/config.rb
|
|
83
|
+
- lib/yard/lint/validators/documentation/blank_line_before_definition/messages_builder.rb
|
|
84
|
+
- lib/yard/lint/validators/documentation/blank_line_before_definition/parser.rb
|
|
85
|
+
- lib/yard/lint/validators/documentation/blank_line_before_definition/result.rb
|
|
86
|
+
- lib/yard/lint/validators/documentation/blank_line_before_definition/validator.rb
|
|
87
|
+
- lib/yard/lint/validators/documentation/empty_comment_line.rb
|
|
88
|
+
- lib/yard/lint/validators/documentation/empty_comment_line/config.rb
|
|
89
|
+
- lib/yard/lint/validators/documentation/empty_comment_line/messages_builder.rb
|
|
90
|
+
- lib/yard/lint/validators/documentation/empty_comment_line/parser.rb
|
|
91
|
+
- lib/yard/lint/validators/documentation/empty_comment_line/result.rb
|
|
92
|
+
- lib/yard/lint/validators/documentation/empty_comment_line/validator.rb
|
|
77
93
|
- lib/yard/lint/validators/documentation/markdown_syntax.rb
|
|
78
94
|
- lib/yard/lint/validators/documentation/markdown_syntax/config.rb
|
|
79
95
|
- lib/yard/lint/validators/documentation/markdown_syntax/messages_builder.rb
|
|
@@ -126,6 +142,12 @@ files:
|
|
|
126
142
|
- lib/yard/lint/validators/tags/example_syntax/parser.rb
|
|
127
143
|
- lib/yard/lint/validators/tags/example_syntax/result.rb
|
|
128
144
|
- lib/yard/lint/validators/tags/example_syntax/validator.rb
|
|
145
|
+
- lib/yard/lint/validators/tags/informal_notation.rb
|
|
146
|
+
- lib/yard/lint/validators/tags/informal_notation/config.rb
|
|
147
|
+
- lib/yard/lint/validators/tags/informal_notation/messages_builder.rb
|
|
148
|
+
- lib/yard/lint/validators/tags/informal_notation/parser.rb
|
|
149
|
+
- lib/yard/lint/validators/tags/informal_notation/result.rb
|
|
150
|
+
- lib/yard/lint/validators/tags/informal_notation/validator.rb
|
|
129
151
|
- lib/yard/lint/validators/tags/invalid_types.rb
|
|
130
152
|
- lib/yard/lint/validators/tags/invalid_types/config.rb
|
|
131
153
|
- lib/yard/lint/validators/tags/invalid_types/messages_builder.rb
|
|
@@ -138,6 +160,12 @@ files:
|
|
|
138
160
|
- lib/yard/lint/validators/tags/meaningless_tag/parser.rb
|
|
139
161
|
- lib/yard/lint/validators/tags/meaningless_tag/result.rb
|
|
140
162
|
- lib/yard/lint/validators/tags/meaningless_tag/validator.rb
|
|
163
|
+
- lib/yard/lint/validators/tags/non_ascii_type.rb
|
|
164
|
+
- lib/yard/lint/validators/tags/non_ascii_type/config.rb
|
|
165
|
+
- lib/yard/lint/validators/tags/non_ascii_type/messages_builder.rb
|
|
166
|
+
- lib/yard/lint/validators/tags/non_ascii_type/parser.rb
|
|
167
|
+
- lib/yard/lint/validators/tags/non_ascii_type/result.rb
|
|
168
|
+
- lib/yard/lint/validators/tags/non_ascii_type/validator.rb
|
|
141
169
|
- lib/yard/lint/validators/tags/option_tags.rb
|
|
142
170
|
- lib/yard/lint/validators/tags/option_tags/config.rb
|
|
143
171
|
- lib/yard/lint/validators/tags/option_tags/messages_builder.rb
|
|
@@ -156,6 +184,12 @@ files:
|
|
|
156
184
|
- lib/yard/lint/validators/tags/redundant_param_description/parser.rb
|
|
157
185
|
- lib/yard/lint/validators/tags/redundant_param_description/result.rb
|
|
158
186
|
- lib/yard/lint/validators/tags/redundant_param_description/validator.rb
|
|
187
|
+
- lib/yard/lint/validators/tags/tag_group_separator.rb
|
|
188
|
+
- lib/yard/lint/validators/tags/tag_group_separator/config.rb
|
|
189
|
+
- lib/yard/lint/validators/tags/tag_group_separator/messages_builder.rb
|
|
190
|
+
- lib/yard/lint/validators/tags/tag_group_separator/parser.rb
|
|
191
|
+
- lib/yard/lint/validators/tags/tag_group_separator/result.rb
|
|
192
|
+
- lib/yard/lint/validators/tags/tag_group_separator/validator.rb
|
|
159
193
|
- lib/yard/lint/validators/tags/tag_type_position.rb
|
|
160
194
|
- lib/yard/lint/validators/tags/tag_type_position/config.rb
|
|
161
195
|
- lib/yard/lint/validators/tags/tag_type_position/messages_builder.rb
|
|
@@ -190,11 +224,13 @@ files:
|
|
|
190
224
|
- lib/yard/lint/validators/warnings/unknown_directive/validator.rb
|
|
191
225
|
- lib/yard/lint/validators/warnings/unknown_parameter_name.rb
|
|
192
226
|
- lib/yard/lint/validators/warnings/unknown_parameter_name/config.rb
|
|
227
|
+
- lib/yard/lint/validators/warnings/unknown_parameter_name/messages_builder.rb
|
|
193
228
|
- lib/yard/lint/validators/warnings/unknown_parameter_name/parser.rb
|
|
194
229
|
- lib/yard/lint/validators/warnings/unknown_parameter_name/result.rb
|
|
195
230
|
- lib/yard/lint/validators/warnings/unknown_parameter_name/validator.rb
|
|
196
231
|
- lib/yard/lint/validators/warnings/unknown_tag.rb
|
|
197
232
|
- lib/yard/lint/validators/warnings/unknown_tag/config.rb
|
|
233
|
+
- lib/yard/lint/validators/warnings/unknown_tag/messages_builder.rb
|
|
198
234
|
- lib/yard/lint/validators/warnings/unknown_tag/parser.rb
|
|
199
235
|
- lib/yard/lint/validators/warnings/unknown_tag/result.rb
|
|
200
236
|
- lib/yard/lint/validators/warnings/unknown_tag/validator.rb
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Yard
|
|
4
|
-
module Lint
|
|
5
|
-
# Cache for YARD command executions to avoid running identical commands multiple times
|
|
6
|
-
# This provides a transparent optimization layer - validators don't need to know about it
|
|
7
|
-
class CommandCache
|
|
8
|
-
def initialize
|
|
9
|
-
@cache = {}
|
|
10
|
-
@hits = 0
|
|
11
|
-
@misses = 0
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Execute a command through the cache
|
|
15
|
-
# If the command has been executed before, return cached result
|
|
16
|
-
# Otherwise execute and cache the result
|
|
17
|
-
# @param command_string [String] the shell command to execute
|
|
18
|
-
# @return [Hash] hash with stdout, stderr, exit_code keys
|
|
19
|
-
# @note Returns a deep clone to prevent validators from modifying cached data
|
|
20
|
-
def execute(command_string)
|
|
21
|
-
cache_key = generate_cache_key(command_string)
|
|
22
|
-
|
|
23
|
-
if @cache.key?(cache_key)
|
|
24
|
-
@hits += 1
|
|
25
|
-
deep_clone(@cache[cache_key])
|
|
26
|
-
else
|
|
27
|
-
@misses += 1
|
|
28
|
-
result = execute_command(command_string)
|
|
29
|
-
@cache[cache_key] = deep_clone(result)
|
|
30
|
-
result
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Get cache statistics
|
|
35
|
-
# @return [Hash] hash with hits, misses, and total executions
|
|
36
|
-
def stats
|
|
37
|
-
{
|
|
38
|
-
hits: @hits,
|
|
39
|
-
misses: @misses,
|
|
40
|
-
total: @hits + @misses,
|
|
41
|
-
saved_executions: @hits
|
|
42
|
-
}
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
# Generate a cache key for the command
|
|
48
|
-
# Normalizes the command to handle whitespace differences
|
|
49
|
-
# @param command_string [String] the command to generate key for
|
|
50
|
-
# @return [String] SHA256 hash of normalized command
|
|
51
|
-
def generate_cache_key(command_string)
|
|
52
|
-
# Normalize whitespace: collapse multiple spaces/newlines into single spaces
|
|
53
|
-
normalized = command_string.strip.gsub(/\s+/, ' ')
|
|
54
|
-
Digest::SHA256.hexdigest(normalized)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Actually execute the command
|
|
58
|
-
# @param command_string [String] the command to execute
|
|
59
|
-
# @return [Hash] hash with stdout, stderr, exit_code keys
|
|
60
|
-
def execute_command(command_string)
|
|
61
|
-
# Set up environment to load IRB shim before YARD (Ruby 3.5+ compatibility)
|
|
62
|
-
env = build_environment_with_shim
|
|
63
|
-
|
|
64
|
-
stdout, stderr, status = Open3.capture3(env, command_string)
|
|
65
|
-
{
|
|
66
|
-
stdout: stdout,
|
|
67
|
-
stderr: stderr,
|
|
68
|
-
exit_code: status.exitstatus
|
|
69
|
-
}
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Build environment hash with RUBYOPT to load IRB shim
|
|
73
|
-
# This ensures the shim is loaded in subprocesses (like yard list commands)
|
|
74
|
-
# @return [Hash] environment variables for command execution
|
|
75
|
-
def build_environment_with_shim
|
|
76
|
-
shim_path = File.expand_path('ext/irb_notifier_shim.rb', __dir__)
|
|
77
|
-
rubyopt = "-r#{shim_path}"
|
|
78
|
-
|
|
79
|
-
# Preserve existing RUBYOPT if present
|
|
80
|
-
rubyopt = "#{ENV['RUBYOPT'].strip} #{rubyopt}" if ENV['RUBYOPT']
|
|
81
|
-
|
|
82
|
-
{ 'RUBYOPT' => rubyopt }
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Deep clone a hash to prevent modifications to cached data
|
|
86
|
-
# @param hash [Hash] the hash to clone
|
|
87
|
-
# @return [Hash] deep cloned hash
|
|
88
|
-
def deep_clone(hash)
|
|
89
|
-
Marshal.load(Marshal.dump(hash))
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|