@@ -47,6 +47,7 @@ use std::time::Duration;
4747use tower_lsp:: lsp_types:: { Diagnostic , DiagnosticSeverity , NumberOrString , Position , Range } ;
4848
4949use crate :: config:: PhpcsConfig ;
50+ use crate :: process:: paths_match;
5051
5152/// Default PHPCS timeout in milliseconds (30 seconds).
5253const DEFAULT_TIMEOUT_MS : u64 = 30_000 ;
@@ -80,59 +81,13 @@ pub(crate) fn resolve_phpcs(
8081 Some ( cmd) => Some ( ResolvedPhpcs {
8182 path : PathBuf :: from ( cmd) ,
8283 } ) ,
83- // Auto-detect.
84- None => auto_detect ( workspace_root, bin_dir) ,
84+ // Auto-detect: `<bin_dir>/phpcs` under the workspace root,
85+ // then `$PATH`.
86+ None => crate :: process:: auto_detect_binary ( workspace_root, bin_dir, "phpcs" )
87+ . map ( |path| ResolvedPhpcs { path } ) ,
8588 }
8689}
8790
88- /// Auto-detect PHPCS by checking `<bin_dir>/phpcs` then `$PATH`.
89- fn auto_detect ( workspace_root : Option < & Path > , bin_dir : Option < & str > ) -> Option < ResolvedPhpcs > {
90- // Check the Composer bin directory first.
91- if let Some ( root) = workspace_root {
92- let bin = bin_dir. unwrap_or ( "vendor/bin" ) ;
93- let candidate = root. join ( bin) . join ( "phpcs" ) ;
94- if candidate. is_file ( ) {
95- return Some ( ResolvedPhpcs { path : candidate } ) ;
96- }
97- }
98-
99- // Fall back to $PATH.
100- if let Ok ( path) = which ( "phpcs" ) {
101- return Some ( ResolvedPhpcs { path } ) ;
102- }
103-
104- None
105- }
106-
107- /// Simple `which`-like lookup: search `$PATH` for an executable with
108- /// the given name.
109- fn which ( binary_name : & str ) -> Result < PathBuf , String > {
110- let path_var = std:: env:: var ( "PATH" ) . map_err ( |_| "PATH not set" . to_string ( ) ) ?;
111-
112- for dir in std:: env:: split_paths ( & path_var) {
113- let candidate = dir. join ( binary_name) ;
114- if candidate. is_file ( ) && is_executable ( & candidate) {
115- return Ok ( candidate) ;
116- }
117- }
118-
119- Err ( format ! ( "{} not found on PATH" , binary_name) )
120- }
121-
122- /// Check whether a file is executable.
123- #[ cfg( unix) ]
124- fn is_executable ( path : & Path ) -> bool {
125- use std:: os:: unix:: fs:: PermissionsExt ;
126- std:: fs:: metadata ( path)
127- . map ( |m| m. permissions ( ) . mode ( ) & 0o111 != 0 )
128- . unwrap_or ( false )
129- }
130-
131- #[ cfg( not( unix) ) ]
132- fn is_executable ( _path : & Path ) -> bool {
133- true
134- }
135-
13691// ── PHPCS execution ─────────────────────────────────────────────────
13792
13893/// Run PHPCS on the given buffer content and return LSP diagnostics.
@@ -438,27 +393,6 @@ fn parse_phpcs_message(msg: &serde_json::Value) -> Option<Diagnostic> {
438393 } )
439394}
440395
441- /// Check whether two file paths refer to the same file.
442- ///
443- /// PHPCS may use the `--stdin-path` value as the key. We compare by
444- /// checking suffix matches (one path ends with the other) to handle
445- /// cases where one path is relative and the other is absolute, or
446- /// where symlinks produce different prefixes.
447- fn paths_match ( a : & str , b : & str ) -> bool {
448- if a == b {
449- return true ;
450- }
451- // Normalize separators for comparison.
452- let a_norm = a. replace ( '\\' , "/" ) ;
453- let b_norm = b. replace ( '\\' , "/" ) ;
454- if a_norm == b_norm {
455- return true ;
456- }
457- // Check suffix match (one is a suffix of the other), requiring a
458- // path separator boundary so that e.g. "AFoo.php" does not match "Foo.php".
459- a_norm. ends_with ( & format ! ( "/{}" , b_norm) ) || b_norm. ends_with ( & format ! ( "/{}" , a_norm) )
460- }
461-
462396// ── Tests ───────────────────────────────────────────────────────────
463397
464398#[ cfg( test) ]
@@ -491,29 +425,6 @@ mod tests {
491425 assert_eq ! ( map[ Path :: new( "/proj/src/B.php" ) ] [ 0 ] . range. start. line, 7 ) ;
492426 }
493427
494- // ── paths_match ─────────────────────────────────────────────────
495-
496- #[ test]
497- fn paths_match_identical ( ) {
498- assert ! ( paths_match(
499- "/home/user/project/src/Foo.php" ,
500- "/home/user/project/src/Foo.php"
501- ) ) ;
502- }
503-
504- #[ test]
505- fn paths_match_suffix ( ) {
506- assert ! ( paths_match( "/home/user/project/src/Foo.php" , "src/Foo.php" ) ) ;
507- }
508-
509- #[ test]
510- fn paths_match_different_files ( ) {
511- assert ! ( !paths_match(
512- "/home/user/project/src/Foo.php" ,
513- "src/Bar.php"
514- ) ) ;
515- }
516-
517428 // ── parse_phpcs_json ────────────────────────────────────────────
518429
519430 #[ test]
0 commit comments