isort 0.1.5 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c121d3b6fba0a8fcf8a644eb26ebce0e35b11452cb1a3a644dfbee265a6bd1d
4
- data.tar.gz: a80ae99d0f3d627407f7ccdd377365d10412ef9ade8b196b839285cee916a935
3
+ metadata.gz: b0b54367ec2e05c1827acb9d80bdc606634b858a98530ccf7c05d0e196269918
4
+ data.tar.gz: 4a7ab81c94061b440edd1f63cfc7569376278c4df39aab331bc84f70c2508ec5
5
5
  SHA512:
6
- metadata.gz: 4a92208a0dfd4b76459832abb07ee6878976adfd8c40f8dd249bd330c1d2717e046bd4427a1078a43ff113c3d6adf82b994f3b4dcdf30471f1b1d17a1e54d3d1
7
- data.tar.gz: 5ca8c19a71ed5c79135ed98b8f0ca74fe74a7b0ccb22d1de67d93e8fb75192d5f2f69957ab776a231989064a45b966ec2fb9f6a3cea02791acd5740939680635
6
+ metadata.gz: ada7d48c391b8a3e168a8ec8e3bbfb8579312a605789499f1a89188b5f5bd8af45e308ac1d8663c6f4b0c8b743a0c6d1e9b3c18e6072abb94c887cef14b72d66
7
+ data.tar.gz: a1f3ad1c5d95b411dd3292a8c452357f6e81472ace0d686fcd30cfe792a83b7b1dc79d3ef01d6b1bfc0a337fb6edd0057e60560bc769029018c3b7f0e4d1edf9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  ## [Released]
2
2
 
3
+ ## [0.2.0] - 2026-01-03
4
+
5
+ Major rewrite with robust new architecture
6
+
7
+ ### Added
8
+ - Complete architecture rewrite with modular components:
9
+ - `Parser` class for line classification
10
+ - `ImportStatement` class for import metadata
11
+ - `ImportBlock` class for contiguous import groups
12
+ - `FileProcessor` class for file orchestration
13
+ - Comprehensive duplicate import removal
14
+ - Proper handling of shebang and magic comments
15
+ - Support for all six import types: require, require_relative, include, extend, autoload, using
16
+ - Inline comment preservation
17
+ - Leading comment preservation (comments stay with their imports when sorted)
18
+ - Idempotent sorting (running multiple times produces same result)
19
+ - Proper encoding validation (UTF-8)
20
+ - Heredoc preservation
21
+ - Conditional import structure preservation
22
+ - Nested import sorting (inside classes/modules)
23
+ - Mixed quote style support
24
+ - Parenthesized require support
25
+ - 69 comprehensive unit tests covering edge cases
26
+
27
+ ### Changed
28
+ - Imports are now grouped by type with blank lines between groups
29
+ - Multiple consecutive blank lines are normalized to single blank lines
30
+ - Better error messages for invalid files
31
+
32
+ ### Fixed
33
+ - Comment handling no longer causes unexpected reordering
34
+ - Encoding errors are now properly detected and reported
35
+ - Files with no imports are left untouched
36
+ - Non-import code no longer breaks import sorting
37
+
3
38
  ## [0.1.3] - 2024-12-28
4
39
 
5
40
  - Initial release
@@ -17,3 +52,12 @@
17
52
 
18
53
  ### Added
19
54
  - Import sorting functionality support for a whole directory
55
+
56
+ ## [0.1.5] - 2024-12-29
57
+
58
+ - Third release
59
+
60
+ ### Added
61
+ - Preserve comments associated with imports
62
+ - Add new line between different groups sorted
63
+ - Don't do anything if no imports are found in a file
data/README.md CHANGED
@@ -1,44 +1,290 @@
1
- # Isort
1
+ # isort
2
2
 
3
- A Ruby gem that automatically sorts and organizes your import statements in Ruby files.
3
+ A Ruby gem that automatically sorts and organizes import statements in Ruby files. Inspired by Python's [isort](https://pycqa.github.io/isort/), it brings the same powerful import organization to Ruby projects.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/isort.svg)](https://rubygems.org/gems/isort)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - **Section-based grouping**: Automatically groups imports into stdlib, third-party, first-party, and local sections
11
+ - **Alphabetical sorting**: Sorts imports alphabetically within each section
12
+ - **Comment preservation**: Keeps inline and leading comments attached to their imports
13
+ - **Skip directives**: Skip individual imports or entire files from sorting
14
+ - **Safety modes**: Check, diff, and atomic modes to prevent unwanted changes
15
+ - **Duplicate removal**: Automatically removes duplicate imports
16
+ - **Idempotent**: Running multiple times produces the same result
17
+ - **Directory support**: Process single files or entire directories recursively
4
18
 
5
19
  ## Installation
6
20
 
21
+ Add to your Gemfile:
22
+
23
+ ```ruby
24
+ gem 'isort'
25
+ ```
26
+
27
+ Or install directly:
28
+
7
29
  ```bash
8
30
  gem install isort
9
31
  ```
10
32
 
33
+ ## Quick Start
34
+
35
+ ```bash
36
+ # Sort imports in a file
37
+ isort path/to/file.rb
38
+
39
+ # Sort all Ruby files in a directory
40
+ isort path/to/directory
41
+
42
+ # Check if files need sorting (dry-run)
43
+ isort --check path/to/file.rb
44
+
45
+ # Show diff without modifying
46
+ isort --diff path/to/file.rb
47
+ ```
48
+
11
49
  ## Usage
12
50
 
13
- ### Command Line
51
+ ### Command Line Interface
52
+
53
+ ```bash
54
+ isort [options] [file_or_directory]
55
+ ```
56
+
57
+ #### Basic Options
58
+
59
+ | Option | Description |
60
+ |--------|-------------|
61
+ | `-f, --file=FILE` | Sort a specific file |
62
+ | `-d, --directory=DIR` | Sort all Ruby files in directory recursively |
63
+
64
+ #### Safety Options
65
+
66
+ | Option | Description |
67
+ |--------|-------------|
68
+ | `-c, --check` | Check if files need sorting without modifying. Returns exit code 1 if changes needed. |
69
+ | `--diff` | Show unified diff of changes without modifying files |
70
+ | `--atomic` | Validate Ruby syntax before and after sorting. Won't save if it would introduce syntax errors. |
71
+
72
+ #### Output Options
73
+
74
+ | Option | Description |
75
+ |--------|-------------|
76
+ | `-q, --quiet` | Suppress all output except errors |
77
+ | `--verbose` | Show detailed output |
78
+ | `-h, --help` | Show help message |
79
+ | `-v, --version` | Show version |
80
+
81
+ ### Examples
14
82
 
15
83
  ```bash
16
- isort --file path/to/your/file.rb
17
- or
18
- isort -f path/to/your/file.rb
84
+ # Sort a single file
85
+ isort app/models/user.rb
86
+
87
+ # Sort with verbose output
88
+ isort --verbose lib/
89
+
90
+ # Check if files are sorted (useful in CI)
91
+ isort --check app/
92
+
93
+ # Preview changes without applying
94
+ isort --diff app/models/user.rb
95
+
96
+ # Safe mode - validates syntax before saving
97
+ isort --atomic lib/complex_file.rb
19
98
  ```
20
99
 
21
- ### In Ruby Code
100
+ ### Ruby API
22
101
 
23
102
  ```ruby
24
103
  require 'isort'
25
104
 
26
- sorter = Isort::FileSorter.new('path/to/your/file.rb')
105
+ # Sort imports in a file
106
+ sorter = Isort::FileSorter.new('path/to/file.rb')
107
+ changed = sorter.sort_and_format_imports # Returns true if file was modified
108
+
109
+ # Check mode (dry-run)
110
+ sorter = Isort::FileSorter.new('path/to/file.rb', check: true)
111
+ needs_sorting = sorter.check # Returns true if file needs sorting
112
+
113
+ # Diff mode
114
+ sorter = Isort::FileSorter.new('path/to/file.rb', diff: true)
115
+ diff_output = sorter.diff # Returns diff string or nil
116
+
117
+ # Atomic mode (validates syntax)
118
+ sorter = Isort::FileSorter.new('path/to/file.rb', atomic: true)
27
119
  sorter.sort_and_format_imports
28
120
  ```
29
121
 
30
- ## Features
122
+ ## Import Sections
123
+
124
+ isort organizes imports into four sections, separated by blank lines:
125
+
126
+ | Section | Order | Import Types |
127
+ |---------|-------|--------------|
128
+ | **stdlib** | 1 | `require` statements for Ruby standard library (json, yaml, csv, etc.) |
129
+ | **thirdparty** | 2 | `require` statements for external gems |
130
+ | **firstparty** | 3 | `include`, `extend`, `autoload`, `using` statements |
131
+ | **localfolder** | 4 | `require_relative` statements |
132
+
133
+ ### Before
134
+
135
+ ```ruby
136
+ require_relative 'helper'
137
+ require 'yaml'
138
+ extend ActiveSupport::Concern
139
+ require 'json'
140
+ include Enumerable
141
+ require_relative 'version'
142
+ require 'csv'
143
+ ```
144
+
145
+ ### After
146
+
147
+ ```ruby
148
+ require 'csv'
149
+ require 'json'
150
+ require 'yaml'
151
+
152
+ include Enumerable
153
+
154
+ extend ActiveSupport::Concern
155
+
156
+ require_relative 'helper'
157
+ require_relative 'version'
158
+ ```
159
+
160
+ ## Skip Directives
161
+
162
+ ### Skip Individual Imports
163
+
164
+ Add `# isort:skip` to keep an import in its original position:
165
+
166
+ ```ruby
167
+ require 'must_load_first' # isort:skip
168
+ require 'csv'
169
+ require 'json'
170
+ ```
171
+
172
+ The `must_load_first` import will remain at the top, while others are sorted below it.
173
+
174
+ ### Skip Entire Files
175
+
176
+ Add `# isort:skip_file` anywhere in the first 50 lines:
177
+
178
+ ```ruby
179
+ # frozen_string_literal: true
180
+ # isort:skip_file
181
+
182
+ require 'special_loader'
183
+ require 'another_special'
184
+ # This file won't be modified by isort
185
+ ```
186
+
187
+ ## Supported Import Types
188
+
189
+ isort recognizes and sorts the following Ruby import statements:
190
+
191
+ | Statement | Example |
192
+ |-----------|---------|
193
+ | `require` | `require 'json'` |
194
+ | `require_relative` | `require_relative 'helper'` |
195
+ | `include` | `include Enumerable` |
196
+ | `extend` | `extend ActiveSupport::Concern` |
197
+ | `autoload` | `autoload :MyClass, 'my_class'` |
198
+ | `using` | `using MyRefinement` |
199
+
200
+ ## CI/CD Integration
201
+
202
+ Use the `--check` flag in your CI pipeline to ensure imports are sorted:
203
+
204
+ ```yaml
205
+ # GitHub Actions example
206
+ - name: Check import sorting
207
+ run: |
208
+ gem install isort
209
+ isort --check app/ lib/
210
+ ```
211
+
212
+ ```bash
213
+ # Exit codes
214
+ # 0 = All files are sorted correctly
215
+ # 1 = Files need sorting or errors occurred
216
+ ```
217
+
218
+ ## Safety Features
219
+
220
+ ### Atomic Mode
221
+
222
+ The `--atomic` flag validates Ruby syntax before and after sorting:
223
+
224
+ ```bash
225
+ isort --atomic file.rb
226
+ ```
227
+
228
+ - Skips files that already have syntax errors
229
+ - Won't save changes if sorting would introduce syntax errors
230
+ - Provides clear error messages
231
+
232
+ ### Preserves Code Structure
31
233
 
32
- - Sorts require statements alphabetically
33
- - Groups imports by type (require, require_relative, include, extend)
34
- - Preserves code structure and spacing
35
- - Maintains conditional requires
36
- - Respects nested class and module definitions
234
+ isort only modifies import statements and:
235
+
236
+ - Preserves shebang lines (`#!/usr/bin/env ruby`)
237
+ - Preserves magic comments (`# frozen_string_literal: true`)
238
+ - Preserves inline comments on imports
239
+ - Preserves leading comments above imports
240
+ - Maintains proper indentation
241
+ - Handles encoding correctly
242
+
243
+ ## Development
244
+
245
+ ```bash
246
+ # Clone the repository
247
+ git clone https://github.com/abhinvv1/isort.git
248
+ cd isort
249
+
250
+ # Install dependencies
251
+ bundle install
252
+
253
+ # Run tests
254
+ bundle exec rspec
255
+
256
+ # Test locally without installing
257
+ bundle exec ruby -Ilib -e "require 'isort'; Isort::CLI.start" -- path/to/file.rb
258
+ ```
37
259
 
38
260
  ## Contributing
39
261
 
40
262
  Bug reports and pull requests are welcome on GitHub at https://github.com/abhinvv1/isort.
