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

Function detect_docblock_trigger

src/completion/phpdoc/generation.rs:394–488  ·  view source on GitHub ↗

Check if the cursor is immediately after `/**` with only whitespace before it on the line, and that there is no existing docblock (i.e. the `/**` is not already closed with `*/`). Returns the range covering the `/**` text (to be replaced by the snippet) and the leading indentation string.

(content: &str, position: Position)

Source from the content-addressed store, hash-verified

392/// Returns the range covering the `/**` text (to be replaced by the
393/// snippet) and the leading indentation string.
394fn detect_docblock_trigger(content: &str, position: Position) -> Option<(Range, String)> {
395 let lines: Vec<&str> = content.lines().collect();
396 let line_idx = position.line as usize;
397 if line_idx >= lines.len() {
398 return None;
399 }
400
401 let line = lines[line_idx];
402
403 // Convert the UTF-16 column offset to a byte offset within the line.
404 // LSP positions use UTF-16 code units, which diverge from byte offsets
405 // when the line contains multibyte characters (e.g. "ń" is 2 bytes in
406 // UTF-8 but 1 UTF-16 code unit).
407 let col = utf16_col_to_byte_offset(line, position.character);
408
409 // The cursor column must be at least 3 (for `/**`).
410 if col < 3 {
411 return None;
412 }
413
414 // Get the text up to the cursor on this line.
415 let before_cursor = if col <= line.len() {
416 &line[..col]
417 } else {
418 line
419 };
420
421 // Must end with `/**`.
422 if !before_cursor.ends_with("/**") {
423 return None;
424 }
425
426 // Everything before `/**` must be whitespace.
427 let prefix = &before_cursor[..before_cursor.len() - 3];
428 if !prefix.chars().all(|c| c == ' ' || c == '\t') {
429 return None;
430 }
431
432 // Check what follows the `/**` on this line.
433 let after_trigger = if col <= line.len() { &line[col..] } else { "" };
434
435 // Editors like VS Code auto-close `/**` into `/** */` on the same
436 // line. We allow this when the only thing after `/**` is optional
437 // whitespace and `*/` (i.e. an empty auto-closed block).
438 let after_trimmed = after_trigger.trim();
439 let auto_closed = after_trimmed == "*/" || after_trimmed.is_empty();
440
441 // If there is a `*/` with real content between `/**` and `*/`
442 // (e.g. `/** @var int */`), this is an existing single-line
443 // docblock — don't trigger.
444 if !auto_closed && after_trigger.contains("*/") {
445 return None;
446 }
447
448 // Also check that the next few lines don't form an existing
449 // docblock (i.e. don't generate a new block inside an existing one).
450 // A simple heuristic: if the next non-empty line starts with `*` or
451 // contains `*/`, there's already a docblock.

Calls 6

utf16_col_to_byte_offsetFunction · 0.85
byte_offset_to_utf16_colFunction · 0.85
containsMethod · 0.80
iterMethod · 0.80
allMethod · 0.45
is_emptyMethod · 0.45