no_comments 0.1.4 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f4d0bbabf8d2083a22cb850026add69671c3af79b1d0cb75a0a891e5187aea4
4
- data.tar.gz: a8a312eea4658d6da9ac64007d9da4ea94efc642fc0381406e4fbc82b0b84726
3
+ metadata.gz: '0778f4eb4bbe1723231e1668730a87d462f154221fb7097fa35d21d5b34e832c'
4
+ data.tar.gz: f65eec62db3eab6acec0f44f4b02a2bc5983bf559aae3cec96855c4996d66a5e
5
5
  SHA512:
6
- metadata.gz: aa15e36876ee00b72f23b2043eb661a736df5de5876b14e5e587cb04c99e7af9858d8f030520efac7af0ff8939901571a12f67f72182f87e6e94ae9abc1eb779
7
- data.tar.gz: a3aa64ab64f4c7ba06da376b7fe16e027d363b8a22626b5b23e7a5c6d99051939dd23039751ac14ed3f27258abdcf7c5869516c5fdcfc772c0f81c67e1d23478
6
+ metadata.gz: 4d8805b6f04b9a1ac48c8992b274f29c5f5ca0574ebebe2ed28fa9b2abf92e2bc87063100ade5365b73fd8d0b185142b521683e3b84a48c863897a7d656c7b27
7
+ data.tar.gz: 9c1af8324d295c9bc1fc1174d4789004e001758ac3e198d8c1735c27ef64e317367158b0097301331a4bb591810567f4c8e33687b45dc6a956c52e99fff2d706
data/.rubocop.yml CHANGED
@@ -16,6 +16,10 @@ Metrics/BlockLength:
16
16
  Exclude:
17
17
  - 'spec/no_comments/**/*'
18
18
 
19
+ Metrics/ClassLength:
20
+ Max: 120
21
+
22
+
19
23
  Style/Documentation:
20
24
  Enabled: false
21
25
 
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 comments and inline comments, leaving your code clean and readable.
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 comments and inline comments from the file or directory.
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
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "optparse"
5
- require "no_comments"
5
+ require "no_comments/remover"
6
6
 
7
7
  options = {}
8
8
  OptionParser.new do |opts|
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NoComments
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
data/lib/no_comments.rb CHANGED
@@ -1,76 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "no_comments/version"
4
- require "ripper"
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
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: