@@ -723,6 +723,10 @@ pub struct LspArgs {
723723 #[ arg( long) ]
724724 pub server : Option < String > ,
725725
726+ /// Filter by programming language (e.g., python, rust, go).
727+ #[ arg( long, short = 'l' ) ]
728+ pub language : Option < String > ,
729+
726730 /// Test LSP connection for a specific file.
727731 #[ arg( long) ]
728732 pub file : Option < PathBuf > ,
@@ -807,6 +811,11 @@ async fn run_lsp(args: LspArgs) -> Result<()> {
807811 servers. retain ( |s| s. name . to_lowercase ( ) . contains ( & server_name. to_lowercase ( ) ) ) ;
808812 }
809813
814+ // Filter by language if specified
815+ if let Some ( ref lang) = args. language {
816+ servers. retain ( |s| s. language . to_lowercase ( ) . contains ( & lang. to_lowercase ( ) ) ) ;
817+ }
818+
810819 // Connection test placeholder (actual implementation would require LSP client)
811820 let connection_test = if args. server . is_some ( ) || args. file . is_some ( ) {
812821 let server = args. server . as_deref ( ) . unwrap_or ( "auto-detect" ) ;
@@ -918,6 +927,10 @@ pub struct RipgrepArgs {
918927 #[ arg( long) ]
919928 pub dir : Option < PathBuf > ,
920929
930+ /// Offer to install ripgrep if not found.
931+ #[ arg( long) ]
932+ pub install : bool ,
933+
921934 /// Output as JSON.
922935 #[ arg( long) ]
923936 pub json : bool ,
@@ -955,6 +968,123 @@ fn get_path_directories() -> Vec<PathBuf> {
955968 . unwrap_or_default ( )
956969}
957970
971+ /// Attempt to install ripgrep using the system package manager.
972+ async fn install_ripgrep ( ) -> Result < ( ) > {
973+ #[ cfg( target_os = "macos" ) ]
974+ {
975+ // Try Homebrew first
976+ let output = tokio:: process:: Command :: new ( "brew" )
977+ . args ( [ "install" , "ripgrep" ] )
978+ . status ( )
979+ . await ;
980+
981+ match output {
982+ Ok ( status) if status. success ( ) => {
983+ println ! ( "ripgrep installed successfully via Homebrew!" ) ;
984+ return Ok ( ( ) ) ;
985+ }
986+ _ => {
987+ bail ! ( "Failed to install via Homebrew. Is Homebrew installed?" ) ;
988+ }
989+ }
990+ }
991+
992+ #[ cfg( target_os = "linux" ) ]
993+ {
994+ // Try apt first (Debian/Ubuntu)
995+ if tokio:: process:: Command :: new ( "which" )
996+ . arg ( "apt" )
997+ . output ( )
998+ . await
999+ . map ( |o| o. status . success ( ) )
1000+ . unwrap_or ( false )
1001+ {
1002+ let output = tokio:: process:: Command :: new ( "sudo" )
1003+ . args ( [ "apt" , "install" , "-y" , "ripgrep" ] )
1004+ . status ( )
1005+ . await ;
1006+
1007+ match output {
1008+ Ok ( status) if status. success ( ) => {
1009+ println ! ( "ripgrep installed successfully via apt!" ) ;
1010+ return Ok ( ( ) ) ;
1011+ }
1012+ _ => { }
1013+ }
1014+ }
1015+
1016+ // Try dnf (Fedora/RHEL)
1017+ if tokio:: process:: Command :: new ( "which" )
1018+ . arg ( "dnf" )
1019+ . output ( )
1020+ . await
1021+ . map ( |o| o. status . success ( ) )
1022+ . unwrap_or ( false )
1023+ {
1024+ let output = tokio:: process:: Command :: new ( "sudo" )
1025+ . args ( [ "dnf" , "install" , "-y" , "ripgrep" ] )
1026+ . status ( )
1027+ . await ;
1028+
1029+ match output {
1030+ Ok ( status) if status. success ( ) => {
1031+ println ! ( "ripgrep installed successfully via dnf!" ) ;
1032+ return Ok ( ( ) ) ;
1033+ }
1034+ _ => { }
1035+ }
1036+ }
1037+
1038+ // Try pacman (Arch)
1039+ if tokio:: process:: Command :: new ( "which" )
1040+ . arg ( "pacman" )
1041+ . output ( )
1042+ . await
1043+ . map ( |o| o. status . success ( ) )
1044+ . unwrap_or ( false )
1045+ {
1046+ let output = tokio:: process:: Command :: new ( "sudo" )
1047+ . args ( [ "pacman" , "-S" , "--noconfirm" , "ripgrep" ] )
1048+ . status ( )
1049+ . await ;
1050+
1051+ match output {
1052+ Ok ( status) if status. success ( ) => {
1053+ println ! ( "ripgrep installed successfully via pacman!" ) ;
1054+ return Ok ( ( ) ) ;
1055+ }
1056+ _ => { }
1057+ }
1058+ }
1059+
1060+ bail ! ( "Could not detect a supported package manager (apt, dnf, pacman)" ) ;
1061+ }
1062+
1063+ #[ cfg( target_os = "windows" ) ]
1064+ {
1065+ // Try winget
1066+ let output = tokio:: process:: Command :: new ( "winget" )
1067+ . args ( [ "install" , "-e" , "--id" , "BurntSushi.ripgrep.MSVC" ] )
1068+ . status ( )
1069+ . await ;
1070+
1071+ match output {
1072+ Ok ( status) if status. success ( ) => {
1073+ println ! ( "ripgrep installed successfully via winget!" ) ;
1074+ return Ok ( ( ) ) ;
1075+ }
1076+ _ => {
1077+ bail ! ( "Failed to install via winget. Is winget available?" ) ;
1078+ }
1079+ }
1080+ }
1081+
1082+ #[ cfg( not( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ) ]
1083+ {
1084+ bail ! ( "Automatic installation not supported on this platform" ) ;
1085+ }
1086+ }
1087+
9581088async fn run_ripgrep ( args : RipgrepArgs ) -> Result < ( ) > {
9591089 let ( available, path, version) = check_command_installed ( "rg" ) . await ;
9601090
@@ -1076,6 +1206,16 @@ async fn run_ripgrep(args: RipgrepArgs) -> Result<()> {
10761206 println ! ( " macOS: brew install ripgrep" ) ;
10771207 println ! ( " Linux: apt install ripgrep / dnf install ripgrep" ) ;
10781208 println ! ( " Windows: winget install BurntSushi.ripgrep.MSVC" ) ;
1209+
1210+ // Offer to install if --install flag was provided
1211+ if args. install {
1212+ println ! ( ) ;
1213+ println ! ( "Attempting to install ripgrep..." ) ;
1214+ if let Err ( e) = install_ripgrep ( ) . await {
1215+ eprintln ! ( "Failed to install ripgrep: {e}" ) ;
1216+ eprintln ! ( "Please install manually using the commands above." ) ;
1217+ }
1218+ }
10791219 }
10801220 }
10811221
0 commit comments