1use ::serde::{Deserialize, Serialize};
2use gpui::{App, Entity, WeakEntity};
3use language::Buffer;
4use language::{File as _, LocalFile as _};
5use lsp::{DidCloseTextDocumentParams, DidOpenTextDocumentParams, LanguageServer};
6use util::ResultExt as _;
7
8use crate::{LspStore, Project};
9
10// https://github.com/microsoft/vscode/blob/main/extensions/json-language-features/server/README.md#schema-associations-notification
11struct SchemaAssociationsNotification {}
12
13/// interface ISchemaAssociation {
14/// /**
15/// * The URI of the schema, which is also the identifier of the schema.
16/// */
17/// uri: string;
18///
19/// /**
20/// * A list of file path patterns that are associated to the schema. The '*' wildcard can be used. Exclusion patterns starting with '!'.
21/// * For example '*.schema.json', 'package.json', '!foo*.schema.json'.
22/// * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
23/// */
24/// fileMatch: string[];
25/// /**
26/// * If provided, the association is only used if the validated document is located in the given folder (directly or in a subfolder)
27/// */
28/// folderUri?: string;
29/// /*
30/// * The schema for the given URI.
31/// * If no schema is provided, the schema will be fetched with the schema request service (if available).
32/// */
33/// schema?: JSONSchema;
34/// }
35#[derive(Serialize, Deserialize, Debug, Clone)]
36#[serde(rename_all = "camelCase")]
37pub struct SchemaAssociation {
38 pub uri: String,
39 pub file_match: Vec<String>,
40 pub folder_uri: Option<String>,
41 pub schema: Option<serde_json::Value>,
42}
43
44impl lsp::notification::Notification for SchemaAssociationsNotification {
45 type Params = Vec<SchemaAssociation>;
46 const METHOD: &'static str = "json/schemaAssociations";
47}
48
49pub fn send_schema_associations_notification(
50 project: Entity<Project>,
51 buffer: Entity<Buffer>,
52 schema_associations: &Vec<SchemaAssociation>,
53 cx: &mut App,
54) {
55 let lsp_store = project.read(cx).lsp_store();
56 lsp_store.update(cx, |lsp_store, cx| {
57 let Some(local) = lsp_store.as_local_mut() else {
58 return;
59 };
60 buffer.update(cx, |buffer, cx| {
61 for (adapter, server) in local
62 .language_servers_for_buffer(buffer, cx)
63 .map(|(a, b)| (a.clone(), b.clone()))
64 .collect::<Vec<_>>()
65 {
66 if dbg!(!adapter.adapter.is_primary_zed_json_schema_adapter()) {
67 continue;
68 }
69
70 server
71 .notify::<SchemaAssociationsNotification>(schema_associations)
72 .log_err(); // todo! don't ignore error
73
74 let file = match worktree::File::from_dyn(buffer.file()) {
75 Some(file) => file,
76 None => continue,
77 };
78 let language = match buffer.language() {
79 Some(language) => language,
80 None => continue,
81 };
82 let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
83
84 let versions = local
85 .buffer_snapshots
86 .entry(buffer.remote_id())
87 .or_default()
88 .entry(server.server_id())
89 // .and_modify(|_| {
90 // assert!(
91 // false,
92 // "There should not be an existing snapshot for a newly inserted buffer"
93 // )
94 // })
95 .or_insert_with(|| {
96 vec![crate::lsp_store::LspBufferSnapshot {
97 version: 0,
98 snapshot: buffer.text_snapshot(),
99 }]
100 });
101
102 let snapshot = versions.last().unwrap();
103 let version = snapshot.version;
104 let initial_snapshot = &snapshot.snapshot;
105
106 // if file.worktree.read(cx).id() != key.0
107 // || !self
108 // .languages
109 // .lsp_adapters(&language.name())
110 // .iter()
111 // .any(|a| a.name == key.1)
112 // {
113 // continue;
114 // }
115
116 // didOpen
117 let file = match file.as_local() {
118 Some(file) => file,
119 None => continue,
120 };
121 let Some(_) = server
122 .notify::<lsp::notification::DidCloseTextDocument>(
123 &DidCloseTextDocumentParams {
124 text_document: lsp::TextDocumentIdentifier { uri: uri.clone() },
125 },
126 )
127 .log_err()
128 else {
129 continue;
130 };
131
132 let initial_text = buffer.text();
133
134 server
135 .notify::<lsp::notification::DidOpenTextDocument>(&DidOpenTextDocumentParams {
136 text_document: lsp::TextDocumentItem::new(
137 uri,
138 adapter.language_id(&language.name()),
139 version,
140 initial_text,
141 ),
142 })
143 .log_err();
144 }
145 })
146 })
147}