41
263
 
264
+ 1. Fork the repository
265
+ 2. Create your feature branch (`git checkout -b feature/my-feature`)
266
+ 3. Commit your changes (`git commit -am 'Add my feature'`)
267
+ 4. Push to the branch (`git push origin feature/my-feature`)
268
+ 5. Create a Pull Request
269
+
42
270
  ## License
43
271
 
44
- The gem is available as open source under the terms of the MIT License.
272
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
273
+
274
+ ## Changelog
275
+
276
+ ### v0.2.0
277
+
278
+ - Complete reimplementation with robust architecture
279
+ - Section-based import grouping (stdlib, thirdparty, firstparty, localfolder)
280
+ - Added `--check` flag for dry-run mode
281
+ - Added `--diff` flag to preview changes
282
+ - Added `--atomic` flag for syntax validation
283
+ - Added skip directives (`# isort:skip` and `# isort:skip_file`)
284
+ - Automatic duplicate removal
285
+ - Improved comment handling
286
+ - Better detection of imports vs strings containing import keywords
287
+
288
+ ### v0.1.x
289
+
290
+ - Initial release with basic import sorting
data/lib/isort/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Isort
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/isort.rb CHANGED
@@ -1,136 +1,287 @@
1
- require 'optparse'
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
2
5
  require_relative "isort/version"
6
+ require_relative "isort/parser"
7
+ require_relative "isort/import_statement"
8
+ require_relative "isort/import_block"
9
+ require_relative "isort/file_processor"
10
+ require_relative "isort/syntax_validator"
11
+ require_relative "isort/wrap_modes"
3
12
 
4
13
  module Isort
5
14
  class Error < StandardError; end
6
15
 
7
- class FileSorter
16
+ # Raised when the input file already has syntax errors
17
+ class ExistingSyntaxErrors < Error
8
18
  def initialize(file_path)
9
- @file_path = file_path
19
+ super("#{file_path} has existing syntax errors - skipping")
10
20
  end
21
+ end
11
22
 
12
- def sort_imports
13
- # Read the file content
14
- lines = File.readlines(@file_path, chomp: true).map { |line| line.gsub("\r", "") }
15
-
16
- # Separate import-related lines and other content
17
- imports = lines.select { |line| line =~ /^\s*(require|require_relative|include)\s/ }
18
- non_imports = lines.reject { |line| line =~ /^\s*(require|require_relative|include)\s/ }
23
+ # Raised when isort would introduce syntax errors
24
+ class IntroducedSyntaxErrors < Error
25
+ def initialize(file_path)
26
+ super("isort would introduce syntax errors in #{file_path} - not saving")
27
+ end
28
+ end
19
29
 
20
- # Sort the import lines alphabetically
21
- sorted_imports = imports.sort
30
+ # Raised when file contains skip directive
31
+ class FileSkipped < Error
32
+ def initialize(file_path, reason = "skip directive")
33
+ super("#{file_path} skipped due to #{reason}")
34
+ end
35
+ end
22
36
 
23
- # Combine sorted imports with other lines
24
- sorted_content = (sorted_imports + non_imports).join
37
+ # FileSorter provides the public API for sorting imports in a Ruby file
38
+ class FileSorter
39
+ attr_reader :file_path
25
40
 
26
- # Write the sorted content back to the file
27
- File.write(@file_path, sorted_content)
41
+ def initialize(file_path, options = {})
42
+ @file_path = file_path
43
+ @options = {
44
+ check: false,
45
+ diff: false,
46
+ atomic: false,
47
+ quiet: false
48
+ }.merge(options)
49
+ @processor = FileProcessor.new(file_path, @options)
28
50
  end
29
51
 
52
+ # Sort and format imports in the file
53
+ # Returns true if changes were made (or would be made in check mode)
30
54
  def sort_and_format_imports
