no_comments 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/README.md +2 -3
- data/exe/no_comments +1 -1
- data/lib/no_comments/content_processor.rb +157 -0
- data/lib/no_comments/remover.rb +49 -0
- data/lib/no_comments/version.rb +1 -1
- data/lib/no_comments.rb +2 -71
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0778f4eb4bbe1723231e1668730a87d462f154221fb7097fa35d21d5b34e832c'
|
4
|
+
data.tar.gz: f65eec62db3eab6acec0f44f4b02a2bc5983bf559aae3cec96855c4996d66a5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d8805b6f04b9a1ac48c8992b274f29c5f5ca0574ebebe2ed28fa9b2abf92e2bc87063100ade5365b73fd8d0b185142b521683e3b84a48c863897a7d656c7b27
|
7
|
+
data.tar.gz: 9c1af8324d295c9bc1fc1174d4789004e001758ac3e198d8c1735c27ef64e317367158b0097301331a4bb591810567f4c8e33687b45dc6a956c52e99fff2d706
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# no_comments
|
2
2
|
|
3
|
-
`no_comments` is a simple Ruby gem that removes comments from `.rb` files. It can handle single-line
|
3
|
+
`no_comments` is a simple Ruby gem that removes comments from `.rb` files. It can handle single-line, block and inline comments, leaving your code clean and readable.
|
4
4
|
|
5
5
|
---
|
6
6
|
|
@@ -35,7 +35,7 @@ gem install no_comments
|
|
35
35
|
|
36
36
|
|
37
37
|
## Usage
|
38
|
-
To clean up comments from a .rb file or directory, use the `NoComments::Remover.clean` method. This will remove all single-line
|
38
|
+
To clean up comments from a .rb file or directory, use the `NoComments::Remover.clean` method. This will remove all single-line, block and inline comments from the file or directory.
|
39
39
|
|
40
40
|
```ruby
|
41
41
|
require 'no_comments'
|
@@ -115,7 +115,6 @@ The gem is available as open source under the terms of the MIT License.
|
|
115
115
|
|
116
116
|
|
117
117
|
## TODO
|
118
|
-
- Add support multi-line comments (`=begin`...`=end`)
|
119
118
|
- Add support to magic comments (e.g. `# frozen_string_literal: true`) https://docs.ruby-lang.org/en/3.2/syntax/comments_rdoc.html - thanks [Chris](https://github.com/khasinski)!
|
120
119
|
- Option to keep documentation comments (e.g. `# @param`) https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/Documentation
|
121
120
|
- Option to clean all files in a directory except for a specified file
|
data/exe/no_comments
CHANGED
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "no_comments/version"
|
4
|
+
|
5
|
+
module NoComments
|
6
|
+
class ContentProcessor
|
7
|
+
def initialize
|
8
|
+
@comments = []
|
9
|
+
@result_lines = []
|
10
|
+
@in_multiline_comment = false
|
11
|
+
@in_heredoc = false
|
12
|
+
@heredoc_delimiter = nil
|
13
|
+
@line_number = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(content)
|
17
|
+
content.each_line do |line|
|
18
|
+
@line_number += 1
|
19
|
+
stripped_line = line.strip
|
20
|
+
process_line(line, stripped_line)
|
21
|
+
end
|
22
|
+
|
23
|
+
cleaned_content = @result_lines.join("\n")
|
24
|
+
cleaned_content += "\n" unless cleaned_content.empty?
|
25
|
+
[cleaned_content, @comments]
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_line(line, stripped_line)
|
29
|
+
if @in_multiline_comment
|
30
|
+
handle_multiline_comment(stripped_line)
|
31
|
+
elsif @in_heredoc
|
32
|
+
handle_heredoc(line, stripped_line)
|
33
|
+
else
|
34
|
+
handle_regular_line(line, stripped_line)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_multiline_comment(stripped_line)
|
39
|
+
@comments << [@line_number, stripped_line]
|
40
|
+
@in_multiline_comment = false if stripped_line == "=end"
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_heredoc(line, stripped_line)
|
44
|
+
@result_lines << line.rstrip
|
45
|
+
@in_heredoc, @heredoc_delimiter = self.class.update_heredoc_state(
|
46
|
+
stripped_line, @heredoc_delimiter
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_regular_line(line, stripped_line)
|
51
|
+
if stripped_line == "=begin"
|
52
|
+
start_multiline_comment(stripped_line)
|
53
|
+
elsif (heredoc_start = self.class.detect_heredoc_start(line))
|
54
|
+
start_heredoc(line, heredoc_start)
|
55
|
+
else
|
56
|
+
process_code_line(line)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def start_multiline_comment(stripped_line)
|
61
|
+
@in_multiline_comment = true
|
62
|
+
@comments << [@line_number, stripped_line]
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_heredoc(line, heredoc_start)
|
66
|
+
@in_heredoc = true
|
67
|
+
@heredoc_delimiter = heredoc_start
|
68
|
+
@result_lines << line.rstrip
|
69
|
+
end
|
70
|
+
|
71
|
+
def process_code_line(line)
|
72
|
+
code_part, comment_part = self.class.split_line(line)
|
73
|
+
@comments << [@line_number, comment_part.strip] if comment_part
|
74
|
+
return if code_part.strip.empty?
|
75
|
+
|
76
|
+
@result_lines << code_part.rstrip
|
77
|
+
end
|
78
|
+
|
79
|
+
# rubocop:disable Metrics/AbcSize
|
80
|
+
# rubocop:disable Metrics/MethodLength
|
81
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
82
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
83
|
+
# rubocop:disable Metrics/BlockNesting
|
84
|
+
def self.split_line(line)
|
85
|
+
in_single_quote = false
|
86
|
+
in_double_quote = false
|
87
|
+
in_regex = false
|
88
|
+
escape = false
|
89
|
+
index = 0
|
90
|
+
|
91
|
+
while index < line.length
|
92
|
+
char = line[index]
|
93
|
+
|
94
|
+
if escape
|
95
|
+
escape = false
|
96
|
+
else
|
97
|
+
case char
|
98
|
+
when "\\"
|
99
|
+
escape = true
|
100
|
+
when "'"
|
101
|
+
in_single_quote = !in_single_quote unless in_double_quote || in_regex
|
102
|
+
when '"'
|
103
|
+
in_double_quote = !in_double_quote unless in_single_quote || in_regex
|
104
|
+
when "/"
|
105
|
+
if in_regex
|
106
|
+
in_regex = false
|
107
|
+
elsif !in_single_quote && !in_double_quote &&
|
108
|
+
preceding_char_is_operator?(line, index)
|
109
|
+
in_regex = true
|
110
|
+
end
|
111
|
+
when "#"
|
112
|
+
result = handle_comment_character(line, index, in_single_quote, in_double_quote, in_regex)
|
113
|
+
return result if result
|
114
|
+
end
|
115
|
+
end
|
116
|
+
index += 1
|
117
|
+
end
|
118
|
+
[line, nil]
|
119
|
+
end
|
120
|
+
|
121
|
+
# rubocop:enable Metrics/AbcSize
|
122
|
+
# rubocop:enable Metrics/MethodLength
|
123
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
124
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
125
|
+
# rubocop:enable Metrics/BlockNesting
|
126
|
+
|
127
|
+
def self.handle_comment_character(line, index, in_single_quote, in_double_quote, in_regex)
|
128
|
+
unless in_single_quote || in_double_quote || in_regex
|
129
|
+
code_part = line[0...index]
|
130
|
+
comment_part = line[index..]
|
131
|
+
return [code_part, comment_part]
|
132
|
+
end
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.update_heredoc_state(stripped_line, heredoc_delimiter)
|
137
|
+
if stripped_line == heredoc_delimiter
|
138
|
+
[false, nil]
|
139
|
+
else
|
140
|
+
[true, heredoc_delimiter]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.detect_heredoc_start(line)
|
145
|
+
if (match = line.match(/<<[-~]?(["'`]?)(\w+)\1/))
|
146
|
+
match[2]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.preceding_char_is_operator?(line, index)
|
151
|
+
return true if index.zero?
|
152
|
+
|
153
|
+
prev_char = line[index - 1]
|
154
|
+
prev_char =~ %r{[\s(,=+\-*/%|&!<>?:]}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "no_comments/version"
|
4
|
+
require "no_comments/content_processor"
|
5
|
+
|
6
|
+
module NoComments
|
7
|
+
class Remover
|
8
|
+
def self.clean(file_path, audit: false)
|
9
|
+
if File.directory?(file_path)
|
10
|
+
Dir.glob("#{file_path}/**/*.rb").each do |file|
|
11
|
+
process_file(file, audit: audit)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
process_file(file_path, audit: audit)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.process_file(file_path, audit: false)
|
19
|
+
validate_file_extension(file_path)
|
20
|
+
content = File.read(file_path)
|
21
|
+
|
22
|
+
cleaned_content, comments = process_content(content)
|
23
|
+
|
24
|
+
if audit
|
25
|
+
print_audit(file_path, comments)
|
26
|
+
else
|
27
|
+
File.write(file_path, cleaned_content)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.validate_file_extension(file_path)
|
32
|
+
raise "Only Ruby files are supported" unless file_path.end_with?(".rb")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.process_content(content)
|
36
|
+
processor = NoComments::ContentProcessor.new
|
37
|
+
processor.process(content)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.print_audit(file_path, comments)
|
41
|
+
return if comments.empty?
|
42
|
+
|
43
|
+
puts "File: #{file_path}"
|
44
|
+
comments.each do |line_number, comment_text|
|
45
|
+
puts " Line #{line_number}: #{comment_text}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/no_comments/version.rb
CHANGED
data/lib/no_comments.rb
CHANGED
@@ -1,76 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "no_comments/
|
4
|
-
require "
|
3
|
+
require "no_comments/content_processor"
|
4
|
+
require "no_comments/remover"
|
5
5
|
|
6
6
|
module NoComments
|
7
|
-
class Remover
|
8
|
-
def self.clean(file_path, audit: false)
|
9
|
-
if File.directory?(file_path)
|
10
|
-
Dir.glob("#{file_path}/**/*.rb").each do |file|
|
11
|
-
process_file(file, audit: audit)
|
12
|
-
end
|
13
|
-
else
|
14
|
-
process_file(file_path, audit: audit)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.process_file(file_path, audit: false)
|
19
|
-
validate_file_extension(file_path)
|
20
|
-
file_content = File.read(file_path)
|
21
|
-
|
22
|
-
if audit
|
23
|
-
audit_comments(file_path, file_content)
|
24
|
-
else
|
25
|
-
content_cleaned = remove_comments(file_content)
|
26
|
-
File.write(file_path, content_cleaned)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.validate_file_extension(file_path)
|
31
|
-
raise "Only Ruby files are supported" unless file_path.end_with?(".rb")
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.audit_comments(file_path, content)
|
35
|
-
comments = extract_comments(content)
|
36
|
-
puts "File: #{file_path}" unless comments.empty?
|
37
|
-
|
38
|
-
comments.each do |(pos, _, tok, _)|
|
39
|
-
line, = pos
|
40
|
-
puts " Line #{line}: #{tok.strip}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.remove_comments(content)
|
45
|
-
comments = extract_comments(content)
|
46
|
-
content = process_comments(content, comments)
|
47
|
-
remove_empty_lines(content)
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.extract_comments(content)
|
51
|
-
Ripper.lex(content).select { |_pos, type, _tok, _| type == :on_comment }
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.process_comments(content, comments)
|
55
|
-
comments.sort_by { |(pos, _, _, _)| [pos[0], pos[1]] }.reverse.each do |(pos, _, _, _)|
|
56
|
-
line, col = pos
|
57
|
-
lines = content.lines
|
58
|
-
lines[line - 1] = process_comment_line(lines[line - 1], col)
|
59
|
-
content = lines.join
|
60
|
-
end
|
61
|
-
content
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.process_comment_line(line, col)
|
65
|
-
if line[col..].strip.start_with?("#")
|
66
|
-
"#{line[0...col].rstrip}\n"
|
67
|
-
else
|
68
|
-
"\n"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.remove_empty_lines(content)
|
73
|
-
content.gsub(/^\s*$\n/, "")
|
74
|
-
end
|
75
|
-
end
|
76
7
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: no_comments
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justyna
|
@@ -25,6 +25,8 @@ files:
|
|
25
25
|
- Rakefile
|
26
26
|
- exe/no_comments
|
27
27
|
- lib/no_comments.rb
|
28
|
+
- lib/no_comments/content_processor.rb
|
29
|
+
- lib/no_comments/remover.rb
|
28
30
|
- lib/no_comments/version.rb
|
29
31
|
- sig/no_comments.rbs
|
30
32
|
homepage:
|