diff --git a/extension.toml b/extension.toml index bcddd7a..856d6ee 100644 --- a/extension.toml +++ b/extension.toml @@ -53,6 +53,11 @@ kind = "process:exec" command = "gem" args = ["install", "--norc", "--no-user-install", "--no-format-executable", "--no-document", "*"] +[[capabilities]] +kind = "process:exec" +command = "gem" +args = ["uninstall", "--norc", "*", "--version", "*"] + [[capabilities]] kind = "process:exec" command = "gem" diff --git a/src/gemset.rs b/src/gemset.rs index a302b46..0af8b35 100644 --- a/src/gemset.rs +++ b/src/gemset.rs @@ -53,6 +53,14 @@ impl Gemset { Ok(()) } + pub fn uninstall_gem(&self, name: &str, version: &str) -> Result<(), String> { + let args = &[name, "--version", version]; + self.execute_gem_command("uninstall", args) + .map_err(|e| format!("Failed to uninstall gem '{name}': {e}"))?; + + Ok(()) + } + pub fn installed_gem_version(&self, name: &str) -> Result, String> { let re = Regex::new(r"^(\S+) \((.+)\)$").map_err(|e| format!("Failed to compile regex: {e}"))?; @@ -470,4 +478,72 @@ mod tests { .unwrap_err() .contains("Gem command failed (status: 1)")); } + + #[test] + fn test_uninstall_gem_success() { + let mock_executor = MockGemCommandExecutor::new(); + let gem_name = "solargraph"; + let gem_version = "0.55.1"; + + mock_executor.expect( + "gem", + &["uninstall", "--norc", gem_name, "--version", gem_version], + &[("GEM_HOME", TEST_GEM_HOME)], + Ok(Output { + status: Some(0), + stdout: format!("Successfully uninstalled {gem_name}-{gem_version}") + .as_bytes() + .to_vec(), + stderr: Vec::new(), + }), + ); + let gemset = create_gemset(mock_executor); + assert!(gemset.uninstall_gem(gem_name, gem_version).is_ok()); + } + + #[test] + fn test_uninstall_gem_failure() { + let mock_executor = MockGemCommandExecutor::new(); + let gem_name = "solargraph"; + let gem_version = "0.55.1"; + + mock_executor.expect( + "gem", + &["uninstall", "--norc", gem_name, "--version", gem_version], + &[("GEM_HOME", TEST_GEM_HOME)], + Ok(Output { + status: Some(1), + stdout: Vec::new(), + stderr: format!("ERROR: While executing gem ... (Gem::InstallError)\n gem \"{gem_name}\" is not installed") + .as_bytes() + .to_vec(), + }), + ); + let gemset = create_gemset(mock_executor); + let result = gemset.uninstall_gem(gem_name, gem_version); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .contains("Failed to uninstall gem 'solargraph'")); + } + + #[test] + fn test_uninstall_gem_command_execution_error() { + let mock_executor = MockGemCommandExecutor::new(); + let gem_name = "solargraph"; + let gem_version = "0.55.1"; + + mock_executor.expect( + "gem", + &["uninstall", "--norc", gem_name, "--version", gem_version], + &[("GEM_HOME", TEST_GEM_HOME)], + Err("Command not found: gem".to_string()), + ); + let gemset = create_gemset(mock_executor); + let result = gemset.uninstall_gem(gem_name, gem_version); + assert!(result.is_err()); + let error_message = result.unwrap_err(); + assert!(error_message.contains("Failed to uninstall gem 'solargraph'")); + assert!(error_message.contains("Command not found: gem")); + } } diff --git a/src/language_servers/language_server.rs b/src/language_servers/language_server.rs index d784764..e39bed6 100644 --- a/src/language_servers/language_server.rs +++ b/src/language_servers/language_server.rs @@ -226,7 +226,7 @@ pub trait LanguageServer { ); match gemset.installed_gem_version(Self::GEM_NAME) { - Ok(Some(_version)) => { + Ok(Some(version)) => { if gemset .is_outdated_gem(Self::GEM_NAME) .map_err(|e| e.to_string())? @@ -239,6 +239,10 @@ pub trait LanguageServer { gemset .update_gem(Self::GEM_NAME) .map_err(|e| e.to_string())?; + + gemset + .uninstall_gem(Self::GEM_NAME, &version) + .map_err(|e| e.to_string())?; } let executable_path = gemset