| 478 | } |
| 479 | |
| 480 | fn extract_docstring(state: &ExtractionState, node: TsNode<'_>) -> Option<String> { |
| 481 | // F# XML doc comments: /// text |
| 482 | let mut comments = Vec::new(); |
| 483 | let mut prev = node.prev_named_sibling(); |
| 484 | while let Some(p) = prev { |
| 485 | if p.kind() == "xml_doc" || p.kind() == "line_comment" { |
| 486 | let text = state.node_text(p); |
| 487 | if text.starts_with("///") { |
| 488 | comments.push(text.trim_start_matches("///").trim().to_string()); |
| 489 | prev = p.prev_named_sibling(); |
| 490 | } else { |
| 491 | break; |
| 492 | } |
| 493 | } else { |
| 494 | break; |
| 495 | } |
| 496 | } |
| 497 | if comments.is_empty() { |
| 498 | return None; |
| 499 | } |
| 500 | comments.reverse(); |
| 501 | Some(comments.join("\n")) |
| 502 | } |
| 503 | |
| 504 | fn first_line(state: &ExtractionState, node: TsNode<'_>) -> Option<String> { |
| 505 | let text = state.node_text(node); |