31
- # Read the file content
32
- lines = File.readlines(@file_path)
33
-
34
- # Separate and group lines
35
- requires = extract_lines_with_comments(lines, /^require\s/)
36
- require_relatives = extract_lines_with_comments(lines, /^require_relative\s/)
37
- includes = extract_lines_with_comments(lines, /^include\s/)
38
- extends = extract_lines_with_comments(lines, /^extend\s/)
39
- autoloads = extract_lines_with_comments(lines, /^autoload\s/)
40
- usings = extract_lines_with_comments(lines, /^using\s/)
41
- others = lines.reject { |line| [requires, require_relatives, includes, extends, autoloads, usings].flatten.include?(line) }
42
-
43
- # Format and sort each group
44
- formatted_imports = []
45
- formatted_imports << format_group("require", requires)
46
- formatted_imports << format_group("require_relative", require_relatives)
47
- formatted_imports << format_group("include", includes)
48
- formatted_imports << format_group("extend", extends)
49
- formatted_imports << format_group("autoload", autoloads)
50
- formatted_imports << format_group("using", usings)
51
-
52
- return if [requires, require_relatives, includes, extends, autoloads, usings].all?(&:empty?)
53
-
54
- # Combine formatted imports with the rest of the file
55
- sorted_content = "#{formatted_imports.reject(&:empty?).join("\n")}\n#{others.join}".strip
56
-
57
- # Add a trailing newline only if imports exist
58
- sorted_content = "#{sorted_content.rstrip}\n" if !formatted_imports.empty? && !sorted_content.empty?
59
-
60
- # Write the sorted content back to the file only if imports exist
61
- File.write(@file_path, sorted_content) unless formatted_imports.empty? && sorted_content.empty?
55
+ @processor.process
56
+ rescue Errno::ENOENT
57
+ raise
58
+ rescue ExistingSyntaxErrors, IntroducedSyntaxErrors, FileSkipped
59
+ raise
60
+ rescue StandardError => e
61
+ puts "An error occurred: #{e.message}" unless @options[:quiet]
62
+ raise
62
63
  end
63
64
 
64
- private
65
-
66
- def extract_lines(lines, regex)
67
- lines.select { |line| line =~ regex }
65
+ # Check if file would change (dry-run)
66
+ def check
67
+ @processor.check
68
68
  end
69
69
 
70
- def extract_lines_with_comments(lines, regex)
71
- grouped_lines = []
72
- buffer = []
73
-
74
- lines.each do |line|
75
- if line.strip.start_with?("#") || line.strip.empty?
76
- # If the line is a comment or blank, add it to the buffer
77
- buffer << line
78
- elsif line =~ regex
79
- # If it's an import line matching the regex, attach the buffer as comments
80
- grouped_lines << (buffer + [line])
81
- buffer = [] # Reset buffer
82
- else
83
- # If it's a non-matching line, reset the buffer
84
- buffer = []
85
- end
86
- end
87
-
88
- grouped_lines
70
+ # Get diff of changes without applying
71
+ def diff
72
+ @processor.diff
89
73
  end
90
74
 
91
- def format_group(type, grouped_lines)
92
- return [] if grouped_lines.empty?
93
-
94
- # Flatten and sort each group by the import line
95
- grouped_lines
96
- .sort_by { |lines| lines.last.strip } # Sort by the actual import statement
97
- .map { |lines| lines.join } # Combine comments with the import
98
- .map(&:strip) # Remove trailing newlines
99
- .join("\n") # Join all imports in the group with newlines
100
- .concat("\n") # Add a newline between groups
75
+ # Legacy method for backward compatibility
76
+ # @deprecated Use sort_and_format_imports instead
77
+ def sort_imports
78
+ sort_and_format_imports
101
79
  end
102
-
103
80
  end
104
81
 
82
+ # CLI handles command-line interface for the isort gem
105
83
  class CLI
106
84
  def self.start
107
- options = {}
108
- OptionParser.new do |opts|
109
- opts.banner = "Usage: sort [options]"
85
+ options = {
86
+ check: false,
87
+ diff: false,
88
+ atomic: false,
89
+ quiet: false,
90
+ verbose: false
91
+ }
92
+
93
+ parser = OptionParser.new do |opts|
94
+ opts.banner = "Usage: isort [options] [file_or_directory]"
95
+ opts.separator ""
96
+ opts.separator "Options:"
110
97
 
111
- opts.on("-fFILE", "--file=FILE", "File to sort") do |file|
98
+ opts.on("-f", "--file=FILE", "File to sort") do |file|
112
99
  options[:file] = file
113
100
  end
114
- opts.on("-dDIRECTORY", "--directory=DIRECTORY", "Specify a directory to sort") do |dir|
101
+
102
+ opts.on("-d", "--directory=DIRECTORY", "Directory to sort (recursive)") do |dir|
115
103
  options[:directory] = dir
116
104
  end
