Skip to main content

omena_lsp_server/
boundary.rs

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    /// Declared local-disk write surfaces. The LSP historically wrote NO
50    /// files; the disk diagnostics cache (RFC 0009 Pillar C, rfcs#66) is the
51    /// first declared write surface. Local workspace disk only — this does
52    /// not weaken the `neverFetch` network invariant.
53    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}