Class: Isort::ImportBlock
- Inherits:
-
Object
- Object
- Isort::ImportBlock
- Defined in:
- lib/isort/import_block.rb
Overview
Represents a contiguous block of import statements Handles sorting, deduplication, and reconstruction
Instance Attribute Summary collapse
-
#end_line ⇒ Object
Returns the value of attribute end_line.
-
#indentation ⇒ Object
readonly
Returns the value of attribute indentation.
-
#leading_content ⇒ Object
Returns the value of attribute leading_content.
-
#start_line ⇒ Object
Returns the value of attribute start_line.
-
#statements ⇒ Object
readonly
Returns the value of attribute statements.
-
#trailing_blank_lines ⇒ Object
Returns the value of attribute trailing_blank_lines.
Instance Method Summary collapse
- #add_statement(statement) ⇒ Object
- #empty? ⇒ Boolean
-
#initialize(indentation: "") ⇒ ImportBlock
constructor
A new instance of ImportBlock.
-
#original_line_count ⇒ Object
Calculate how many original lines this block spans.
- #size ⇒ Object
-
#sort_and_dedupe! ⇒ Object
Sort statements by type then alphabetically, and remove duplicates Imports with isort:skip directive stay in their original position.
-
#to_lines ⇒ Object
Convert the sorted block back to lines.
Constructor Details
#initialize(indentation: "") ⇒ ImportBlock
Returns a new instance of ImportBlock.
12 13 14 15 16 17 18 19 |
# File 'lib/isort/import_block.rb', line 12 def initialize(indentation: "") @statements = [] @start_line = nil @end_line = nil @indentation = indentation @leading_content = [] # Comments/blanks before first import in block @trailing_blank_lines = 0 end |
Instance Attribute Details
#end_line ⇒ Object
Returns the value of attribute end_line.
10 11 12 |
# File 'lib/isort/import_block.rb', line 10 def end_line @end_line end |
#indentation ⇒ Object (readonly)
Returns the value of attribute indentation.
9 10 11 |
# File 'lib/isort/import_block.rb', line 9 def indentation @indentation end |
#leading_content ⇒ Object
Returns the value of attribute leading_content.
10 11 12 |
# File 'lib/isort/import_block.rb', line 10 def leading_content @leading_content end |
#start_line ⇒ Object
Returns the value of attribute start_line.
10 11 12 |
# File 'lib/isort/import_block.rb', line 10 def start_line @start_line end |
#statements ⇒ Object (readonly)
Returns the value of attribute statements.
9 10 11 |
# File 'lib/isort/import_block.rb', line 9 def statements @statements end |
#trailing_blank_lines ⇒ Object
Returns the value of attribute trailing_blank_lines.
10 11 12 |
# File 'lib/isort/import_block.rb', line 10 def trailing_blank_lines @trailing_blank_lines end |
Instance Method Details
#add_statement(statement) ⇒ Object
21 22 23 |
# File 'lib/isort/import_block.rb', line 21 def add_statement(statement) @statements << statement end |
#empty? ⇒ Boolean
25 26 27 |
# File 'lib/isort/import_block.rb', line 25 def empty? @statements.empty? end |
#original_line_count ⇒ Object
Calculate how many original lines this block spans
158 159 160 161 162 |
# File 'lib/isort/import_block.rb', line 158 def original_line_count return 0 if @start_line.nil? || @end_line.nil? @end_line - @start_line + 1 end |
#size ⇒ Object
29 30 31 |
# File 'lib/isort/import_block.rb', line 29 def size @statements.size end |
#sort_and_dedupe! ⇒ Object
Sort statements by type then alphabetically, and remove duplicates Imports with isort:skip directive stay in their original position
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/isort/import_block.rb', line 35 def sort_and_dedupe! return if @statements.empty? # Separate skipped and sortable statements skipped_with_positions = [] sortable = [] @statements.each_with_index do |stmt, index| if stmt.skip_sorting? skipped_with_positions << { statement: stmt, position: index } else sortable << stmt end end # Sort only the sortable statements sortable.sort! # Remove duplicates from sortable (keep first occurrence with its comments) seen = {} sortable = sortable.reject do |stmt| key = stmt.normalized_key if seen[key] true # Remove this duplicate else seen[key] = true false # Keep this one end end # If no skipped statements, just use the sorted list if skipped_with_positions.empty? @statements = sortable return self end # Re-insert skipped statements at their original relative positions # We need to calculate where each skipped statement should go # in the new sorted list based on its original position ratio result = sortable.dup # Sort skipped statements by their original position skipped_with_positions.sort_by! { |s| s[:position] } # Insert each skipped statement skipped_with_positions.each_with_index do |skipped_info, skip_idx| original_pos = skipped_info[:position] original_count = @statements.size # Calculate the relative position in the new array if original_count <= 1 insert_pos = skip_idx else # Scale the position to the new array size ratio = original_pos.to_f / (original_count - 1) new_max = result.size + skip_idx insert_pos = (ratio * new_max).round end # Clamp to valid range insert_pos = [[insert_pos, 0].max, result.size].min result.insert(insert_pos, skipped_info[:statement]) end @statements = result self end |
#to_lines ⇒ Object
Convert the sorted block back to lines
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/isort/import_block.rb', line 105 def to_lines return [] if @statements.empty? result = [] # Add any leading content (comments before first import) result.concat(@leading_content) unless @leading_content.empty? # Group statements by section and type for proper spacing current_section = nil current_type = nil @statements.each do |stmt| # Filter out blank lines from the statement's leading comments # (we'll add our own spacing between groups) non_blank_comments = stmt.leading_comments.reject { |c| c.strip.empty? } # Add blank line between different sections (highest priority separator) if current_section && current_section != stmt.section && !result.empty? # Add blank line between sections (unless last line is already blank) unless result.last&.strip&.empty? result << "#{@indentation}\n" end # Add blank line between different import types within the same section elsif current_type && current_type != stmt.type && current_section == stmt.section && !result.empty? # Only add blank line if last line isn't already blank unless result.last&.strip&.empty? result << "#{@indentation}\n" end elsif current_type && current_type == stmt.type && non_blank_comments.any? # Same type but has comments - might need blank line before the comment # Only add if last line isn't blank unless result.last&.strip&.empty? result << "#{@indentation}\n" if stmt.leading_comments.any? { |c| c.strip.empty? } end end # Add non-blank leading comments non_blank_comments.each do |line| result << line end # Add the import line itself result << stmt.raw_line current_section = stmt.section current_type = stmt.type end result end |