Repository: microsoft/edit
File: src/buffer/mod.rs (Rust)

Code before:
            return Cow::Borrowed(replacement);
        };

        if !search.options.use_regex || !replacement.contains('$') {
            return Cow::Borrowed(replacement);
        }

        let scratch = scratch_arena(None);
        let mut result = String::with_capacity(replacement.len());
        let mut chars = replacement.chars().peekable();

        while let Some(ch) = chars.next() {
            match ch {
                '$' => {
                    let mut digits = ArenaString::new_in(&scratch);

                    while let Some(&next_ch) = chars.peek() {
                        if digits.is_empty() && next_ch == '$' {
                            // Consume the escaped dollar sign.
                            chars.next();
                            break;
                        }

                        if !next_ch.is_ascii_digit() {
                            break;
                        }

                        digits.push(next_ch);
                        chars.next();
                    }

                    if !digits.is_empty() {
                        if let Ok(group_num) = digits.parse::<i32>() {
                            if let Some(range) = search.regex.get_captured_group_range(group_num) {
                                let mut out = Vec::new();
                                self.buffer.extract_raw(range.start, range.end, &mut out, 0);
                                result.push_str(&String::from_utf8_lossy(&out));
                            }
                        }
                    } else {
                        result.push(ch);
                    }
                }
                _ => result.push(ch),
            }
        }

        Cow::Owned(result)
    }
}


Review comment (suggestion):
One feedback I can give early is that this is not quite correct, I believe. Try something like "$12345" in VS Code for instance. It'll only recognize "$1" as replacement but then use "2345" as a literal string.

I believe this requires us to recognize here how many capture groups we have and then replace those. This may be outside of the scope of what you have time for to implement (probably requires adding new ICU FFIs!), so please feel free to say so and we'll merge it as is for now.

Code after:
        let cursor_before = self.cursor;
        self.set_cursor_internal(cursor);

        // If both the last and this are a Write/Delete operation, we skip allocating a new undo history item.
        if history_type != self.last_history_type
            || !matches!(history_type, HistoryType::Write | HistoryType::Delete)
        {
            self.redo_stack.clear();
            while self.undo_stack.len() > 1000 {
                self.undo_stack.pop_front();
            }

            self.last_history_type = history_type;
            self.undo_stack.push_back(SemiRefCell::new(HistoryEntry {
                cursor_before: cursor_before.logical_pos,
                selection_before: self.selection,
                stats_before: self.stats,
                generation_before: self.buffer.generation(),
                cursor: cursor.logical_pos,
                deleted: Vec::new(),
                added: Vec::new(),
            }));
        }

        self.active_edit_off = cursor.offset;

        // If word-wrap is enabled, the visual layout of all logical lines affected by the write
        // may have changed. This includes even text before the insertion point up to the line
        // start, because this write may have joined with a word before the initial cursor.
        // See other uses of `word_wrap_cursor_next_line` in this function.
        if self.word_wrap_column > 0 {
            let safe_start = self.goto_line_start(cursor, cursor.logical_pos.y);
            let next_line = self.cursor_move_to_logical_internal(
                cursor,
                Point { x: 0, y: cursor.logical_pos.y + 1 },
            );
            self.active_edit_line_info = Some(ActiveEditLineInfo {
                safe_start,
                line_height_in_rows: next_line.visual_pos.y - safe_start.visual_pos.y,
                distance_next_line_start: next_line.offset - cursor.offset,
            });
        }
    }

    /// Writes `text` into the buffer at the current cursor position.
    /// It records the change in the undo stack.
    fn edit_write(&mut self, text: &[u8]) {
        let logical_y_before = self.cursor.logical_pos.y;

        // Copy the written portion into the undo entry.
        {