strict_hover_range_test.rs
1 use ecolog_lsp::analysis::document::DocumentManager; 2 use ecolog_lsp::analysis::query::QueryEngine; 3 use ecolog_lsp::languages::LanguageRegistry; 4 use std::sync::Arc; 5 use tower_lsp::lsp_types::{Position, Url}; 6 7 async fn setup_manager() -> DocumentManager { 8 let query_engine = Arc::new(QueryEngine::new()); 9 let mut registry = LanguageRegistry::new(); 10 registry.register(Arc::new(ecolog_lsp::languages::javascript::JavaScript)); 11 registry.register(Arc::new(ecolog_lsp::languages::typescript::TypeScript)); 12 registry.register(Arc::new(ecolog_lsp::languages::python::Python)); 13 registry.register(Arc::new(ecolog_lsp::languages::rust::Rust)); 14 registry.register(Arc::new(ecolog_lsp::languages::go::Go)); 15 let languages = Arc::new(registry); 16 DocumentManager::new(query_engine, languages.clone()) 17 } 18 19 #[tokio::test] 20 async fn test_js_hover_only_on_var_name_dot_notation() { 21 let doc_manager = setup_manager().await; 22 let uri = Url::parse("file:///test.js").unwrap(); 23 24 let content = "const a = process.env.DB_URL;"; 25 doc_manager 26 .open(uri.clone(), "javascript".into(), content.to_string(), 0) 27 .await; 28 29 let ref_on_process = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 10)); 30 assert!( 31 ref_on_process.is_none(), 32 "Hover should NOT trigger on 'process'" 33 ); 34 35 let ref_on_env = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 18)); 36 assert!(ref_on_env.is_none(), "Hover should NOT trigger on 'env'"); 37 38 let ref_on_dot = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 21)); 39 assert!(ref_on_dot.is_none(), "Hover should NOT trigger on '.'"); 40 41 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 22)); 42 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'DB_URL'"); 43 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 44 45 let ref_on_var_mid = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 25)); 46 assert!( 47 ref_on_var_mid.is_some(), 48 "Hover SHOULD trigger in middle of 'DB_URL'" 49 ); 50 } 51 52 #[tokio::test] 53 async fn test_js_hover_only_on_var_name_bracket_notation() { 54 let doc_manager = setup_manager().await; 55 let uri = Url::parse("file:///test.js").unwrap(); 56 57 let content = "const a = process.env['DB_URL'];"; 58 doc_manager 59 .open(uri.clone(), "javascript".into(), content.to_string(), 0) 60 .await; 61 62 let ref_on_bracket = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 21)); 63 assert!(ref_on_bracket.is_none(), "Hover should NOT trigger on '['"); 64 65 let ref_on_quote = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 22)); 66 assert!( 67 ref_on_quote.is_none(), 68 "Hover should NOT trigger on opening quote" 69 ); 70 71 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 24)); 72 assert!( 73 ref_on_var.is_some(), 74 "Hover SHOULD trigger inside 'DB_URL' string" 75 ); 76 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 77 } 78 79 #[tokio::test] 80 async fn test_ts_hover_only_on_var_name_process_env() { 81 let doc_manager = setup_manager().await; 82 let uri = Url::parse("file:///test.ts").unwrap(); 83 84 let content = "const a = process.env.VITE_API;"; 85 doc_manager 86 .open(uri.clone(), "typescript".into(), content.to_string(), 0) 87 .await; 88 89 let ref_on_process = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 12)); 90 assert!( 91 ref_on_process.is_none(), 92 "Hover should NOT trigger on 'process'" 93 ); 94 95 let ref_on_env = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 19)); 96 assert!(ref_on_env.is_none(), "Hover should NOT trigger on 'env'"); 97 98 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 24)); 99 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'VITE_API'"); 100 assert_eq!(ref_on_var.unwrap().name, "VITE_API"); 101 } 102 103 #[tokio::test] 104 async fn test_py_hover_only_on_var_name_environ_subscript() { 105 let doc_manager = setup_manager().await; 106 let uri = Url::parse("file:///test.py").unwrap(); 107 108 let content = "import os\nx = os.environ['DB_URL']"; 109 doc_manager 110 .open(uri.clone(), "python".into(), content.to_string(), 0) 111 .await; 112 113 let ref_on_os = doc_manager.get_env_reference_cloned(&uri, Position::new(1, 5)); 114 assert!(ref_on_os.is_none(), "Hover should NOT trigger on 'os'"); 115 116 let ref_on_environ = doc_manager.get_env_reference_cloned(&uri, Position::new(1, 9)); 117 assert!( 118 ref_on_environ.is_none(), 119 "Hover should NOT trigger on 'environ'" 120 ); 121 122 let ref_on_bracket = doc_manager.get_env_reference_cloned(&uri, Position::new(1, 14)); 123 assert!(ref_on_bracket.is_none(), "Hover should NOT trigger on '['"); 124 125 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(1, 18)); 126 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'DB_URL'"); 127 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 128 } 129 130 #[tokio::test] 131 async fn test_py_hover_only_on_var_name_getenv() { 132 let doc_manager = setup_manager().await; 133 let uri = Url::parse("file:///test.py").unwrap(); 134 135 let content = "import os\nx = os.getenv('DB_URL')"; 136 doc_manager 137 .open(uri.clone(), "python".into(), content.to_string(), 0) 138 .await; 139 140 let ref_on_getenv = doc_manager.get_env_reference_cloned(&uri, Position::new(1, 8)); 141 assert!( 142 ref_on_getenv.is_none(), 143 "Hover should NOT trigger on 'getenv'" 144 ); 145 146 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(1, 16)); 147 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'DB_URL'"); 148 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 149 } 150 151 #[tokio::test] 152 async fn test_rust_hover_only_on_var_name_std_env_var() { 153 let doc_manager = setup_manager().await; 154 let uri = Url::parse("file:///test.rs").unwrap(); 155 156 let content = r#"fn main() { std::env::var("DB_URL"); }"#; 157 doc_manager 158 .open(uri.clone(), "rust".into(), content.to_string(), 0) 159 .await; 160 161 let ref_on_std = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 13)); 162 assert!(ref_on_std.is_none(), "Hover should NOT trigger on 'std'"); 163 164 let ref_on_env = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 18)); 165 assert!(ref_on_env.is_none(), "Hover should NOT trigger on 'env'"); 166 167 let ref_on_var_fn = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 23)); 168 assert!( 169 ref_on_var_fn.is_none(), 170 "Hover should NOT trigger on 'var' function name" 171 ); 172 173 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 28)); 174 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'DB_URL'"); 175 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 176 } 177 178 #[tokio::test] 179 async fn test_rust_hover_only_on_var_name_env_macro() { 180 let doc_manager = setup_manager().await; 181 let uri = Url::parse("file:///test.rs").unwrap(); 182 183 let content = r#"fn main() { env!("DB_URL"); }"#; 184 doc_manager 185 .open(uri.clone(), "rust".into(), content.to_string(), 0) 186 .await; 187 188 let ref_on_env = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 13)); 189 assert!( 190 ref_on_env.is_none(), 191 "Hover should NOT trigger on 'env' macro name" 192 ); 193 194 let ref_on_bang = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 15)); 195 assert!(ref_on_bang.is_none(), "Hover should NOT trigger on '!'"); 196 197 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 19)); 198 assert!( 199 ref_on_var.is_some(), 200 "Hover SHOULD trigger on 'DB_URL' in env! macro" 201 ); 202 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 203 } 204 205 #[tokio::test] 206 async fn test_go_hover_only_on_var_name_getenv() { 207 let doc_manager = setup_manager().await; 208 let uri = Url::parse("file:///test.go").unwrap(); 209 210 let content = "package main\nimport \"os\"\nfunc main() {\n val := os.Getenv(\"DB_URL\")\n}"; 211 doc_manager 212 .open(uri.clone(), "go".into(), content.to_string(), 0) 213 .await; 214 215 let ref_on_os = doc_manager.get_env_reference_cloned(&uri, Position::new(3, 10)); 216 assert!(ref_on_os.is_none(), "Hover should NOT trigger on 'os'"); 217 218 let ref_on_getenv = doc_manager.get_env_reference_cloned(&uri, Position::new(3, 14)); 219 assert!( 220 ref_on_getenv.is_none(), 221 "Hover should NOT trigger on 'Getenv'" 222 ); 223 224 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(3, 22)); 225 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'DB_URL'"); 226 assert_eq!(ref_on_var.unwrap().name, "DB_URL"); 227 } 228 229 #[tokio::test] 230 async fn test_go_hover_only_on_var_name_lookupenv() { 231 let doc_manager = setup_manager().await; 232 let uri = Url::parse("file:///test.go").unwrap(); 233 234 let content = 235 "package main\nimport \"os\"\nfunc main() {\n val, ok := os.LookupEnv(\"API_KEY\")\n}"; 236 doc_manager 237 .open(uri.clone(), "go".into(), content.to_string(), 0) 238 .await; 239 240 let ref_on_lookupenv = doc_manager.get_env_reference_cloned(&uri, Position::new(3, 18)); 241 assert!( 242 ref_on_lookupenv.is_none(), 243 "Hover should NOT trigger on 'LookupEnv'" 244 ); 245 246 let ref_on_var = doc_manager.get_env_reference_cloned(&uri, Position::new(3, 28)); 247 assert!(ref_on_var.is_some(), "Hover SHOULD trigger on 'API_KEY'"); 248 assert_eq!(ref_on_var.unwrap().name, "API_KEY"); 249 } 250 251 #[tokio::test] 252 async fn test_js_no_hover_on_semicolon_after_var() { 253 let doc_manager = setup_manager().await; 254 let uri = Url::parse("file:///test.js").unwrap(); 255 256 let content = "const a = process.env.DB_URL;"; 257 doc_manager 258 .open(uri.clone(), "javascript".into(), content.to_string(), 0) 259 .await; 260 261 let ref_on_last_char = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 27)); 262 assert!( 263 ref_on_last_char.is_some(), 264 "Hover SHOULD trigger on last char of 'DB_URL'" 265 ); 266 267 let ref_on_semicolon = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 28)); 268 assert!( 269 ref_on_semicolon.is_none(), 270 "Hover should NOT trigger on ';' after variable" 271 ); 272 } 273 274 #[tokio::test] 275 async fn test_js_no_hover_on_closing_quote_bracket_notation() { 276 let doc_manager = setup_manager().await; 277 let uri = Url::parse("file:///test.js").unwrap(); 278 279 let content = "const a = process.env['DB_URL'];"; 280 doc_manager 281 .open(uri.clone(), "javascript".into(), content.to_string(), 0) 282 .await; 283 284 let ref_on_last_char = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 28)); 285 assert!( 286 ref_on_last_char.is_some(), 287 "Hover SHOULD trigger on last char of 'DB_URL'" 288 ); 289 290 let ref_on_quote = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 29)); 291 assert!( 292 ref_on_quote.is_none(), 293 "Hover should NOT trigger on closing quote" 294 ); 295 296 let ref_on_bracket = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 30)); 297 assert!(ref_on_bracket.is_none(), "Hover should NOT trigger on ']'"); 298 } 299 300 #[tokio::test] 301 async fn test_returned_range_is_name_range_not_full_range() { 302 let doc_manager = setup_manager().await; 303 let uri = Url::parse("file:///test.js").unwrap(); 304 305 let content = "const a = process.env.DB_URL;"; 306 doc_manager 307 .open(uri.clone(), "javascript".into(), content.to_string(), 0) 308 .await; 309 310 let ref_result = doc_manager.get_env_reference_cloned(&uri, Position::new(0, 24)); 311 assert!(ref_result.is_some()); 312 let reference = ref_result.unwrap(); 313 314 assert_eq!(reference.name_range.start.line, 0); 315 assert_eq!(reference.name_range.start.character, 22); 316 assert_eq!(reference.name_range.end.line, 0); 317 assert_eq!(reference.name_range.end.character, 28); 318 319 assert_eq!(reference.full_range.start.line, 0); 320 assert_eq!(reference.full_range.start.character, 10); 321 assert_eq!(reference.full_range.end.line, 0); 322 assert_eq!(reference.full_range.end.character, 28); 323 }