Repository: astral-sh/ruff
File: crates/ruff_linter/src/rules/pydoclint/rules/check_docstring.rs (Rust)

Code before:
        return entries;
    };
    let indentation = &dashes[..dashes.len() - dashes.trim_start().len()];

    let mut current_pos = content.full_line_end(dashes.text_len());
    for potential in lines {
        let line_start = current_pos;
        current_pos = content.full_line_end(line_start);

        if let Some(entry) = potential.strip_prefix(indentation) {
            if entry
                .chars()
                .next()
                .is_some_and(|first_char| !first_char.is_whitespace())
            {
                if let Some(before_colon) = entry.split(':').next() {
                    let param_line = before_colon.trim_end();

                    // Split on commas to handle comma-separated parameters
                    for param_part in param_line.split(',') {
                        let param_part_trimmed = param_part.trim();
                        let param_name = param_part_trimmed.trim_start_matches('*');
                        if is_identifier(param_name) {
                            // Calculate the position of this specific parameter part within the line
                            // Safety: searching for a substring we just split above
                            let param_start_in_line = param_line.find(param_part_trimmed).unwrap();
                            let param_start = line_start
                                + indentation.text_len()
                                + TextSize::try_from(param_start_in_line).unwrap();
                            let param_end = param_start + param_part_trimmed.text_len();

                            entries.push(ParameterEntry {
                                name: param_name,
                                range: TextRange::new(
                                    content_start + param_start,
                                    content_start + param_end,
                                ),
                            });
                        }
                    }
                }
            }
        }
    }
    entries
}

/// Parse the entries in a "Raises" section of a docstring.
///
/// Attempts to parse using the specified [`SectionStyle`], falling back to the other style if no
/// entries are found.

Review comment (suggestion):
I'd be inclined to keep track of the offset instead of finding the parameter aggain. 

```
let mut current_offset = TextSize::MIN;

for param_part in ... {
	...

	let param_start_in_line = current_offset + (param_part.text_len() - param_part_trimmed.text_len());

	current_offset = current_offset + param_part.text_len() + ','.text_len()
}
```

Code after:
        return entries;
    };
    let indentation = &dashes[..dashes.len() - dashes.trim_start().len()];

    let mut current_pos = content.full_line_end(dashes.text_len());
    for potential in lines {
        let line_start = current_pos;
        current_pos = content.full_line_end(line_start);

        if let Some(entry) = potential.strip_prefix(indentation) {
            if entry
                .chars()
                .next()
                .is_some_and(|first_char| !first_char.is_whitespace())
            {
                if let Some(before_colon) = entry.split(':').next() {
                    let param_line = before_colon.trim_end();

                    // Split on commas to handle comma-separated parameters
                    let mut current_offset = TextSize::from(0);
                    for param_part in param_line.split(',') {
                        let param_part_trimmed = param_part.trim();
                        let param_name = param_part_trimmed.trim_start_matches('*');
                        if is_identifier(param_name) {
                            // Calculate the position of this specific parameter part within the line
                            // Account for leading whitespace that gets trimmed
                            let param_start_in_line = current_offset
                                + (param_part.text_len() - param_part_trimmed.text_len());
                            let param_start =
                                line_start + indentation.text_len() + param_start_in_line;

                            entries.push(ParameterEntry {
                                name: param_name,
                                range: TextRange::at(
                                    content_start + param_start,
                                    param_part_trimmed.text_len(),
                                ),
                            });
                        }
                        // Update offset for next iteration: add the part length plus comma length
                        current_offset = current_offset + param_part.text_len() + ','.text_len();
                    }
                }
            }
        }
    }
    entries
}

/// Parse the entries in a "Raises" section of a docstring.
///