1use crate::workspace_runtime_registry::WorkspaceRuntimeRegistry;
2use omena_incremental::IncrementalCancellationRegistryV0;
3use omena_query::{
4 OmenaQueryExternalSifInputV0, OmenaQuerySourceSyntaxIndexV0 as SourceSyntaxIndex,
5 OmenaQueryStylePackageManifestV0, OmenaQueryStyleResolutionInputsV0, ParserPositionV0,
6 ParserRangeV0,
7};
8use omena_tsgo_client::TsgoWorkspaceProcessPoolV0;
9use serde::Serialize;
10use std::collections::{BTreeMap, BTreeSet};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct LspTextDocumentState {
15 pub uri: String,
16 pub workspace_folder_uri: Option<String>,
17 pub language_id: String,
18 pub version: i64,
19 pub text: String,
20 pub style_summary: Option<LspStyleDocumentSummary>,
21 #[serde(skip)]
22 pub style_candidates: Vec<LspStyleHoverCandidate>,
23 #[serde(skip)]
24 pub(crate) source_syntax_index: SourceSyntaxIndex,
25 #[serde(skip)]
26 pub source_selector_candidates: Vec<LspStyleHoverCandidate>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
30#[serde(rename_all = "camelCase")]
31pub struct LspStyleDocumentSummary {
32 pub language: &'static str,
33 pub selector_names: Vec<String>,
34 pub custom_property_decl_names: Vec<String>,
35 pub custom_property_ref_names: Vec<String>,
36 pub sass_module_use_sources: Vec<String>,
37 pub sass_module_forward_sources: Vec<String>,
38 pub diagnostic_count: usize,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
42#[serde(rename_all = "camelCase")]
43pub struct LspStyleHoverCandidatesResult {
44 pub schema_version: &'static str,
45 pub product: &'static str,
46 pub document_uri: String,
47 pub workspace_folder_uri: Option<String>,
48 pub language: Option<&'static str>,
49 pub query_position: Option<ParserPositionV0>,
50 pub candidate_count: usize,
51 pub candidates: Vec<LspStyleHoverCandidate>,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
55#[serde(rename_all = "camelCase")]
56pub struct LspStyleHoverCandidate {
57 pub kind: &'static str,
58 pub name: String,
59 pub range: ParserRangeV0,
60 pub source: &'static str,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub target_style_uri: Option<String>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub namespace: Option<String>,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
68#[serde(rename_all = "camelCase")]
69pub struct LspWorkspaceFolderState {
70 pub uri: String,
71 pub name: String,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
75#[serde(rename_all = "camelCase")]
76pub struct LspWatchedFileChangeState {
77 pub uri: String,
78 pub change_type: u64,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
82#[serde(rename_all = "camelCase")]
83pub struct LspShellStateSnapshot {
84 pub shutdown_requested: bool,
85 pub should_exit: bool,
86 pub features: LspFeatureSettings,
87 pub diagnostics: LspDiagnosticSettings,
88 pub resolution: LspResolutionSettings,
89 pub cancelled_request_count: usize,
90 pub workspace_style_index_exhausted_count: usize,
91 pub document_count: usize,
92 pub workspace_folder_count: usize,
93 pub configuration_change_count: usize,
94 pub watched_file_event_count: usize,
95 pub cached_workspace_resolution_input_count: usize,
96 pub documents: Vec<LspTextDocumentState>,
97 pub workspace_folders: Vec<LspWorkspaceFolderState>,
98 pub watched_file_changes: Vec<LspWatchedFileChangeState>,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
102#[serde(rename_all = "camelCase")]
103pub struct LspFeatureSettings {
104 pub definition: bool,
105 pub hover: bool,
106 pub completion: bool,
107 pub references: bool,
108 pub rename: bool,
109}
110
111impl Default for LspFeatureSettings {
112 fn default() -> Self {
113 Self {
114 definition: true,
115 hover: true,
116 completion: true,
117 references: true,
118 rename: true,
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
124#[serde(rename_all = "camelCase")]
125pub struct LspDiagnosticSettings {
126 pub severity: u8,
127}
128
129impl Default for LspDiagnosticSettings {
130 fn default() -> Self {
131 Self { severity: 2 }
132 }
133}
134
135#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
136#[serde(rename_all = "camelCase")]
137pub struct LspResolutionSettings {
138 pub package_manifest_paths: Vec<String>,
139 #[serde(skip)]
140 pub package_manifests: Vec<OmenaQueryStylePackageManifestV0>,
141 #[serde(skip)]
142 pub workspace_style_resolution_inputs: BTreeMap<String, OmenaQueryStyleResolutionInputsV0>,
143 #[serde(skip)]
148 pub external_sifs: Vec<OmenaQueryExternalSifInputV0>,
149}
150
151#[derive(Debug, Default)]
152pub struct LspShellState {
153 pub shutdown_requested: bool,
154 pub should_exit: bool,
155 pub(crate) features: LspFeatureSettings,
156 pub(crate) diagnostics: LspDiagnosticSettings,
157 pub(crate) resolution: LspResolutionSettings,
158 pub(crate) cancelled_request_ids: IncrementalCancellationRegistryV0,
159 pub(crate) workspace_style_index_exhausted_count: usize,
160 pub(crate) configuration_change_count: usize,
161 pub(crate) documents: BTreeMap<String, LspTextDocumentState>,
162 pub(crate) open_document_uris: BTreeSet<String>,
163 pub(crate) workspace_runtime_registry: WorkspaceRuntimeRegistry,
164 pub(crate) tsgo_workspace_process_pool: TsgoWorkspaceProcessPoolV0,
165 pub(crate) watched_file_changes: Vec<LspWatchedFileChangeState>,
166}
167
168impl LspShellState {
169 pub fn document_count(&self) -> usize {
170 self.documents.len()
171 }
172
173 pub fn workspace_folder_count(&self) -> usize {
174 self.workspace_runtime_registry.len()
175 }
176
177 pub fn document(&self, uri: &str) -> Option<&LspTextDocumentState> {
178 let storage_uri = Self::document_storage_uri(uri);
179 self.documents.get(storage_uri.as_str()).or_else(|| {
180 self.documents
181 .iter()
182 .find(|(document_uri, _)| crate::protocol::file_uri_equivalent(document_uri, uri))
183 .map(|(_, document)| document)
184 })
185 }
186
187 pub(crate) fn document_mut(&mut self, uri: &str) -> Option<&mut LspTextDocumentState> {
188 let storage_uri = Self::document_storage_uri(uri);
189 if self.documents.contains_key(storage_uri.as_str()) {
190 return self.documents.get_mut(storage_uri.as_str());
191 }
192 let equivalent_uri = self
193 .documents
194 .keys()
195 .find(|document_uri| crate::protocol::file_uri_equivalent(document_uri, uri))
196 .cloned();
197 equivalent_uri.and_then(|document_uri| self.documents.get_mut(document_uri.as_str()))
198 }
199
200 pub(crate) fn document_storage_uri(uri: &str) -> String {
201 crate::protocol::canonical_file_uri(uri).unwrap_or_else(|| uri.to_string())
202 }
203
204 pub(crate) fn insert_open_document_uri(&mut self, uri: &str) -> String {
205 let storage_uri = Self::document_storage_uri(uri);
206 self.remove_open_document_uri(uri);
207 self.open_document_uris.insert(storage_uri.clone());
208 storage_uri
209 }
210
211 pub(crate) fn remove_open_document_uri(&mut self, uri: &str) {
212 let storage_uri = Self::document_storage_uri(uri);
213 self.open_document_uris.remove(storage_uri.as_str());
214 let equivalent_uris = self
215 .open_document_uris
216 .iter()
217 .filter(|candidate| crate::protocol::file_uri_equivalent(candidate, uri))
218 .cloned()
219 .collect::<Vec<_>>();
220 for candidate in equivalent_uris {
221 self.open_document_uris.remove(candidate.as_str());
222 }
223 }
224
225 pub(crate) fn has_open_document_uri(&self, uri: &str) -> bool {
226 let storage_uri = Self::document_storage_uri(uri);
227 self.open_document_uris.contains(storage_uri.as_str())
228 || self
229 .open_document_uris
230 .iter()
231 .any(|candidate| crate::protocol::file_uri_equivalent(candidate, uri))
232 }
233
234 pub(crate) fn insert_document(&mut self, uri: &str, document: LspTextDocumentState) {
235 let storage_uri = Self::document_storage_uri(uri);
236 self.remove_document_uri(uri);
237 self.documents.insert(storage_uri, document);
238 }
239
240 pub(crate) fn remove_document_uri(&mut self, uri: &str) -> Option<LspTextDocumentState> {
241 let storage_uri = Self::document_storage_uri(uri);
242 let removed = self.documents.remove(storage_uri.as_str());
243 let equivalent_uris = self
244 .documents
245 .keys()
246 .filter(|candidate| crate::protocol::file_uri_equivalent(candidate, uri))
247 .cloned()
248 .collect::<Vec<_>>();
249 for candidate in equivalent_uris {
250 self.documents.remove(candidate.as_str());
251 }
252 removed
253 }
254
255 pub(crate) fn contains_document_uri(&self, uri: &str) -> bool {
256 self.document(uri).is_some()
257 }
258
259 pub fn workspace_folder(&self, uri: &str) -> Option<&LspWorkspaceFolderState> {
260 self.workspace_runtime_registry.get(uri)
261 }
262
263 pub fn snapshot(&self) -> LspShellStateSnapshot {
264 LspShellStateSnapshot {
265 shutdown_requested: self.shutdown_requested,
266 should_exit: self.should_exit,
267 features: self.features.clone(),
268 diagnostics: self.diagnostics.clone(),
269 resolution: self.resolution.clone(),
270 cancelled_request_count: self.cancelled_request_ids.len(),
271 workspace_style_index_exhausted_count: self.workspace_style_index_exhausted_count,
272 document_count: self.document_count(),
273 workspace_folder_count: self.workspace_folder_count(),
274 configuration_change_count: self.configuration_change_count,
275 watched_file_event_count: self.watched_file_changes.len(),
276 cached_workspace_resolution_input_count: self
277 .resolution
278 .workspace_style_resolution_inputs
279 .len(),
280 documents: self.documents.values().cloned().collect(),
281 workspace_folders: self.workspace_runtime_registry.folder_snapshots(),
282 watched_file_changes: self.watched_file_changes.clone(),
283 }
284 }
285}