|
|
@@ -9,8 +9,8 @@ RectangleManager::RectangleManager(EditorCore& core) : core_(core) {
|
|
|
}
|
|
|
|
|
|
void RectangleManager::kill_rectangle() {
|
|
|
- auto& buf = core_.buffer(); // Delegated access
|
|
|
- Position cursor = core_.active_window()->cursor(); // Delegated access
|
|
|
+ auto& buf = core_.buffer();
|
|
|
+ Position cursor = core_.active_window()->cursor();
|
|
|
|
|
|
auto region = buf.get_region(cursor);
|
|
|
if (!region) {
|
|
|
@@ -18,49 +18,57 @@ void RectangleManager::kill_rectangle() {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // Ensure start is top-left, end is bottom-right
|
|
|
- Position start = region->start;
|
|
|
- Position end = region->end;
|
|
|
+ Position start_pos = region->start;
|
|
|
+ Position end_pos = region->end;
|
|
|
|
|
|
- if (start.line > end.line || (start.line == end.line && start.column > end.column)) {
|
|
|
- std::swap(start, end);
|
|
|
+ if (start_pos.line > end_pos.line || (start_pos.line == end_pos.line && start_pos.column > end_pos.column)) {
|
|
|
+ std::swap(start_pos, end_pos);
|
|
|
}
|
|
|
|
|
|
+ // Determine the rectangular column range (consistent across all lines)
|
|
|
+ size_t min_col = std::min(start_pos.column, end_pos.column);
|
|
|
+ size_t max_col = std::max(start_pos.column, end_pos.column);
|
|
|
+ size_t rect_width = max_col - min_col;
|
|
|
+
|
|
|
rectangle_kill_ring_.clear();
|
|
|
|
|
|
// Extract rectangle line by line
|
|
|
- for (size_t line = start.line; line <= end.line; ++line) {
|
|
|
- if (line >= buf.line_count()) break;
|
|
|
-
|
|
|
- std::string line_text = buf.line(line);
|
|
|
- size_t start_col = (line == start.line) ? start.column : std::min(start.column, end.column);
|
|
|
- size_t end_col = (line == end.line) ? end.column : std::max(start.column, end.column);
|
|
|
+ for (size_t line_idx = start_pos.line; line_idx <= end_pos.line; ++line_idx) {
|
|
|
+ if (line_idx >= buf.line_count()) {
|
|
|
+ rectangle_kill_ring_.push_back(std::string(rect_width, ' ')); // Extract spaces for lines out of buffer bounds
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- // Ensure we don't go beyond the line length
|
|
|
- start_col = std::min(start_col, line_text.size());
|
|
|
- end_col = std::min(end_col, line_text.size());
|
|
|
+ std::string line_text = buf.line(line_idx);
|
|
|
|
|
|
- if (start_col < end_col) {
|
|
|
- rectangle_kill_ring_.push_back(line_text.substr(start_col, end_col - start_col));
|
|
|
+ // Build the substring to extract, padding with spaces if necessary
|
|
|
+ std::string extracted_text;
|
|
|
+ if (min_col >= line_text.size()) {
|
|
|
+ // Entirely outside the line, fill with spaces
|
|
|
+ extracted_text = std::string(rect_width, ' ');
|
|
|
} else {
|
|
|
- rectangle_kill_ring_.push_back("");
|
|
|
+ // Part of or entirely within the line
|
|
|
+ size_t actual_extract_len = std::min(rect_width, line_text.size() - min_col);
|
|
|
+ extracted_text = line_text.substr(min_col, actual_extract_len);
|
|
|
+ if (extracted_text.length() < rect_width) {
|
|
|
+ // Pad with spaces if the extracted part is shorter than rect_width
|
|
|
+ extracted_text += std::string(rect_width - extracted_text.length(), ' ');
|
|
|
+ }
|
|
|
}
|
|
|
+ rectangle_kill_ring_.push_back(extracted_text);
|
|
|
}
|
|
|
|
|
|
// Now delete the rectangle content (from bottom to top to preserve line indices)
|
|
|
- for (int line = static_cast<int>(end.line); line >= static_cast<int>(start.line); --line) {
|
|
|
- if (line >= static_cast<int>(buf.line_count())) continue;
|
|
|
-
|
|
|
- std::string line_text = buf.line(line);
|
|
|
- size_t start_col = (line == static_cast<int>(start.line)) ? start.column : std::min(start.column, end.column);
|
|
|
- size_t end_col = (line == static_cast<int>(end.line)) ? end.column : std::max(start.column, end.column);
|
|
|
+ for (int line_idx = static_cast<int>(end_pos.line); line_idx >= static_cast<int>(start_pos.line); --line_idx) {
|
|
|
+ if (line_idx >= static_cast<int>(buf.line_count())) continue; // Line might have been deleted/shortened
|
|
|
|
|
|
- start_col = std::min(start_col, line_text.size());
|
|
|
- end_col = std::min(end_col, line_text.size());
|
|
|
+ std::string current_line_text = buf.line(line_idx);
|
|
|
|
|
|
- if (start_col < end_col) {
|
|
|
- Range del_range{Position(line, start_col), Position(line, end_col)};
|
|
|
- buf.erase(del_range);
|
|
|
+ if (min_col < current_line_text.size()) { // Only delete if min_col is within current line bounds
|
|
|
+ size_t actual_max_col = std::min(max_col, current_line_text.size());
|
|
|
+ if (min_col < actual_max_col) {
|
|
|
+ buf.erase(Range{Position(line_idx, min_col), Position(line_idx, actual_max_col)});
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -76,25 +84,48 @@ void RectangleManager::yank_rectangle() {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- auto& buf = core_.buffer(); // Delegated access
|
|
|
- Position cursor = core_.active_window()->cursor(); // Delegated access
|
|
|
+ auto& buf = core_.buffer();
|
|
|
+ Position cursor = core_.active_window()->cursor();
|
|
|
|
|
|
+ // Determine the width of the rectangle from the first line of the killed content
|
|
|
+ size_t rect_width = 0;
|
|
|
+ if (!rectangle_kill_ring_.empty()) {
|
|
|
+ rect_width = rectangle_kill_ring_[0].length();
|
|
|
+ }
|
|
|
+
|
|
|
// Insert rectangle starting at cursor position
|
|
|
for (size_t i = 0; i < rectangle_kill_ring_.size(); ++i) {
|
|
|
Position insert_pos{cursor.line + i, cursor.column};
|
|
|
|
|
|
- // Ensure we have enough lines
|
|
|
+ // Ensure we have enough lines in the buffer
|
|
|
while (buf.line_count() <= insert_pos.line) {
|
|
|
+ // Insert new empty lines at the end of the buffer until insert_pos.line is valid
|
|
|
buf.insert_newline(Position{buf.line_count() - 1, buf.line(buf.line_count() - 1).size()});
|
|
|
}
|
|
|
|
|
|
- // Pad line with spaces if necessary
|
|
|
- std::string current_line = buf.line(insert_pos.line);
|
|
|
- if (current_line.size() < insert_pos.column) {
|
|
|
- std::string padding(insert_pos.column - current_line.size(), ' ');
|
|
|
- buf.insert(Position{insert_pos.line, current_line.size()}, padding);
|
|
|
+ // Get the current line content where we want to insert/replace
|
|
|
+ std::string current_line_text = buf.line(insert_pos.line);
|
|
|
+
|
|
|
+ // Pad line with spaces if the insertion point is beyond current line length
|
|
|
+ if (current_line_text.size() < insert_pos.column) {
|
|
|
+ std::string padding(insert_pos.column - current_line_text.size(), ' ');
|
|
|
+ buf.insert(Position{insert_pos.line, current_line_text.size()}, padding);
|
|
|
+ current_line_text = buf.line(insert_pos.line); // Refresh line after padding
|
|
|
+ }
|
|
|
+
|
|
|
+ // Calculate the range to replace or insert
|
|
|
+ // size_t effective_end_col = insert_pos.column + rect_width; // Removed unused variable
|
|
|
+ size_t existing_text_len_in_rect = 0;
|
|
|
+ if (current_line_text.size() > insert_pos.column) {
|
|
|
+ existing_text_len_in_rect = std::min(rect_width, current_line_text.size() - insert_pos.column);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Erase existing text within the rectangular region if any
|
|
|
+ if (existing_text_len_in_rect > 0) {
|
|
|
+ buf.erase(Range{insert_pos, Position{insert_pos.line, insert_pos.column + existing_text_len_in_rect}});
|
|
|
}
|
|
|
|
|
|
+ // Insert the killed rectangle content for the current line
|
|
|
buf.insert(insert_pos, rectangle_kill_ring_[i]);
|
|
|
}
|
|
|
|
|
|
@@ -112,41 +143,60 @@ void RectangleManager::string_rectangle(const std::string& text) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- Position start = region->start;
|
|
|
- Position end = region->end;
|
|
|
+ Position start_pos = region->start;
|
|
|
+ Position end_pos = region->end;
|
|
|
|
|
|
- if (start.line > end.line || (start.line == end.line && start.column > end.column)) {
|
|
|
- std::swap(start, end);
|
|
|
+ if (start_pos.line > end_pos.line || (start_pos.line == end_pos.line && start_pos.column > end_pos.column)) {
|
|
|
+ std::swap(start_pos, end_pos);
|
|
|
}
|
|
|
|
|
|
+ // Determine the rectangular column range (consistent across all lines)
|
|
|
+ size_t min_col = std::min(start_pos.column, end_pos.column);
|
|
|
+ size_t max_col = std::max(start_pos.column, end_pos.column);
|
|
|
+ size_t rect_width = max_col - min_col;
|
|
|
+
|
|
|
+ if (rect_width == 0) { // No width to fill
|
|
|
+ core_.set_message("Rectangle has zero width");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Prepare fill text
|
|
|
+ std::string fill_text_pattern = text.empty() ? " " : text; // Default to space if empty
|
|
|
+ std::string repeated_fill_text;
|
|
|
+ while (repeated_fill_text.length() < rect_width) {
|
|
|
+ repeated_fill_text += fill_text_pattern;
|
|
|
+ }
|
|
|
+ repeated_fill_text = repeated_fill_text.substr(0, rect_width);
|
|
|
+
|
|
|
// Fill rectangle with the given text
|
|
|
- for (size_t line = start.line; line <= end.line; ++line) {
|
|
|
- if (line >= buf.line_count()) break;
|
|
|
+ for (size_t line_idx = start_pos.line; line_idx <= end_pos.line; ++line_idx) {
|
|
|
+ if (line_idx >= buf.line_count()) {
|
|
|
+ // Lines out of buffer bounds are implicitly "empty" and don't get filled unless
|
|
|
+ // the buffer is extended. For simplicity, we only operate on existing lines.
|
|
|
+ // A more complete implementation would insert new lines.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- std::string line_text = buf.line(line);
|
|
|
- size_t start_col = std::min(start.column, end.column);
|
|
|
- size_t end_col = std::max(start.column, end.column);
|
|
|
+ std::string current_line_text = buf.line(line_idx);
|
|
|
|
|
|
- // Pad line if necessary
|
|
|
- if (line_text.size() < end_col) {
|
|
|
- std::string padding(end_col - line_text.size(), ' ');
|
|
|
- buf.insert(Position{line, line_text.size()}, padding);
|
|
|
- line_text = buf.line(line); // Refresh
|
|
|
+ // Pad line with spaces if min_col extends beyond current line length
|
|
|
+ if (current_line_text.size() < min_col) {
|
|
|
+ std::string padding(min_col - current_line_text.size(), ' ');
|
|
|
+ buf.insert(Position{line_idx, current_line_text.size()}, padding);
|
|
|
+ current_line_text = buf.line(line_idx); // Refresh line after padding
|
|
|
}
|
|
|
|
|
|
- // Replace rectangle content with text
|
|
|
- if (start_col < line_text.size()) {
|
|
|
- end_col = std::min(end_col, line_text.size());
|
|
|
- if (start_col < end_col) {
|
|
|
- Range replace_range{Position(line, start_col), Position(line, end_col)};
|
|
|
- std::string fill_text = text;
|
|
|
- if (fill_text.size() > end_col - start_col) {
|
|
|
- fill_text = fill_text.substr(0, end_col - start_col);
|
|
|
- } else if (fill_text.size() < end_col - start_col) {
|
|
|
- fill_text += std::string(end_col - start_col - fill_text.size(), ' ');
|
|
|
- }
|
|
|
- buf.replace(replace_range, fill_text);
|
|
|
- }
|
|
|
+ // Determine the actual range to replace within the line
|
|
|
+ size_t effective_replace_end_col = std::min(max_col, current_line_text.size());
|
|
|
+
|
|
|
+ // Perform replacement
|
|
|
+ if (min_col < effective_replace_end_col) {
|
|
|
+ buf.replace(Range{Position(line_idx, min_col), Position(line_idx, effective_replace_end_col)},
|
|
|
+ repeated_fill_text.substr(0, effective_replace_end_col - min_col));
|
|
|
+ } else if (min_col >= effective_replace_end_col) {
|
|
|
+ // If the rectangle starts at or beyond the current line's length,
|
|
|
+ // we insert the fill text (with padding if necessary)
|
|
|
+ buf.insert(Position(line_idx, min_col), repeated_fill_text);
|
|
|
}
|
|
|
}
|
|
|
|