1use crate::diagnostics_scheduler::{
2 RustDiagnosticsSchedulerBoundaryV0, rust_diagnostics_scheduler_contract,
3};
4use crate::disk_cache::{DiskDiagnosticsCacheBoundaryV0, disk_diagnostics_cache_contract};
5use crate::query_reuse::{RustQueryReuseBoundaryV0, rust_query_reuse_contract};
6use crate::workspace_runtime_registry::{
7 WorkspaceRuntimeRegistryBoundaryV0, workspace_runtime_registry_contract,
8};
9use crate::{
10 CANCEL_REQUEST_METHOD, CASCADE_AT_POSITION_REQUEST, EXPLAIN_HOVER_TRACE_REQUEST,
11 EXPLAIN_REQUEST, NODE_TEXT_DOCUMENT_SYNC_KIND, STYLE_CONTEXT_INDEX_REQUEST,
12};
13use omena_tsgo_client::{OmenaTsgoClientBoundarySummaryV0, summarize_omena_tsgo_client_boundary};
14use serde::Serialize;
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct OmenaLspServerBoundarySummaryV0 {
19 pub schema_version: &'static str,
20 pub product: &'static str,
21 pub server_name: &'static str,
22 pub migration_status: &'static str,
23 pub transport_contract: &'static str,
24 pub trust_boundary: LspTrustBoundaryV0,
25 pub capabilities: OmenaLspServerCapabilitiesV0,
26 pub handler_surfaces: Vec<LspHandlerSurfaceV0>,
27 pub migration_phases: Vec<LspMigrationPhaseV0>,
28 pub blocking_work_policy: Vec<&'static str>,
29 pub tsgo_client_boundary: OmenaTsgoClientBoundarySummaryV0,
30 pub source_provider_adapter: SourceProviderDirectRustAdapterV0,
31 pub workspace_runtime_registry: WorkspaceRuntimeRegistryBoundaryV0,
32 pub diagnostics_scheduler: RustDiagnosticsSchedulerBoundaryV0,
33 pub query_reuse: RustQueryReuseBoundaryV0,
34 pub disk_diagnostics_cache: DiskDiagnosticsCacheBoundaryV0,
35 pub thin_client_endpoint: ThinClientEndpointV0,
36 pub multi_editor_distribution: MultiEditorDistributionV0,
37 pub node_parity_contracts: Vec<&'static str>,
38 pub next_decoupling_targets: Vec<&'static str>,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
42#[serde(rename_all = "camelCase")]
43pub struct LspTrustBoundaryV0 {
44 pub product: &'static str,
45 pub network_access: &'static str,
46 pub verification_owner: &'static str,
47 pub request_path_policy: Vec<&'static str>,
48 pub forbidden_runtime_capabilities: Vec<&'static str>,
49 pub disk_write_surfaces: Vec<&'static str>,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub struct OmenaLspServerCapabilitiesV0 {
59 pub text_document_sync: u8,
60 pub definition_provider: bool,
61 pub hover_provider: bool,
62 pub color_provider: bool,
63 pub completion_provider: CompletionProviderCapabilityV0,
64 pub code_action_provider: CodeActionProviderCapabilityV0,
65 pub references_provider: bool,
66 pub code_lens_provider: ResolveProviderCapabilityV0,
67 pub document_link_provider: ResolveProviderCapabilityV0,
68 pub workspace_symbol_provider: bool,
69 pub rename_provider: RenameProviderCapabilityV0,
70 pub workspace: WorkspaceCapabilityV0,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
74#[serde(rename_all = "camelCase")]
75pub struct CompletionProviderCapabilityV0 {
76 pub trigger_characters: Vec<&'static str>,
77 pub resolve_provider: bool,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
81#[serde(rename_all = "camelCase")]
82pub struct CodeActionProviderCapabilityV0 {
83 pub code_action_kinds: Vec<&'static str>,
84 pub resolve_provider: bool,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
88#[serde(rename_all = "camelCase")]
89pub struct ResolveProviderCapabilityV0 {
90 pub resolve_provider: bool,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
94#[serde(rename_all = "camelCase")]
95pub struct RenameProviderCapabilityV0 {
96 pub prepare_provider: bool,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct WorkspaceCapabilityV0 {
102 pub workspace_folders: WorkspaceFoldersCapabilityV0,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
106#[serde(rename_all = "camelCase")]
107pub struct WorkspaceFoldersCapabilityV0 {
108 pub supported: bool,
109 pub change_notifications: bool,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
113#[serde(rename_all = "camelCase")]
114pub struct LspHandlerSurfaceV0 {
115 pub method: &'static str,
116 pub node_owner: &'static str,
117 pub rust_owner_target: &'static str,
118 pub migration_state: &'static str,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
122#[serde(rename_all = "camelCase")]
123pub struct LspMigrationPhaseV0 {
124 pub phase: &'static str,
125 pub goal: &'static str,
126 pub exit_gate: &'static str,
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
130#[serde(rename_all = "camelCase")]
131pub struct ThinClientEndpointV0 {
132 pub product: &'static str,
133 pub endpoint_name: &'static str,
134 pub transport_contract: &'static str,
135 pub command_owner: &'static str,
136 pub standalone_package: &'static str,
137 pub split_repository: &'static str,
138 pub cargo_install_command: &'static str,
139 pub node_fallback_allowed: bool,
140 pub file_watcher_globs: Vec<&'static str>,
141 pub host_responsibilities: Vec<&'static str>,
142 pub rust_responsibilities: Vec<&'static str>,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
146#[serde(rename_all = "camelCase")]
147pub struct MultiEditorDistributionV0 {
148 pub product: &'static str,
149 pub owner: &'static str,
150 pub distribution_model: &'static str,
151 pub supported_editors: Vec<&'static str>,
152 pub install_surfaces: Vec<&'static str>,
153 pub documentation: Vec<&'static str>,
154 pub endpoint_policy: Vec<&'static str>,
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
158#[serde(rename_all = "camelCase")]
159pub struct SourceProviderDirectRustAdapterV0 {
160 pub product: &'static str,
161 pub candidate_owner: &'static str,
162 pub style_definition_owner: &'static str,
163 pub type_fact_owner: &'static str,
164 pub request_path_policy: Vec<&'static str>,
165 pub provider_surfaces: Vec<&'static str>,
166}
167
168pub fn summarize_omena_lsp_server_boundary() -> OmenaLspServerBoundarySummaryV0 {
169 OmenaLspServerBoundarySummaryV0 {
170 schema_version: "0",
171 product: "omena-lsp-server.boundary",
172 server_name: "omena-css",
173 migration_status: "rustStable",
174 transport_contract: "LSP stdio or IPC JSON-RPC",
175 trust_boundary: lsp_trust_boundary_contract(),
176 capabilities: current_node_lsp_capability_contract(),
177 handler_surfaces: lsp_handler_surfaces(),
178 migration_phases: lsp_migration_phases(),
179 blocking_work_policy: vec![
180 "noFullWorkspaceProgramOnRequestPath",
181 "queuedRequestCancellationBeforeProviderWork",
182 "dispatchedRequestCancellationAtCompletionBoundary",
183 "noMidComputationCancellationClaim",
184 "workerQueriesUseSnapshotReadView",
185 "tsgoProviderCancellationTokenBoundary",
186 "backgroundIndexAndTypeFactWarmup",
187 "staleOrUnresolvableFastReturn",
188 ],
189 tsgo_client_boundary: summarize_omena_tsgo_client_boundary(),
190 source_provider_adapter: source_provider_direct_rust_adapter_contract(),
191 workspace_runtime_registry: workspace_runtime_registry_contract(),
192 diagnostics_scheduler: rust_diagnostics_scheduler_contract(),
193 query_reuse: rust_query_reuse_contract(),
194 disk_diagnostics_cache: disk_diagnostics_cache_contract(),
195 thin_client_endpoint: thin_client_endpoint_contract(),
196 multi_editor_distribution: multi_editor_distribution_contract(),
197 node_parity_contracts: vec![
198 "initializeCapabilities",
199 "textDocumentSync",
200 "workspaceFolders",
201 "dynamicFileWatchers",
202 "diagnosticsPush",
203 "codeLensRefresh",
204 ],
205 next_decoupling_targets: vec![],
206 }
207}
208
209pub fn lsp_trust_boundary_contract() -> LspTrustBoundaryV0 {
210 LspTrustBoundaryV0 {
211 product: "omena-lsp-server.trust-boundary",
212 network_access: "neverFetch",
213 verification_owner: "omena-cli.lock-provenance",
214 request_path_policy: vec![
215 "analysisTimeUsesLocalWorkspaceOnly",
216 "lockAndSifEvidenceReadFromDisk",
217 "attestationVerificationOwnedByCli",
218 "noRegistryFetchOnLspRequestPath",
219 "noTransparencyLogLookupOnLspRequestPath",
220 "diskDiagnosticsCacheLocalWorkspaceWritesOnly",
221 ],
222 forbidden_runtime_capabilities: vec![
223 "registryHttpClient",
224 "sigstoreBundleVerifier",
225 "transparencyLogClient",
226 "socketNetworkIo",
227 ],
228 disk_write_surfaces: vec!["<workspaceFolder>/.cache/omena/**"],
229 }
230}
231
232pub fn source_provider_direct_rust_adapter_contract() -> SourceProviderDirectRustAdapterV0 {
233 SourceProviderDirectRustAdapterV0 {
234 product: "omena-lsp-server.source-provider-direct-rust-adapter",
235 candidate_owner: "omena-query/sourceSyntaxIndex",
236 style_definition_owner: "omena-query/styleHoverCandidates",
237 type_fact_owner: "omena-tsgo-client",
238 request_path_policy: vec![
239 "noNodeWorkspaceTypeResolverOnSourceProviderPath",
240 "buildQuerySourceSyntaxIndexOnDocumentChange",
241 "dedupeTargetAwareSourceCandidates",
242 "consumeQueryStyleHoverCandidates",
243 "consumeQuerySassModuleSources",
244 "consumeConfiguredPackageManifestPaths",
245 "consumeTsgoTypeFactsForTypedCxProjection",
246 "consumeSassPartialEvaluatorGeneratedSelectors",
247 "useOpenedDocumentIndexesBeforeWorkspaceFallback",
248 "unresolvedCandidatesRemainFastDiagnostics",
249 ],
250 provider_surfaces: vec![
251 "textDocument/hover",
252 "textDocument/definition",
253 "textDocument/references",
254 "textDocument/completion",
255 "textDocument/publishDiagnostics",
256 CASCADE_AT_POSITION_REQUEST,
257 STYLE_CONTEXT_INDEX_REQUEST,
258 EXPLAIN_HOVER_TRACE_REQUEST,
259 EXPLAIN_REQUEST,
260 ],
261 }
262}
263
264pub fn thin_client_endpoint_contract() -> ThinClientEndpointV0 {
265 ThinClientEndpointV0 {
266 product: "omena-lsp-server.thin-client-endpoint",
267 endpoint_name: "omena-css.thin-client-runtime-endpoint",
268 transport_contract: "LSP stdio JSON-RPC",
269 command_owner: "dist/bin/<platform>-<arch>/omena-lsp-server",
270 standalone_package: "omena-lsp-server",
271 split_repository: env!("CARGO_PKG_REPOSITORY"),
272 cargo_install_command: concat!(
273 "cargo install omena-lsp-server --version ",
274 env!("CARGO_PKG_VERSION")
275 ),
276 node_fallback_allowed: false,
277 file_watcher_globs: vec![
278 "**/*.module.{scss,css,less}",
279 "**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,d.ts,vue,html,svelte,astro,md,mdx,liquid,twig,njk,nunjucks,hbs,handlebars,erb,ejs,html.eex,heex}",
280 "**/tsconfig*.json",
281 "**/jsconfig*.json",
282 "**/package.json",
283 "**/vite.config.{ts,mts,cts,js,mjs,cjs}",
284 "**/webpack.config.{ts,mts,cts,js,mjs,cjs}",
285 ],
286 host_responsibilities: vec![
287 "resolvePackagedRustBinary",
288 "resolveStandaloneRustCommand",
289 "buildThinClientServerOptions",
290 "declareStaticDocumentSelector",
291 "startLanguageClient",
292 "registerStaticFileWatchers",
293 "translateShowReferencesArguments",
294 "renderHoverTracePanel",
295 "surfaceStartupErrors",
296 ],
297 rust_responsibilities: vec![
298 "ownLspLifecycle",
299 "ownWorkspaceState",
300 "ownDiagnosticsScheduling",
301 "ownProviderExecution",
302 "ownTsgoClientLifecycle",
303 ],
304 }
305}
306
307pub fn multi_editor_distribution_contract() -> MultiEditorDistributionV0 {
308 MultiEditorDistributionV0 {
309 product: "omena-lsp-server.multi-editor-distribution",
310 owner: "omena-lsp-server/distribution",
311 distribution_model: "standaloneRustLspServerWithThinEditorHosts",
312 supported_editors: vec!["vscode", "neovim", "zed"],
313 install_surfaces: vec![
314 "vsixBundledDistBinary",
315 "cargoInstallOmenaLspServer",
316 "repoLocalDistBin",
317 ],
318 documentation: vec![
319 "client/src/extension.ts",
320 "docs/clients/neovim.md",
321 "docs/clients/zed.md",
322 ],
323 endpoint_policy: vec![
324 "standaloneRustServerIsPrimaryMultiEditorEndpoint",
325 "nodeLspServerIsNotPrimaryEndpoint",
326 "editorClientsDoNotImplementProviderSemantics",
327 "editorsMayRunBesideNativeTypeScriptServer",
328 ],
329 }
330}
331
332pub fn current_node_lsp_capability_contract() -> OmenaLspServerCapabilitiesV0 {
333 OmenaLspServerCapabilitiesV0 {
334 text_document_sync: NODE_TEXT_DOCUMENT_SYNC_KIND,
335 definition_provider: true,
336 hover_provider: true,
337 color_provider: true,
338 completion_provider: CompletionProviderCapabilityV0 {
339 trigger_characters: vec!["'", "\"", "`", ",", ".", "$", "@", "-"],
340 resolve_provider: false,
341 },
342 code_action_provider: CodeActionProviderCapabilityV0 {
343 code_action_kinds: vec!["quickfix", "refactor.extract", "refactor.inline"],
344 resolve_provider: false,
345 },
346 references_provider: true,
347 code_lens_provider: ResolveProviderCapabilityV0 {
348 resolve_provider: false,
349 },
350 document_link_provider: ResolveProviderCapabilityV0 {
351 resolve_provider: false,
352 },
353 workspace_symbol_provider: true,
354 rename_provider: RenameProviderCapabilityV0 {
355 prepare_provider: true,
356 },
357 workspace: WorkspaceCapabilityV0 {
358 workspace_folders: WorkspaceFoldersCapabilityV0 {
359 supported: true,
360 change_notifications: true,
361 },
362 },
363 }
364}
365
366pub fn lsp_handler_surfaces() -> Vec<LspHandlerSurfaceV0> {
367 vec![
368 style_provider_handler("textDocument/definition"),
369 style_provider_handler("textDocument/hover"),
370 style_provider_handler("textDocument/completion"),
371 style_provider_handler("textDocument/codeAction"),
372 style_provider_handler("textDocument/references"),
373 style_provider_handler("textDocument/codeLens"),
374 style_provider_handler("textDocument/documentColor"),
375 style_provider_handler("textDocument/colorPresentation"),
376 style_provider_handler("textDocument/documentLink"),
377 style_provider_handler("workspace/symbol"),
378 style_provider_handler("textDocument/prepareRename"),
379 style_provider_handler("textDocument/rename"),
380 runtime_handler("initialized"),
381 runtime_handler("textDocument/didOpen"),
382 runtime_handler("textDocument/didChange"),
383 runtime_handler("textDocument/didClose"),
384 runtime_handler("workspace/didChangeWatchedFiles"),
385 runtime_handler("workspace/didChangeConfiguration"),
386 runtime_handler("workspace/didChangeWorkspaceFolders"),
387 diagnostics_handler("textDocument/publishDiagnostics"),
388 query_inspection_handler(CASCADE_AT_POSITION_REQUEST),
389 query_inspection_handler(STYLE_CONTEXT_INDEX_REQUEST),
390 query_inspection_handler(EXPLAIN_HOVER_TRACE_REQUEST),
391 query_inspection_handler(EXPLAIN_REQUEST),
392 runtime_handler(CANCEL_REQUEST_METHOD),
393 ]
394}
395
396fn style_provider_handler(method: &'static str) -> LspHandlerSurfaceV0 {
397 LspHandlerSurfaceV0 {
398 method,
399 node_owner: "server/lsp-server/src/providers",
400 rust_owner_target: "omena-lsp-server/providers/style-source",
401 migration_state: "providerParity",
402 }
403}
404
405fn runtime_handler(method: &'static str) -> LspHandlerSurfaceV0 {
406 LspHandlerSurfaceV0 {
407 method,
408 node_owner: "server/lsp-server/src/handler-registration.ts",
409 rust_owner_target: "omena-lsp-server/runtime",
410 migration_state: "implemented",
411 }
412}
413
414fn diagnostics_handler(method: &'static str) -> LspHandlerSurfaceV0 {
415 LspHandlerSurfaceV0 {
416 method,
417 node_owner: "server/lsp-server/src/diagnostics-scheduler.ts",
418 rust_owner_target: "omena-lsp-server/diagnostics",
419 migration_state: "implemented",
420 }
421}
422
423fn query_inspection_handler(method: &'static str) -> LspHandlerSurfaceV0 {
424 LspHandlerSurfaceV0 {
425 method,
426 node_owner: "server/lsp-server/src/query-inspection",
427 rust_owner_target: "omena-lsp-server/query-inspection",
428 migration_state: "implemented",
429 }
430}
431
432pub fn lsp_migration_phases() -> Vec<LspMigrationPhaseV0> {
433 vec![
434 LspMigrationPhaseV0 {
435 phase: "phase-0-boundary",
436 goal: "declare Rust LSP capability and handler parity with the Node server",
437 exit_gate: "rust/omena-lsp-server/boundary",
438 },
439 LspMigrationPhaseV0 {
440 phase: "phase-1-shell",
441 goal: "own initialize, shutdown, text sync, workspace folders, and watcher state in Rust",
442 exit_gate: "rust/omena-lsp-server/runtime-loop",
443 },
444 LspMigrationPhaseV0 {
445 phase: "phase-2-style-providers",
446 goal: "serve style-side hover, definition, references, diagnostics, and code lens from Rust",
447 exit_gate: "rust/omena-lsp-server/provider-parity",
448 },
449 LspMigrationPhaseV0 {
450 phase: "phase-3-source-providers",
451 goal: "replace Node WorkspaceTypeResolver hot path with a long-lived tsgo client and Rust query runtime",
452 exit_gate: "rust/omena-tsgo-client/boundary",
453 },
454 LspMigrationPhaseV0 {
455 phase: "phase-4-thin-client",
456 goal: "shrink the VS Code extension to UI commands and Rust LSP process orchestration",
457 exit_gate: "rust/omena-lsp-server/thin-client-boundary",
458 },
459 ]
460}