MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / enclosing_docblock_text

Function enclosing_docblock_text

src/diagnostics/mod.rs:531–596  ·  view source on GitHub ↗

Find the docblock text for the function/method enclosing `diag_line`. Searches backward from `diag_line` to find the nearest `function` keyword (which may be on the diagnostic line itself, e.g. on the signature, or on a preceding line when the diagnostic is inside the body or in the docblock above). Then looks for a preceding `/** ... */` block. Returns the raw docblock text (from `/**` to `*/`

(content: &str, diag_line: usize)

Source from the content-addressed store, hash-verified

529/// `/** ... */` block. Returns the raw docblock text (from `/**` to
530/// `*/` inclusive) if found, or an empty string if no docblock exists.
531fn enclosing_docblock_text(content: &str, diag_line: usize) -> String {
532 use crate::util::{contains_function_keyword, strip_trailing_modifiers};
533
534 let lines: Vec<&str> = content.lines().collect();
535 if diag_line >= lines.len() {
536 return String::new();
537 }
538
539 // Scan backward from `diag_line` looking for a line that contains
540 // the `function` keyword. This handles three cases:
541 // 1. Diagnostic inside the function body → walks up to the
542 // signature line.
543 // 2. Diagnostic on the signature line → matches immediately.
544 // 3. Diagnostic on the docblock above → walks down would be
545 // needed, but PHPStan diagnostics land on the signature or
546 // body, not the docblock lines. If we reach the docblock
547 // line we still need to find the function below it. As a
548 // pragmatic fallback we also scan forward a few lines.
549 let mut func_line: Option<usize> = None;
550 for idx in (0..=diag_line).rev() {
551 if contains_function_keyword(lines[idx]) {
552 func_line = Some(idx);
553 break;
554 }
555 }
556
557 // Fallback: if the diagnostic is on a docblock line above the
558 // function, scan forward a few lines to find the signature.
559 if func_line.is_none() {
560 let start = diag_line + 1;
561 let limit = (diag_line + 10).min(lines.len());
562 for (i, line) in lines[start..limit].iter().enumerate() {
563 if contains_function_keyword(line) {
564 func_line = Some(start + i);
565 break;
566 }
567 }
568 }
569
570 let func_line = match func_line {
571 Some(l) => l,
572 None => return String::new(),
573 };
574
575 // Compute the byte offset of the `function` keyword on that line.
576 let line_byte_start: usize = lines.iter().take(func_line).map(|l| l.len() + 1).sum();
577 let func_kw_rel = match lines[func_line].find("function") {
578 Some(p) => p,
579 None => return String::new(),
580 };
581 let func_kw_pos = line_byte_start + func_kw_rel;
582
583 // Look for a `/** ... */` block before the function keyword
584 // (skipping modifiers and whitespace).
585 let before_func = &content[..func_kw_pos];
586 let trimmed = before_func.trim_end();
587
588 let after_mods = strip_trailing_modifiers(trimmed);

Calls 6

strip_trailing_modifiersFunction · 0.85
iterMethod · 0.80
mapMethod · 0.45
takeMethod · 0.45
findMethod · 0.45