deps_lsp/handlers/
code_actions.rs1use crate::config::DepsConfig;
4use crate::document::{ServerState, ensure_document_loaded};
5use std::sync::Arc;
6use tokio::sync::RwLock;
7use tower_lsp_server::Client;
8use tower_lsp_server::ls_types::{CodeActionOrCommand, CodeActionParams};
9
10pub async fn handle_code_actions(
12 state: Arc<ServerState>,
13 params: CodeActionParams,
14 client: Client,
15 config: Arc<RwLock<DepsConfig>>,
16) -> Vec<CodeActionOrCommand> {
17 let uri = ¶ms.text_document.uri;
18 let position = params.range.start;
19
20 if !ensure_document_loaded(uri, Arc::clone(&state), client, config).await {
22 tracing::warn!("Could not load document for code actions: {:?}", uri);
23 return vec![];
24 }
25
26 let doc = match state.get_document(uri) {
28 Some(d) => d,
29 None => return vec![],
30 };
31
32 let ecosystem = match state.ecosystem_registry.get(doc.ecosystem_id) {
33 Some(e) => e,
34 None => return vec![],
35 };
36
37 let parse_result = match doc.parse_result() {
38 Some(p) => p,
39 None => return vec![],
40 };
41
42 let actions = ecosystem
44 .generate_code_actions(parse_result, position, &doc.cached_versions, uri)
45 .await;
46
47 actions
48 .into_iter()
49 .map(CodeActionOrCommand::CodeAction)
50 .collect()
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::document::ServerState;
57 use crate::test_utils::test_helpers::create_test_client_and_config;
58 use tower_lsp_server::ls_types::{Position, Range, TextDocumentIdentifier, Uri};
59
60 #[tokio::test]
63 async fn test_handle_code_actions_missing_document() {
64 let state = Arc::new(ServerState::new());
65 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
66
67 let params = CodeActionParams {
68 text_document: TextDocumentIdentifier { uri },
69 range: Range::new(Position::new(0, 0), Position::new(0, 0)),
70 context: Default::default(),
71 work_done_progress_params: Default::default(),
72 partial_result_params: Default::default(),
73 };
74
75 let (client, config) = create_test_client_and_config();
76 let result = handle_code_actions(state, params, client, config).await;
77 assert!(result.is_empty());
78 }
79
80 #[cfg(feature = "cargo")]
82 mod cargo_tests {
83 use super::*;
84 use crate::document::{DocumentState, Ecosystem};
85
86 #[tokio::test]
87 async fn test_handle_code_actions() {
88 let state = Arc::new(ServerState::new());
89 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
90
91 let ecosystem = state.ecosystem_registry.get("cargo").unwrap();
92 let content = r#"[dependencies]
93serde = "1.0.0"
94"#
95 .to_string();
96
97 let parse_result = ecosystem
98 .parse_manifest(&content, &uri)
99 .await
100 .expect("Failed to parse manifest");
101
102 let doc_state = DocumentState::new_from_parse_result("cargo", content, parse_result);
103 state.update_document(uri.clone(), doc_state);
104
105 let params = CodeActionParams {
106 text_document: TextDocumentIdentifier { uri },
107 range: Range::new(Position::new(1, 9), Position::new(1, 16)),
108 context: Default::default(),
109 work_done_progress_params: Default::default(),
110 partial_result_params: Default::default(),
111 };
112
113 let (client, config) = create_test_client_and_config();
114 let _result = handle_code_actions(state, params, client, config).await;
115 }
117
118 #[tokio::test]
119 async fn test_handle_code_actions_no_parse_result() {
120 let state = Arc::new(ServerState::new());
121 let uri = Uri::from_file_path("/test/Cargo.toml").unwrap();
122
123 let doc_state = DocumentState::new(Ecosystem::Cargo, String::new(), vec![]);
124 state.update_document(uri.clone(), doc_state);
125
126 let params = CodeActionParams {
127 text_document: TextDocumentIdentifier { uri },
128 range: Range::new(Position::new(0, 0), Position::new(0, 0)),
129 context: Default::default(),
130 work_done_progress_params: Default::default(),
131 partial_result_params: Default::default(),
132 };
133
134 let (client, config) = create_test_client_and_config();
135 let result = handle_code_actions(state, params, client, config).await;
136 assert!(result.is_empty());
137 }
138 }
139
140 #[cfg(feature = "npm")]
142 mod npm_tests {
143 use super::*;
144 use crate::document::DocumentState;
145
146 #[tokio::test]
147 async fn test_handle_code_actions() {
148 let state = Arc::new(ServerState::new());
149 let uri = Uri::from_file_path("/test/package.json").unwrap();
150
151 let ecosystem = state.ecosystem_registry.get("npm").unwrap();
152 let content = r#"{"dependencies": {"express": "4.0.0"}}"#.to_string();
153
154 let parse_result = ecosystem
155 .parse_manifest(&content, &uri)
156 .await
157 .expect("Failed to parse manifest");
158
159 let doc_state = DocumentState::new_from_parse_result("npm", content, parse_result);
160 state.update_document(uri.clone(), doc_state);
161
162 let params = CodeActionParams {
163 text_document: TextDocumentIdentifier { uri },
164 range: Range::new(Position::new(0, 25), Position::new(0, 32)),
165 context: Default::default(),
166 work_done_progress_params: Default::default(),
167 partial_result_params: Default::default(),
168 };
169
170 let (client, config) = create_test_client_and_config();
171 let _result = handle_code_actions(state, params, client, config).await;
172 }
174 }
175}