117
- end.parse!
105
+
106
+ opts.separator ""
107
+ opts.separator "Safety options:"
108
+
109
+ opts.on("-c", "--check", "--check-only",
110
+ "Check if files need sorting without modifying them.",
111
+ "Returns exit code 0 if sorted, 1 if changes needed.") do
112
+ options[:check] = true
113
+ end
114
+
115
+ opts.on("--diff", "Show diff of changes without modifying files.") do
116
+ options[:diff] = true
117
+ end
118
+
119
+ opts.on("--atomic", "Validate Ruby syntax before and after sorting.",
120
+ "Won't save if it would introduce syntax errors.") do
121
+ options[:atomic] = true
122
+ end
123
+
124
+ opts.separator ""
125
+ opts.separator "Output options:"
126
+
127
+ opts.on("-q", "--quiet", "Suppress all output except errors.") do
128
+ options[:quiet] = true
129
+ end
130
+
131
+ opts.on("--verbose", "Show detailed output.") do
132
+ options[:verbose] = true
133
+ end
134
+
135
+ opts.separator ""
136
+ opts.separator "Information:"
137
+
138
+ opts.on("-h", "--help", "Show this help message.") do
139
+ puts opts
140
+ exit
141
+ end
142
+
143
+ opts.on("-v", "--version", "Show version.") do
144
+ puts "isort #{Isort::VERSION}"
145
+ exit
146
+ end
147
+ end
148
+
149
+ # Parse arguments
150
+ parser.parse!
151
+
152
+ # Handle positional arguments
153
+ if ARGV.any? && !options[:file] && !options[:directory]
154
+ target = ARGV.first
155
+ if File.directory?(target)
156
+ options[:directory] = target
157
+ elsif File.file?(target)
158
+ options[:file] = target
159
+ else
160
+ puts "Error: #{target} is not a valid file or directory"
161
+ exit 1
162
+ end
163
+ end
118
164
 
119
165
  if options[:file]
120
- sorter = FileSorter.new(options[:file])
121
- sorter.sort_and_format_imports
122
- puts "Imports sorted in #{options[:file]}"
166
+ exit_code = process_file(options[:file], options)
167
+ exit exit_code
123
168
  elsif options[:directory]
124
- count = 0
125
- Dir.glob("#{options[:directory]}/**/*.rb").each do |file|
126
- count += 1
127
- sorter = FileSorter.new(file)
128
- sorter.sort_and_format_imports
129
- end
130
- puts "Sorted imports in #{count} files in directory: #{options[:directory]}"
169
+ exit_code = process_directory(options[:directory], options)
170
+ exit exit_code
171
+ else
172
+ puts parser
173
+ exit 1
174
+ end
175
+ end
176
+
177
+ def self.process_file(file, options)
178
+ unless File.exist?(file)
179
+ puts "Error: File not found: #{file}" unless options[:quiet]
180
+ return 1
181
+ end
182
+
183
+ processor = FileProcessor.new(file, options)
184
+
185
+ if options[:check]
186
+ return handle_check(file, processor, options)
187
+ elsif options[:diff]
188
+ return handle_diff(file, processor, options)
189
+ else
190
+ return handle_sort(file, processor, options)
191
+ end
192
+ rescue ExistingSyntaxErrors => e
193
+ puts "ERROR: #{e.message}" unless options[:quiet]
194
+ return 1
195
+ rescue IntroducedSyntaxErrors => e
196
+ puts "ERROR: #{e.message}" unless options[:quiet]
197
+ return 1
198
+ rescue FileSkipped => e
199
+ puts e.message if options[:verbose]
200
+ return 0
201
+ rescue StandardError => e
202
+ puts "Error processing #{file}: #{e.message}" unless options[:quiet]
203
+ return 1
204
+ end
205
+
206
+ def self.handle_check(file, processor, options)
207
+ would_change = processor.check
208
+ if would_change
209
+ puts "#{file} - imports are not sorted" unless options[:quiet]
210
+ 1
211
+ else
212
+ puts "#{file} - imports are sorted" if options[:verbose]
213
+ 0
214
+ end
215
+ end
216
+
217
+ def self.handle_diff(file, processor, options)
218
+ diff_output = processor.diff
219
+ if diff_output && !diff_output.empty?
220
+ puts diff_output
221
+ 1
131
222
  else
