mz_deploy/lsp/
workspace_symbol.rs1use crate::project::compiler::cache::ProjectCache;
25use std::path::Path;
26use tower_lsp::lsp_types::{Location, Range, SymbolInformation, Url};
27
28use super::symbol_kind::object_kind_to_symbol_kind;
29
30#[allow(deprecated)] pub(super) fn workspace_symbols(
36 query: &str,
37 project_cache: &ProjectCache,
38 root: &Path,
39) -> Vec<SymbolInformation> {
40 let query_lower = query.to_lowercase();
41
42 project_cache
43 .list_objects()
44 .into_iter()
45 .filter(|summary| {
46 query_lower.is_empty() || summary.fqn.to_lowercase().contains(&query_lower)
47 })
48 .filter_map(|summary| {
49 let full_path = root.join(&summary.file_path);
50 let uri = Url::from_file_path(&full_path).ok()?;
51 let kind = object_kind_to_symbol_kind(summary.kind);
52 let container = format!("{}.{}", summary.database, summary.schema);
53
54 Some(SymbolInformation {
55 name: summary.name,
56 kind,
57 tags: None,
58 deprecated: None,
59 location: Location {
60 uri,
61 range: Range::default(),
62 },
63 container_name: Some(container),
64 })
65 })
66 .collect()
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::project::compiler::cache::ProjectCache;
73 use std::path::Path;
74
75 #[cfg_attr(miri, ignore)] #[mz_ore::test]
77 fn empty_query_returns_all_objects() {
78 let (root, cache) = build_test_project_cache();
79 let symbols = workspace_symbols("", &cache, root.path());
80 assert_eq!(symbols.len(), 2); }
82
83 #[cfg_attr(miri, ignore)] #[mz_ore::test]
85 fn query_matches_by_substring() {
86 let (root, cache) = build_test_project_cache();
87 let symbols = workspace_symbols("foo", &cache, root.path());
88 assert_eq!(symbols.len(), 1);
89 assert_eq!(symbols[0].name, "foo");
90 }
91
92 #[cfg_attr(miri, ignore)] #[mz_ore::test]
94 fn query_case_insensitive() {
95 let (root, cache) = build_test_project_cache();
96 let symbols = workspace_symbols("FOO", &cache, root.path());
97 assert_eq!(symbols.len(), 1);
98 assert_eq!(symbols[0].name, "foo");
99 }
100
101 #[cfg_attr(miri, ignore)] #[mz_ore::test]
103 fn query_matches_schema_in_fqn() {
104 let (root, cache) = build_test_project_cache();
105 let symbols = workspace_symbols("public", &cache, root.path());
106 assert_eq!(symbols.len(), 2);
108 }
109
110 #[cfg_attr(miri, ignore)] #[mz_ore::test]
112 fn no_matches_returns_empty() {
113 let (root, cache) = build_test_project_cache();
114 let symbols = workspace_symbols("nonexistent", &cache, root.path());
115 assert!(symbols.is_empty());
116 }
117
118 #[cfg_attr(miri, ignore)] #[mz_ore::test]
120 fn symbols_have_container_name() {
121 let (root, cache) = build_test_project_cache();
122 let symbols = workspace_symbols("foo", &cache, root.path());
123 assert_eq!(symbols[0].container_name.as_deref(), Some("mydb.public"));
124 }
125
126 fn build_test_project_cache() -> (tempfile::TempDir, ProjectCache) {
127 let root = tempfile::tempdir().unwrap();
128 let models = root.path().join("models/mydb/public");
129 std::fs::create_dir_all(&models).unwrap();
130 std::fs::write(models.join("foo.sql"), "CREATE VIEW foo AS SELECT 1 AS id;").unwrap();
131 std::fs::write(
132 models.join("bar.sql"),
133 "CREATE VIEW bar AS SELECT * FROM foo;",
134 )
135 .unwrap();
136 write_project_toml(root.path());
137
138 let _project = crate::project::plan_sync(
139 &crate::fs::FileSystem::new(),
140 root.path(),
141 None,
142 None,
143 &Default::default(),
144 )
145 .expect("project should compile");
146 let cache = ProjectCache::open(root.path(), "", None, &Default::default())
147 .expect("cache should open")
148 .expect("cache DB should exist");
149 (root, cache)
150 }
151
152 fn write_project_toml(root: &Path) {
153 std::fs::write(root.join("project.toml"), "[project]\nname = \"test\"\n").unwrap();
154 }
155}