Build the updated docblock text.
(
info: &FunctionWithDocblock,
content: &str,
local_classes: &[Arc<ClassInfo>],
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
function_loader: FunctionLoader<'_>,
use_
| 769 | |
| 770 | /// Build the updated docblock text. |
| 771 | fn build_updated_docblock( |
| 772 | info: &FunctionWithDocblock, |
| 773 | content: &str, |
| 774 | local_classes: &[Arc<ClassInfo>], |
| 775 | class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>, |
| 776 | function_loader: FunctionLoader<'_>, |
| 777 | use_map: &HashMap<String, String>, |
| 778 | file_namespace: &Option<String>, |
| 779 | ) -> String { |
| 780 | let indent = &info.indent; |
| 781 | |
| 782 | // Parse the existing docblock into lines, categorizing each line. |
| 783 | let mut lines = parse_docblock_lines(&info.docblock_text); |
| 784 | |
| 785 | // Remove existing @param lines. |
| 786 | lines.retain(|l| !matches!(l, DocLine::Param(_))); |
| 787 | |
| 788 | // Clean up orphaned empty lines left after removing @param lines. |
| 789 | // Remove Empty lines that directly follow Open (no summary text). |
| 790 | while lines.len() >= 2 |
| 791 | && matches!(lines[0], DocLine::Open) |
| 792 | && matches!(lines[1], DocLine::Empty) |
| 793 | && lines.get(2).is_some_and(|l| !matches!(l, DocLine::Text(_))) |
| 794 | { |
| 795 | lines.remove(1); |
| 796 | } |
| 797 | |
| 798 | // Remove @return if it's redundant (void) or contradicted. |
| 799 | let should_remove_return = should_remove_return(info); |
| 800 | let should_update_return = should_update_return(info); |
| 801 | if should_remove_return { |
| 802 | lines.retain(|l| !matches!(l, DocLine::Return(_))); |
| 803 | } |
| 804 | |
| 805 | // Find where to insert new @param lines. |
| 806 | // Prefer inserting before the first @return or @throws, or at the end |
| 807 | // before the closing `*/`. |
| 808 | let insert_pos = find_param_insert_position(&lines); |
| 809 | |
| 810 | // Build new @param entries: (type_str, name_with_prefix, description). |
| 811 | let param_entries: Vec<(String, String, String)> = info |
| 812 | .sig_params |
| 813 | .iter() |
| 814 | .filter_map(|sig| { |
| 815 | // Try to preserve the existing description for this param. |
| 816 | let existing = info.doc_params.iter().find(|dp| { |
| 817 | let n = dp.name.as_str(); |
| 818 | let n = n.strip_prefix("...").unwrap_or(n); |
| 819 | n == sig.name |
| 820 | }); |
| 821 | |
| 822 | let has_any_doc_params = !info.doc_params.is_empty(); |
| 823 | |
| 824 | let type_str = if let Some(existing) = existing { |
| 825 | // If the existing type is a refinement, keep it. |
| 826 | if let Some(native) = &sig.type_hint { |
| 827 | let native_str = native.to_string(); |
| 828 | if is_type_contradiction(&existing.type_parsed, native) { |