132
- puts "Please specify a file using -f or --file"
223
+ puts "#{file} - no changes" if options[:verbose]
224
+ 0
133
225
  end
134
226
  end
227
+
228
+ def self.handle_sort(file, processor, options)
229
+ changed = processor.process
230
+ if changed
231
+ puts "Imports sorted in #{file}" unless options[:quiet]
232
+ else
233
+ puts "#{file} - no changes needed" if options[:verbose]
234
+ end
235
+ 0
236
+ end
237
+
238
+ def self.process_directory(dir, options)
239
+ unless Dir.exist?(dir)
240
+ puts "Error: Directory not found: #{dir}" unless options[:quiet]
241
+ return 1
242
+ end
243
+
244
+ files = Dir.glob("#{dir}/**/*.rb")
245
+ if files.empty?
246
+ puts "No Ruby files found in #{dir}" unless options[:quiet]
247
+ return 0
248
+ end
249
+
250
+ total = 0
251
+ changed = 0
252
+ errors = 0
253
+ error_messages = []
254
+
255
+ files.each do |file|
256
+ result = process_file(file, options.merge(quiet: true))
257
+ total += 1
258
+ if result == 0
259
+ changed += 1 if options[:check] || options[:diff]
260
+ else
261
+ errors += 1
262
+ end
263
+ rescue StandardError => e
264
+ errors += 1
265
+ error_messages << "#{file}: #{e.message}"
266
+ end
267
+
268
+ unless options[:quiet]
269
+ if options[:check]
270
+ unsorted = total - changed
271
+ puts "Checked #{total} files: #{changed} sorted, #{unsorted} need sorting"
272
+ elsif options[:diff]
273
+ puts "Checked #{total} files: #{total - changed} would change"
274
+ else
275
+ puts "Sorted imports in #{total} files in directory: #{dir}"
276
+ end
277
+
278
+ if error_messages.any?
279
+ puts "\nErrors encountered:"
280
+ error_messages.each { |err| puts " - #{err}" }
281
+ end
282
+ end
283
+
284
+ errors > 0 || (options[:check] && changed < total) ? 1 : 0
285
+ end
135
286
  end
136
287
  end
@@ -7,5 +7,13 @@ module Isort
7
7
  def sort_directory: -> untyped
8
8
 
9
9
  def sort_imports: -> untyped
10
+
11
+ private
12
+
13
+ def extract_lines: -> untyped
14
+
15
+ def extract_lines_with_comments: -> untyped
16
+
17
+ def format_group: -> untyped
10
18
  end
11
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isort
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - abhinvv1
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-29 00:00:00.000000000 Z
11
+ date: 2026-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: optparse
@@ -66,11 +66,13 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
- description: Isort automatically sorts and organizes your Ruby imports, including
70
- require, require_relative, include, using, and extend statements.Has the ability
71
- to group different types of imports together. It can process single files or entire
72
- directories directly throughcommand-line interface, and preserve the code comments
73
- and inline documentation
69
+ description: 'isort automatically sorts and organizes import statements in Ruby files.
70
+ It groups imports into sections (stdlib, third-party, first-party, local), sorts
71
+ alphabetically within each section, removes duplicates, and preserves comments.
72
+ Supports require, require_relative, include, extend, autoload, and using statements.
73
+ Features include: check mode for CI integration, diff preview, atomic mode with
74
+ syntax validation, skip directives for fine-grained control, and recursive directory
75
+ processing. Inspired by Python''s isort.'
74
76
  email:
75
77
  - abhinav.p@browserstack.com
76
78
  executables:
@@ -97,7 +99,7 @@ metadata:
97
99
  homepage_uri: https://github.com/abhinvv1/isort
98
100
  source_code_uri: https://github.com/abhinvv1/isort
99
101
  changelog_uri: https://github.com/abhinvv1/isort
100
- post_install_message:
102
+ post_install_message:
101
103
  rdoc_options: []
102
104
  require_paths:
103
105
  - lib
@@ -112,8 +114,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
114
  - !ruby/object:Gem::Version
113
115
  version: '0'
114
116
  requirements: []
115
- rubygems_version: 3.5.11
116
- signing_key:
117
+ rubygems_version: 3.0.9
118
+ signing_key:
117
119
  specification_version: 4
118
- summary: A Ruby gem for sorting and organizing import statements
120
+ summary: Automatic import sorting for Ruby - sort require, include, extend and more
119
121
  test_files: []