1use std::borrow::Borrow;
18use std::path::PathBuf;
19
20use serde::de::{self, Deserialize, Deserializer};
21use serde::ser::{self, Serialize, Serializer};
22use serde_json::{self, Value};
23
24use super::PluginPid;
25use crate::annotations::AnnotationType;
26use crate::config::Table;
27use crate::syntax::LanguageId;
28use crate::tabs::{BufferIdentifier, ViewId};
29use xi_rope::{LinesMetric, Rope, RopeDelta};
30use xi_rpc::RemoteError;
31
32#[derive(Serialize, Deserialize, Debug, Clone)]
42pub struct PluginBufferInfo {
43 pub buffer_id: BufferIdentifier,
45 pub views: Vec<ViewId>,
47 pub rev: u64,
48 pub buf_size: usize,
49 pub nb_lines: usize,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub path: Option<String>,
52 pub syntax: LanguageId,
53 pub config: Table,
54}
55
56#[derive(Serialize, Deserialize, Debug)]
60pub struct ClientPluginInfo {
61 pub name: String,
62 pub running: bool,
63}
64
65#[derive(Serialize, Deserialize, Debug, Clone)]
67pub struct PluginUpdate {
68 pub view_id: ViewId,
69 pub delta: Option<RopeDelta>,
75 pub new_len: usize,
77 pub new_line_count: usize,
79 pub rev: u64,
80 pub undo_group: Option<usize>,
86 pub edit_type: String,
87 pub author: String,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct EmptyStruct {}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
94#[serde(rename_all = "snake_case")]
95#[serde(tag = "method", content = "params")]
96pub enum HostRequest {
98 Update(PluginUpdate),
99 CollectTrace(EmptyStruct),
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(rename_all = "snake_case")]
104#[serde(tag = "method", content = "params")]
105pub enum HostNotification {
107 Ping(EmptyStruct),
108 Initialize { plugin_id: PluginPid, buffer_info: Vec<PluginBufferInfo> },
109 DidSave { view_id: ViewId, path: PathBuf },
110 ConfigChanged { view_id: ViewId, changes: Table },
111 NewBuffer { buffer_info: Vec<PluginBufferInfo> },
112 DidClose { view_id: ViewId },
113 GetHover { view_id: ViewId, request_id: usize, position: usize },
114 Shutdown(EmptyStruct),
115 TracingConfig { enabled: bool },
116 LanguageChanged { view_id: ViewId, new_lang: LanguageId },
117 CustomCommand { view_id: ViewId, method: String, params: Value },
118}
119
120#[derive(Serialize, Deserialize, Debug, Clone)]
126pub struct PluginEdit {
127 pub rev: u64,
128 pub delta: RopeDelta,
129 pub priority: u64,
132 pub after_cursor: bool,
134 pub undo_group: Option<usize>,
137 pub author: String,
138}
139
140#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
141pub struct ScopeSpan {
142 pub start: usize,
143 pub end: usize,
144 pub scope_id: u32,
145}
146
147#[derive(Serialize, Deserialize, Debug, Clone)]
148pub struct DataSpan {
149 pub start: usize,
150 pub end: usize,
151 pub data: Value,
152}
153
154#[derive(Debug, Serialize, Deserialize)]
156pub struct GetDataResponse {
157 pub chunk: String,
158 pub offset: usize,
159 pub first_line: usize,
160 pub first_line_offset: usize,
161}
162
163#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
165#[serde(rename_all = "snake_case")]
166pub enum TextUnit {
167 Utf8,
170 Line,
173}
174
175#[derive(Serialize, Deserialize, Debug, Clone)]
176#[serde(rename_all = "snake_case")]
177#[serde(tag = "method", content = "params")]
178pub enum PluginRequest {
180 GetData { start: usize, unit: TextUnit, max_size: usize, rev: u64 },
181 LineCount,
182 GetSelections,
183}
184
185#[derive(Serialize, Deserialize, Debug, Clone)]
186#[serde(rename_all = "snake_case")]
187#[serde(tag = "method", content = "params")]
188pub enum PluginNotification {
190 AddScopes {
191 scopes: Vec<Vec<String>>,
192 },
193 UpdateSpans {
194 start: usize,
195 len: usize,
196 spans: Vec<ScopeSpan>,
197 rev: u64,
198 },
199 Edit {
200 edit: PluginEdit,
201 },
202 Alert {
203 msg: String,
204 },
205 AddStatusItem {
206 key: String,
207 value: String,
208 alignment: String,
209 },
210 UpdateStatusItem {
211 key: String,
212 value: String,
213 },
214 RemoveStatusItem {
215 key: String,
216 },
217 ShowHover {
218 request_id: usize,
219 result: Result<Hover, RemoteError>,
220 },
221 UpdateAnnotations {
222 start: usize,
223 len: usize,
224 spans: Vec<DataSpan>,
225 annotation_type: AnnotationType,
226 rev: u64,
227 },
228}
229
230#[derive(Serialize, Deserialize, Debug, Clone)]
233#[serde(rename_all = "snake_case")]
234pub struct Range {
235 pub start: usize,
236 pub end: usize,
237}
238
239#[derive(Serialize, Deserialize, Debug, Clone)]
241#[serde(rename_all = "snake_case")]
242pub struct Hover {
243 pub content: String,
244 pub range: Option<Range>,
245}
246
247pub struct PluginCommand<T> {
249 pub view_id: ViewId,
250 pub plugin_id: PluginPid,
251 pub cmd: T,
252}
253
254impl<T: Serialize> Serialize for PluginCommand<T> {
255 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: Serializer,
258 {
259 let mut v = serde_json::to_value(&self.cmd).map_err(ser::Error::custom)?;
260 v["params"]["view_id"] = json!(self.view_id);
261 v["params"]["plugin_id"] = json!(self.plugin_id);
262 v.serialize(serializer)
263 }
264}
265
266impl<'de, T: Deserialize<'de>> Deserialize<'de> for PluginCommand<T> {
267 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
268 where
269 D: Deserializer<'de>,
270 {
271 #[derive(Deserialize)]
272 struct InnerIds {
273 view_id: ViewId,
274 plugin_id: PluginPid,
275 }
276 #[derive(Deserialize)]
277 struct IdsWrapper {
278 params: InnerIds,
279 }
280
281 let v = Value::deserialize(deserializer)?;
282 let helper = IdsWrapper::deserialize(&v).map_err(de::Error::custom)?;
283 let InnerIds { view_id, plugin_id } = helper.params;
284 let cmd = T::deserialize(v).map_err(de::Error::custom)?;
285 Ok(PluginCommand { view_id, plugin_id, cmd })
286 }
287}
288
289impl PluginBufferInfo {
290 pub fn new(
291 buffer_id: BufferIdentifier,
292 views: &[ViewId],
293 rev: u64,
294 buf_size: usize,
295 nb_lines: usize,
296 path: Option<PathBuf>,
297 syntax: LanguageId,
298 config: Table,
299 ) -> Self {
300 let path = path.map(|p| p.to_str().unwrap().to_owned());
302 let views = views.to_owned();
303 PluginBufferInfo { buffer_id, views, rev, buf_size, nb_lines, path, syntax, config }
304 }
305}
306
307impl PluginUpdate {
308 pub fn new<D>(
309 view_id: ViewId,
310 rev: u64,
311 delta: D,
312 new_len: usize,
313 new_line_count: usize,
314 undo_group: Option<usize>,
315 edit_type: String,
316 author: String,
317 ) -> Self
318 where
319 D: Into<Option<RopeDelta>>,
320 {
321 let delta = delta.into();
322 PluginUpdate { view_id, delta, new_len, new_line_count, rev, undo_group, edit_type, author }
323 }
324}
325
326impl TextUnit {
329 pub fn resolve_offset<T: Borrow<Rope>>(self, text: T, offset: usize) -> Option<usize> {
332 let text = text.borrow();
333 match self {
334 TextUnit::Utf8 => {
335 if offset > text.len() {
336 None
337 } else {
338 text.at_or_prev_codepoint_boundary(offset)
339 }
340 }
341 TextUnit::Line => {
342 let max_line_number = text.measure::<LinesMetric>() + 1;
343 if offset > max_line_number {
344 None
345 } else {
346 text.offset_of_line(offset).into()
347 }
348 }
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356 use serde_json;
357
358 #[test]
359 fn test_plugin_update() {
360 let json = r#"{
361 "view_id": "view-id-42",
362 "delta": {"base_len": 6, "els": [{"copy": [0,5]}, {"insert":"rofls"}, {"copy": [5,6]}]},
363 "new_len": 11,
364 "new_line_count": 1,
365 "rev": 5,
366 "undo_group": 6,
367 "edit_type": "something",
368 "author": "me"
369 }"#;
370
371 let val: PluginUpdate = match serde_json::from_str(json) {
372 Ok(val) => val,
373 Err(err) => panic!("{:?}", err),
374 };
375 assert!(val.delta.is_some());
376 assert!(val.delta.unwrap().as_simple_insert().is_some());
377 }
378
379 #[test]
380 fn test_deserde_init() {
381 let json = r#"
382 {"buffer_id": 42,
383 "views": ["view-id-4"],
384 "rev": 1,
385 "buf_size": 20,
386 "nb_lines": 5,
387 "path": "some_path",
388 "syntax": "toml",
389 "config": {"some_key": 420}}"#;
390
391 let val: PluginBufferInfo = match serde_json::from_str(json) {
392 Ok(val) => val,
393 Err(err) => panic!("{:?}", err),
394 };
395 assert_eq!(val.rev, 1);
396 assert_eq!(val.path, Some("some_path".to_owned()));
397 assert_eq!(val.syntax, "toml".into());
398 }
399
400 #[test]
401 fn test_de_plugin_rpc() {
402 let json = r#"{"method": "alert", "params": {"view_id": "view-id-1", "plugin_id": 42, "msg": "ahhh!"}}"#;
403 let de: PluginCommand<PluginNotification> = serde_json::from_str(json).unwrap();
404 assert_eq!(de.view_id, ViewId(1));
405 assert_eq!(de.plugin_id, PluginPid(42));
406 match de.cmd {
407 PluginNotification::Alert { ref msg } if msg == "ahhh!" => (),
408 _ => panic!("{:?}", de.cmd),
409 }
410 }
411}