use crate::common::create_test_backend;
use tower_lsp::lsp_types::*;
fn get_folding_ranges(php: &str) -> Vec<FoldingRange> {
let backend = create_test_backend();
let uri = "file:///test.php";
backend.update_ast(uri, php);
backend.handle_folding_range(php).unwrap_or_default()
}
fn has_range(ranges: &[FoldingRange], start_line: u32, end_line: u32) -> bool {
ranges
.iter()
.any(|r| r.start_line == start_line && r.end_line == end_line)
}
fn has_comment_range(ranges: &[FoldingRange], start_line: u32, end_line: u32) -> bool {
ranges.iter().any(|r| {
r.start_line == start_line
&& r.end_line == end_line
&& r.kind == Some(FoldingRangeKind::Comment)
})
}
#[test]
fn empty_file_returns_empty() {
let ranges = get_folding_ranges("<?php\n");
assert!(ranges.is_empty());
}
#[test]
fn class_body_produces_range() {
let php = r#"<?php
class Foo {
public $bar;
public $baz;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected class body range (1..4), got: {ranges:?}"
);
}
#[test]
fn method_body_produces_range() {
let php = r#"<?php
class Foo {
public function bar() {
return 1;
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 4),
"Expected method body range (2..4), got: {ranges:?}"
);
}
#[test]
fn function_body_produces_range() {
let php = r#"<?php
function hello() {
echo "hello";
echo "world";
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected function body range (1..4), got: {ranges:?}"
);
}
#[test]
fn nested_class_and_method_produce_two_ranges() {
let php = r#"<?php
class Outer {
public function inner() {
return 42;
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected class body range (1..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 4),
"Expected method body range (2..4), got: {ranges:?}"
);
}
#[test]
fn doc_comment_produces_comment_range() {
let php = r#"<?php
/**
* This is a doc comment
* spanning multiple lines.
*/
function foo() {}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_comment_range(&ranges, 1, 4),
"Expected doc comment range (1..4), got: {ranges:?}"
);
}
#[test]
fn consecutive_single_line_comments_produce_comment_range() {
let php = r#"<?php
// line one
// line two
// line three
function foo() {}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_comment_range(&ranges, 1, 3),
"Expected consecutive comment range (1..3), got: {ranges:?}"
);
}
#[test]
fn multi_line_block_comment_produces_comment_range() {
let php = r#"<?php
/* This is
a block
comment */
function foo() {}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_comment_range(&ranges, 1, 3),
"Expected block comment range (1..3), got: {ranges:?}"
);
}
#[test]
fn if_else_blocks_produce_ranges() {
let php = r#"<?php
if ($x) {
echo "a";
} else {
echo "b";
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected if block range (1..3), got: {ranges:?}"
);
assert!(
has_range(&ranges, 3, 5),
"Expected else block range (3..5), got: {ranges:?}"
);
}
#[test]
fn switch_body_produces_range() {
let php = r#"<?php
switch ($x) {
case 1:
break;
default:
break;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 6),
"Expected switch body range (1..6), got: {ranges:?}"
);
}
#[test]
fn try_catch_finally_produce_ranges() {
let php = r#"<?php
try {
foo();
} catch (Exception $e) {
bar();
} finally {
baz();
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected try block range (1..3), got: {ranges:?}"
);
assert!(
has_range(&ranges, 3, 5),
"Expected catch block range (3..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 5, 7),
"Expected finally block range (5..7), got: {ranges:?}"
);
}
#[test]
fn for_loop_body_produces_range() {
let php = r#"<?php
for ($i = 0; $i < 10; $i++) {
echo $i;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected for loop body range (1..3), got: {ranges:?}"
);
}
#[test]
fn foreach_loop_body_produces_range() {
let php = r#"<?php
foreach ($items as $item) {
echo $item;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected foreach loop body range (1..3), got: {ranges:?}"
);
}
#[test]
fn while_loop_body_produces_range() {
let php = r#"<?php
while ($x > 0) {
$x--;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected while loop body range (1..3), got: {ranges:?}"
);
}
#[test]
fn do_while_body_produces_range() {
let php = r#"<?php
do {
$x++;
} while ($x < 10);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected do-while body range (1..3), got: {ranges:?}"
);
}
#[test]
fn multi_line_array_produces_range() {
let php = r#"<?php
$arr = [
'a',
'b',
'c',
];
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected array range (1..5), got: {ranges:?}"
);
}
#[test]
fn single_line_array_no_range() {
let php = "<?php\n$arr = ['a', 'b', 'c'];\n";
let ranges = get_folding_ranges(php);
for r in &ranges {
assert!(
r.start_line != r.end_line,
"Single-line range should have been filtered: {r:?}"
);
}
}
#[test]
fn enum_body_produces_range() {
let php = r#"<?php
enum Color {
case Red;
case Green;
case Blue;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected enum body range (1..5), got: {ranges:?}"
);
}
#[test]
fn backed_enum_produces_range() {
let php = r#"<?php
enum Status: string {
case Active = 'active';
case Inactive = 'inactive';
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected backed enum body range (1..4), got: {ranges:?}"
);
}
#[test]
fn interface_body_produces_range() {
let php = r#"<?php
interface Foo {
public function bar(): void;
public function baz(): int;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected interface body range (1..4), got: {ranges:?}"
);
}
#[test]
fn trait_body_produces_range() {
let php = r#"<?php
trait Foo {
public function bar() {
return 1;
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected trait body range (1..5), got: {ranges:?}"
);
}
#[test]
fn multi_line_closure_produces_range() {
let php = r#"<?php
$fn = function ($x) {
return $x + 1;
};
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected closure body range (1..3), got: {ranges:?}"
);
}
#[test]
fn single_line_closure_no_range() {
let php = "<?php\n$fn = function ($x) { return $x + 1; };\n";
let ranges = get_folding_ranges(php);
for r in &ranges {
assert!(
r.start_line != r.end_line,
"Single-line range should have been filtered: {r:?}"
);
}
}
#[test]
fn match_expression_produces_range() {
let php = r#"<?php
$result = match ($x) {
1 => 'one',
2 => 'two',
default => 'other',
};
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected match expression range (1..5), got: {ranges:?}"
);
}
#[test]
fn multi_line_argument_list_produces_range() {
let php = r#"<?php
foo(
$a,
$b,
$c
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected argument list range (1..5), got: {ranges:?}"
);
}
#[test]
fn multi_line_parameter_list_produces_range() {
let php = r#"<?php
function foo(
int $a,
string $b,
bool $c
) {
return $a;
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected parameter list range (1..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 5, 7),
"Expected function body range (5..7), got: {ranges:?}"
);
}
#[test]
fn single_line_constructs_produce_no_range() {
let php = "<?php\nif (true) { echo 1; }\n";
let ranges = get_folding_ranges(php);
for r in &ranges {
assert!(
r.start_line != r.end_line,
"Single-line range should have been filtered: {r:?}"
);
}
}
#[test]
fn brace_delimited_namespace_produces_range() {
let php = r#"<?php
namespace App {
class Foo {
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected namespace body range (1..4), got: {ranges:?}"
);
}
#[test]
fn ranges_are_sorted_by_start_line() {
let php = r#"<?php
function a() {
return 1;
}
function b() {
return 2;
}
"#;
let ranges = get_folding_ranges(php);
for w in ranges.windows(2) {
assert!(
w[0].start_line <= w[1].start_line,
"Ranges not sorted: {:?} came before {:?}",
w[0],
w[1]
);
}
}
#[test]
fn complex_nesting_produces_all_expected_ranges() {
let php = r#"<?php
class Service {
/**
* Handle the request.
*/
public function handle() {
if ($this->check()) {
foreach ($this->items() as $item) {
try {
$item->process();
} catch (\Exception $e) {
log($e);
}
}
}
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(has_range(&ranges, 1, 16), "class body: {ranges:?}");
assert!(has_comment_range(&ranges, 2, 4), "doc comment: {ranges:?}");
assert!(has_range(&ranges, 5, 15), "method body: {ranges:?}");
assert!(has_range(&ranges, 6, 14), "if block: {ranges:?}");
assert!(has_range(&ranges, 7, 13), "foreach block: {ranges:?}");
assert!(has_range(&ranges, 8, 10), "try block: {ranges:?}");
assert!(has_range(&ranges, 10, 12), "catch block: {ranges:?}");
}
#[test]
fn anonymous_class_body_produces_range() {
let php = r#"<?php
$obj = new class {
public function foo() {
return 1;
}
};
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected anonymous class body range (1..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 4),
"Expected method body range (2..4), got: {ranges:?}"
);
}
#[test]
fn multi_line_legacy_array_produces_range() {
let php = r#"<?php
$arr = array(
'a',
'b',
'c',
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected legacy array range (1..5), got: {ranges:?}"
);
}
#[test]
fn arrow_function_spanning_multiple_lines() {
let php = r#"<?php
$fn = fn($x) =>
$x + 1;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 2),
"Expected arrow function range (1..2), got: {ranges:?}"
);
}
#[test]
fn elseif_blocks_produce_ranges() {
let php = r#"<?php
if ($x) {
echo "a";
} elseif ($y) {
echo "b";
} else {
echo "c";
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected if block range (1..3), got: {ranges:?}"
);
assert!(
has_range(&ranges, 3, 5),
"Expected elseif block range (3..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 5, 7),
"Expected else block range (5..7), got: {ranges:?}"
);
}
#[test]
fn colon_delimited_if_elseif_else() {
let php = r#"<?php
if ($x):
$a = [
1,
2,
];
elseif ($y):
$b = [
3,
4,
];
else:
$c = [
5,
6,
];
endif;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected array in if body range (2..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 7, 10),
"Expected array in elseif body range (7..10), got: {ranges:?}"
);
assert!(
has_range(&ranges, 12, 15),
"Expected array in else body range (12..15), got: {ranges:?}"
);
}
#[test]
fn colon_delimited_while() {
let php = r#"<?php
while ($x > 0):
$arr = [
1,
2,
];
endwhile;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected array in while body range (2..5), got: {ranges:?}"
);
}
#[test]
fn colon_delimited_for() {
let php = r#"<?php
for ($i = 0; $i < 10; $i++):
$arr = [
$i,
$i + 1,
];
endfor;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected array in for body range (2..5), got: {ranges:?}"
);
}
#[test]
fn colon_delimited_foreach() {
let php = r#"<?php
foreach ($items as $item):
$arr = [
$item,
$item,
];
endforeach;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected array in foreach body range (2..5), got: {ranges:?}"
);
}
#[test]
fn colon_delimited_switch() {
let php = r#"<?php
switch ($x):
case 1:
$arr = [
'one',
'two',
];
break;
default:
break;
endswitch;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 3, 6),
"Expected array in switch case range (3..6), got: {ranges:?}"
);
}
#[test]
fn declare_brace_delimited_body() {
let php = r#"<?php
declare(strict_types=1) {
echo "a";
echo "b";
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected declare block range (1..4), got: {ranges:?}"
);
}
#[test]
fn declare_colon_delimited_body() {
let php = r#"<?php
declare(strict_types=1):
$arr = [
1,
2,
];
enddeclare;
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected array in declare body range (2..5), got: {ranges:?}"
);
}
#[test]
fn constant_declaration_with_array_value() {
let php = r#"<?php
const FOO = [
1,
2,
3,
];
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected const array range (1..5), got: {ranges:?}"
);
}
#[test]
fn unset_walks_expressions() {
let php = r#"<?php
unset($arr[foo(
1,
2
)]);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected call arg list range in unset (1..4), got: {ranges:?}"
);
}
#[test]
fn echo_tag_walks_expressions() {
let php = r#"<?= [
1,
2,
] ?>"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 0, 3),
"Expected echo tag array range (0..3), got: {ranges:?}"
);
}
#[test]
fn return_with_multi_line_expression() {
let php = r#"<?php
function foo() {
return [
1,
2,
3,
];
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 7),
"Expected function body range (1..7), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 6),
"Expected return array range (2..6), got: {ranges:?}"
);
}
#[test]
fn trait_use_concrete_specification() {
let php = r#"<?php
class Foo {
use Bar, Baz {
Bar::hello as private;
Baz::world insteadof Bar;
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 6),
"Expected class body range (1..6), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 5),
"Expected trait use concrete range (2..5), got: {ranges:?}"
);
}
#[test]
fn property_hooks_produce_ranges() {
let php = r#"<?php
class Foo {
public string $name {
get {
return $this->name;
}
set {
$this->name = $value;
}
}
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 10),
"Expected class body range (1..10), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 9),
"Expected hook list range (2..9), got: {ranges:?}"
);
assert!(
has_range(&ranges, 3, 5),
"Expected get hook body range (3..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 6, 8),
"Expected set hook body range (6..8), got: {ranges:?}"
);
}
#[test]
fn method_call_multi_line_args() {
let php = r#"<?php
$obj->method(
$a,
$b,
$c
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected method call arg list range (1..5), got: {ranges:?}"
);
}
#[test]
fn null_safe_method_call_multi_line_args() {
let php = r#"<?php
$obj?->method(
$a,
$b,
$c
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected null-safe method call arg list range (1..5), got: {ranges:?}"
);
}
#[test]
fn static_method_call_multi_line_args() {
let php = r#"<?php
Foo::bar(
$a,
$b,
$c
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected static method call arg list range (1..5), got: {ranges:?}"
);
}
#[test]
fn instantiation_multi_line_args() {
let php = r#"<?php
$obj = new Foo(
$a,
$b,
$c
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected instantiation arg list range (1..5), got: {ranges:?}"
);
}
#[test]
fn anonymous_class_with_args_and_members() {
let php = r#"<?php
$obj = new class(
$a,
$b
) {
public function foo() {
return 1;
}
};
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected anonymous class arg list range (1..4), got: {ranges:?}"
);
assert!(
has_range(&ranges, 4, 8),
"Expected anonymous class body range (4..8), got: {ranges:?}"
);
assert!(
has_range(&ranges, 5, 7),
"Expected method body range (5..7), got: {ranges:?}"
);
}
#[test]
fn match_with_default_arm_expression() {
let php = r#"<?php
$result = match ($x) {
1 => 'one',
default => [
'fallback',
'value',
],
};
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 7),
"Expected match expression range (1..7), got: {ranges:?}"
);
assert!(
has_range(&ranges, 3, 6),
"Expected default arm array range (3..6), got: {ranges:?}"
);
}
#[test]
fn array_with_nested_closures() {
let php = r#"<?php
$handlers = [
'a' => function () {
return 1;
},
'b' => function () {
return 2;
},
];
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 8),
"Expected outer array range (1..8), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 4),
"Expected first closure range (2..4), got: {ranges:?}"
);
assert!(
has_range(&ranges, 5, 7),
"Expected second closure range (5..7), got: {ranges:?}"
);
}
#[test]
fn binary_expression_with_multi_line_rhs() {
let php = r#"<?php
$result = $a + foo(
1,
2
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected call arg list range in binary (1..4), got: {ranges:?}"
);
}
#[test]
fn assignment_with_closure_rhs() {
let php = r#"<?php
$fn = function () {
return true;
};
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 3),
"Expected closure body range (1..3), got: {ranges:?}"
);
}
#[test]
fn conditional_ternary_with_multi_line_expressions() {
let php = r#"<?php
$result = $condition
? [
1,
2,
]
: [
3,
4,
];
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected then-branch array range (2..5), got: {ranges:?}"
);
assert!(
has_range(&ranges, 6, 9),
"Expected else-branch array range (6..9), got: {ranges:?}"
);
}
#[test]
fn yield_value_expression() {
let php = r#"<?php
function gen() {
yield [
1,
2,
];
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 6),
"Expected generator function body range (1..6), got: {ranges:?}"
);
assert!(
has_range(&ranges, 2, 5),
"Expected yield value array range (2..5), got: {ranges:?}"
);
}
#[test]
fn yield_pair_expression() {
let php = r#"<?php
function gen() {
yield 'key' => [
3,
4,
];
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected yield pair value array range (2..5), got: {ranges:?}"
);
}
#[test]
fn yield_from_expression() {
let php = r#"<?php
function gen() {
yield from foo(
1,
2
);
}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 2, 5),
"Expected yield from call arg list range (2..5), got: {ranges:?}"
);
}
#[test]
fn throw_expression_with_instantiation() {
let php = r#"<?php
throw new Exception(
'error',
500
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected throw instantiation arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn clone_expression_walks_inner() {
let php = r#"<?php
$b = clone new Foo(
1,
2
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected clone instantiation arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn list_destructuring() {
let php = r#"<?php
list($a, $b) = [
1,
2,
];
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected list RHS array range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_print_walks_expression() {
let php = r#"<?php
print implode(",", [
1,
2,
3,
]);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected print call arg list range (1..5), got: {ranges:?}"
);
}
#[test]
fn construct_exit_walks_arguments() {
let php = r#"<?php
exit(foo(
1,
2
));
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected exit call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_die_walks_arguments() {
let php = r#"<?php
die(foo(
1,
2
));
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected die call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_isset_walks_values() {
let php = r#"<?php
$x = isset(foo(
1,
2
));
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected isset call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_empty_walks_value() {
let php = r#"<?php
$x = empty(foo(
1,
2
));
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected empty call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_eval_walks_value() {
let php = r#"<?php
eval(implode("\n", [
'$a = 1;',
'$b = 2;',
]));
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected eval call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_include_walks_value() {
let php = r#"<?php
include foo(
'a',
'b'
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected include call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_include_once_walks_value() {
let php = r#"<?php
include_once foo(
'a',
'b'
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected include_once call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_require_walks_value() {
let php = r#"<?php
require foo(
'a',
'b'
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected require call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn construct_require_once_walks_value() {
let php = r#"<?php
require_once foo(
'a',
'b'
);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected require_once call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn composite_string_walks_embedded_expressions() {
let php = r#"<?php
$str = "value: {$arr[foo(
1,
2
)]}";
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 4),
"Expected composite string call arg list range (1..4), got: {ranges:?}"
);
}
#[test]
fn pipe_expression_walks_both_sides() {
let php = r#"<?php
$result = [
1,
2,
3,
] |> array_sum(...);
"#;
let ranges = get_folding_ranges(php);
assert!(
has_range(&ranges, 1, 5),
"Expected pipe input array range (1..5), got: {ranges:?}"
);
}
#[test]
fn non_adjacent_single_line_comments_produce_separate_groups() {
let php = r#"<?php
// group one
// group one continued
// group two
// group two continued
function foo() {}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_comment_range(&ranges, 1, 2),
"Expected first comment group range (1..2), got: {ranges:?}"
);
assert!(
has_comment_range(&ranges, 4, 5),
"Expected second comment group range (4..5), got: {ranges:?}"
);
}
#[test]
fn hash_style_comments_produce_comment_range() {
let php = r#"<?php
# comment one
# comment two
# comment three
function foo() {}
"#;
let ranges = get_folding_ranges(php);
assert!(
has_comment_range(&ranges, 1, 3),
"Expected hash comment group range (1..3), got: {ranges:?}"
);
}