1#![doc = include_str!("README.md")]
2
3pub mod lsp;
4pub mod treesitter;
5
6use lsp::{
7 HierarchyClient as LspHierarchyClient, LspProvider,
8 WorkspaceSymbolClient as LspWorkspaceSymbolClient,
9};
10use treesitter::{
11 HierarchyClient as TreeSitterHierarchyClient, TreeSitterProvider,
12 WorkspaceSymbolClient as TreeSitterWorkspaceSymbolClient,
13};
14
15use crate::state::{HierarchyDirection, SymbolIdentity};
16use tower_lsp::lsp_types::{Range, SymbolKind, Url};
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct WorkspaceSymbolMatch {
20 pub name: String,
23 pub kind: SymbolKind,
24 pub container_name: Option<String>,
25 pub uri: Url,
26 pub range: Option<Range>,
27}
28
29impl WorkspaceSymbolMatch {
30 pub fn display_name(&self) -> String {
31 self.name.clone()
32 }
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum FetchSource {
37 Lsp,
38 TreeSitter,
39}
40
41#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
42pub enum CachePolicy {
43 #[default]
44 UseCache,
45 Refresh,
46}
47
48#[derive(Clone, Debug, Eq, PartialEq)]
50pub struct HierarchyQuery {
51 pub symbol: SymbolIdentity,
52 pub direction: HierarchyDirection,
53}
54
55#[derive(Clone, Debug, Eq, PartialEq)]
57pub struct HierarchyResponse {
58 pub query: HierarchyQuery,
59 pub children: Vec<SymbolIdentity>,
60 pub source: FetchSource,
61}
62
63#[derive(Clone, Debug)]
64pub enum WorkspaceSymbolClient {
65 Lsp(LspWorkspaceSymbolClient),
66 TreeSitter(TreeSitterWorkspaceSymbolClient),
67}
68
69impl WorkspaceSymbolClient {
70 pub fn set_workspace_only(&mut self, workspace_only: bool) {
71 if let Self::Lsp(client) = self {
72 client.set_workspace_only(workspace_only);
73 }
74 }
75
76 pub async fn query(&self, query: &str) -> anyhow::Result<Vec<WorkspaceSymbolMatch>> {
77 match self {
78 Self::Lsp(client) => client.query(query).await,
79 Self::TreeSitter(client) => client.query(query).await,
80 }
81 }
82}
83
84impl From<LspWorkspaceSymbolClient> for WorkspaceSymbolClient {
85 fn from(client: LspWorkspaceSymbolClient) -> Self {
86 Self::Lsp(client)
87 }
88}
89
90impl From<TreeSitterWorkspaceSymbolClient> for WorkspaceSymbolClient {
91 fn from(client: TreeSitterWorkspaceSymbolClient) -> Self {
92 Self::TreeSitter(client)
93 }
94}
95
96#[derive(Clone, Debug)]
97pub enum HierarchyClient {
98 Lsp(LspHierarchyClient),
99 TreeSitter(TreeSitterHierarchyClient),
100 Hybrid {
101 lsp: LspHierarchyClient,
102 tree_sitter: TreeSitterHierarchyClient,
103 },
104}
105
106impl HierarchyClient {
107 pub fn set_workspace_only(&mut self, workspace_only: bool) {
108 match self {
109 Self::Lsp(client) => client.set_workspace_only(workspace_only),
110 Self::TreeSitter(_) => {}
111 Self::Hybrid { lsp, .. } => lsp.set_workspace_only(workspace_only),
112 }
113 }
114
115 pub fn with_fallback(lsp: LspHierarchyClient, tree_sitter: TreeSitterHierarchyClient) -> Self {
116 Self::Hybrid { lsp, tree_sitter }
117 }
118
119 pub async fn query(&self, query: HierarchyQuery) -> anyhow::Result<HierarchyResponse> {
120 match self {
121 Self::Lsp(client) => client.query(query).await,
122 Self::TreeSitter(client) => client.query(query).await,
123 Self::Hybrid { lsp, tree_sitter } => {
124 if lsp.supports(query.symbol.kind) {
125 lsp.query(query).await
126 } else {
127 tree_sitter.query(query).await
128 }
129 }
130 }
131 }
132}
133
134impl From<LspHierarchyClient> for HierarchyClient {
135 fn from(client: LspHierarchyClient) -> Self {
136 Self::Lsp(client)
137 }
138}
139
140impl From<TreeSitterHierarchyClient> for HierarchyClient {
141 fn from(client: TreeSitterHierarchyClient) -> Self {
142 Self::TreeSitter(client)
143 }
144}
145
146#[derive(Debug, Default)]
147pub struct FetchCoordinator {
148 lsp: Option<LspProvider>,
149 tree_sitter: Option<TreeSitterProvider>,
150}
151
152impl FetchCoordinator {
153 pub fn with_lsp(lsp: LspProvider) -> Self {
154 Self {
155 lsp: Some(lsp),
156 tree_sitter: None,
157 }
158 }
159
160 pub fn with_tree_sitter(tree_sitter: TreeSitterProvider) -> Self {
161 Self {
162 lsp: None,
163 tree_sitter: Some(tree_sitter),
164 }
165 }
166
167 pub fn lsp(&self) -> Option<&LspProvider> {
168 self.lsp.as_ref()
169 }
170
171 pub fn workspace_symbol_client(&self) -> Option<WorkspaceSymbolClient> {
172 self.lsp
173 .as_ref()
174 .map(LspProvider::workspace_symbol_client)
175 .map(WorkspaceSymbolClient::from)
176 .or_else(|| {
177 self.tree_sitter
178 .as_ref()
179 .map(TreeSitterProvider::workspace_symbol_client)
180 .map(WorkspaceSymbolClient::from)
181 })
182 }
183
184 pub fn hierarchy_client(&self) -> Option<HierarchyClient> {
185 self.lsp
186 .as_ref()
187 .map(LspProvider::hierarchy_client)
188 .map(HierarchyClient::from)
189 .or_else(|| {
190 self.tree_sitter
191 .as_ref()
192 .map(TreeSitterProvider::hierarchy_client)
193 .map(HierarchyClient::from)
194 })
195 }
196
197 pub async fn workspace_symbols(
198 &self,
199 query: &str,
200 ) -> anyhow::Result<Vec<WorkspaceSymbolMatch>> {
201 self.workspace_symbol_client()
202 .ok_or_else(|| anyhow::anyhow!("no workspace-symbol provider is configured"))?
203 .query(query)
204 .await
205 }
206
207 pub async fn hierarchy(&self, query: HierarchyQuery) -> anyhow::Result<HierarchyResponse> {
208 self.hierarchy_client()
209 .ok_or_else(|| anyhow::anyhow!("no hierarchy provider is configured"))?
210 .query(query)
211 .await
212 }
213}